Make WordPress Core

Changeset 44136


Ignore:
Timestamp:
12/14/2018 01:40:50 AM (8 years ago)
Author:
pento
Message:

KSES: Allow url() to be used in inline CSS.

The cover image block uses the url() function in its inline CSS, to show the cover image. KSES didn't allow this, causing the block to not save correctly for Author and Contributor users. As KSES does already check each attribute name against an allowed list, we're able to add an extra check for certain attributes to be able to use the url() function, too.

Merges [43781] from the 5.0 branch to core.

Props peterwilsoncc, azaozz, pento, dd32.
Fixes #45067.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/wp-includes/kses.php

    r43984 r44136  
    19861986        $css = str_replace( array( "\n", "\r", "\t" ), '', $css );
    19871987
    1988         if ( preg_match( '%[\\\\(&=}]|/\*%', $css ) ) { // remove any inline css containing \ ( & } = or comments
    1989                 return '';
    1990         }
     1988        $allowed_protocols = wp_allowed_protocols();
    19911989
    19921990        $css_array = explode( ';', trim( $css ) );
     
    19991997         * @since 4.6.0 Added support for `list-style-type`.
    20001998         * @since 5.0.0 Added support for `text-transform`.
     1999         * @since 5.0.0 Added support for `background-image`.
    20012000         *
    20022001         * @param string[] $attr Array of allowed CSS attributes.
     
    20072006                        'background',
    20082007                        'background-color',
     2008                        'background-image',
    20092009
    20102010                        'border',
     
    20772077        );
    20782078
     2079        /*
     2080         * CSS attributes that accept URL data types.
     2081         *
     2082         * This is in accordance to the CSS spec and unrelated to
     2083         * the sub-set of supported attributes above.
     2084         *
     2085         * See: https://developer.mozilla.org/en-US/docs/Web/CSS/url
     2086         */
     2087        $css_url_data_types = array(
     2088                'background',
     2089                'background-image',
     2090
     2091                'cursor',
     2092
     2093                'list-style',
     2094                'list-style-image',
     2095        );
     2096
    20792097        if ( empty( $allowed_attr ) ) {
    20802098                return $css;
     
    20862104                        continue;
    20872105                }
    2088                 $css_item = trim( $css_item );
    2089                 $found    = false;
     2106
     2107                $css_item        = trim( $css_item );
     2108                $css_test_string = $css_item;
     2109                $found           = false;
     2110                $url_attr        = false;
     2111
    20902112                if ( strpos( $css_item, ':' ) === false ) {
    20912113                        $found = true;
    20922114                } else {
    2093                         $parts = explode( ':', $css_item );
    2094                         if ( in_array( trim( $parts[0] ), $allowed_attr ) ) {
    2095                                 $found = true;
     2115                        $parts        = explode( ':', $css_item, 2 );
     2116                        $css_selector = trim( $parts[0] );
     2117
     2118                        if ( in_array( $css_selector, $allowed_attr, true ) ) {
     2119                                $found    = true;
     2120                                $url_attr = in_array( $css_selector, $css_url_data_types, true );
    20962121                        }
    20972122                }
    2098                 if ( $found ) {
     2123
     2124                if ( $found && $url_attr ) {
     2125                        // Simplified: matches the sequence `url(*)`.
     2126                        preg_match_all( '/url\([^)]+\)/', $parts[1], $url_matches );
     2127
     2128                        foreach ( $url_matches[0] as $url_match ) {
     2129                                // Clean up the URL from each of the matches above.
     2130                                preg_match( '/^url\(\s*([\'\"]?)(.*)(\g1)\s*\)$/', $url_match, $url_pieces );
     2131
     2132                                if ( empty( $url_pieces[2] ) ) {
     2133                                        $found = false;
     2134                                        break;
     2135                                }
     2136
     2137                                $url = trim( $url_pieces[2] );
     2138
     2139                                if ( empty( $url ) || $url !== wp_kses_bad_protocol( $url, $allowed_protocols ) ) {
     2140                                        $found = false;
     2141                                        break;
     2142                                } else {
     2143                                        // Remove the whole `url(*)` bit that was matched above from the CSS.
     2144                                        $css_test_string = str_replace( $url_match, '', $css_test_string );
     2145                                }
     2146                        }
     2147                }
     2148
     2149                // Remove any CSS containing containing \ ( & } = or comments, except for url() useage checked above.
     2150                if ( $found && ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ) ) {
    20992151                        if ( $css != '' ) {
    21002152                                $css .= ';';
    21012153                        }
     2154
    21022155                        $css .= $css_item;
    21032156                }
  • trunk/tests/phpunit/tests/kses.php

    r43981 r44136  
    819819                                'expected' => 'margin: 10px 20px;padding: 5px 10px',
    820820                        ),
    821                         // Parenthesis ( isn't supported.
     821                        // Parenthesis ( is supported for some attributes.
    822822                        array(
    823823                                'css'      => 'background: green url("foo.jpg") no-repeat fixed center',
    824                                 'expected' => '',
     824                                'expected' => 'background: green url("foo.jpg") no-repeat fixed center',
    825825                        ),
    826826                );
     
    921921                );
    922922        }
     923
     924        /**
     925         * Test URL sanitization in the style tag.
     926         *
     927         * @dataProvider data_kses_style_attr_with_url
     928         *
     929         * @ticket 45067
     930         *
     931         * @param $input string The style attribute saved in the editor.
     932         * @param $expected string The sanitized style attribute.
     933         */
     934        function test_kses_style_attr_with_url( $input, $expected ) {
     935                $actual = safecss_filter_attr( $input );
     936
     937                $this->assertSame( $expected, $actual );
     938        }
     939
     940        /**
     941         * Data provider testing style attribute sanitization.
     942         *
     943         * @return array Nested array of input, expected pairs.
     944         */
     945        function data_kses_style_attr_with_url() {
     946                return array(
     947                        /*
     948                         * Valid use cases.
     949                         */
     950
     951                        // Double quotes.
     952                        array(
     953                                'background-image: url( "http://example.com/valid.gif" );',
     954                                'background-image: url( "http://example.com/valid.gif" )',
     955                        ),
     956
     957                        // Single quotes.
     958                        array(
     959                                "background-image: url( 'http://example.com/valid.gif' );",
     960                                "background-image: url( 'http://example.com/valid.gif' )",
     961                        ),
     962
     963                        // No quotes.
     964                        array(
     965                                'background-image: url( http://example.com/valid.gif );',
     966                                'background-image: url( http://example.com/valid.gif )',
     967                        ),
     968
     969                        // Single quotes, extra spaces.
     970                        array(
     971                                "background-image: url( '  http://example.com/valid.gif ' );",
     972                                "background-image: url( '  http://example.com/valid.gif ' )",
     973                        ),
     974
     975                        // Line breaks, single quotes.
     976                        array(
     977                                "background-image: url(\n'http://example.com/valid.gif' );",
     978                                "background-image: url('http://example.com/valid.gif' )",
     979                        ),
     980
     981                        // Tabs not spaces, single quotes.
     982                        array(
     983                                "background-image: url(\t'http://example.com/valid.gif'\t\t);",
     984                                "background-image: url('http://example.com/valid.gif')",
     985                        ),
     986
     987                        // Single quotes, absolute path.
     988                        array(
     989                                "background: url('/valid.gif');",
     990                                "background: url('/valid.gif')",
     991                        ),
     992
     993                        // Single quotes, relative path.
     994                        array(
     995                                "background: url('../wp-content/uploads/2018/10/valid.gif');",
     996                                "background: url('../wp-content/uploads/2018/10/valid.gif')",
     997                        ),
     998
     999                        // Error check: valid property not containing a URL.
     1000                        array(
     1001                                'background: red',
     1002                                'background: red',
     1003                        ),
     1004
     1005                        /*
     1006                         * Invalid use cases.
     1007                         */
     1008
     1009                        // Attribute doesn't support URL properties.
     1010                        array(
     1011                                'color: url( "http://example.com/invalid.gif" );',
     1012                                '',
     1013                        ),
     1014
     1015                        // Mismatched quotes.
     1016                        array(
     1017                                'background-image: url( "http://example.com/valid.gif\' );',
     1018                                '',
     1019                        ),
     1020
     1021                        // Bad protocol, double quotes.
     1022                        array(
     1023                                'background-image: url( "bad://example.com/invalid.gif" );',
     1024                                '',
     1025                        ),
     1026
     1027                        // Bad protocol, single quotes.
     1028                        array(
     1029                                "background-image: url( 'bad://example.com/invalid.gif' );",
     1030                                '',
     1031                        ),
     1032
     1033                        // Bad protocol, single quotes.
     1034                        array(
     1035                                "background-image: url( 'bad://example.com/invalid.gif' );",
     1036                                '',
     1037                        ),
     1038
     1039                        // Bad protocol, single quotes, strange spacing.
     1040                        array(
     1041                                "background-image: url( '  \tbad://example.com/invalid.gif ' );",
     1042                                '',
     1043                        ),
     1044
     1045                        // Bad protocol, no quotes.
     1046                        array(
     1047                                'background-image: url( bad://example.com/invalid.gif );',
     1048                                '',
     1049                        ),
     1050
     1051                        // No URL inside url().
     1052                        array(
     1053                                'background-image: url();',
     1054                                '',
     1055                        ),
     1056
     1057                        // Malformed, no closing `)`.
     1058                        array(
     1059                                'background-image: url( "http://example.com" ;',
     1060                                '',
     1061                        ),
     1062
     1063                        // Malformed, no closing `"`.
     1064                        array(
     1065                                'background-image: url( "http://example.com );',
     1066                                '',
     1067                        ),
     1068                );
     1069        }
    9231070}
  • trunk/tests/phpunit/tests/shortcode.php

    r43571 r44136  
    562562                        ),
    563563                        array(
    564                                 '<div style="background:url([[gallery]])">',
    565                                 '<div style="background:url([[gallery]])">',
     564                                '<div style="selector:url([[gallery]])">',
     565                                '<div style="selector:url([[gallery]])">',
    566566                        ),
    567567                        array(
Note: See TracChangeset for help on using the changeset viewer.