diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php index de9f194..ec5134f 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php @@ -112,188 +112,6 @@ function testLineBreakFilter() { } } - /** - * 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(''); - $this->assertNoNormalized($f, 'script', 'HTML tag stripping -- simple script without special characters.'); - - $f = filter_xss(''); - $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- non whitespace character after tag name.'); - - $f = filter_xss(''); - $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)"); - $this->assertNoNormalized($f, 'ipt', 'HTML tag stripping evasion -- breaking HTML with nulls.'); - - $f = filter_xss(""); - $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- filter just removing "script".'); - - $f = filter_xss('<'); - $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- double opening brackets.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- a malformed image tag.'); - - $f = filter_xss('
', array('blockquote')); - $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script in a blockqoute.'); - - $f = filter_xss(""); - $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script within a comment.'); - - // Dangerous attributes removal. - $f = filter_xss('

', array('p')); - $this->assertNoNormalized($f, 'onmouseover', 'HTML filter attributes removal -- events, no evasion.'); - - $f = filter_xss('

  • ', array('li')); - $this->assertNoNormalized($f, 'style', 'HTML filter attributes removal -- style, no evasion.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'onerror', 'HTML filter attributes removal evasion -- spaces before equals sign.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'onabort', 'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'onmediaerror', 'HTML filter attributes removal evasion -- varying case.'); - - // Works at least with IE6. - $f = filter_xss("", array('img')); - $this->assertNoNormalized($f, 'focus', 'HTML filter attributes removal evasion -- breaking with nulls.'); - - // Only whitelisted scheme names allowed in attributes. - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- no evasion.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no quotes.'); - - // A bit like CVE-2006-0070. - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no alert ;)'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- grave accents.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- rare attribute.'); - - $f = filter_xss('', array('table')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- another tag.'); - - $f = filter_xss('', array('base')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- one more attribute and tag.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- varying case.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 decimal encoding.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- long UTF-8 encoding.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 hex encoding.'); - - $f = filter_xss("", array('img')); - $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an embedded tab.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded tab.'); - - $f = filter_xss('', 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('', array('img')); - $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded carriage return.'); - - $f = filter_xss("", array('img')); - $this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- broken into many lines.'); - - $f = filter_xss("", array('img')); - $this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- embedded nulls.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'vbscript', 'HTML scheme clearing evasion -- another scheme.'); - - $f = filter_xss('', array('img')); - $this->assertNoNormalized($f, 'nosuchscheme', 'HTML scheme clearing evasion -- unknown scheme.'); - - // Netscape 4.x javascript entities. - $f = filter_xss('
    ', 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("

    \" style=\"background-image: url(javascript:alert(0));\"\xe0

    ", 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. diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php index 57863ff..433d145 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php @@ -35,16 +35,6 @@ protected function setUp() { } /** - * 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. */ function testT() { diff --git a/core/tests/Drupal/Tests/Component/Utility/XssTest.php b/core/tests/Drupal/Tests/Component/Utility/XssTest.php new file mode 100644 index 0000000..a15aa0f --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php @@ -0,0 +1,519 @@ + '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($expected, Xss::filter($value), $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() { + return array( + // Tag stripping, different ways to work around removal of HTML tags. + array( + '', + 'script', + 'HTML tag stripping -- simple script without special characters.', + ), + array( + '', + 'script', + 'HTML tag stripping evasion -- non whitespace character after tag name.', + ), + array( + '', + '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)", + 'ipt', + 'HTML tag stripping evasion -- breaking HTML with nulls.', + ), + array( + "", + 'script', + 'HTML tag stripping evasion -- filter just removing "script".', + ), + array( + '<', + 'script', + 'HTML tag stripping evasion -- double opening brackets.', + ), + array( + '', + 'script', + 'HTML tag stripping evasion -- a malformed image tag.', + array('img'), + ), + array( + '

    ', + 'script', + 'HTML tag stripping evasion -- script in a blockqoute.', + array('blockquote'), + ), + array( + "", + 'script', + 'HTML tag stripping evasion -- script within a comment.', + ), + // Dangerous attributes removal. + array( + '

    ', + 'onmouseover', + 'HTML filter attributes removal -- events, no evasion.', + array('p'), + ), + array( + '

  • ', + 'style', + 'HTML filter attributes removal -- style, no evasion.', + array('li'), + ), + array( + '', + 'onerror', + 'HTML filter attributes removal evasion -- spaces before equals sign.', + array('img'), + ), + array( + '', + 'onabort', + 'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.', + array('img'), + ), + array( + '', + 'onmediaerror', + 'HTML filter attributes removal evasion -- varying case.', + array('img'), + ), + // Works at least with IE6. + array( + "", + 'focus', + 'HTML filter attributes removal evasion -- breaking with nulls.', + array('img'), + ), + // Only whitelisted scheme names allowed in attributes. + array( + '', + 'javascript', + 'HTML scheme clearing -- no evasion.', + array('img'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- no quotes.', + array('img'), + ), + // A bit like CVE-2006-0070. + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- no alert ;)', + array('img'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- grave accents.', + array('img'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing -- rare attribute.', + array('img'), + ), + array( + '
  • ', + 'javascript', + 'HTML scheme clearing -- another tag.', + array('table'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing -- one more attribute and tag.', + array('base'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- varying case.', + array('img'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- UTF-8 decimal encoding.', + array('img'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- long UTF-8 encoding.', + array('img'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- UTF-8 hex encoding.', + array('img'), + ), + array( + "", + 'script', + 'HTML scheme clearing evasion -- an embedded tab.', + array('img'), + ), + array( + '', + 'script', + 'HTML scheme clearing evasion -- an encoded, embedded tab.', + array('img'), + ), + array( + '', + '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( + '', + 'script', + 'HTML scheme clearing evasion -- an encoded, embedded carriage return.', + array('img'), + ), + array( + "", + 'cript', + 'HTML scheme clearing evasion -- broken into many lines.', + array('img'), + ), + array( + "", + 'cript', + 'HTML scheme clearing evasion -- embedded nulls.', + array('img'), + ), + array( + '', + 'javascript', + 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.', + array('img'), + ), + array( + '', + 'vbscript', + 'HTML scheme clearing evasion -- another scheme.', + array('img'), + ), + array( + '', + 'nosuchscheme', + 'HTML scheme clearing evasion -- unknown scheme.', + array('img'), + ), + // Netscape 4.x javascript entities. + array( + '
    ', + '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( + "

    \" style=\"background-image: url(javascript:alert(0));\"\xe0

    ", + 'style', + 'HTML filter -- invalid UTF-8.', + array('p'), + ), + ); + } + + /** + * 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(''); + $this->assertTrue(stripos($value, '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); + } + +}