From 0f961c6af4e39d388eab09e32bbab652e209a6d7 Mon Sep 17 00:00:00 2001
From: "Edward Z. Yang"
Date: Sun, 16 Dec 2007 23:16:45 +0000
Subject: [PATCH] [3.0.0] [BACKPORT] More work for hire from Chris !
Experimental support for some proprietary CSS attributes allowed: opacity
(and all of the browser-specific equivalents) and scrollbar colors. Enable by
setting %CSS.Proprietary to true. - Colors missing # but in hex form will be
corrected - CSS Number algorithm improved . New classes: +
HTMLPurifier_AttrDef_CSS_AlphaValue + HTMLPurifier_AttrDef_CSS_Filter
git-svn-id: http://htmlpurifier.org/svnroot/htmlpurifier/trunk@1473 48356398-32a2-884e-a903-53898d9a118a
---
NEWS | 8 +++
.../HTMLPurifier/AttrDef/CSS/AlphaValue.php | 22 ++++++++
library/HTMLPurifier/AttrDef/CSS/Color.php | 22 ++++----
library/HTMLPurifier/AttrDef/CSS/Filter.php | 54 +++++++++++++++++++
library/HTMLPurifier/AttrDef/CSS/Number.php | 10 ++--
library/HTMLPurifier/CSSDefinition.php | 33 ++++++++++++
.../AttrDef/CSS/AlphaValueTest.php | 30 +++++++++++
tests/HTMLPurifier/AttrDef/CSS/ColorTest.php | 7 +++
tests/HTMLPurifier/AttrDef/CSS/FilterTest.php | 29 ++++++++++
tests/HTMLPurifier/AttrDef/CSS/NumberTest.php | 14 +++++
tests/HTMLPurifier/AttrDef/CSSTest.php | 18 +++++++
tests/test_files.php | 2 +
12 files changed, 237 insertions(+), 12 deletions(-)
create mode 100644 library/HTMLPurifier/AttrDef/CSS/AlphaValue.php
create mode 100644 library/HTMLPurifier/AttrDef/CSS/Filter.php
create mode 100644 tests/HTMLPurifier/AttrDef/CSS/AlphaValueTest.php
create mode 100644 tests/HTMLPurifier/AttrDef/CSS/FilterTest.php
diff --git a/NEWS b/NEWS
index bc632c29..d25aa407 100644
--- a/NEWS
+++ b/NEWS
@@ -22,7 +22,15 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
documents and cleaning their contents up. Requires the CSSTidy library
. You can access the blocks with the
'StyleBlocks' Context variable ($purifier->context->get('StyleBlocks'))
+! Experimental support for some proprietary CSS attributes allowed:
+ opacity (and all of the browser-specific equivalents) and scrollbar colors.
+ Enable by setting %CSS.Proprietary to true.
+- Colors missing # but in hex form will be corrected
+- CSS Number algorithm improved
. Unit tests for Injector improved
+. New classes:
+ + HTMLPurifier_AttrDef_CSS_AlphaValue
+ + HTMLPurifier_AttrDef_CSS_Filter
2.1.3, released 2007-11-05
! tests/multitest.php allows you to test multiple versions by running
diff --git a/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php
new file mode 100644
index 00000000..2492ee2e
--- /dev/null
+++ b/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php
@@ -0,0 +1,22 @@
+ 1.0) $result = '1';
+ return $result;
+ }
+
+}
diff --git a/library/HTMLPurifier/AttrDef/CSS/Color.php b/library/HTMLPurifier/AttrDef/CSS/Color.php
index 6b09ee5a..b86dc091 100644
--- a/library/HTMLPurifier/AttrDef/CSS/Color.php
+++ b/library/HTMLPurifier/AttrDef/CSS/Color.php
@@ -39,20 +39,13 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
if ($colors === null) $colors = $config->get('Core', 'ColorKeywords');
$color = trim($color);
- if (!$color) return false;
+ if ($color === '') return false;
$lower = strtolower($color);
if (isset($colors[$lower])) return $colors[$lower];
- if ($color[0] === '#') {
- // hexadecimal handling
- $hex = substr($color, 1);
- $length = strlen($hex);
- if ($length !== 3 && $length !== 6) return false;
- if (!ctype_xdigit($hex)) return false;
- } else {
+ if (strpos($color, 'rgb(') !== false) {
// rgb literal handling
- if (strpos($color, 'rgb(')) return false;
$length = strlen($color);
if (strpos($color, ')') !== $length - 1) return false;
$triad = substr($color, 4, $length - 4 - 1);
@@ -90,6 +83,17 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
}
$new_triad = implode(',', $new_parts);
$color = "rgb($new_triad)";
+ } else {
+ // hexadecimal handling
+ if ($color[0] === '#') {
+ $hex = substr($color, 1);
+ } else {
+ $hex = $color;
+ $color = '#' . $color;
+ }
+ $length = strlen($hex);
+ if ($length !== 3 && $length !== 6) return false;
+ if (!ctype_xdigit($hex)) return false;
}
return $color;
diff --git a/library/HTMLPurifier/AttrDef/CSS/Filter.php b/library/HTMLPurifier/AttrDef/CSS/Filter.php
new file mode 100644
index 00000000..31fe8bc9
--- /dev/null
+++ b/library/HTMLPurifier/AttrDef/CSS/Filter.php
@@ -0,0 +1,54 @@
+intValidator = new HTMLPurifier_AttrDef_Integer();
+ }
+
+ public function validate($value, $config, &$context) {
+ $value = $this->parseCDATA($value);
+ // if we looped this we could support multiple filters
+ $function_length = strcspn($value, '(');
+ $function = trim(substr($value, 0, $function_length));
+ if ($function !== 'alpha' &&
+ $function !== 'Alpha' &&
+ $function !== 'progid:DXImageTransform.Microsoft.Alpha'
+ ) return false;
+ $cursor = $function_length + 1;
+ $parameters_length = strcspn($value, ')', $cursor);
+ $parameters = substr($value, $cursor, $parameters_length);
+ $params = explode(',', $parameters);
+ $ret_params = array();
+ $lookup = array();
+ foreach ($params as $param) {
+ list($key, $value) = explode('=', $param);
+ $key = trim($key);
+ $value = trim($value);
+ if (isset($lookup[$key])) continue;
+ if ($key !== 'opacity') continue;
+ $value = $this->intValidator->validate($value, $config, $context);
+ if ($value === false) continue;
+ $int = (int) $value;
+ if ($int > 100) $value = '100';
+ if ($int < 0) $value = '0';
+ $ret_params[] = "$key=$value";
+ $lookup[$key] = true;
+ }
+ $ret_parameters = implode(',', $ret_params);
+ $ret_function = "$function($ret_parameters)";
+ return $ret_function;
+ }
+
+}
diff --git a/library/HTMLPurifier/AttrDef/CSS/Number.php b/library/HTMLPurifier/AttrDef/CSS/Number.php
index 228600c9..c5f46399 100644
--- a/library/HTMLPurifier/AttrDef/CSS/Number.php
+++ b/library/HTMLPurifier/AttrDef/CSS/Number.php
@@ -23,6 +23,7 @@ class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
$number = $this->parseCDATA($number);
if ($number === '') return false;
+ if ($number === '0') return '0';
$sign = '';
switch ($number[0]) {
@@ -37,13 +38,16 @@ class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
$number = ltrim($number, '0');
return $number ? $sign . $number : '0';
}
- if (!strpos($number, '.')) return false;
+
+ // Period is the only non-numeric character allowed
+ if (strpos($number, '.') === false) return false;
list($left, $right) = explode('.', $number, 2);
- if (!ctype_digit($left)) return false;
- $left = ltrim($left, '0');
+ if ($left === '' && $right === '') return false;
+ if ($left !== '' && !ctype_digit($left)) return false;
+ $left = ltrim($left, '0');
$right = rtrim($right, '0');
if ($right === '') {
diff --git a/library/HTMLPurifier/CSSDefinition.php b/library/HTMLPurifier/CSSDefinition.php
index 123c69ed..5295dc4a 100644
--- a/library/HTMLPurifier/CSSDefinition.php
+++ b/library/HTMLPurifier/CSSDefinition.php
@@ -2,11 +2,13 @@
require_once 'HTMLPurifier/Definition.php';
+require_once 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require_once 'HTMLPurifier/AttrDef/CSS/Background.php';
require_once 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require_once 'HTMLPurifier/AttrDef/CSS/Border.php';
require_once 'HTMLPurifier/AttrDef/CSS/Color.php';
require_once 'HTMLPurifier/AttrDef/CSS/Composite.php';
+require_once 'HTMLPurifier/AttrDef/CSS/Filter.php';
require_once 'HTMLPurifier/AttrDef/CSS/Font.php';
require_once 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
require_once 'HTMLPurifier/AttrDef/CSS/Length.php';
@@ -26,6 +28,14 @@ HTMLPurifier_ConfigSchema::define(
');
+HTMLPurifier_ConfigSchema::define(
+ 'CSS', 'Proprietary', false, 'bool', '
+
+ Whether or not to allow safe, proprietary CSS values. This directive
+ has been available since 3.0.0.
+
+');
+
/**
* Defines allowed CSS attributes and what their values are.
* @see HTMLPurifier_HTMLDefinition
@@ -224,6 +234,29 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
// partial support
$this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap'));
+ if ($config->get('CSS', 'Proprietary')) {
+ $this->doSetupProprietary($config);
+ }
+
+ }
+
+ protected function doSetupProprietary($config) {
+ // Internet Explorer only scrollbar colors
+ $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+ $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+ $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+ $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+ $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+ $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
+
+ // technically not proprietary, but CSS3, and no one supports it
+ $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+ $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+ $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+
+ // only opacity, for now
+ $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
+
}
}
diff --git a/tests/HTMLPurifier/AttrDef/CSS/AlphaValueTest.php b/tests/HTMLPurifier/AttrDef/CSS/AlphaValueTest.php
new file mode 100644
index 00000000..74fcb494
--- /dev/null
+++ b/tests/HTMLPurifier/AttrDef/CSS/AlphaValueTest.php
@@ -0,0 +1,30 @@
+def = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+
+ $this->assertDef('0');
+ $this->assertDef('1');
+ $this->assertDef('.2');
+
+ // clamping to [0.0, 1,0]
+ $this->assertDef('1.2', '1');
+ $this->assertDef('-3', '0');
+
+ $this->assertDef('0.0', '0');
+ $this->assertDef('1.0', '1');
+ $this->assertDef('000', '0');
+
+ $this->assertDef('asdf', false);
+
+ }
+
+}
+
diff --git a/tests/HTMLPurifier/AttrDef/CSS/ColorTest.php b/tests/HTMLPurifier/AttrDef/CSS/ColorTest.php
index 030c6224..4cb8602b 100644
--- a/tests/HTMLPurifier/AttrDef/CSS/ColorTest.php
+++ b/tests/HTMLPurifier/AttrDef/CSS/ColorTest.php
@@ -11,6 +11,8 @@ class HTMLPurifier_AttrDef_CSS_ColorTest extends HTMLPurifier_AttrDefHarness
$this->def = new HTMLPurifier_AttrDef_CSS_Color();
$this->assertDef('#F00');
+ $this->assertDef('#fff');
+ $this->assertDef('#eeeeee');
$this->assertDef('#808080');
$this->assertDef('rgb(255, 0, 0)', 'rgb(255,0,0)'); // rm spaces
$this->assertDef('rgb(100%,0%,0%)');
@@ -27,6 +29,11 @@ class HTMLPurifier_AttrDef_CSS_ColorTest extends HTMLPurifier_AttrDefHarness
// color keywords, of course
$this->assertDef('red', '#FF0000');
+ // malformed hex declaration
+ $this->assertDef('808080', '#808080');
+ $this->assertDef('000000', '#000000');
+ $this->assertDef('fed', '#fed');
+
// maybe hex transformations would be another nice feature
// at the very least transform rgb percent to rgb integer
diff --git a/tests/HTMLPurifier/AttrDef/CSS/FilterTest.php b/tests/HTMLPurifier/AttrDef/CSS/FilterTest.php
new file mode 100644
index 00000000..046d3bae
--- /dev/null
+++ b/tests/HTMLPurifier/AttrDef/CSS/FilterTest.php
@@ -0,0 +1,29 @@
+def = new HTMLPurifier_AttrDef_CSS_Filter();
+
+ $this->assertDef('alpha(opacity=0)');
+ $this->assertDef('alpha(opacity=100)');
+ $this->assertDef('alpha(opacity=50)');
+ $this->assertDef('alpha(opacity=342)', 'alpha(opacity=100)');
+ $this->assertDef('alpha(opacity=-23)', 'alpha(opacity=0)');
+
+ $this->assertDef('alpha ( opacity = 0 )', 'alpha(opacity=0)');
+ $this->assertDef('alpha(opacity=0,opacity=100)', 'alpha(opacity=0)');
+
+ $this->assertDef('progid:DXImageTransform.Microsoft.Alpha(opacity=20)');
+
+ $this->assertDef('progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)', false);
+
+ }
+
+}
+
diff --git a/tests/HTMLPurifier/AttrDef/CSS/NumberTest.php b/tests/HTMLPurifier/AttrDef/CSS/NumberTest.php
index 071135be..8aa3ce23 100644
--- a/tests/HTMLPurifier/AttrDef/CSS/NumberTest.php
+++ b/tests/HTMLPurifier/AttrDef/CSS/NumberTest.php
@@ -11,10 +11,24 @@ class HTMLPurifier_AttrDef_CSS_NumberTest extends HTMLPurifier_AttrDefHarness
$this->def = new HTMLPurifier_AttrDef_CSS_Number();
$this->assertDef('0');
+ $this->assertDef('0.0', '0');
+ $this->assertDef('1.0', '1');
$this->assertDef('34');
$this->assertDef('4.5');
+ $this->assertDef('.5');
+ $this->assertDef('0.5', '.5');
$this->assertDef('-56.9');
+ $this->assertDef('0.', '0');
+ $this->assertDef('.0', '0');
+ $this->assertDef('0.0', '0');
+
+ $this->assertDef('1.', '1');
+ $this->assertDef('.1', '.1');
+
+ $this->assertDef('1.0', '1');
+ $this->assertDef('0.1', '.1');
+
$this->assertDef('000', '0');
$this->assertDef(' 9', '9');
$this->assertDef('+5.0000', '5');
diff --git a/tests/HTMLPurifier/AttrDef/CSSTest.php b/tests/HTMLPurifier/AttrDef/CSSTest.php
index 59d86e2e..a4ffc50c 100644
--- a/tests/HTMLPurifier/AttrDef/CSSTest.php
+++ b/tests/HTMLPurifier/AttrDef/CSSTest.php
@@ -112,5 +112,23 @@ class HTMLPurifier_AttrDef_CSSTest extends HTMLPurifier_AttrDefHarness
}
+ function testProprietary() {
+ $this->config->set('CSS', 'Proprietary', true);
+ $this->def = new HTMLPurifier_AttrDef_CSS();
+
+ $this->assertDef('scrollbar-arrow-color:#ff0;');
+ $this->assertDef('scrollbar-base-color:#ff6347;');
+ $this->assertDef('scrollbar-darkshadow-color:#ffa500;');
+ $this->assertDef('scrollbar-face-color:#008080;');
+ $this->assertDef('scrollbar-highlight-color:#ff69b4;');
+ $this->assertDef('scrollbar-shadow-color:#f0f;');
+
+ $this->assertDef('opacity:.2;');
+ $this->assertDef('-moz-opacity:.2;');
+ $this->assertDef('-khtml-opacity:.2;');
+ $this->assertDef('filter:alpha(opacity=20);');
+
+ }
+
}
diff --git a/tests/test_files.php b/tests/test_files.php
index 3e7126f8..35e385a4 100644
--- a/tests/test_files.php
+++ b/tests/test_files.php
@@ -7,11 +7,13 @@ if (!defined('HTMLPurifierTest')) exit;
// HTML Purifier main library
$test_files[] = 'HTMLPurifier/AttrCollectionsTest.php';
+$test_files[] = 'HTMLPurifier/AttrDef/CSS/AlphaValueTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/BackgroundPositionTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/BackgroundTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/BorderTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/ColorTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/CompositeTest.php';
+$test_files[] = 'HTMLPurifier/AttrDef/CSS/FilterTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/FontFamilyTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/FontTest.php';
$test_files[] = 'HTMLPurifier/AttrDef/CSS/LengthTest.php';