diff --git a/model/fieldtypes/HTMLText.php b/model/fieldtypes/HTMLText.php index 46d462aa8..b47049744 100644 --- a/model/fieldtypes/HTMLText.php +++ b/model/fieldtypes/HTMLText.php @@ -55,8 +55,9 @@ class HTMLText extends Text { * - whitelist: If provided, a comma-separated list of elements that will be allowed to be stored * (be careful on relying on this for XSS protection - some seemingly-safe elements allow * attributes that can be exploited, for instance ) - * - * @return void + * Text nodes outside of HTML tags are filtered out by default, but may be included by adding + * the text() directive. E.g. 'link,meta,text()' will allow only and text at + * the root level. */ public function setOptions(array $options = array()) { parent::setOptions($options); @@ -184,20 +185,36 @@ class HTMLText extends Text { } public function prepValueForDB($value) { + return parent::prepValueForDB($this->whitelistContent($value)); + } + + /** + * Filter the given $value string through the whitelist filter + * + * @param string $value Input html content + * @return string Value with all non-whitelisted content stripped (if applicable) + */ + public function whitelistContent($value) { if($this->whitelist) { $dom = Injector::inst()->create('HTMLValue', $value); $query = array(); - foreach ($this->whitelist as $tag) $query[] = 'not(self::'.$tag.')'; + $textFilter = ' | //body/text()'; + foreach ($this->whitelist as $tag) { + if($tag === 'text()') { + $textFilter = ''; // Disable text filter if allowed + } else { + $query[] = 'not(self::'.$tag.')'; + } + } - foreach($dom->query('//body//*['.implode(' and ', $query).']') as $el) { + foreach($dom->query('//body//*['.implode(' and ', $query).']'.$textFilter) as $el) { if ($el->parentNode) $el->parentNode->removeChild($el); } $value = $dom->getContent(); } - - return parent::prepValueForDB($value); + return $value; } /** diff --git a/tests/model/HTMLTextTest.php b/tests/model/HTMLTextTest.php index ac46cd647..4e91688ab 100644 --- a/tests/model/HTMLTextTest.php +++ b/tests/model/HTMLTextTest.php @@ -179,11 +179,17 @@ class HTMLTextTest extends SapphireTest { function testWhitelist() { $textObj = new HTMLText('Test', 'meta,link'); - $this->assertEquals( - $textObj->prepValueForDB(''), - $textObj->prepValueForDB('

Remove

'), - 'Removes any elements not in whitelist' + '', + $textObj->whitelistContent('

Remove

Remove Text'), + 'Removes any elements not in whitelist excluding text elements' + ); + + $textObj = new HTMLText('Test', 'meta,link,text()'); + $this->assertEquals( + 'Keep Text', + $textObj->whitelistContent('

Remove

Keep Text'), + 'Removes any elements not in whitelist including text elements' ); } }