Make WordPress Core

Changeset 62433


Ignore:
Timestamp:
05/31/2026 07:30:28 AM (20 hours ago)
Author:
dmsnell
Message:

KSES: Decode style attribute before sending to safecss_filter_attr().

safecss_filter_attr() assumes that it receives already-unescaped HTML
attribute values. For example, consider the raw HTML string:

style="background:url("bg.png")"

This should be decoded and passed into safecss_filter_attr() as:

background:url("bg.png")

Unfortuantely this hasn’t been done in wp_kses_attr_check(), which
takes the output from wp_kses_hair() and sends it directly to the
filtering function.

In this patch, wp_kses_attr_check() unescapes the style attribute,
filters it, and then re-escapes it when updating the style value.

Tests added by Codex

Developed in: https://github.com/WordPress/wordpress-develop/pull/11868
Discussed in: https://core.trac.wordpress.org/ticket/65270

Props dmsnell, westonruter.
Fixes #65270.

Location:
trunk
Files:
2 edited

Legend:

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

    r61882 r62433  
    15571557
    15581558    if ( 'style' === $name_low ) {
    1559         $new_value = safecss_filter_attr( $value );
     1559        $decoded_value = WP_HTML_Decoder::decode_attribute( $value );
     1560        $new_value     = safecss_filter_attr( $decoded_value );
    15601561
    15611562        if ( empty( $new_value ) ) {
     
    15661567        }
    15671568
    1568         $whole = str_replace( $value, $new_value, $whole );
    1569         $value = $new_value;
     1569        if ( $new_value !== $decoded_value ) {
     1570            $encoded_value = esc_attr( $new_value );
     1571            $whole         = str_replace( $value, $encoded_value, $whole );
     1572            $value         = $encoded_value;
     1573        }
    15701574    }
    15711575
     
    25552559 * @since 6.9.0 Added support for `white-space`.
    25562560 *
    2557  * @param string $css        A string of CSS rules.
     2561 * @param string $css        A string of CSS rules, decoded from an HTML `style` attribute.
    25582562 * @param string $deprecated Not used.
    2559  * @return string Filtered string of CSS rules.
     2563 * @return string Filtered string of CSS rules, needing HTML escaping before sending back to a `style` attribute.
    25602564 */
    25612565function safecss_filter_attr( $css, $deprecated = '' ) {
     
    25692573    $allowed_protocols = wp_allowed_protocols();
    25702574
     2575    /** @todo Parse enough CSS to split rules without breaking on things like quoted strings. */
    25712576    $css_array = explode( ';', trim( $css ) );
    25722577
  • trunk/tests/phpunit/tests/kses.php

    r61565 r62433  
    15851585
    15861586    /**
     1587     * Tests that style attribute values are decoded before CSS filtering.
     1588     *
     1589     * @ticket 65270
     1590     *
     1591     * @dataProvider data_wp_kses_style_attr_decodes_entities_before_css_filtering
     1592     *
     1593     * @param string $content  A string of HTML to test.
     1594     * @param string $expected Expected result after passing through KSES.
     1595     */
     1596    public function test_wp_kses_style_attr_decodes_entities_before_css_filtering( $content, $expected ) {
     1597        $allowed_html = array(
     1598            'div' => array(
     1599                'style' => true,
     1600            ),
     1601        );
     1602
     1603        $this->assertEqualHTML( $expected, wp_kses( $content, $allowed_html ) );
     1604    }
     1605
     1606    /**
     1607     * Data provider for test_wp_kses_style_attr_decodes_entities_before_css_filtering().
     1608     *
     1609     * @return array[]
     1610     */
     1611    public function data_wp_kses_style_attr_decodes_entities_before_css_filtering() {
     1612        return array(
     1613            'background image URL with single quotes' => array(
     1614                '<div style="background-image: url(\'https://localhost/image.jpg\');"></div>',
     1615                '<div style="background-image: url(&#039;https://localhost/image.jpg&#039;)"></div>',
     1616            ),
     1617            'background image URL with entity-encoded double quotes' => array(
     1618                '<div style="background-image: url(&quot;https://localhost/image.jpg&quot;);"></div>',
     1619                '<div style="background-image: url(&quot;https://localhost/image.jpg&quot;)"></div>',
     1620            ),
     1621            'background image URL with query string ampersand' => array(
     1622                '<div style="background-image: url(https://localhost/image.jpg?a=1&b=2);"></div>',
     1623                '<div style="background-image: url(https://localhost/image.jpg?a=1&amp;b=2)"></div>',
     1624            ),
     1625            'background image URL followed by another declaration' => array(
     1626                '<div style="background-image:url(\'https://localhost/image.jpg\');background-size:cover;"></div>',
     1627                '<div style="background-image:url(&#039;https://localhost/image.jpg&#039;);background-size:cover"></div>',
     1628            ),
     1629        );
     1630    }
     1631
     1632    /**
    15871633     * Test URL sanitization in the style tag.
    15881634     *
Note: See TracChangeset for help on using the changeset viewer.