1
0
mirror of https://github.com/ezyang/htmlpurifier.git synced 2025-07-30 19:00:10 +02:00

[3.0.0] Add global scoping support for ExtractStyleBlocks; scoped="" attribute bumped off for some 'other' time.

git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1478 48356398-32a2-884e-a903-53898d9a118a
This commit is contained in:
Edward Z. Yang
2008-01-05 19:19:55 +00:00
parent a7fab00cdd
commit 8779b46fc4
6 changed files with 182 additions and 28 deletions

View File

@@ -73,6 +73,7 @@ class HTMLPurifier_ConfigSchema {
$this->defineNamespace('CSS', 'Configuration regarding allowed CSS.');
$this->defineNamespace('AutoFormat', 'Configuration for activating auto-formatting functionality (also known as <code>Injector</code>s)');
$this->defineNamespace('AutoFormatParam', 'Configuration for customizing auto-formatting functionality');
$this->defineNamespace('Filter', 'Configuration for filters');
$this->defineNamespace('Output', 'Configuration relating to the generation of (X)HTML.');
$this->defineNamespace('Cache', 'Configuration for DefinitionCache and related subclasses.');
$this->defineNamespace('Test', 'Developer testing configuration for our unit tests.');

View File

@@ -2,32 +2,76 @@
require_once 'HTMLPurifier/Filter.php';
HTMLPurifier_ConfigSchema::define(
'Filter', 'ExtractStyleBlocksEscaping', true, 'bool', '
<p>
Whether or not to escape the dangerous characters &lt;, &gt; and &amp;
as \3C, \3E and \26, respectively. This is can be safely set to false
if the contents of StyleBlocks will be placed in an external stylesheet,
where there is no risk of it being interpreted as HTML. This directive
has been available since 3.0.0.
</p>
'
);
HTMLPurifier_ConfigSchema::define(
'Filter', 'ExtractStyleBlocksScope', null, 'string/null', '
<p>
If you would like users to be able to define external stylesheets, but
only allow them to specify CSS declarations for a specific node and
prevent them from fiddling with other elements, use this directive.
It accepts any valid CSS selector, and will prepend this to any
CSS declaration extracted from the document. For example, if this
directive is set to <code>#user-content</code> and a user uses the
selector <code>a:hover</code>, the final selector will be
<code>#user-content a:hover</code>.
</p>
<p>
The comma shorthand may be used; consider the above example, with
<code>#user-content, #user-content2</code>, the final selector will
be <code>#user-content a:hover, #user-content2 a:hover</code>.
</p>
<p>
<strong>Warning:</strong> It is possible for users to bypass this measure
using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML
Purifier, and I am working to get it fixed. Until then, HTML Purifier
performs a basic check to prevent this.
</p>
<p>
This directive has been available since 3.0.0.
</p>
'
);
/**
* This filter extracts <style> blocks from input HTML, cleans them up
* using CSSTidy, and then places them in $purifier->context->get('StyleBlocks')
* so they can be used elsewhere in the document.
* @note See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php
* @todo Allow for selectors to be munged/checked
* @todo Expose CSSTidy configuration so that custom changes can be made
*
* @note
* See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for
* sample usage.
*
* @note
* This filter can also be used on stylesheets not included in the
* document--something purists would probably prefer. Just directly
* call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS()
*/
class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
{
public $name = 'ExtractStyleBlocks';
private $_styleMatches = array();
private $_tidy, $_disableCharacterEscaping;
private $_tidy;
/**
* @param $tidy Instance of csstidy to use, false to turn off cleaning,
* and null to automatically instantiate
* @param $disable_character_escaping Whether or not to stop munging
* <, > and &. This can be set to true if the CSS will
* be placed in an external style and not inline.
* @param $tidy
* Instance of csstidy to use, false to turn off cleaning,
* and null to automatically instantiate
*/
public function __construct($tidy = null, $disable_character_escaping = false) {
public function __construct($tidy = null) {
if ($tidy === null) $tidy = new csstidy();
$this->_tidy = $tidy;
$this->_disableCharacterEscaping = $disable_character_escaping;
}
/**
@@ -58,17 +102,45 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
/**
* Takes CSS (the stuff found in <style>) and cleans it.
* @warning Requires CSSTidy <http://csstidy.sourceforge.net/>
* @param $css CSS styling to clean
* @param $config Instance of HTMLPurifier_Config
* @param $css CSS styling to clean
* @param $config Instance of HTMLPurifier_Config
* @param $context Instance of HTMLPurifier_Context
* @return Cleaned CSS
*/
public function cleanCSS($css, $config, $context) {
// prepare scope
$scope = $config->get('Filter', 'ExtractStyleBlocksScope');
if ($scope !== null) {
$scopes = array_map('trim', explode(',', $scope));
} else {
$scopes = array();
}
$this->_tidy->parse($css);
$css_definition = $config->getDefinition('CSS');
foreach ($this->_tidy->css as &$decls) {
foreach ($this->_tidy->css as $k => $decls) {
// $decls are all CSS declarations inside an @ selector
foreach ($decls as &$style) {
$new_decls = array();
foreach ($decls as $selector => $style) {
$selector = trim($selector);
if ($selector === '') continue; // should not happen
if ($selector[0] === '+') {
while ($selector !== '' && $selector[0] === '+') {
// we need to perform this multiple times
// to prevent +++ from getting through
$selector = trim(substr($selector, 1));
}
if ($selector === '') continue;
}
if (!empty($scopes)) {
$new_selector = array(); // because multiple ones are possible
$selectors = array_map('trim', explode(',', $selector));
foreach ($scopes as $s1) {
foreach ($selectors as $s2) {
$new_selector[] = "$s1 $s2";
}
}
$selector = implode(', ', $new_selector); // now it's a string
}
foreach ($style as $name => $value) {
if (!isset($css_definition->info[$name])) {
unset($style[$name]);
@@ -79,7 +151,9 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
if ($ret === false) unset($style[$name]);
else $style[$name] = $ret;
}
$new_decls[$selector] = $style;
}
$this->_tidy->css[$k] = $new_decls;
}
// remove stuff that shouldn't be used, could be reenabled
// after security risks are analyzed
@@ -90,7 +164,7 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
$css = $printer->plain();
// we are going to escape any special characters <>& to ensure
// that no funny business occurs (i.e. </style> in a font-family prop).
if (!$this->_disableCharacterEscaping) {
if ($config->get('Filter', 'ExtractStyleBlocksEscaping')) {
$css = str_replace(
array('<', '>', '&'),
array('\3C ', '\3E ', '\26 '),