WordPress.org

Make WordPress Core

Ticket #29807: 29807.8.diff

File 29807.8.diff, 9.0 KB (added by 1000camels, 14 months ago)

Creates specific function to handle this case of potential uri candidates in certain attributes

  • src/wp-includes/kses.php

    diff --git src/wp-includes/kses.php src/wp-includes/kses.php
    index 20790d1c44..30f6f9b2b7 100644
    if ( ! CUSTOM_TAGS ) { 
    239239                        'longdesc' => true,
    240240                        'vspace'   => true,
    241241                        'src'      => true,
     242                        'srcset'   => true,
    242243                        'usemap'   => true,
    243244                        'width'    => true,
    244245                ),
    if ( ! CUSTOM_TAGS ) { 
    276277                        'lang'     => true,
    277278                        'xml:lang' => true,
    278279                ),
     280                'picture'       => array(),
    279281                'pre'        => array(
    280282                        'width' => true,
    281283                ),
    if ( ! CUSTOM_TAGS ) { 
    297299                        'xml:lang' => true,
    298300                ),
    299301                'small'      => array(),
     302                'source'          => array(
     303                        'srcset'  => true,
     304                        'type'    => true,
     305                        'media'   => true,
     306                        'sizes'   => true,
     307                ),
    300308                'strike'     => array(),
    301309                'strong'     => array(),
    302310                'sub'        => array(),
    function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) { 
    756764 * @return string Filtered attribute.
    757765 */
    758766function wp_kses_one_attr( $string, $element ) {
    759         $uris              = array( 'xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action' );
    760767        $allowed_html      = wp_kses_allowed_html( 'post' );
    761768        $allowed_protocols = wp_allowed_protocols();
    762769        $string            = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
    function wp_kses_one_attr( $string, $element ) { 
    799806                $value = esc_attr( $value );
    800807
    801808                // Sanitize URI values.
    802                 if ( in_array( strtolower( $name ), $uris ) ) {
    803                         $value = wp_kses_bad_protocol( $value, $allowed_protocols );
    804                 }
     809                $value = wp_kses_sanitize_uris( $name, $value, $allowed_protocols );
    805810
    806811                $string = "$name=$quote$value$quote";
    807812                $vless  = 'n';
    function wp_kses_hair( $attr, $allowed_protocols ) { 
    11401145        $attrarr  = array();
    11411146        $mode     = 0;
    11421147        $attrname = '';
    1143         $uris     = array( 'xmlns', 'profile', 'href', 'src', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action' );
    11441148
    11451149        // Loop through the whole attribute list
    11461150
    function wp_kses_hair( $attr, $allowed_protocols ) { 
    11851189                                if ( preg_match( '%^"([^"]*)"(\s+|/?$)%', $attr, $match ) ) {
    11861190                                        // "value"
    11871191                                        $thisval = $match[1];
    1188                                         if ( in_array( strtolower( $attrname ), $uris ) ) {
    1189                                                 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
    1190                                         }
     1192                                       
     1193                                        // Sanitize URI values.
     1194                                        $thisval = wp_kses_sanitize_uris( $attrname, $thisval, $allowed_protocols );
    11911195
    11921196                                        if ( false === array_key_exists( $attrname, $attrarr ) ) {
    11931197                                                $attrarr[ $attrname ] = array(
    function wp_kses_hair( $attr, $allowed_protocols ) { 
    12061210                                if ( preg_match( "%^'([^']*)'(\s+|/?$)%", $attr, $match ) ) {
    12071211                                        // 'value'
    12081212                                        $thisval = $match[1];
    1209                                         if ( in_array( strtolower( $attrname ), $uris ) ) {
    1210                                                 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
    1211                                         }
     1213
     1214                                        // Sanitize URI values.
     1215                                        $thisval = wp_kses_sanitize_uris( $attrname, $thisval, $allowed_protocols );
    12121216
    12131217                                        if ( false === array_key_exists( $attrname, $attrarr ) ) {
    12141218                                                $attrarr[ $attrname ] = array(
    function wp_kses_hair( $attr, $allowed_protocols ) { 
    12271231                                if ( preg_match( "%^([^\s\"']+)(\s+|/?$)%", $attr, $match ) ) {
    12281232                                        // value
    12291233                                        $thisval = $match[1];
    1230                                         if ( in_array( strtolower( $attrname ), $uris ) ) {
    1231                                                 $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
    1232                                         }
     1234
     1235                                        // Sanitize URI values.
     1236                                        $thisval = wp_kses_sanitize_uris( $attrname, $thisval, $allowed_protocols );
    12331237
    12341238                                        if ( false === array_key_exists( $attrname, $attrarr ) ) {
    12351239                                                $attrarr[ $attrname ] = array(
    function wp_kses_hair( $attr, $allowed_protocols ) { 
    12681272        return $attrarr;
    12691273}
    12701274
     1275/**
     1276 * Santizes uris in attributes
     1277 *
     1278 * This condenses code that was spread around two functions and several cases. It places the list of attributes
     1279 * which can have uris in them and checks for ones that can have multiple uri candidates (like srcset)
     1280 * It ultimately passes everything to wp_kses_bad_protocol() to do the actual work.
     1281 *
     1282 * @since ?
     1283 *
     1284 * @param string $attrname Attribute name to test against.
     1285 * @param string $attrvalue Content to filter bad protocols from.
     1286 * @param string[] $allowed_protocols Array of allowed URL protocols.
     1287 * @return string Filtered content.
     1288 */
     1289function wp_kses_sanitize_uris( $attrname, $attrvalue, $allowed_protocols ) {
     1290        $uris     = array('xmlns', 'profile', 'href', 'src', 'srcset', 'sizes', 'cite', 'classid', 'codebase', 'data', 'usemap', 'longdesc', 'action');
     1291        $uri_candidates = array( 'srcset' );
     1292
     1293        if ( ! in_array( strtolower( $attrname ), $uris ) ) {
     1294                return $attrvalue;
     1295        } else {
     1296                if ( in_array( strtolower( $attrname ), $uri_candidates ) ) {
     1297                        $thesevals = preg_split( '/\s*,\s+/', $attrvalue );
     1298                } else {
     1299                        $thesevals = array( $attrvalue );
     1300                }
     1301        }
     1302
     1303        foreach ( (array) $thesevals as $key => $val ) {
     1304                $thesevals[$key] = wp_kses_bad_protocol( $val, $allowed_protocols );
     1305        }
     1306       
     1307        return join(', ', $thesevals);
     1308}
     1309
    12711310/**
    12721311 * Finds all attributes of an HTML element.
    12731312 *
  • tests/phpunit/tests/kses.php

    diff --git tests/phpunit/tests/kses.php tests/phpunit/tests/kses.php
    index 5ae0101dab..83fd81b103 100644
    class Tests_Kses extends WP_UnitTestCase { 
    7575                }
    7676        }
    7777
    78         function test_feed_links() {
     78        /**
     79         * @ticket 29807
     80         */
     81        function test_wp_filter_post_kses_img() {
    7982                global $allowedposttags;
    8083
     84                $attributes = array(
     85                        'class' => 'classname',
     86                        'id' => 'idattr',
     87                        'style' => 'color: red;',
     88                        'alt' => 'alt',
     89                        'src' => '/test.png',
     90                        'srcset' => '/test.png 1x, /test-2x.png 2x',
     91                        'width' => '100',
     92                        'height' => '100',
     93                        'usemap' => '#hash',
     94                        'vspace' => '20',
     95                        'hspace' => '20',
     96                        'longdesc' => 'this is the longdesc',
     97                        'align' => 'middle',
     98                        'border' => '5',
     99                );
     100
     101                foreach ( $attributes as $name => $value ) {
     102                        if ( $name === $value ) {
     103                                $string = "<img $value />";
     104                                $expect_string = '<img ' . trim( $value, ';' ) . ' />';
     105                        } else {
     106                                $string = "<img $name='$value' />";
     107                                $expect_string = "<img $name='" . trim( $value, ';' ) . "' />";
     108                        }
     109
     110                        $this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
     111                }
     112        }
     113
     114        /**
     115         * @ticket 29807
     116         *
     117         * @param string $unfiltered Unfiltered srcset value before wp_kses.
     118         * @param string $expected   Expected srset value after wp_kses.
     119         *
     120         * @dataProvider data_wp_kses_srcset
     121         */
     122        function test_wp_kses_srcset( $unfiltered, $expected ) {
     123                $unfiltered = "<img src='test.png' srcset='{$unfiltered}' />";
     124                $expected = "<img src='test.png' srcset='{$expected}' />";
     125                $this->assertEquals( $expected, wp_kses_post( $unfiltered ) );
     126        }
     127
     128        function data_wp_kses_srcset() {
     129                return array(
     130                        array(
     131                                '/test.png 1x, /test-2x.png 2x',
     132                                '/test.png 1x, /test-2x.png 2x',
     133                        ),
     134                        array(
     135                                'bad://localhost/test.png 1x, http://localhost/test-2x.png 2x',
     136                                '//localhost/test.png 1x, http://localhost/test-2x.png 2x',
     137                        ),
     138                        array(
     139                                'http://localhost/test.png 1x, bad://localhost/test-2x.png 2x',
     140                                'http://localhost/test.png 1x, //localhost/test-2x.png 2x',
     141                        ),
     142                        array(
     143                                'http://localhost/test.png,big 1x, bad://localhost/test.png,medium 2x',
     144                                'http://localhost/test.png,big 1x, //localhost/test.png,medium 2x',
     145                        ),
     146                        array(
     147                                'path/to/test.png 1x, path/to/test-2x.png 2x',
     148                                'path/to/test.png 1x, path/to/test-2x.png 2x',
     149                        ),
     150                );
     151        }
     152
     153        /**
     154         * @ticket 29807
     155         */
     156        function test_wp_filter_post_kses_picture() {
     157                global $allowedposttags;
     158
     159                $html = '<picture><source srcset="pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
     160                $this->assertEquals( $html, wp_kses( $html, $allowedposttags ) );
     161
     162                $html = '<picture><source srcset="https://wordpress.org/pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="https://wordpress.org/pear-tablet.jpeg 500w, https://wordpress.org/pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
     163                $this->assertEquals( $html, wp_kses( $html, $allowedposttags ) );
     164
     165                // Test bad protocol in srcset
     166                $original = '<picture><source srcset="bad://pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
     167                $expected = '<picture><source srcset="//pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
     168                $this->assertEquals( $expected, wp_kses( $original, $allowedposttags ) );
     169        }
     170
     171        function test_feed_links() {
     172                global $allowedposttags;
    81173                $content = <<<EOF
    82174<a href="feed:javascript:alert(1)">CLICK ME</a>
    83175<a href="feed:javascript:feed:alert(1)">CLICK ME</a>