', 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
- // 
, 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's Online");
- $this->assertNormalized($f, "who's online", 'HTML filter -- encoded html entity number');
-
- $f = filter_xss("Who' Online");
- $this->assertNormalized($f, "who' 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..8b6434b
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
@@ -0,0 +1,290 @@
+ '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.
+ *
+ * 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.
+ */
+ public function testFilterXSS() {
+ // Tag stripping, different ways to work around removal of HTML tags.
+ $f = Xss::filter('');
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping -- simple script without special characters.');
+
+ $f = Xss::filter('');
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping -- empty script with source.');
+
+ $f = Xss::filter('');
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- non whitespace character after tag name.');
+
+ $f = Xss::filter('');
+ $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 = Xss::filter("<\0scr\0ipt>alert(0)");
+ $this->assertNoNormalized($f, 'ipt', 'HTML tag stripping evasion -- breaking HTML with nulls.');
+
+ $f = Xss::filter("");
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- filter just removing "script".');
+
+ $f = Xss::filter('<');
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- double opening brackets.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- a malformed image tag.');
+
+ $f = Xss::filter('', array('blockquote'));
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script in a blockqoute.');
+
+ $f = Xss::filter("");
+ $this->assertNoNormalized($f, 'script', 'HTML tag stripping evasion -- script within a comment.');
+
+ // Dangerous attributes removal.
+ $f = Xss::filter('', array('p'));
+ $this->assertNoNormalized($f, 'onmouseover', 'HTML filter attributes removal -- events, no evasion.');
+
+ $f = Xss::filter('
', array('li'));
+ $this->assertNoNormalized($f, 'style', 'HTML filter attributes removal -- style, no evasion.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'onerror', 'HTML filter attributes removal evasion -- spaces before equals sign.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'onabort', 'HTML filter attributes removal evasion -- non alphanumeric characters before equals sign.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'onmediaerror', 'HTML filter attributes removal evasion -- varying case.');
+
+ // Works at least with IE6.
+ $f = Xss::filter("", array('img'));
+ $this->assertNoNormalized($f, 'focus', 'HTML filter attributes removal evasion -- breaking with nulls.');
+
+ // Only whitelisted scheme names allowed in attributes.
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- no evasion.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no quotes.');
+
+ // A bit like CVE-2006-0070.
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- no alert ;)');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- grave accents.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- rare attribute.');
+
+ $f = Xss::filter('', array('table'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- another tag.');
+
+ $f = Xss::filter('', array('base'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing -- one more attribute and tag.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- varying case.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 decimal encoding.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- long UTF-8 encoding.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- UTF-8 hex encoding.');
+
+ $f = Xss::filter("", array('img'));
+ $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an embedded tab.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded tab.');
+
+ $f = Xss::filter('', 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
+ // 
, so it's OK.
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'script', 'HTML scheme clearing evasion -- an encoded, embedded carriage return.');
+
+ $f = Xss::filter("", array('img'));
+ $this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- broken into many lines.');
+
+ $f = Xss::filter("", array('img'));
+ $this->assertNoNormalized($f, 'cript', 'HTML scheme clearing evasion -- embedded nulls.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'javascript', 'HTML scheme clearing evasion -- spaces and metacharacters before scheme.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'vbscript', 'HTML scheme clearing evasion -- another scheme.');
+
+ $f = Xss::filter('', array('img'));
+ $this->assertNoNormalized($f, 'nosuchscheme', 'HTML scheme clearing evasion -- unknown scheme.');
+
+ // Netscape 4.x javascript entities.
+ $f = Xss::filter('
', 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 = Xss::filter("\" style=\"background-image: url(javascript:alert(0));\"\xe0
", array('p'));
+ $this->assertNoNormalized($f, 'style', 'HTML filter -- invalid UTF-8.');
+
+ $f = Xss::filter("\xc0aaa");
+ $this->assertEquals($f, '', 'HTML filter -- overlong UTF-8 sequences.');
+
+ $f = Xss::filter("Who's Online");
+ $this->assertNormalized($f, "who's online", 'HTML filter -- html entity number');
+
+ $f = Xss::filter("Who's Online");
+ $this->assertNormalized($f, "who's online", 'HTML filter -- encoded html entity number');
+
+ $f = Xss::filter("Who&#039; Online");
+ $this->assertNormalized($f, "who' online", 'HTML filter -- double encoded html entity number');
+ }
+
+ /**
+ * Checks that invalid multi-byte sequences are rejected.
+ */
+ public function testInvalidMultiByte() {
+ $text = Xss::filter("Foo\xC0barbaz");
+ $this->assertEquals($text, '', 'filter_xss() accepted invalid sequence "Foo\xC0barbaz"');
+ $text = Xss::filter("Fooÿñ");
+ $this->assertEquals($text, "Fooÿñ", 'filter_xss() rejects valid sequence Fooÿñ');
+ }
+
+ /**
+ * 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 assertNoNormalized($haystack, $needle, $message = '', $group = 'Other') {
+ $this->assertTrue(strpos(strtolower(String::decodeEntities($haystack)), $needle) === FALSE, $message, $group);
+ }
+
+}