[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/docs/contributor/ -> rendering_html.diviner (source)

   1  @title Rendering HTML
   2  @group developer
   3  
   4  Rendering HTML in the Phabricator environment.
   5  
   6  = Overview =
   7  
   8  Phabricator attempts to prevent XSS by treating strings as default-unsafe when
   9  rendering. This means that if you try to build HTML through string
  10  concatenation, it won't work: the string will be escaped by the rendering
  11  pipeline, and the browser will treat it as plain text, not HTML.
  12  
  13  This document describes the right way to build HTML components so they are safe
  14  from XSS and render correctly. Broadly:
  15  
  16    - Use @{function@libphutil:phutil_tag} (and @{function:javelin_tag}) to build
  17      tags.
  18    - Use @{function@libphutil:hsprintf} where @{function@libphutil:phutil_tag}
  19      is awkward.
  20    - Combine elements with arrays, not string concatenation.
  21    - @{class:AphrontView} subclasses should return a
  22      @{class@libphutil:PhutilSafeHTML} object from their `render()` method.
  23    - @{class:AphrontView} subclasses act like tags when rendering.
  24    - @{function:pht} has some special rules.
  25    - There are some other things that you should be aware of.
  26  
  27  See below for discussion.
  28  
  29  = Building Tags: phutil_tag() =
  30  
  31  Build HTML tags with @{function@libphutil:phutil_tag}. For example:
  32  
  33    phutil_tag(
  34      'div',
  35      array(
  36        'class' => 'some-class',
  37      ),
  38      $content);
  39  
  40  @{function@libphutil:phutil_tag} will properly escape the content and all the
  41  attributes, and return a @{class@libphutil:PhutilSafeHTML} object. The rendering
  42  pipeline knows that this object represents a properly escaped HTML tag. This
  43  allows @{function@libphutil:phutil_tag} to render tags with other tags as
  44  content correctly (without double-escaping):
  45  
  46    phutil_tag(
  47      'div',
  48      array(),
  49      phutil_tag(
  50        'strong',
  51        array(),
  52        $content));
  53  
  54  In Phabricator, the @{function:javelin_tag} function is similar to
  55  @{function@libphutil:phutil_tag}, but provides special handling for the
  56  `sigil` and `meta` attributes.
  57  
  58  = Building Blocks: hsprintf() =
  59  
  60  Sometimes, @{function@libphutil:phutil_tag} can be particularly awkward to
  61  use. You can use @{function@libphutil:hsprintf} to build larger and more
  62  complex blocks of HTML, when @{function@libphutil:phutil_tag} is a poor fit.
  63  @{function:hsprintf} has `sprintf()` semantics, but `%s` escapes HTML:
  64  
  65    // Safely build fragments or unwieldy blocks.
  66    hsprintf(
  67      '<div id="%s">',
  68      $div_id);
  69  
  70  @{function:hsprintf} can be especially useful when:
  71  
  72    - You need to build a block with a lot of tags, like a table with rows and
  73      cells.
  74    - You need to build part of a tag (usually you should avoid this, but if you
  75      do need to, @{function@libphutil:phutil_tag} can not do it).
  76  
  77  Note that it is unsafe to provide any user-controlled data to the first
  78  parameter of @{function@libphutil:hsprintf} (the `sprintf()`-style pattern).
  79  
  80  Like @{function@libphutil:phutil_tag}, this function returns a
  81  @{class@libphutil:PhutilSafeHTML} object.
  82  
  83  = Composing Tags =
  84  
  85  When you are building a view which combines smaller components, like a section
  86  with a header and a body:
  87  
  88    $header = phutil_tag('h1', ...);
  89    $body = phutil_tag('p', ...);
  90  
  91  ...you should NOT use string concatenation:
  92  
  93    COUNTEREXAMPLE
  94    // Not dangerous, but does the wrong thing.
  95    phutil_tag('div', array(), $header.$body);
  96  
  97  Instead, use an array:
  98  
  99    // Render a tag containing other tags safely.
 100    phutil_tag('div', array(), array($header, $body));
 101  
 102  If you concatenate @{class@libphutil:PhutilSafeHTML} objects, they revert to
 103  normal strings and are no longer marked as properly escaped tags.
 104  
 105  (In the future, these objects may stop converting to strings, but for now they
 106  must to maintain backward compatibility.)
 107  
 108  If you need to build a list of items with some element in between each of them
 109  (like a middot, comma, or vertical bar) you can use
 110  @{function:phutil_implode_html}:
 111  
 112    // Render links with commas between them.
 113    phutil_tag(
 114      'div',
 115      array(),
 116      phutil_implode_html(', ', $list_of_links));
 117  
 118  = AphrontView Classes =
 119  
 120  Subclasses of @{class:AphrontView} in Phabricator should return a
 121  @{class@libphutil:PhutilSafeHTML} object. The easiest way to do this is to
 122  return `phutil_tag()` or `javelin_tag()`:
 123  
 124    return phutil_tag('div', ...);
 125  
 126  You can use an @{class:AphrontView} subclass like you would a tag:
 127  
 128    phutil_tag('div', array(), $view);
 129  
 130  = Internationalization: pht() =
 131  
 132  The @{function:pht} function has some special rules. If any input to
 133  @{function:pht} is a @{class@libphutil:PhutilSafeHTML} object, @{function:pht}
 134  returns a @{class@libphutil:PhutilSafeHTML} object itself. Otherwise, it returns
 135  normal text.
 136  
 137  This is generally safe because translations are not permitted to have more tags
 138  than the original text did (so if the original text had no tags, translations
 139  can not add any).
 140  
 141  Normally, this just means that @{function:pht} does the right thing and behaves
 142  like you would expect, but it is worth being aware of.
 143  
 144  = Special Cases =
 145  
 146  NOTE: This section describes dangerous methods which can bypass XSS protections.
 147  If possible, do not use them.
 148  
 149  You can build @{class@libphutil:PhutilSafeHTML} out of a string explicitly by
 150  calling @{function:phutil_safe_html} on it. This is **dangerous**, because if
 151  you are wrong and the string is not actually safe, you have introduced an XSS
 152  vulnerability. Consequently, you should avoid calling this if possible.
 153  
 154  You can use @{function@libphutil:phutil_escape_html_newlines} to escape HTML
 155  while converting newlines to `<br />`. You should not need to explicitly use
 156  @{function@libphutil:phutil_escape_html} anywhere.
 157  
 158  If you need to apply a string function (such as `trim()`) to safe HTML, use
 159  @{method@libphutil:PhutilSafeHTML::applyFunction}.
 160  
 161  If you need to extract the content of a @{class@libphutil:PhutilSafeHTML}
 162  object, you should call `getHTMLContent()`, not cast it to a string. Eventually,
 163  we would like to remove the string cast entirely.
 164  
 165  Functions @{function@libphutil:phutil_tag} and @{function@libphutil:hsprintf}
 166  are not safe if you pass the user input for the tag or attribute name. All the
 167  following examples are dangerous:
 168  
 169    counterexample
 170    phutil_tag($evil);
 171  
 172    phutil_tag('span', array($evil => $evil2));
 173  
 174    phutil_tag('span', array('onmouseover' => $evil));
 175  
 176    // Use PhutilURI to check if $evil is valid HTTP link.
 177    hsprintf('<a href="%s">', $evil);
 178  
 179    hsprintf('<%s>%s</%s>', $evil, $evil2, $evil);
 180  
 181    // We have a lint rule disallowing this.
 182    hsprintf($evil);


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1