WordPress.org

Make WordPress Core

Changeset 44136


Ignore:
Timestamp:
12/14/2018 01:40:50 AM (3 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.