be669106d5
git-svn-id: https://develop.svn.wordpress.org/trunk@26431 602fd350-edb4-49c9-b593-d223f7449a82
378 lines
13 KiB
PHP
378 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* Some simple test cases for KSES post content filtering
|
|
*
|
|
* @group formatting
|
|
* @group kses
|
|
*/
|
|
class Tests_Kses extends WP_UnitTestCase {
|
|
|
|
/**
|
|
* @ticket 20210
|
|
*/
|
|
function test_wp_filter_post_kses_address() {
|
|
global $allowedposttags;
|
|
|
|
$attributes = array(
|
|
'class' => 'classname',
|
|
'id' => 'id',
|
|
'style' => 'color: red;',
|
|
'style' => 'color: red',
|
|
'style' => 'color: red; text-align:center',
|
|
'style' => 'color: red; text-align:center;',
|
|
'title' => 'title',
|
|
);
|
|
|
|
foreach ( $attributes as $name => $value ) {
|
|
$string = "<address $name='$value'>1 WordPress Avenue, The Internet.</address>";
|
|
$expect_string = "<address $name='" . str_replace( '; ', ';', trim( $value, ';' ) ) . "'>1 WordPress Avenue, The Internet.</address>";
|
|
$this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ticket 20210
|
|
*/
|
|
function test_wp_filter_post_kses_a() {
|
|
global $allowedposttags;
|
|
|
|
$attributes = array(
|
|
'class' => 'classname',
|
|
'id' => 'id',
|
|
'style' => 'color: red;',
|
|
'title' => 'title',
|
|
'href' => 'http://example.com',
|
|
'rel' => 'related',
|
|
'rev' => 'revision',
|
|
'name' => 'name',
|
|
'target' => '_blank',
|
|
);
|
|
|
|
foreach ( $attributes as $name => $value ) {
|
|
$string = "<a $name='$value'>I link this</a>";
|
|
$expect_string = "<a $name='" . trim( $value, ';' ) . "'>I link this</a>";
|
|
$this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ticket 20210
|
|
*/
|
|
function test_wp_filter_post_kses_abbr() {
|
|
global $allowedposttags;
|
|
|
|
$attributes = array(
|
|
'class' => 'classname',
|
|
'id' => 'id',
|
|
'style' => 'color: red;',
|
|
'title' => 'title',
|
|
);
|
|
|
|
foreach ( $attributes as $name => $value ) {
|
|
$string = "<abbr $name='$value'>WP</abbr>";
|
|
$expect_string = "<abbr $name='" . trim( $value, ';' ) . "'>WP</abbr>";
|
|
$this->assertEquals( $expect_string, wp_kses( $string, $allowedposttags ) );
|
|
}
|
|
}
|
|
|
|
function test_feed_links() {
|
|
global $allowedposttags;
|
|
|
|
$content = <<<EOF
|
|
<a href="feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="feed:javascript:feed:alert(1)">CLICK ME</a>
|
|
<a href="feed:feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="javascript:feed:alert(1)">CLICK ME</a>
|
|
<a href="javascript:feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="feed:feed:feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="feed:feed:feed:feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="feed:feed:feed:feed:feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="feed:javascript:feed:javascript:feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="feed:javascript:feed:javascript:feed:javascript:feed:javascript:feed:javascript:alert(1)">CLICK ME</a>
|
|
<a href="feed:feed:feed:http:alert(1)">CLICK ME</a>
|
|
EOF;
|
|
|
|
$expected = <<<EOF
|
|
<a href="feed:alert(1)">CLICK ME</a>
|
|
<a href="feed:feed:alert(1)">CLICK ME</a>
|
|
<a href="feed:feed:alert(1)">CLICK ME</a>
|
|
<a href="feed:alert(1)">CLICK ME</a>
|
|
<a href="feed:alert(1)">CLICK ME</a>
|
|
<a href="">CLICK ME</a>
|
|
<a href="">CLICK ME</a>
|
|
<a href="">CLICK ME</a>
|
|
<a href="">CLICK ME</a>
|
|
<a href="">CLICK ME</a>
|
|
<a href="">CLICK ME</a>
|
|
EOF;
|
|
|
|
$this->assertEquals( $expected, wp_kses( $content, $allowedposttags ) );
|
|
}
|
|
|
|
function test_wp_kses_bad_protocol() {
|
|
$bad = array(
|
|
'dummy:alert(1)',
|
|
'javascript:alert(1)',
|
|
'JaVaScRiPt:alert(1)',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert(1);',
|
|
'javascript:alert('XSS')',
|
|
'jav ascript:alert(1);',
|
|
'jav	ascript:alert(1);',
|
|
'jav
ascript:alert(1);',
|
|
'jav
ascript:alert(1);',
|
|
'  javascript:alert(1);',
|
|
'javascript:javascript:alert(1);',
|
|
'javascript:javascript:alert(1);',
|
|
'javascript:javascript:alert(1);',
|
|
'javascript:javascript:alert(1);',
|
|
'javascript:javascript:alert(1);',
|
|
'javascript:alert(1)//?:',
|
|
'feed:javascript:alert(1)',
|
|
'feed:javascript:feed:javascript:feed:javascript:alert(1)',
|
|
);
|
|
foreach ( $bad as $k => $x ) {
|
|
$result = wp_kses_bad_protocol( wp_kses_normalize_entities( $x ), wp_allowed_protocols() );
|
|
if ( ! empty( $result ) && $result != 'alert(1);' && $result != 'alert(1)' ) {
|
|
switch ( $k ) {
|
|
case 6: $this->assertEquals( 'javascript&#0000058alert(1);', $result ); break;
|
|
case 12:
|
|
$this->assertEquals( str_replace( '&', '&', $x ), $result );
|
|
break;
|
|
case 22: $this->assertEquals( 'javascript&#0000058alert(1);', $result ); break;
|
|
case 23: $this->assertEquals( 'javascript&#0000058alert(1)//?:', $result ); break;
|
|
case 24: $this->assertEquals( 'feed:alert(1)', $result ); break;
|
|
default: $this->fail( "wp_kses_bad_protocol failed on $x. Result: $result" );
|
|
}
|
|
}
|
|
}
|
|
|
|
$safe = array(
|
|
'dummy:alert(1)',
|
|
'HTTP://example.org/',
|
|
'http://example.org/',
|
|
'http://example.org/',
|
|
'http://example.org/',
|
|
'https://example.org',
|
|
'http://example.org/wp-admin/post.php?post=2&action=edit',
|
|
'http://example.org/index.php?test='blah'',
|
|
);
|
|
foreach ( $safe as $x ) {
|
|
$result = wp_kses_bad_protocol( wp_kses_normalize_entities( $x ), array( 'http', 'https', 'dummy' ) );
|
|
if ( $result != $x && $result != 'http://example.org/' )
|
|
$this->fail( "wp_kses_bad_protocol incorrectly blocked $x" );
|
|
}
|
|
}
|
|
|
|
public function test_hackers_attacks() {
|
|
$xss = simplexml_load_file( DIR_TESTDATA . '/formatting/xssAttacks.xml' );
|
|
foreach ( $xss->attack as $attack ) {
|
|
if ( in_array( $attack->name, array( 'IMG Embedded commands 2', 'US-ASCII encoding', 'OBJECT w/Flash 2', 'Character Encoding Example' ) ) )
|
|
continue;
|
|
|
|
$code = (string) $attack->code;
|
|
|
|
if ( $code == 'See Below' )
|
|
continue;
|
|
|
|
if ( substr( $code, 0, 4 ) == 'perl' ) {
|
|
$pos = strpos( $code, '"' ) + 1;
|
|
$code = substr( $code, $pos, strrpos($code, '"') - $pos );
|
|
$code = str_replace( '\0', "\0", $code );
|
|
}
|
|
|
|
$result = trim( wp_kses_data( $code ) );
|
|
|
|
if ( $result == '' || $result == 'XSS' || $result == 'alert("XSS");' || $result == "alert('XSS');" )
|
|
continue;
|
|
|
|
switch ( $attack->name ) {
|
|
case 'XSS Locator':
|
|
$this->assertEquals('\';alert(String.fromCharCode(88,83,83))//\\\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\\";alert(String.fromCharCode(88,83,83))//-->">\'>alert(String.fromCharCode(88,83,83))=', $result);
|
|
break;
|
|
case 'XSS Quick Test':
|
|
$this->assertEquals('\'\';!--"=', $result);
|
|
break;
|
|
case 'SCRIPT w/Alert()':
|
|
$this->assertEquals( "alert('XSS')", $result );
|
|
break;
|
|
case 'SCRIPT w/Char Code':
|
|
$this->assertEquals('alert(String.fromCharCode(88,83,83))', $result);
|
|
break;
|
|
case 'IMG STYLE w/expression':
|
|
$this->assertEquals('exp/*', $result);
|
|
break;
|
|
case 'List-style-image':
|
|
$this->assertEquals('li {list-style-image: url("javascript:alert(\'XSS\')");}XSS', $result);
|
|
break;
|
|
case 'STYLE':
|
|
$this->assertEquals( "alert('XSS');", $result);
|
|
break;
|
|
case 'STYLE w/background-image':
|
|
$this->assertEquals('.XSS{background-image:url("javascript:alert(\'XSS\')");}<A></A>', $result);
|
|
break;
|
|
case 'STYLE w/background':
|
|
$this->assertEquals('BODY{background:url("javascript:alert(\'XSS\')")}', $result);
|
|
break;
|
|
case 'Remote Stylesheet 2':
|
|
$this->assertEquals( "@import'http://ha.ckers.org/xss.css';", $result );
|
|
break;
|
|
case 'Remote Stylesheet 3':
|
|
$this->assertEquals( '<META HTTP-EQUIV="Link" Content="; REL=stylesheet">', $result );
|
|
break;
|
|
case 'Remote Stylesheet 4':
|
|
$this->assertEquals('BODY{-moz-binding:url("http://ha.ckers.org/xssmoz.xml#xss")}', $result);
|
|
break;
|
|
case 'XML data island w/CDATA':
|
|
$this->assertEquals( "<![CDATA[]]>", $result );
|
|
break;
|
|
case 'XML data island w/comment':
|
|
$this->assertEquals( "<I><B><IMG SRC="javas<!-- -->cript:alert('XSS')\"></B></I>", $result );
|
|
break;
|
|
case 'XML HTML+TIME':
|
|
$this->assertEquals( '<t:set attributeName="innerHTML" to="XSSalert(\'XSS\')">', $result );
|
|
break;
|
|
case 'Commented-out Block':
|
|
$this->assertEquals( "<!--[if gte IE 4]>-->\nalert('XSS');", $result );
|
|
break;
|
|
case 'Cookie Manipulation':
|
|
$this->assertEquals( '<META HTTP-EQUIV="Set-Cookie" Content="USERID=alert(\'XSS\')">', $result );
|
|
break;
|
|
case 'SSI':
|
|
$this->assertEquals( '<!--#exec cmd="/bin/echo '<!--#exec cmd="/bin/echo \'=http://ha.ckers.org/xss.js>\'"-->', $result );
|
|
break;
|
|
case 'PHP':
|
|
$this->assertEquals( '<? echo('alert("XSS")\'); ?>', $result );
|
|
break;
|
|
case 'UTF-7 Encoding':
|
|
$this->assertEquals( '+ADw-SCRIPT+AD4-alert(\'XSS\');+ADw-/SCRIPT+AD4-', $result );
|
|
break;
|
|
case 'Escaping JavaScript escapes':
|
|
$this->assertEquals('\";alert(\'XSS\');//', $result);
|
|
break;
|
|
case 'STYLE w/broken up JavaScript':
|
|
$this->assertEquals( '@im\port\'\ja\vasc\ript:alert("XSS")\';', $result );
|
|
break;
|
|
case 'Null Chars 2':
|
|
$this->assertEquals( '&alert("XSS")', $result );
|
|
break;
|
|
case 'No Closing Script Tag':
|
|
$this->assertEquals( '<SCRIPT SRC=http://ha.ckers.org/xss.js', $result );
|
|
break;
|
|
case 'Half-Open HTML/JavaScript':
|
|
$this->assertEquals( '<IMG SRC="javascript:alert('XSS')"', $result );
|
|
break;
|
|
case 'Double open angle brackets':
|
|
$this->assertEquals( '<IFRAME SRC=http://ha.ckers.org/scriptlet.html <', $result );
|
|
break;
|
|
case 'Extraneous Open Brackets':
|
|
$this->assertEquals( '<alert("XSS");//<', $result );
|
|
break;
|
|
case 'Malformed IMG Tags':
|
|
$this->assertEquals('alert("XSS")">', $result);
|
|
break;
|
|
case 'No Quotes/Semicolons':
|
|
$this->assertEquals( "a=/XSS/\nalert(a.source)", $result );
|
|
break;
|
|
case 'Evade Regex Filter 1':
|
|
$this->assertEquals( '" SRC="http://ha.ckers.org/xss.js">', $result );
|
|
break;
|
|
case 'Evade Regex Filter 4':
|
|
$this->assertEquals( '\'" SRC="http://ha.ckers.org/xss.js">', $result );
|
|
break;
|
|
case 'Evade Regex Filter 5':
|
|
$this->assertEquals( '` SRC="http://ha.ckers.org/xss.js">', $result );
|
|
break;
|
|
case 'Filter Evasion 1':
|
|
$this->assertEquals( 'document.write("<SCRI");PT SRC="http://ha.ckers.org/xss.js">', $result );
|
|
break;
|
|
case 'Filter Evasion 2':
|
|
$this->assertEquals( '\'>" SRC="http://ha.ckers.org/xss.js">', $result );
|
|
break;
|
|
default:
|
|
$this->fail( 'KSES failed on ' . $attack->name . ': ' . $result );
|
|
}
|
|
}
|
|
}
|
|
|
|
function _wp_kses_allowed_html_filter( $html, $context ) {
|
|
if ( 'post' == $context )
|
|
return array( 'a' => array( 'href' => true ) );
|
|
else
|
|
return array( 'a' => array( 'href' => false ) );
|
|
}
|
|
|
|
/**
|
|
* @ticket 20210
|
|
*/
|
|
public function test_wp_kses_allowed_html() {
|
|
global $allowedposttags, $allowedtags, $allowedentitynames;
|
|
|
|
$this->assertEquals( $allowedposttags, wp_kses_allowed_html( 'post' ) );
|
|
|
|
$tags = wp_kses_allowed_html( 'post' ) ;
|
|
|
|
foreach ( $tags as $tag ) {
|
|
$this->assertTrue( $tag['class'] );
|
|
$this->assertTrue( $tag['id'] );
|
|
$this->assertTrue( $tag['style'] );
|
|
$this->assertTrue( $tag['title'] );
|
|
}
|
|
|
|
$this->assertEquals( $allowedtags, wp_kses_allowed_html( 'data' ) );
|
|
$this->assertEquals( $allowedtags, wp_kses_allowed_html( '' ) );
|
|
$this->assertEquals( $allowedtags, wp_kses_allowed_html() );
|
|
|
|
$tags = wp_kses_allowed_html( 'user_description' );
|
|
$this->assertTrue( $tags['a']['rel'] );
|
|
|
|
$tags = wp_kses_allowed_html();
|
|
$this->assertFalse( isset( $tags['a']['rel'] ) );
|
|
|
|
$this->assertEquals( array(), wp_kses_allowed_html( 'strip' ) );
|
|
|
|
$custom_tags = array(
|
|
'a' => array(
|
|
'href' => true,
|
|
'rel' => true,
|
|
'rev' => true,
|
|
'name' => true,
|
|
'target' => true,
|
|
),
|
|
);
|
|
|
|
$this->assertEquals( $custom_tags, wp_kses_allowed_html( $custom_tags ) );
|
|
|
|
add_filter( 'wp_kses_allowed_html', array( $this, '_wp_kses_allowed_html_filter' ), 10, 2 );
|
|
|
|
$this->assertEquals( array( 'a' => array( 'href' => true ) ), wp_kses_allowed_html( 'post' ) );
|
|
$this->assertEquals( array( 'a' => array( 'href' => false ) ), wp_kses_allowed_html( 'data' ) );
|
|
|
|
remove_filter( 'wp_kses_allowed_html', array( $this, '_wp_kses_allowed_html_filter' ) );
|
|
$this->assertEquals( $allowedposttags, wp_kses_allowed_html( 'post' ) );
|
|
$this->assertEquals( $allowedtags, wp_kses_allowed_html( 'data' ) );
|
|
}
|
|
|
|
/**
|
|
* @ticket 26290
|
|
*/
|
|
public function test_wp_kses_normalize_entities() {
|
|
$this->assertEquals( '♠', wp_kses_normalize_entities( '♠' ) );
|
|
|
|
$this->assertEquals( '¹', wp_kses_normalize_entities( '¹' ) );
|
|
$this->assertEquals( '²', wp_kses_normalize_entities( '²' ) );
|
|
$this->assertEquals( '³', wp_kses_normalize_entities( '³' ) );
|
|
$this->assertEquals( '¼', wp_kses_normalize_entities( '¼' ) );
|
|
$this->assertEquals( '½', wp_kses_normalize_entities( '½' ) );
|
|
$this->assertEquals( '¾', wp_kses_normalize_entities( '¾' ) );
|
|
$this->assertEquals( '∴', wp_kses_normalize_entities( '∴' ) );
|
|
}
|
|
}
|