NEW Allow specifying element whitelist in HTMLText fields

This commit is contained in:
Hamish Friedlander 2014-02-17 11:22:26 +13:00
parent 30e3904e4c
commit d6630d240a
3 changed files with 74 additions and 1 deletions

View File

@ -0,0 +1,13 @@
# 3.1.3-rc1
## Overview
* ExtraMeta fields can now only contain `meta` and `link` elements
## Upgrading
### ExtraMeta fields can now only contain `meta` and `link` elements
Previously ExtraMeta fields could contain any HTML elements. From 3.1.3-rc1 the contents are filtered
on write to only allow `meta` and `link` elements. The first time after upgrading that you save a page
that has other elements in ExtraMeta they will be deleted.

View File

@ -34,12 +34,45 @@ class HTMLText extends Text {
protected $processShortcodes = true;
protected $whitelist = false;
public function __construct($name = null, $options = array()) {
if(is_string($options)) {
$options = array('whitelist' => $options);
}
return parent::__construct($name, $options);
}
/**
* @param array $options
*
* Options accepted in addition to those provided by Text:
*
* - shortcodes: If true, shortcodes will be turned into the appropriate HTML.
* If false, shortcodes will not be processed.
*
* - 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 <img onload="exploiting_code();" src="..." />)
*
* @return void
*/
public function setOptions(array $options = array()) {
parent::setOptions($options);
if(array_key_exists("shortcodes", $options)) {
$this->processShortcodes = !!$options["shortcodes"];
}
if(array_key_exists("whitelist", $options)) {
if(is_array($options['whitelist'])) {
$this->whitelist = $options['whitelist'];
}
else {
$this->whitelist = preg_split('/,\s*/', $options['whitelist']);
}
}
}
/**
@ -149,7 +182,24 @@ class HTMLText extends Text {
return $this->value;
}
}
public function prepValueForDB($value) {
if($this->whitelist) {
$dom = Injector::inst()->create('HTMLValue', $value);
$query = array();
foreach ($this->whitelist as $tag) $query[] = 'not(self::'.$tag.')';
foreach($dom->query('//body//*['.implode(' and ', $query).']') as $el) {
if ($el->parentNode) $el->parentNode->removeChild($el);
}
$value = $dom->getContent();
}
return parent::prepValueForDB($value);
}
/**
* Returns true if the field has meaningful content.
* Excludes null content like <h1></h1>, <p></p> ,etc

View File

@ -176,4 +176,14 @@ class HTMLTextTest extends SapphireTest {
$h->setValue("<p>test</p>");
$this->assertTrue($h->exists());
}
function testWhitelist() {
$textObj = new HTMLText('Test', 'meta,link');
$this->assertEquals(
$textObj->prepValueForDB('<meta content="Keep"><link href="Also Keep">'),
$textObj->prepValueForDB('<meta content="Keep"><p>Remove</p><link href="Also Keep" />'),
'Removes any elements not in whitelist'
);
}
}