WordPress.org

Make WordPress Core

Changeset 43781


Ignore:
Timestamp:
10/22/2018 04:03:07 AM (13 months 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.

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

Location:
branches/5.0
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • branches/5.0/src/wp-includes/kses.php

    r43731 r43781  
    17081708 */
    17091709function safecss_filter_attr( $css, $deprecated = '' ) {
    1710     if ( !empty( $deprecated ) )
     1710    if ( ! empty( $deprecated ) ) {
    17111711        _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
    1712 
    1713     $css = wp_kses_no_null($css);
    1714     $css = str_replace(array("\n","\r","\t"), '', $css);
    1715 
    1716     if ( preg_match( '%[\\\\(&=}]|/\*%', $css ) ) // remove any inline css containing \ ( & } = or comments
    1717         return '';
     1712    }
     1713
     1714    $css = wp_kses_no_null( $css );
     1715    $css = str_replace( array( "\n", "\r", "\t" ), '', $css );
     1716
     1717    $allowed_protocols = wp_allowed_protocols();
    17181718
    17191719    $css_array = explode( ';', trim( $css ) );
     
    17251725     * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`.
    17261726     * @since 4.6.0 Added support for `list-style-type`.
     1727     * @since 5.0.0 Added support for `background-image`.
    17271728     *
    17281729     * @param array $attr List of allowed CSS attributes.
     
    17311732        'background',
    17321733        'background-color',
     1734        'background-image',
    17331735
    17341736        'border',
     
    17991801    ) );
    18001802
    1801     if ( empty($allowed_attr) )
     1803
     1804    /*
     1805     * CSS attributes that accept URL data types.
     1806     *
     1807     * This is in accordance to the CSS spec and unrelated to
     1808     * the sub-set of supported attributes above.
     1809     *
     1810     * See: https://developer.mozilla.org/en-US/docs/Web/CSS/url
     1811     */
     1812    $css_url_data_types = array(
     1813        'background',
     1814        'background-image',
     1815
     1816        'cursor',
     1817
     1818        'list-style',
     1819        'list-style-image',
     1820    );
     1821
     1822    if ( empty( $allowed_attr ) ) {
    18021823        return $css;
     1824    }
    18031825
    18041826    $css = '';
    18051827    foreach ( $css_array as $css_item ) {
    1806         if ( $css_item == '' )
     1828        if ( $css_item == '' ) {
    18071829            continue;
    1808         $css_item = trim( $css_item );
    1809         $found = false;
     1830        }
     1831
     1832        $css_item        = trim( $css_item );
     1833        $css_test_string = $css_item;
     1834        $found           = false;
     1835        $url_attr        = false;
     1836
    18101837        if ( strpos( $css_item, ':' ) === false ) {
    18111838            $found = true;
    18121839        } else {
    1813             $parts = explode( ':', $css_item );
    1814             if ( in_array( trim( $parts[0] ), $allowed_attr ) )
     1840            $parts = explode( ':', $css_item, 2 );
     1841            $css_selector = trim( $parts[0] );
     1842
     1843            if ( in_array( $css_selector, $allowed_attr, true ) ) {
    18151844                $found = true;
     1845                $url_attr = in_array( $css_selector, $css_url_data_types, true );
     1846            }
    18161847        }
    1817         if ( $found ) {
    1818             if( $css != '' )
     1848
     1849        if ( $found && $url_attr ) {
     1850            // Simplified: matches the sequence `url(*)`.
     1851            preg_match_all( '/url\([^)]+\)/', $parts[1], $url_matches );
     1852
     1853            foreach ( $url_matches[0] as $url_match ) {
     1854                // Clean up the URL from each of the matches above.
     1855                preg_match( '/^url\(\s*([\'\"]?)(.*)(\g1)\s*\)$/', $url_match, $url_pieces );
     1856
     1857                if ( empty( $url_pieces[2] ) ) {
     1858                    $found = false;
     1859                    break;
     1860                }
     1861
     1862                $url = trim( $url_pieces[2] );
     1863
     1864                if ( empty( $url ) || $url !== wp_kses_bad_protocol( $url, $allowed_protocols ) ) {
     1865                    $found = false;
     1866                    break;
     1867                } else {
     1868                    // Remove the whole `url(*)` bit that was matched above from the CSS.
     1869                    $css_test_string = str_replace( $url_match, '', $css_test_string );
     1870                }
     1871            }
     1872        }
     1873
     1874        // Remove any CSS containing containing \ ( & } = or comments, except for url() useage checked above.
     1875        if ( $found && ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ) ) {
     1876            if ( $css != '' ) {
    18191877                $css .= ';';
     1878            }
     1879
    18201880            $css .= $css_item;
    18211881        }
  • branches/5.0/tests/phpunit/tests/kses.php

    r43727 r43781  
    814814        );
    815815    }
     816
     817    /**
     818     * Test URL sanitization in the style tag.
     819     *
     820     * @dataProvider data_kses_style_attr_with_url
     821     *
     822     * @ticket 45067
     823     *
     824     * @param $input string The style attribute saved in the editor.
     825     * @param $expected string The sanitized style attribute.
     826     */
     827    function test_kses_style_attr_with_url( $input, $expected ) {
     828        $actual = safecss_filter_attr( $input );
     829
     830        $this->assertSame( $expected, $actual );
     831    }
     832
     833    /**
     834     * Data provider testing style attribute sanitization.
     835     *
     836     * @return array Nested array of input, expected pairs.
     837     */
     838    function data_kses_style_attr_with_url() {
     839        return array(
     840            /*
     841             * Valid use cases.
     842             */
     843
     844            // Double quotes.
     845            array(
     846                'background-image: url( "http://example.com/valid.gif" );',
     847                'background-image: url( "http://example.com/valid.gif" )',
     848            ),
     849
     850            // Single quotes.
     851            array(
     852                "background-image: url( 'http://example.com/valid.gif' );",
     853                "background-image: url( 'http://example.com/valid.gif' )",
     854            ),
     855
     856            // No quotes.
     857            array(
     858                'background-image: url( http://example.com/valid.gif );',
     859                'background-image: url( http://example.com/valid.gif )',
     860            ),
     861
     862            // Single quotes, extra spaces.
     863            array(
     864                "background-image: url( '  http://example.com/valid.gif ' );",
     865                "background-image: url( '  http://example.com/valid.gif ' )",
     866            ),
     867
     868            // Line breaks, single quotes.
     869            array(
     870                "background-image: url(\n'http://example.com/valid.gif' );",
     871                "background-image: url('http://example.com/valid.gif' )",
     872            ),
     873
     874            // Tabs not spaces, single quotes.
     875            array(
     876                "background-image: url(\t'http://example.com/valid.gif'\t\t);",
     877                "background-image: url('http://example.com/valid.gif')",
     878            ),
     879
     880            // Single quotes, absolute path.
     881            array(
     882                "background: url('/valid.gif');",
     883                "background: url('/valid.gif')",
     884            ),
     885
     886            // Single quotes, relative path.
     887            array(
     888                "background: url('../wp-content/uploads/2018/10/valid.gif');",
     889                "background: url('../wp-content/uploads/2018/10/valid.gif')",
     890            ),
     891
     892            // Error check: valid property not containing a URL.
     893            array(
     894                "background: red",
     895                "background: red",
     896            ),
     897
     898            /*
     899             * Invalid use cases.
     900             */
     901
     902            // Attribute doesn't support URL properties.
     903            array(
     904                'color: url( "http://example.com/invalid.gif" );',
     905                '',
     906            ),
     907
     908            // Mismatched quotes.
     909            array(
     910                'background-image: url( "http://example.com/valid.gif\' );',
     911                '',
     912            ),
     913
     914            // Bad protocol, double quotes.
     915            array(
     916                'background-image: url( "bad://example.com/invalid.gif" );',
     917                '',
     918            ),
     919
     920            // Bad protocol, single quotes.
     921            array(
     922                "background-image: url( 'bad://example.com/invalid.gif' );",
     923                '',
     924            ),
     925
     926            // Bad protocol, single quotes.
     927            array(
     928                "background-image: url( 'bad://example.com/invalid.gif' );",
     929                '',
     930            ),
     931
     932            // Bad protocol, single quotes, strange spacing.
     933            array(
     934                "background-image: url( '  \tbad://example.com/invalid.gif ' );",
     935                '',
     936            ),
     937
     938            // Bad protocol, no quotes.
     939            array(
     940                'background-image: url( bad://example.com/invalid.gif );',
     941                '',
     942            ),
     943
     944            // No URL inside url().
     945            array(
     946                'background-image: url();',
     947                '',
     948            ),
     949
     950            // Malformed, no closing `)`.
     951            array(
     952                'background-image: url( "http://example.com" ;',
     953                '',
     954            ),
     955
     956            // Malformed, no closing `"`.
     957            array(
     958                'background-image: url( "http://example.com );',
     959                '',
     960            ),
     961        );
     962    }
    816963}
  • branches/5.0/tests/phpunit/tests/shortcode.php

    r42838 r43781  
    493493            ),
    494494            array(
    495                 '<div style="background:url([[gallery]])">',
    496                 '<div style="background:url([[gallery]])">',
     495                '<div style="selector:url([[gallery]])">',
     496                '<div style="selector:url([[gallery]])">',
    497497            ),
    498498            array(
Note: See TracChangeset for help on using the changeset viewer.