Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 51 additions & 14 deletions src/wp-includes/kses.php
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
'longdesc' => true,
'vspace' => true,
'src' => true,
'srcset' => true,
'usemap' => true,
'width' => true,
),
Expand Down Expand Up @@ -250,6 +251,7 @@
'p' => array(
'align' => true,
),
'picture' => array(),
'pre' => array(
'width' => true,
),
Expand All @@ -270,6 +272,12 @@
'align' => true,
),
'small' => array(),
'source' => array(
'srcset' => true,
'type' => true,
'media' => true,
'sizes' => true,
),
'strike' => array(),
'strong' => array(),
'sub' => array(),
Expand Down Expand Up @@ -768,7 +776,6 @@ function wp_kses( $content, $allowed_html, $allowed_protocols = array() ) {
* @return string Filtered attribute.
*/
function wp_kses_one_attr( $attr, $element ) {
$uris = wp_kses_uri_attributes();
$allowed_html = wp_kses_allowed_html( 'post' );
$allowed_protocols = wp_allowed_protocols();
$attr = wp_kses_no_null( $attr, array( 'slash_zero' => 'keep' ) );
Expand Down Expand Up @@ -812,10 +819,7 @@ function wp_kses_one_attr( $attr, $element ) {
// Sanitize quotes, angle braces, and entities.
$value = esc_attr( $value );

// Sanitize URI values.
if ( in_array( strtolower( $name ), $uris, true ) ) {
$value = wp_kses_bad_protocol( $value, $allowed_protocols );
}
$value = wp_kses_sanitize_uris( $name, $value, $allowed_protocols );

$attr = "$name=$quote$value$quote";
$vless = 'n';
Expand Down Expand Up @@ -1380,9 +1384,9 @@ function wp_kses_hair( $attr, $allowed_protocols ) {
if ( preg_match( '%^"([^"]*)"(\s+|/?$)%', $attr, $match ) ) {
// "value"
$thisval = $match[1];
if ( in_array( strtolower( $attrname ), $uris, true ) ) {
$thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
}

// Sanitize URI values.
$thisval = wp_kses_sanitize_uris( $attrname, $thisval, $allowed_protocols );

if ( false === array_key_exists( $attrname, $attrarr ) ) {
$attrarr[ $attrname ] = array(
Expand All @@ -1402,9 +1406,8 @@ function wp_kses_hair( $attr, $allowed_protocols ) {
if ( preg_match( "%^'([^']*)'(\s+|/?$)%", $attr, $match ) ) {
// 'value'
$thisval = $match[1];
if ( in_array( strtolower( $attrname ), $uris, true ) ) {
$thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
}
// Sanitize URI values.
$thisval = wp_kses_sanitize_uris( $attrname, $thisval, $allowed_protocols );

if ( false === array_key_exists( $attrname, $attrarr ) ) {
$attrarr[ $attrname ] = array(
Expand All @@ -1424,9 +1427,8 @@ function wp_kses_hair( $attr, $allowed_protocols ) {
if ( preg_match( "%^([^\s\"']+)(\s+|/?$)%", $attr, $match ) ) {
// value
$thisval = $match[1];
if ( in_array( strtolower( $attrname ), $uris, true ) ) {
$thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
}
// Sanitize URI values.
$thisval = wp_kses_sanitize_uris( $attrname, $thisval, $allowed_protocols );

if ( false === array_key_exists( $attrname, $attrarr ) ) {
$attrarr[ $attrname ] = array(
Expand Down Expand Up @@ -1468,6 +1470,41 @@ function wp_kses_hair( $attr, $allowed_protocols ) {
return $attrarr;
}

/**
* Santizes uris in attributes
*
* This condenses code that was spread around two functions and several cases. It places the list of attributes
* which can have uris in them and checks for ones that can have multiple uri candidates (like srcset)
* It ultimately passes everything to wp_kses_bad_protocol() to do the actual work.
*
* @since ?
*
* @param string $attrname Attribute name to test against.
* @param string $attrvalue Content to filter bad protocols from.
* @param string[] $allowed_protocols Array of allowed URL protocols.
* @return string Filtered content.
*/
function wp_kses_sanitize_uris( $attrname, $attrvalue, $allowed_protocols ) {
$uris = wp_kses_uri_attributes();
$uri_candidates = array( 'srcset' );

if ( ! in_array( strtolower( $attrname ), $uris ) ) {
return $attrvalue;
} else {
if ( in_array( strtolower( $attrname ), $uri_candidates ) ) {
$thesevals = preg_split( '/\s*,\s+/', $attrvalue );
} else {
$thesevals = array( $attrvalue );
}
}

foreach ( (array) $thesevals as $key => $val ) {
$thesevals[$key] = wp_kses_bad_protocol( $val, $allowed_protocols );
}

return join(', ', $thesevals);
}

/**
* Finds all attributes of an HTML element.
*
Expand Down
94 changes: 94 additions & 0 deletions tests/phpunit/tests/kses.php
Original file line number Diff line number Diff line change
Expand Up @@ -2188,4 +2188,98 @@ public function data_kses_globals_are_defined() {

return $this->text_array_to_dataprovider( $required_kses_globals );
}

/**
* @ticket 29807
*/
function test_wp_filter_post_kses_img() {
global $allowedposttags;

$attributes = array(
'class' => 'classname',
'id' => 'idattr',
'style' => 'color: red;',
'alt' => 'alt',
'src' => '/test.png',
'srcset' => '/test.png 1x, /test-2x.png 2x',
'width' => '100',
'height' => '100',
'usemap' => '#hash',
'vspace' => '20',
'hspace' => '20',
'longdesc' => 'this is the longdesc',
'align' => 'middle',
'border' => '5',
);

foreach ( $attributes as $name => $value ) {
if ( $name === $value ) {
$string = "<img $value />";
$expect_string = '<img ' . trim( $value, ';' ) . ' />';
} else {
$string = "<img $name='$value' />";
$expect_string = "<img $name='" . trim( $value, ';' ) . "' />";
}

$this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
}
}

/**
* @ticket 29807
*
* @param string $unfiltered Unfiltered srcset value before wp_kses.
* @param string $expected Expected srset value after wp_kses.
*
* @dataProvider data_wp_kses_srcset
*/
function test_wp_kses_srcset( $unfiltered, $expected ) {
$unfiltered = "<img src='test.png' srcset='{$unfiltered}' />";
$expected = "<img src='test.png' srcset='{$expected}' />";
$this->assertEquals( $expected, wp_kses_post( $unfiltered ) );
}

function data_wp_kses_srcset() {
return array(
array(
'/test.png 1x, /test-2x.png 2x',
'/test.png 1x, /test-2x.png 2x',
),
array(
'bad://localhost/test.png 1x, http://localhost/test-2x.png 2x',
'//localhost/test.png 1x, http://localhost/test-2x.png 2x',
),
array(
'http://localhost/test.png 1x, bad://localhost/test-2x.png 2x',
'http://localhost/test.png 1x, //localhost/test-2x.png 2x',
),
array(
'http://localhost/test.png,big 1x, bad://localhost/test.png,medium 2x',
'http://localhost/test.png,big 1x, //localhost/test.png,medium 2x',
),
array(
'path/to/test.png 1x, path/to/test-2x.png 2x',
'path/to/test.png 1x, path/to/test-2x.png 2x',
),
);
}

/**
* @ticket 29807
*/
function test_wp_filter_post_kses_picture() {
global $allowedposttags;

$html = '<picture><source srcset="pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
$this->assertEquals( $html, wp_kses( $html, $allowedposttags ) );

$html = '<picture><source srcset="https://wordpress.org/pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="https://wordpress.org/pear-tablet.jpeg 500w, https://wordpress.org/pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
$this->assertEquals( $html, wp_kses( $html, $allowedposttags ) );

// Test bad protocol in srcset
$original = '<picture><source srcset="bad://pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
$expected = '<picture><source srcset="//pear-mobile.jpeg" media="(max-width: 720px)" type="image/png"><source srcset="pear-tablet.jpeg" media="(max-width: 1280px)" type="image/png"><img src="pear-desktop.jpeg" alt="The pear is juicy."></picture>';
$this->assertEquals( $expected, wp_kses( $original, $allowedposttags ) );
}

}