'), t('Make sure line breaking produces matching paragraph tags.'));
$limit = max(ini_get('pcre.backtrack_limit'), ini_get('pcre.recursion_limit'));
$f = _filter_autop($this->randomName($limit));
$this->assertNotEqual($f, '', t('Make sure line breaking can process long strings.'));
}
/**
* Test limiting allowed tags, XSS prevention and adding 'nofollow' to links.
*
* XSS tests assume that script is dissallowed on default and src is allowed
* on default, but on* and style are dissallowed.
*
* 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 testHtmlFilter() {
// Tag stripping, different ways to work around removal of HTML tags.
$f = filter_xss('');
$this->assertNoNormalized($f, 'script', t('HTML tag stripping -- simple script without special characters.'));
$f = filter_xss('');
$this->assertNoNormalized($f, 'script', t('HTML tag stripping -- empty script with source.'));
$f = filter_xss('');
$this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- non whitespace character after tag name.'));
$f = filter_xss('');
$this->assertNoNormalized($f, 'script', t('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', t('HTML tag stripping evasion -- breaking HTML with nulls.'));
$f = filter_xss("");
$this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- filter just removing "script".'));
$f = filter_xss('<');
$this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- double opening brackets.'));
$f = filter_xss('', array('img'));
$this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- a malformed image tag.'));
$f = filter_xss('', array('blockquote'));
$this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- script in a blockqoute.'));
$f = filter_xss("");
$this->assertNoNormalized($f, 'script', t('HTML tag stripping evasion -- script within a comment.'));
// Dangerous attributes removal.
$f = filter_xss('
", array('p'));
$this->assertNoNormalized($f, 'style', t('HTML filter -- invalid UTF-8.'));
$f = filter_xss("\xc0aaa");
$this->assertEqual($f, '', t('HTML filter -- overlong UTF-8 sequences.'));
$f = filter_xss("Who's Online");
$this->assertNormalized($f, "who's online", t('HTML filter -- html entity number'));
$f = filter_xss("Who's Online");
$this->assertNormalized($f, "who's online", t('HTML filter -- encoded html entity number'));
$f = filter_xss("Who&#039; Online");
$this->assertNormalized($f, "who' online", t('HTML filter -- double encoded html entity number'));
}
/**
* Test filter settings, defaults, access restrictions and similar.
*
* @todo This is for functions like filter_filter and check_markup, whose
* functionality is not completely focused on filtering. Some ideas:
* restricting formats according to user permissions, proper cache
* handling, defaults -- allowed tags/attributes/protocols.
*
* @todo It is possible to add script, iframe etc. to allowed tags, but this
* makes HTML filter completely ineffective.
*
* @todo Class, id, name and xmlns should be added to disallowed attributes,
* or better a whitelist approach should be used for that too.
*/
function testFilter() {
// Check that access restriction really works.
// HTML filter is not able to secure some tags, these should never be
// allowed.
$f = filter_filter('process', 0, 'no_such_format', '');
$this->assertNoNormalized($f, 'script', t('HTML filter should always remove script tags.'));
$f = filter_filter('process', 0, 'no_such_format', '');
$this->assertNoNormalized($f, 'iframe', t('HTML filter should always remove iframe tags.'));
$f = filter_filter('process', 0, 'no_such_format', '');
$this->assertNoNormalized($f, 'object', t('HTML filter should always remove object tags.'));
$f = filter_filter('process', 0, 'no_such_format', '');
$this->assertNoNormalized($f, 'style', t('HTML filter should always remove style tags.'));
// Some tags make CSRF attacks easier, let the user take the risk herself.
$f = filter_filter('process', 0, 'no_such_format', '');
$this->assertNoNormalized($f, 'img', t('HTML filter should remove img tags on default.'));
$f = filter_filter('process', 0, 'no_such_format', '');
$this->assertNoNormalized($f, 'img', t('HTML filter should remove input tags on default.'));
// Filtering content of some attributes is infeasible, these shouldn't be
// allowed too.
$f = filter_filter('process', 0, 'no_such_format', '
');
$this->assertNoNormalized($f, 'style', t('HTML filter should remove style attribute on default.'));
$f = filter_filter('process', 0, 'no_such_format', '');
$this->assertNoNormalized($f, 'onerror', t('HTML filter should remove on* attributes on default.'));
}
/**
* Test the spam deterrent.
*/
function testNoFollowFilter() {
variable_set('filter_html_nofollow_f', TRUE);
// Test if the rel="nofollow" attribute is added, even if we try to prevent
// it.
$f = _filter_html('text', 'f');
$this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent -- no evasion.'));
$f = _filter_html('text', 'f');
$this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- capital A.'));
$f = _filter_html("text", 'f');
$this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- non whitespace character after tag name.'));
$f = _filter_html("<\0a\0 href=\"http://www.example.com/\">text", 'f');
$this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- some nulls.'));
$f = _filter_html('', 'f');
$this->assertNormalized($f, 'rel="nofollow"', t('Spam deterrent evasion -- link within a comment.'));
}
/**
* Test the loose, admin HTML filter.
*/
function testAdminHtmlFilter() {
// DRUPAL-SA-2008-044
$f = filter_xss_admin('');
$this->assertNoNormalized($f, 'object', t('Admin HTML filter -- should not allow object tag.'));
$f = filter_xss_admin('');
$this->assertNoNormalized($f, 'script', t('Admin HTML filter -- should not allow script tag.'));
$f = filter_xss_admin('');
$this->assertEqual($f, '', t('Admin HTML filter -- should never allow some tags.'));
}
/**
* Test the HTML escaping filter.
*
* Here we test only whether check_plain() does what it should.
*/
function testNoHtmlFilter() {
// Test that characters that have special meaning in XML are changed into
// entities.
$f = check_plain('<>&"');
$this->assertEqual($f, '<>&"', t('No HTML filter basic test.'));
// A single quote can also be used for evil things in some contexts.
$f = check_plain('\'');
$this->assertEqual($f, ''', t('No HTML filter -- single quote.'));
// Test that the filter is not fooled by different evasion techniques.
$f = check_plain("\xc2\"");
$this->assertEqual($f, '', t('No HTML filter -- invalid UTF-8.'));
}
/**
* Test the URL filter.
*/
function testUrlFilter() {
variable_set('filter_url_length_f', 496);
// Converting URLs.
$f = _filter_url('http://www.example.com/', 'f');
$this->assertEqual($f, 'http://www.example.com/', t('Converting URLs.'));
$f = _filter_url('http://www.example.com/?a=1&b=2', 'f');
$this->assertEqual($f, 'http://www.example.com/?a=1&b=2', t('Converting URLs -- ampersands.'));
$f = _filter_url('ftp://user:pass@ftp.example.com/dir1/dir2', 'f');
$this->assertEqual($f, 'ftp://user:pass@ftp.example.com/dir1/dir2', t('Converting URLs -- FTP scheme.'));
// Converting domain names.
$f = _filter_url('www.example.com', 'f');
$this->assertEqual($f, 'www.example.com', t('Converting domain names.'));
$f = _filter_url('
', t('Converting domain names -- domain in a list.'));
$f = _filter_url('(www.example.com/dir?a=1&b=2#a)', 'f');
$this->assertEqual($f, '(www.example.com/dir?a=1&b=2#a)', t('Converting domain names -- domain in parentheses.'));
// Converting e-mail addresses.
$f = _filter_url('johndoe@example.com', 'f');
$this->assertEqual($f, 'johndoe@example.com', t('Converting e-mail addresses.'));
$f = _filter_url('aaa@sub.tv', 'f');
$this->assertEqual($f, 'aaa@sub.tv', t('Converting e-mail addresses -- a short e-mail from Tuvalu.'));
// URL trimming.
variable_set('filter_url_length_f', 28);
$f = _filter_url('http://www.example.com/d/ff.ext?a=1&b=2#a1', 'f');
$this->assertNormalized($f, 'http://www.example.com/d/ff....', t('URL trimming.'));
// Not breaking existing links.
$f = _filter_url('www.example.com', 'f');
$this->assertEqual($f, 'www.example.com', t('Converting URLs -- do not break existing links.'));
$f = _filter_url('http://www.example.com', 'f');
$this->assertEqual($f, 'http://www.example.com', t('Converting URLs -- do not break existing, relative links.'));
// Addresses within some tags such as code or script should not be converted.
$f = _filter_url('http://www.example.com', 'f');
$this->assertEqual($f, 'http://www.example.com', t('Converting URLs -- skip code contents.'));
$f = _filter_url('http://www.example.com', 'f');
$this->assertEqual($f, 'http://www.example.com', t('Converting URLs -- really skip code contents.'));
$f = _filter_url('', 'f');
$this->assertEqual($f, '', t('Converting URLs -- do not process scripts.'));
// Addresses in attributes should not be converted.
$f = _filter_url('', 'f');
$this->assertEqual($f, '', t('Converting URLs -- do not convert addresses in attributes.'));
$f = _filter_url('text', 'f');
$this->assertEqual($f, 'text', t('Converting URLs -- do not break existing links with custom title attribute.'));
// Even though a dot at the end of a URL can indicate a fully qualified
// domain name, such usage is rare compared to using a link at the end
// of a sentence, so remove the dot from the link.
// @todo It can also be used at the end of a filename or a query string.
$f = _filter_url('www.example.com.', 'f');
$this->assertEqual($f, 'www.example.com.', t('Converting URLs -- do not recognize a dot at the end of a domain name (FQDNs).'));
$f = _filter_url('http://www.example.com.', 'f');
$this->assertEqual($f, 'http://www.example.com.', t('Converting URLs -- do not recognize a dot at the end of an URL (FQDNs).'));
$f = _filter_url('www.example.com/index.php?a=.', 'f');
$this->assertEqual($f, 'www.example.com/index.php?a=.', t('Converting URLs -- do forget about a dot at the end of a query string.'));
}
/**
* Test the HTML corrector.
*
* @todo This test could really use some validity checking function.
*/
function testHtmlCorrector() {
// Tag closing.
$f = _filter_htmlcorrector('
text');
$this->assertEqual($f, '
text
', t('HTML corrector -- tag closing at the end of input.'));
$f = _filter_htmlcorrector('
text
text');
$this->assertEqual($f, '
text
text
', t('HTML corrector -- tag closing.'));
$f = _filter_htmlcorrector("
e1
e2");
$this->assertEqual($f, "
e1
e2
", t('HTML corrector -- unclosed list tags.'));
$f = _filter_htmlcorrector('
content');
$this->assertEqual($f, '
content
', t('HTML corrector -- unclosed tag with attribute.'));
// XHTML slash for empty elements.
$f = _filter_htmlcorrector(' ');
$this->assertEqual($f, ' ', t('HTML corrector -- XHTML closing slash.'));
}
function createFormat($filter) {
$edit = array(
'name' => $this->randomName(),
'roles[2]' => TRUE,
'filters[filter/' . $filter . ']' => TRUE,
);
$this->drupalPost('admin/settings/filter/add', $edit, t('Save configuration'));
return db_query("SELECT * FROM {filter_format} WHERE name = :name", array(':name' => $edit['name']))->fetchObject();
}
function deleteFormat($format) {
if ($format !== NULL) {
$this->drupalPost('admin/settings/formats/delete/' . $format->format, array(), t('Delete'));
}
}
/**
* 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 $haystack
* Text to look in.
* @param $needle
* Lowercase, plain text to look for.
* @param $message
* Message to display if failed.
* @param $group
* The group this message belongs to, defaults to 'Other'.
* @return
* TRUE on pass, FALSE on fail.
*/
function assertNormalized($haystack, $needle, $message = '', $group = 'Other') {
return $this->assertTrue(strpos(strtolower(decode_entities($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 $haystack
* Text to look in.
* @param $needle
* Lowercase, plain text to look for.
* @param $message
* Message to display if failed.
* @param $group
* The group this message belongs to, defaults to 'Other'.
* @return
* TRUE on pass, FALSE on fail.
*/
function assertNoNormalized($haystack, $needle, $message = '', $group = 'Other') {
return $this->assertTrue(strpos(strtolower(decode_entities($haystack)), $needle) === FALSE, $message, $group);
}
}