[wp-trac] [WordPress Trac] #50867: An API which encourages automatic escaping of HTML

WordPress Trac noreply at wordpress.org
Thu Aug 6 08:37:59 UTC 2020


#50867: An API which encourages automatic escaping of HTML
-------------------------------------------------+-------------------------
 Reporter:  noisysocks                           |       Owner:  (none)
     Type:  enhancement                          |      Status:  new
 Priority:  normal                               |   Milestone:  Awaiting
                                                 |  Review
Component:  General                              |     Version:
 Severity:  normal                               |  Resolution:
 Keywords:  has-patch needs-unit-tests needs-    |     Focuses:
  docs                                           |
-------------------------------------------------+-------------------------
Description changed by noisysocks:

Old description:

> It's common in WordPress to write PHP code that assembles a large bit of
> HTML using conditional logic. A good example of this is
> [https://github.com/WordPress/gutenberg/blob/master/packages/block-
> library/src/navigation-link/index.php#L106
> render_block_core_navigation_link]. Unfortunately this type of code can
> become difficult to read and error prone. For example, we've had several
> reported XSS vulnerabilities in code like this.
>
> How do we feel about adding an API for building large bits of HTML?
>
> Attached is a patch which implements an API inspired by `createElement`
> in `@wordpress/element` and the external `classnames` JavaScript library.
>
> The primary interface is `wp_el`. It takes three arguments: an HTML tag
> name, an array of HTML attributes, and an array of child elements. You
> can nest calls to `wp_el` within each other to cleanly create deeply
> nested HTML.
>
> {{{#!php
> <?php
> echo wp_el(
>         'figure',
>         array( 'class' => 'my-image' ),
>         array(
>                 wp_el( 'img', array( 'src' =>
> 'https://pbs.twimg.com/media/Ed_W9VQXkAAtYQY?format=jpg&name=medium' ) ),
>                 wp_el(
>                         'figcaption',
>                         null,
>                         'A cold refreshing glass of pilk'
>                 ),
>         )
> );
> }}}
>
> {{{
> <figure class="my-image"><img
> src="https://pbs.twimg.com/media/Ed_W9VQXkAAtYQY?format=jpg&name=medium"
> /><figcaption>A cold refreshing glass of pilk</figcaption></figure>
> }}}
>
> Optional arguments and automatic handling of non associative array values
> can be used to make usage quite succinct.
>
> {{{#!php
> <?php
> echo wp_el( 'hr' );
> echo wp_el( 'input', 'required' );
> }}}
>
> {{{
> <hr /><input required />
> }}}
>
> The key design detail is that **all strings are automatically escaped**.
> If you want to output unescaped HTML you have to do it explicitly.
>
> {{{#!php
> <?php
> echo wp_el(
>         'article',
>         null,
>         wp_dangerous_html( '<marquee>Unescaped HTML</marquee>' )
> );
> }}}
>
> {{{
> <article><marquee>Unescaped HTML</marquee></article>
> }}}
>
> Lastly, `wp_classnames` provides a convenient way to assemble HTML
> `class` attributes.
>
> {{{#!php
> <?php
> echo wp_el(
>         'li',
>         array(
>                 'class' => wp_classnames(
>                         array(
>                                 'my-link',
>                                 'is-current' => $post->ID === $id,
>                         )
>                 ),
>         ),
>         array(
>                 wp_el(
>                         'a',
>                         array(
>                                 'href' => $post->guid,
>                         ),
>                         $post->post_name
>                 ),
>         )
> );
> }}}
>
> {{{
> <li class="my-link"><a href="http://localhost:8888/?p=215">navigation-
> stored-in-old-way</a></li>
> }}}
>
> Thoughts? Are there alternative approaches common in the PHP ecosystem?
> Does such an API belong in Core?

New description:

 It's common in WordPress to write PHP code that assembles a large bit of
 HTML using conditional logic. A good example of this is
 [https://github.com/WordPress/gutenberg/blob/master/packages/block-
 library/src/navigation-link/index.php#L106
 render_block_core_navigation_link]. Unfortunately this type of code can
 become difficult to read and error prone. For example, we've had several
 reported XSS vulnerabilities in code like this.

 How do we feel about adding an API for **safely** building large bits of
 HTML?

 Attached is a patch which implements an API inspired by `createElement` in
 `@wordpress/element` and the external `classnames` JavaScript library.

 The primary interface is `wp_el`. It takes three arguments: an HTML tag
 name, an array of HTML attributes, and an array of child elements. You can
 nest calls to `wp_el` within each other to cleanly create deeply nested
 HTML.

 {{{#!php
 <?php
 echo wp_el(
         'figure',
         array( 'class' => 'my-image' ),
         array(
                 wp_el( 'img', array( 'src' =>
 'https://pbs.twimg.com/media/Ed_W9VQXkAAtYQY?format=jpg&name=medium' ) ),
                 wp_el(
                         'figcaption',
                         null,
                         'A cold refreshing glass of pilk'
                 ),
         )
 );
 }}}

 {{{
 <figure class="my-image"><img
 src="https://pbs.twimg.com/media/Ed_W9VQXkAAtYQY?format=jpg&name=medium"
 /><figcaption>A cold refreshing glass of pilk</figcaption></figure>
 }}}

 Optional arguments and automatic handling of non associative array values
 can be used to make usage quite succinct.

 {{{#!php
 <?php
 echo wp_el( 'hr' );
 echo wp_el( 'input', 'required' );
 }}}

 {{{
 <hr /><input required />
 }}}

 **The key design detail is that all strings are automatically escaped**.
 If you want to output unescaped HTML you have to do it explicitly.

 {{{#!php
 <?php
 echo wp_el(
         'article',
         null,
         wp_dangerous_html( '<marquee>Unescaped HTML</marquee>' )
 );
 }}}

 {{{
 <article><marquee>Unescaped HTML</marquee></article>
 }}}

 Lastly, `wp_classnames` provides a convenient way to assemble HTML `class`
 attributes.

 {{{#!php
 <?php
 echo wp_el(
         'li',
         array(
                 'class' => wp_classnames(
                         array(
                                 'my-link',
                                 'is-current' => $post->ID === $id,
                         )
                 ),
         ),
         array(
                 wp_el(
                         'a',
                         array(
                                 'href' => $post->guid,
                         ),
                         $post->post_name
                 ),
         )
 );
 }}}

 {{{
 <li class="my-link"><a href="http://localhost:8888/?p=215">navigation-
 stored-in-old-way</a></li>
 }}}

 Thoughts? Are there alternative approaches common in the PHP ecosystem?
 Does such an API belong in Core? What other approaches can we take to
 prevent unescaped strings from being output?

--

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/50867#comment:2>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list