diff --git a/lib/simpletest/testhtmlpurifier.php b/lib/simpletest/testhtmlpurifier.php index b138becfb77..65725cbce29 100644 --- a/lib/simpletest/testhtmlpurifier.php +++ b/lib/simpletest/testhtmlpurifier.php @@ -193,6 +193,63 @@ class htmlpurifier_test extends UnitTestCase { $text = 'x
x'; $this->assertIdentical('xx', purify_html($text)); } + + /** + * Test internal function used for clean_text() speedup. + */ + function test_is_purify_html_necessary() { + // first our shortcuts + $text = ""; + $this->assertFalse(is_purify_html_necessary($text)); + $this->assertidentical($text, purify_html($text)); + + $text = "666"; + $this->assertFalse(is_purify_html_necessary($text)); + $this->assertidentical($text, purify_html($text)); + + $text = "abc\ndef \" ' "; + $this->assertFalse(is_purify_html_necessary($text)); + $this->assertidentical($text, purify_html($text)); + + $text = "abc\n

def

efg

hij

"; + $this->assertFalse(is_purify_html_necessary($text)); + $this->assertidentical($text, purify_html($text)); + + $text = "
abc\n

defefghi
j

"; + $this->assertFalse(is_purify_html_necessary($text)); + $this->assertidentical($text, purify_html($text)); + + // now failures + $text = " "; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "Gin & Tonic"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "Gin > Tonic"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "Gin < Tonic"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "
abc
"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "abc"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "
abc"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "

abc

"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "

abc

"; + $this->assertTrue(is_purify_html_necessary($text)); + + $text = "

abc"; + $this->assertTrue(is_purify_html_necessary($text)); + + } } - diff --git a/lib/weblib.php b/lib/weblib.php index 22141d27b4e..feb615bcf8d 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -1450,9 +1450,7 @@ function trusttext_active() { * @return string The cleaned up text */ function clean_text($text, $format = FORMAT_HTML, $options = array()) { - if (empty($text) or is_numeric($text)) { - return (string)$text; - } + $text = (string)$text; if ($format != FORMAT_HTML and $format != FORMAT_HTML) { // TODO: we need to standardise cleanup of text when loading it into editor first @@ -1463,7 +1461,9 @@ function clean_text($text, $format = FORMAT_HTML, $options = array()) { return $text; } - $text = purify_html($text, $options); + if (is_purify_html_necessary($text)) { + $text = purify_html($text, $options); + } // Originally we tried to neutralise some script events here, it was a wrong approach because // it was trivial to work around that (for example using style based XSS exploits). @@ -1473,6 +1473,53 @@ function clean_text($text, $format = FORMAT_HTML, $options = array()) { return $text; } +/** + * Is it necessary to use HTMLPurifier? + * @private + * @param string $text + * @return bool false means html is safe and valid, true means use HTMLPurifier + */ +function is_purify_html_necessary($text) { + if ($text === '') { + return false; + } + + if ($text === (string)((int)$text)) { + return false; + } + + if (strpos($text, '&') !== false or preg_match('|<[^pesb/]|', $text)) { + // we need to normalise entities or other tags except p, em, strong and br present + return true; + } + + $altered = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8', true); + if ($altered === $text) { + // no < > or other special chars means this must be safe + return false; + } + + // let's try to convert back some safe html tags + $altered = preg_replace('|<p>(.*?)</p>|m', '

$1

', $altered); + if ($altered === $text) { + return false; + } + $altered = preg_replace('|<em>([^<>]+?)</em>|m', '$1', $altered); + if ($altered === $text) { + return false; + } + $altered = preg_replace('|<strong>([^<>]+?)</strong>|m', '$1', $altered); + if ($altered === $text) { + return false; + } + $altered = str_replace('<br />', '
', $altered); + if ($altered === $text) { + return false; + } + + return true; +} + /** * KSES replacement cleaning function - uses HTML Purifier. *