Make WordPress Core

Changeset 43781


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

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.