Skip to content

Commit a13fbba

Browse files
committed
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: WordPress#11868 Discussed in: https://core.trac.wordpress.org/ticket/65270 Props dmsnell, westonruter. Fixes #65270. git-svn-id: https://develop.svn.wordpress.org/trunk@62433 602fd350-edb4-49c9-b593-d223f7449a82
1 parent f19efd0 commit a13fbba

2 files changed

Lines changed: 56 additions & 5 deletions

File tree

src/wp-includes/kses.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,7 +1556,8 @@ function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowe
15561556
}
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 ) ) {
15621563
$name = '';
@@ -1565,8 +1566,11 @@ function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowe
15651566
return false;
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

15721576
if ( is_array( $allowed_attr[ $name_low ] ) ) {
@@ -2554,9 +2558,9 @@ function kses_init() {
25542558
* @since 6.6.0 Added support for `grid-column`, `grid-row`, and `container-type`.
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
*/
25612565
function safecss_filter_attr( $css, $deprecated = '' ) {
25622566
if ( ! empty( $deprecated ) ) {
@@ -2568,6 +2572,7 @@ function safecss_filter_attr( $css, $deprecated = '' ) {
25682572

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

25732578
/**

tests/phpunit/tests/kses.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,6 +1583,52 @@ public function data_wildcard_attribute_prefixes() {
15831583
);
15841584
}
15851585

1586+
/**
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+
15861632
/**
15871633
* Test URL sanitization in the style tag.
15881634
*

0 commit comments

Comments
 (0)