Issue #2006568 by ParisLiakos, dawehner: Convert filter_xss() tests to unit tests.
parent
466c8c4ca5
commit
81f4bc82a1
|
@ -112,188 +112,6 @@ class FilterUnitTest extends DrupalUnitTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests limiting allowed tags and XSS prevention.
|
||||
*
|
||||
* XSS tests assume that script is disallowed by default and src is allowed
|
||||
* by default, but on* and style attributes are disallowed.
|
||||
*
|
||||
* Script injection vectors mostly adopted from http://ha.ckers.org/xss.html.
|
||||
*
|
||||
* Relevant CVEs:
|
||||
* - CVE-2002-1806, ~CVE-2005-0682, ~CVE-2005-2106, CVE-2005-3973,
|
||||
* CVE-2006-1226 (= rev. 1.112?), CVE-2008-0273, CVE-2008-3740.
|
||||
*/
|
||||
function testFilterXSS() {
|
||||
// Tag stripping, different ways to work around removal of HTML tags.
|
||||
$f = filter_xss('<script>alert(0)</script>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping -- simple script without special characters.');
|
||||
|
||||
$f = filter_xss('<script src="http://www.example.com" />');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping -- empty script with source.');
|
||||
|
||||
$f = filter_xss('<ScRipt sRc=http://www.example.com/>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- varying case.');
|
||||
|
||||
$f = filter_xss("<script\nsrc\n=\nhttp://www.example.com/\n>");
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- multiline tag.');
|
||||
|
||||
$f = filter_xss('<script/a src=http://www.example.com/a.js></script>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- non whitespace character after tag name.');
|
||||
|
||||
$f = filter_xss('<script/src=http://www.example.com/a.js></script>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- no space between tag and attribute.');
|
||||
|
||||
// Null between < and tag name works at least with IE6.
|
||||
$f = filter_xss("<\0scr\0ipt>alert(0)</script>");
|
||||
$this->assertNoNormalized($f, 'ipt', 'HTML tag stripping evasion -- breaking HTML with nulls.');
|
||||
|
||||
$f = filter_xss("<scrscriptipt src=http://www.example.com/a.js>");
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- filter just removing "script".');
|
||||
|
||||
$f = filter_xss('<<script>alert(0);//<</script>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- double opening brackets.');
|
||||
|
||||
$f = filter_xss('<script src=http://www.example.com/a.js?<b>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- no closing tag.');
|
||||
|
||||
// DRUPAL-SA-2008-047: This doesn't seem exploitable, but the filter should
|
||||
// work consistently.
|
||||
$f = filter_xss('<script>>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- double closing tag.');
|
||||
|
||||
$f = filter_xss('<script src=//www.example.com/.a>');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- no scheme or ending slash.');
|
||||
|
||||
$f = filter_xss('<script src=http://www.example.com/.a');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- no closing bracket.');
|
||||
|
||||
$f = filter_xss('<script src=http://www.example.com/ <');
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- opening instead of closing bracket.');
|
||||
|
||||
$f = filter_xss('<nosuchtag attribute="newScriptInjectionVector">');
|
||||
$this->assertNoNormalized($f, 'nosuchtag', 'HTML tag stripping evasion -- unknown tag.');
|
||||
|
||||
$f = filter_xss('<?xml:namespace ns="urn:schemas-microsoft-com:time">');
|
||||
$this->assertTrue(stripos($f, '<?xml') === FALSE, 'HTML tag stripping evasion -- starting with a question sign (processing instructions).');
|
||||
|
||||
$f = filter_xss('<t:set attributeName="innerHTML" to="<script defer>alert(0)</script>">');
|
||||
$this->assertNoNormalized($f, 't:set', 'HTML tag stripping evasion -- colon in the tag name (namespaces\' tricks).');
|
||||
|
||||
$f = filter_xss('<img """><script>alert(0)</script>', array('img'));
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- a malformed image tag.');
|
||||
|
||||
$f = filter_xss('<blockquote><script>alert(0)</script></blockquote>', array('blockquote'));
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script in a blockqoute.');
|
||||
|
||||
$f = filter_xss("<!--[if true]><script>alert(0)</script><![endif]-->");
|
||||
$this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script within a comment.');
|
||||
|
||||
// Dangerous attributes removal.
|
||||
$f = filter_xss('<p onmouseover="http://www.example.com/">', array('p'));
|
||||
$this->assertNoNormalized($f, 'onmouseover', 'HTML filter attributes removal -- events, no evasion.');
|
||||
|
||||
$f = filter_xss('<li style="list-style-image: url(javascript:alert(0))">', array('li'));
|
||||
$this->assertNoNormalized($f, 'style', 'HTML filter attributes removal -- style, no evasion.');
|
||||
|
||||
$f = filter_xss('<img onerror =alert(0)>', array('img'));
|
||||
$this->assertNoNormalized($f, 'onerror', 'HTML filter attributes removal evasion -- spaces before equals sign.');
|
||||
|
||||
$f = filter_xss('<img onabort!#$%&()*~+-_.,:;?@[/|\]^`=alert(0)>', array('img'));
|
||||
$this->assertNoNormalized($f, 'onabort', 'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.');
|
||||
|
||||
$f = filter_xss('<img oNmediAError=alert(0)>', array('img'));
|
||||
$this->assertNoNormalized($f, 'onmediaerror', 'HTML filter attributes removal evasion -- varying case.');
|
||||
|
||||
// Works at least with IE6.
|
||||
$f = filter_xss("<img o\0nfocus\0=alert(0)>", array('img'));
|
||||
$this->assertNoNormalized($f, 'focus', 'HTML filter attributes removal evasion -- breaking with nulls.');
|
||||
|
||||
// Only whitelisted scheme names allowed in attributes.
|
||||
$f = filter_xss('<img src="javascript:alert(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- no evasion.');
|
||||
|
||||
$f = filter_xss('<img src=javascript:alert(0)>', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no quotes.');
|
||||
|
||||
// A bit like CVE-2006-0070.
|
||||
$f = filter_xss('<img src="javascript:confirm(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no alert ;)');
|
||||
|
||||
$f = filter_xss('<img src=`javascript:alert(0)`>', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- grave accents.');
|
||||
|
||||
$f = filter_xss('<img dynsrc="javascript:alert(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- rare attribute.');
|
||||
|
||||
$f = filter_xss('<table background="javascript:alert(0)">', array('table'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- another tag.');
|
||||
|
||||
$f = filter_xss('<base href="javascript:alert(0);//">', array('base'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- one more attribute and tag.');
|
||||
|
||||
$f = filter_xss('<img src="jaVaSCriPt:alert(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- varying case.');
|
||||
|
||||
$f = filter_xss('<img src=javascript:alert(0)>', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 decimal encoding.');
|
||||
|
||||
$f = filter_xss('<img src=javascript:alert(0)>', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- long UTF-8 encoding.');
|
||||
|
||||
$f = filter_xss('<img src=javascript:alert(0)>', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 hex encoding.');
|
||||
|
||||
$f = filter_xss("<img src=\"jav\tascript:alert(0)\">", array('img'));
|
||||
$this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an embedded tab.');
|
||||
|
||||
$f = filter_xss('<img src="jav	ascript:alert(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded tab.');
|
||||
|
||||
$f = filter_xss('<img src="jav
ascript:alert(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded newline.');
|
||||
|
||||
// With 
 this test would fail, but the entity gets turned into
|
||||
// &#xD;, so it's OK.
|
||||
$f = filter_xss('<img src="jav
ascript:alert(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded carriage return.');
|
||||
|
||||
$f = filter_xss("<img src=\"\n\n\nj\na\nva\ns\ncript:alert(0)\">", array('img'));
|
||||
$this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- broken into many lines.');
|
||||
|
||||
$f = filter_xss("<img src=\"jav\0a\0\0cript:alert(0)\">", array('img'));
|
||||
$this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- embedded nulls.');
|
||||
|
||||
$f = filter_xss('<img src="  javascript:alert(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.');
|
||||
|
||||
$f = filter_xss('<img src="vbscript:msgbox(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'vbscript', 'HTML scheme clearing evasion -- another scheme.');
|
||||
|
||||
$f = filter_xss('<img src="nosuchscheme:notice(0)">', array('img'));
|
||||
$this->assertNoNormalized($f, 'nosuchscheme', 'HTML scheme clearing evasion -- unknown scheme.');
|
||||
|
||||
// Netscape 4.x javascript entities.
|
||||
$f = filter_xss('<br size="&{alert(0)}">', array('br'));
|
||||
$this->assertNoNormalized($f, 'alert', 'Netscape 4.x javascript entities.');
|
||||
|
||||
// DRUPAL-SA-2008-006: Invalid UTF-8, these only work as reflected XSS with
|
||||
// Internet Explorer 6.
|
||||
$f = filter_xss("<p arg=\"\xe0\">\" style=\"background-image: url(javascript:alert(0));\"\xe0<p>", array('p'));
|
||||
$this->assertNoNormalized($f, 'style', 'HTML filter -- invalid UTF-8.');
|
||||
|
||||
$f = filter_xss("\xc0aaa");
|
||||
$this->assertEqual($f, '', 'HTML filter -- overlong UTF-8 sequences.');
|
||||
|
||||
$f = filter_xss("Who's Online");
|
||||
$this->assertNormalized($f, "who's online", 'HTML filter -- html entity number');
|
||||
|
||||
$f = filter_xss("Who&#039;s Online");
|
||||
$this->assertNormalized($f, "who's online", 'HTML filter -- encoded html entity number');
|
||||
|
||||
$f = filter_xss("Who&amp;#039; Online");
|
||||
$this->assertNormalized($f, "who&#039; online", 'HTML filter -- double encoded html entity number');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests filter settings, defaults, access restrictions and similar.
|
||||
|
@ -382,21 +200,6 @@ class FilterUnitTest extends DrupalUnitTestBase {
|
|||
$this->assertNormalized($f, 'rel="nofollow"', 'Spam deterrent evasion -- with rel set - rel="nofollow" added.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the loose, admin HTML filter.
|
||||
*/
|
||||
function testFilterXSSAdmin() {
|
||||
// DRUPAL-SA-2008-044
|
||||
$f = filter_xss_admin('<object />');
|
||||
$this->assertNoNormalized($f, 'object', 'Admin HTML filter -- should not allow object tag.');
|
||||
|
||||
$f = filter_xss_admin('<script />');
|
||||
$this->assertNoNormalized($f, 'script', 'Admin HTML filter -- should not allow script tag.');
|
||||
|
||||
$f = filter_xss_admin('<style /><iframe /><frame /><frameset /><meta /><link /><embed /><applet /><param /><layer />');
|
||||
$this->assertEqual($f, '', 'Admin HTML filter -- should never allow some tags.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the HTML escaping filter.
|
||||
*
|
||||
|
|
|
@ -34,16 +34,6 @@ class XssUnitTest extends DrupalUnitTestBase {
|
|||
config_install_default_config('module', 'system');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that invalid multi-byte sequences are rejected.
|
||||
*/
|
||||
function testInvalidMultiByte() {
|
||||
$text = filter_xss("Foo\xC0barbaz");
|
||||
$this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"');
|
||||
$text = filter_xss("Fooÿñ");
|
||||
$this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests t() functionality.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,567 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\Component\Utility\XssTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\Component\Utility;
|
||||
|
||||
use Drupal\Component\Utility\String;
|
||||
use Drupal\Component\Utility\UrlValidator;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Tests the Xss utility.
|
||||
*
|
||||
* Script injection vectors mostly adopted from http://ha.ckers.org/xss.html.
|
||||
*
|
||||
* Relevant CVEs:
|
||||
* - CVE-2002-1806, ~CVE-2005-0682, ~CVE-2005-2106, CVE-2005-3973,
|
||||
* CVE-2006-1226 (= rev. 1.112?), CVE-2008-0273, CVE-2008-3740.
|
||||
*
|
||||
* @see \Drupal\Component\Utility\Xss
|
||||
*/
|
||||
class XssTest extends UnitTestCase {
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Xss filter tests',
|
||||
'description' => 'Confirm that Xss::filter() works as expected.',
|
||||
'group' => 'Common',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$allowed_protocols = array(
|
||||
'http',
|
||||
'https',
|
||||
'ftp',
|
||||
'news',
|
||||
'nntp',
|
||||
'telnet',
|
||||
'mailto',
|
||||
'irc',
|
||||
'ssh',
|
||||
'sftp',
|
||||
'webcal',
|
||||
'rtsp',
|
||||
);
|
||||
UrlValidator::setAllowedProtocols($allowed_protocols);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests limiting allowed tags and XSS prevention.
|
||||
*
|
||||
* XSS tests assume that script is disallowed by default and src is allowed
|
||||
* by default, but on* and style attributes are disallowed.
|
||||
*
|
||||
* @param string $value
|
||||
* The value to filter.
|
||||
* @param string $expected
|
||||
* The expected result.
|
||||
* @param string $message
|
||||
* The assertion message to display upon failure.
|
||||
*
|
||||
* @dataProvider providerTestFilterXssNormalized
|
||||
*/
|
||||
public function testFilterXssNormalized($value, $expected, $message) {
|
||||
$this->assertNormalized(Xss::filter($value), $expected, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testFilterXssNormalized().
|
||||
*
|
||||
* @see testFilterXssNormalized()
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays containing strings:
|
||||
* - The value to filter.
|
||||
* - The value to expect after filtering.
|
||||
* - The assertion message.
|
||||
*/
|
||||
public function providerTestFilterXssNormalized() {
|
||||
return array(
|
||||
array(
|
||||
"Who's Online",
|
||||
"who's online",
|
||||
'HTML filter -- html entity number',
|
||||
),
|
||||
array(
|
||||
"Who&#039;s Online",
|
||||
"who's online",
|
||||
'HTML filter -- encoded html entity number',
|
||||
),
|
||||
array(
|
||||
"Who&amp;#039; Online",
|
||||
"who&#039; online",
|
||||
'HTML filter -- double encoded html entity number',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests limiting allowed tags and XSS prevention.
|
||||
*
|
||||
* XSS tests assume that script is disallowed by default and src is allowed
|
||||
* by default, but on* and style attributes are disallowed.
|
||||
*
|
||||
* @param string $value
|
||||
* The value to filter.
|
||||
* @param string $expected
|
||||
* The expected result.
|
||||
* @param string $message
|
||||
* The assertion message to display upon failure.
|
||||
* @param array $allowed_tags
|
||||
* (Optional) The allowed tags to be passed on Xss::filter().
|
||||
*
|
||||
* @dataProvider providerTestFilterXssNotNormalized
|
||||
*/
|
||||
public function testFilterXssNotNormalized($value, $expected, $message, array $allowed_tags = NULL) {
|
||||
if ($allowed_tags === NULL) {
|
||||
$value = Xss::filter($value);
|
||||
}
|
||||
else {
|
||||
$value = Xss::filter($value, $allowed_tags);
|
||||
}
|
||||
$this->assertNotNormalized($value, $expected, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testFilterXssNotNormalized().
|
||||
*
|
||||
* @see testFilterXssNotNormalized()
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays containing the following elements:
|
||||
* - The value to filter string.
|
||||
* - The value to expect after filtering string.
|
||||
* - The assertion message string.
|
||||
* - (optional) The allowed html tags array that should be passed to
|
||||
* Xss::filter().
|
||||
*/
|
||||
public function providerTestFilterXssNotNormalized() {
|
||||
$cases = array(
|
||||
// Tag stripping, different ways to work around removal of HTML tags.
|
||||
array(
|
||||
'<script>alert(0)</script>',
|
||||
'script',
|
||||
'HTML tag stripping -- simple script without special characters.',
|
||||
),
|
||||
array(
|
||||
'<script src="http://www.example.com" />',
|
||||
'script',
|
||||
'HTML tag stripping -- empty script with source.',
|
||||
),
|
||||
array(
|
||||
'<ScRipt sRc=http://www.example.com/>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- varying case.',
|
||||
),
|
||||
array(
|
||||
"<script\nsrc\n=\nhttp://www.example.com/\n>",
|
||||
'script',
|
||||
'HTML tag stripping evasion -- multiline tag.',
|
||||
),
|
||||
array(
|
||||
'<script/a src=http://www.example.com/a.js></script>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- non whitespace character after tag name.',
|
||||
),
|
||||
array(
|
||||
'<script/src=http://www.example.com/a.js></script>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- no space between tag and attribute.',
|
||||
),
|
||||
// Null between < and tag name works at least with IE6.
|
||||
array(
|
||||
"<\0scr\0ipt>alert(0)</script>",
|
||||
'ipt',
|
||||
'HTML tag stripping evasion -- breaking HTML with nulls.',
|
||||
),
|
||||
array(
|
||||
"<scrscriptipt src=http://www.example.com/a.js>",
|
||||
'script',
|
||||
'HTML tag stripping evasion -- filter just removing "script".',
|
||||
),
|
||||
array(
|
||||
'<<script>alert(0);//<</script>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- double opening brackets.',
|
||||
),
|
||||
array(
|
||||
'<script src=http://www.example.com/a.js?<b>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- no closing tag.',
|
||||
),
|
||||
// DRUPAL-SA-2008-047: This doesn't seem exploitable, but the filter should
|
||||
// work consistently.
|
||||
array(
|
||||
'<script>>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- double closing tag.',
|
||||
),
|
||||
array(
|
||||
'<script src=//www.example.com/.a>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- no scheme or ending slash.',
|
||||
),
|
||||
array(
|
||||
'<script src=http://www.example.com/.a',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- no closing bracket.',
|
||||
),
|
||||
array(
|
||||
'<script src=http://www.example.com/ <',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- opening instead of closing bracket.',
|
||||
),
|
||||
array(
|
||||
'<nosuchtag attribute="newScriptInjectionVector">',
|
||||
'nosuchtag',
|
||||
'HTML tag stripping evasion -- unknown tag.',
|
||||
),
|
||||
array(
|
||||
'<t:set attributeName="innerHTML" to="<script defer>alert(0)</script>">',
|
||||
't:set',
|
||||
'HTML tag stripping evasion -- colon in the tag name (namespaces\' tricks).',
|
||||
),
|
||||
array(
|
||||
'<img """><script>alert(0)</script>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- a malformed image tag.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<blockquote><script>alert(0)</script></blockquote>',
|
||||
'script',
|
||||
'HTML tag stripping evasion -- script in a blockqoute.',
|
||||
array('blockquote'),
|
||||
),
|
||||
array(
|
||||
"<!--[if true]><script>alert(0)</script><![endif]-->",
|
||||
'script',
|
||||
'HTML tag stripping evasion -- script within a comment.',
|
||||
),
|
||||
// Dangerous attributes removal.
|
||||
array(
|
||||
'<p onmouseover="http://www.example.com/">',
|
||||
'onmouseover',
|
||||
'HTML filter attributes removal -- events, no evasion.',
|
||||
array('p'),
|
||||
),
|
||||
array(
|
||||
'<li style="list-style-image: url(javascript:alert(0))">',
|
||||
'style',
|
||||
'HTML filter attributes removal -- style, no evasion.',
|
||||
array('li'),
|
||||
),
|
||||
array(
|
||||
'<img onerror =alert(0)>',
|
||||
'onerror',
|
||||
'HTML filter attributes removal evasion -- spaces before equals sign.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img onabort!#$%&()*~+-_.,:;?@[/|\]^`=alert(0)>',
|
||||
'onabort',
|
||||
'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img oNmediAError=alert(0)>',
|
||||
'onmediaerror',
|
||||
'HTML filter attributes removal evasion -- varying case.',
|
||||
array('img'),
|
||||
),
|
||||
// Works at least with IE6.
|
||||
array(
|
||||
"<img o\0nfocus\0=alert(0)>",
|
||||
'focus',
|
||||
'HTML filter attributes removal evasion -- breaking with nulls.',
|
||||
array('img'),
|
||||
),
|
||||
// Only whitelisted scheme names allowed in attributes.
|
||||
array(
|
||||
'<img src="javascript:alert(0)">',
|
||||
'javascript',
|
||||
'HTML scheme clearing -- no evasion.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src=javascript:alert(0)>',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- no quotes.',
|
||||
array('img'),
|
||||
),
|
||||
// A bit like CVE-2006-0070.
|
||||
array(
|
||||
'<img src="javascript:confirm(0)">',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- no alert ;)',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src=`javascript:alert(0)`>',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- grave accents.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img dynsrc="javascript:alert(0)">',
|
||||
'javascript',
|
||||
'HTML scheme clearing -- rare attribute.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<table background="javascript:alert(0)">',
|
||||
'javascript',
|
||||
'HTML scheme clearing -- another tag.',
|
||||
array('table'),
|
||||
),
|
||||
array(
|
||||
'<base href="javascript:alert(0);//">',
|
||||
'javascript',
|
||||
'HTML scheme clearing -- one more attribute and tag.',
|
||||
array('base'),
|
||||
),
|
||||
array(
|
||||
'<img src="jaVaSCriPt:alert(0)">',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- varying case.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src=javascript:alert(0)>',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- UTF-8 decimal encoding.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src=javascript:alert(0)>',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- long UTF-8 encoding.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src=javascript:alert(0)>',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- UTF-8 hex encoding.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
"<img src=\"jav\tascript:alert(0)\">",
|
||||
'script',
|
||||
'HTML scheme clearing evasion -- an embedded tab.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src="jav	ascript:alert(0)">',
|
||||
'script',
|
||||
'HTML scheme clearing evasion -- an encoded, embedded tab.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src="jav
ascript:alert(0)">',
|
||||
'script',
|
||||
'HTML scheme clearing evasion -- an encoded, embedded newline.',
|
||||
array('img'),
|
||||
),
|
||||
// With 
 this test would fail, but the entity gets turned into
|
||||
// &#xD;, so it's OK.
|
||||
array(
|
||||
'<img src="jav
ascript:alert(0)">',
|
||||
'script',
|
||||
'HTML scheme clearing evasion -- an encoded, embedded carriage return.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
"<img src=\"\n\n\nj\na\nva\ns\ncript:alert(0)\">",
|
||||
'cript',
|
||||
'HTML scheme clearing evasion -- broken into many lines.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
"<img src=\"jav\0a\0\0cript:alert(0)\">",
|
||||
'cript',
|
||||
'HTML scheme clearing evasion -- embedded nulls.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src="vbscript:msgbox(0)">',
|
||||
'vbscript',
|
||||
'HTML scheme clearing evasion -- another scheme.',
|
||||
array('img'),
|
||||
),
|
||||
array(
|
||||
'<img src="nosuchscheme:notice(0)">',
|
||||
'nosuchscheme',
|
||||
'HTML scheme clearing evasion -- unknown scheme.',
|
||||
array('img'),
|
||||
),
|
||||
// Netscape 4.x javascript entities.
|
||||
array(
|
||||
'<br size="&{alert(0)}">',
|
||||
'alert',
|
||||
'Netscape 4.x javascript entities.',
|
||||
array('br'),
|
||||
),
|
||||
// DRUPAL-SA-2008-006: Invalid UTF-8, these only work as reflected XSS with
|
||||
// Internet Explorer 6.
|
||||
array(
|
||||
"<p arg=\"\xe0\">\" style=\"background-image: url(javascript:alert(0));\"\xe0<p>",
|
||||
'style',
|
||||
'HTML filter -- invalid UTF-8.',
|
||||
array('p'),
|
||||
),
|
||||
);
|
||||
// @fixme This dataset currently fails under 5.4 because of
|
||||
// https://drupal.org/node/1210798 . Restore after its fixed.
|
||||
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
|
||||
$cases[] = array(
|
||||
'<img src="  javascript:alert(0)">',
|
||||
'javascript',
|
||||
'HTML scheme clearing evasion -- spaces and metacharacters before scheme.',
|
||||
array('img'),
|
||||
);
|
||||
}
|
||||
return $cases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that invalid multi-byte sequences are rejected.
|
||||
*
|
||||
* @param string $value
|
||||
* The value to filter.
|
||||
* @param string $expected
|
||||
* The expected result.
|
||||
* @param string $message
|
||||
* The assertion message to display upon failure.
|
||||
*
|
||||
* @dataProvider providerTestInvalidMultiByte
|
||||
*/
|
||||
public function testInvalidMultiByte($value, $expected, $message) {
|
||||
$this->assertEquals(Xss::filter($value), $expected, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testInvalidMultiByte().
|
||||
*
|
||||
* @see testInvalidMultiByte()
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays containing strings:
|
||||
* - The value to filter.
|
||||
* - The value to expect after filtering.
|
||||
* - The assertion message.
|
||||
*/
|
||||
public function providerTestInvalidMultiByte() {
|
||||
return array(
|
||||
array("Foo\xC0barbaz", '', 'Xss::filter() accepted invalid sequence "Foo\xC0barbaz"'),
|
||||
array("Fooÿñ", "Fooÿñ", 'Xss::filter() rejects valid sequence Fooÿñ"'),
|
||||
array("\xc0aaa", '', 'HTML filter -- overlong UTF-8 sequences.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that strings starting with a question sign are correctly processed.
|
||||
*/
|
||||
public function testQuestionSign() {
|
||||
$value = Xss::filter('<?xml:namespace ns="urn:schemas-microsoft-com:time">');
|
||||
$this->assertTrue(stripos($value, '<?xml') === FALSE, 'HTML tag stripping evasion -- starting with a question sign (processing instructions).');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that Xss::filterAdmin() correctly strips unallowed tags.
|
||||
*/
|
||||
public function testFilterXSSAdmin() {
|
||||
$value = Xss::filterAdmin('<style /><iframe /><frame /><frameset /><meta /><link /><embed /><applet /><param /><layer />');
|
||||
$this->assertEquals($value, '', 'Admin HTML filter -- should never allow some tags.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the loose, admin HTML filter.
|
||||
*
|
||||
* @param string $value
|
||||
* The value to filter.
|
||||
* @param string $expected
|
||||
* The expected result.
|
||||
* @param string $message
|
||||
* The assertion message to display upon failure.
|
||||
*
|
||||
* @dataProvider providerTestFilterXssAdminNotNormalized
|
||||
*/
|
||||
public function testFilterXssAdminNotNormalized($value, $expected, $message) {
|
||||
$this->assertNotNormalized(Xss::filterAdmin($value), $expected, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testFilterXssAdminNotNormalized().
|
||||
*
|
||||
* @see testFilterXssAdminNotNormalized()
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays containing strings:
|
||||
* - The value to filter.
|
||||
* - The value to expect after filtering.
|
||||
* - The assertion message.
|
||||
*/
|
||||
public function providerTestFilterXssAdminNotNormalized() {
|
||||
return array(
|
||||
// DRUPAL-SA-2008-044
|
||||
array('<object />', 'object', 'Admin HTML filter -- should not allow object tag.'),
|
||||
array('<script />', 'script', 'Admin HTML filter -- should not allow script tag.'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a text transformed to lowercase with HTML entities decoded does contains a given string.
|
||||
*
|
||||
* Otherwise fails the test with a given message, similar to all the
|
||||
* SimpleTest assert* functions.
|
||||
*
|
||||
* Note that this does not remove nulls, new lines and other characters that
|
||||
* could be used to obscure a tag or an attribute name.
|
||||
*
|
||||
* @param string $haystack
|
||||
* Text to look in.
|
||||
* @param string $needle
|
||||
* Lowercase, plain text to look for.
|
||||
* @param string $message
|
||||
* (optional) Message to display if failed. Defaults to an empty string.
|
||||
* @param string $group
|
||||
* (optional) The group this message belongs to. Defaults to 'Other'.
|
||||
*/
|
||||
protected function assertNormalized($haystack, $needle, $message = '', $group = 'Other') {
|
||||
$this->assertTrue(strpos(strtolower(String::decodeEntities($haystack)), $needle) !== FALSE, $message, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that text transformed to lowercase with HTML entities decoded does not contain a given string.
|
||||
*
|
||||
* Otherwise fails the test with a given message, similar to all the
|
||||
* SimpleTest assert* functions.
|
||||
*
|
||||
* Note that this does not remove nulls, new lines, and other character that
|
||||
* could be used to obscure a tag or an attribute name.
|
||||
*
|
||||
* @param string $haystack
|
||||
* Text to look in.
|
||||
* @param string $needle
|
||||
* Lowercase, plain text to look for.
|
||||
* @param string $message
|
||||
* (optional) Message to display if failed. Defaults to an empty string.
|
||||
* @param string $group
|
||||
* (optional) The group this message belongs to. Defaults to 'Other'.
|
||||
*/
|
||||
protected function assertNotNormalized($haystack, $needle, $message = '', $group = 'Other') {
|
||||
$this->assertTrue(strpos(strtolower(String::decodeEntities($haystack)), $needle) === FALSE, $message, $group);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue