diff options
Diffstat (limited to 'library/HTMLPurifier/AttrDef')
42 files changed, 1462 insertions, 513 deletions
diff --git a/library/HTMLPurifier/AttrDef/CSS.php b/library/HTMLPurifier/AttrDef/CSS.php index 953e70675..02c1641fb 100644 --- a/library/HTMLPurifier/AttrDef/CSS.php +++ b/library/HTMLPurifier/AttrDef/CSS.php @@ -14,8 +14,14 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef { - public function validate($css, $config, $context) { - + /** + * @param string $css + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($css, $config, $context) + { $css = $this->parseCDATA($css); $definition = $config->getCSSDefinition(); @@ -36,34 +42,47 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef $context->register('CurrentCSSProperty', $property); foreach ($declarations as $declaration) { - if (!$declaration) continue; - if (!strpos($declaration, ':')) continue; + if (!$declaration) { + continue; + } + if (!strpos($declaration, ':')) { + continue; + } list($property, $value) = explode(':', $declaration, 2); $property = trim($property); - $value = trim($value); + $value = trim($value); $ok = false; do { if (isset($definition->info[$property])) { $ok = true; break; } - if (ctype_lower($property)) break; + if (ctype_lower($property)) { + break; + } $property = strtolower($property); if (isset($definition->info[$property])) { $ok = true; break; } - } while(0); - if (!$ok) continue; + } while (0); + if (!$ok) { + continue; + } // inefficient call, since the validator will do this again if (strtolower(trim($value)) !== 'inherit') { // inherit works for everything (but only on the base property) $result = $definition->info[$property]->validate( - $value, $config, $context ); + $value, + $config, + $context + ); } else { $result = 'inherit'; } - if ($result === false) continue; + if ($result === false) { + continue; + } $propvalues[$property] = $result; } diff --git a/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php index 292c040d4..af2b83dff 100644 --- a/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php +++ b/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php @@ -3,19 +3,32 @@ class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number { - public function __construct() { + public function __construct() + { parent::__construct(false); // opacity is non-negative, but we will clamp it } - public function validate($number, $config, $context) { + /** + * @param string $number + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function validate($number, $config, $context) + { $result = parent::validate($number, $config, $context); - if ($result === false) return $result; - $float = (float) $result; - if ($float < 0.0) $result = '0'; - if ($float > 1.0) $result = '1'; + if ($result === false) { + return $result; + } + $float = (float)$result; + if ($float < 0.0) { + $result = '0'; + } + if ($float > 1.0) { + $result = '1'; + } return $result; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Background.php b/library/HTMLPurifier/AttrDef/CSS/Background.php index 3a3d20cd6..7f1ea3b0f 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Background.php +++ b/library/HTMLPurifier/AttrDef/CSS/Background.php @@ -9,11 +9,16 @@ class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef /** * Local copy of component validators. + * @type HTMLPurifier_AttrDef[] * @note See HTMLPurifier_AttrDef_Font::$info for a similar impl. */ protected $info; - public function __construct($config) { + /** + * @param HTMLPurifier_Config $config + */ + public function __construct($config) + { $def = $config->getCSSDefinition(); $this->info['background-color'] = $def->info['background-color']; $this->info['background-image'] = $def->info['background-image']; @@ -22,40 +27,55 @@ class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef $this->info['background-position'] = $def->info['background-position']; } - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { // regular pre-processing $string = $this->parseCDATA($string); - if ($string === '') return false; + if ($string === '') { + return false; + } // munge rgb() decl if necessary $string = $this->mungeRgb($string); // assumes URI doesn't have spaces in it - $bits = explode(' ', strtolower($string)); // bits to process + $bits = explode(' ', $string); // bits to process $caught = array(); - $caught['color'] = false; - $caught['image'] = false; - $caught['repeat'] = false; + $caught['color'] = false; + $caught['image'] = false; + $caught['repeat'] = false; $caught['attachment'] = false; $caught['position'] = false; $i = 0; // number of catches - $none = false; foreach ($bits as $bit) { - if ($bit === '') continue; + if ($bit === '') { + continue; + } foreach ($caught as $key => $status) { if ($key != 'position') { - if ($status !== false) continue; + if ($status !== false) { + continue; + } $r = $this->info['background-' . $key]->validate($bit, $config, $context); } else { $r = $bit; } - if ($r === false) continue; + if ($r === false) { + continue; + } if ($key == 'position') { - if ($caught[$key] === false) $caught[$key] = ''; + if ($caught[$key] === false) { + $caught[$key] = ''; + } $caught[$key] .= $r . ' '; } else { $caught[$key] = $r; @@ -65,7 +85,9 @@ class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef } } - if (!$i) return false; + if (!$i) { + return false; + } if ($caught['position'] !== false) { $caught['position'] = $this->info['background-position']-> validate($caught['position'], $config, $context); @@ -73,15 +95,17 @@ class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef $ret = array(); foreach ($caught as $value) { - if ($value === false) continue; + if ($value === false) { + continue; + } $ret[] = $value; } - if (empty($ret)) return false; + if (empty($ret)) { + return false; + } return implode(' ', $ret); - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php index fae82eaec..4580ef5a9 100644 --- a/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php +++ b/library/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php @@ -44,15 +44,30 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef { + /** + * @type HTMLPurifier_AttrDef_CSS_Length + */ protected $length; + + /** + * @type HTMLPurifier_AttrDef_CSS_Percentage + */ protected $percentage; - public function __construct() { - $this->length = new HTMLPurifier_AttrDef_CSS_Length(); + public function __construct() + { + $this->length = new HTMLPurifier_AttrDef_CSS_Length(); $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage(); } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = $this->parseCDATA($string); $bits = explode(' ', $string); @@ -74,7 +89,9 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef ); foreach ($bits as $bit) { - if ($bit === '') continue; + if ($bit === '') { + continue; + } // test for keyword $lbit = ctype_lower($bit) ? $bit : strtolower($bit); @@ -104,30 +121,37 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef $measures[] = $r; $i++; } - } - if (!$i) return false; // no valid values were caught + if (!$i) { + return false; + } // no valid values were caught $ret = array(); // first keyword - if ($keywords['h']) $ret[] = $keywords['h']; - elseif ($keywords['ch']) { + if ($keywords['h']) { + $ret[] = $keywords['h']; + } elseif ($keywords['ch']) { $ret[] = $keywords['ch']; $keywords['cv'] = false; // prevent re-use: center = center center + } elseif (count($measures)) { + $ret[] = array_shift($measures); } - elseif (count($measures)) $ret[] = array_shift($measures); - if ($keywords['v']) $ret[] = $keywords['v']; - elseif ($keywords['cv']) $ret[] = $keywords['cv']; - elseif (count($measures)) $ret[] = array_shift($measures); + if ($keywords['v']) { + $ret[] = $keywords['v']; + } elseif ($keywords['cv']) { + $ret[] = $keywords['cv']; + } elseif (count($measures)) { + $ret[] = array_shift($measures); + } - if (empty($ret)) return false; + if (empty($ret)) { + return false; + } return implode(' ', $ret); - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Border.php b/library/HTMLPurifier/AttrDef/CSS/Border.php index 42a1d1b4a..16243ba1e 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Border.php +++ b/library/HTMLPurifier/AttrDef/CSS/Border.php @@ -8,17 +8,29 @@ class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef /** * Local copy of properties this property is shorthand for. + * @type HTMLPurifier_AttrDef[] */ protected $info = array(); - public function __construct($config) { + /** + * @param HTMLPurifier_Config $config + */ + public function __construct($config) + { $def = $config->getCSSDefinition(); $this->info['border-width'] = $def->info['border-width']; $this->info['border-style'] = $def->info['border-style']; $this->info['border-top-color'] = $def->info['border-top-color']; } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = $this->parseCDATA($string); $string = $this->mungeRgb($string); $bits = explode(' ', $string); @@ -26,7 +38,9 @@ class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef $ret = ''; // return value foreach ($bits as $bit) { foreach ($this->info as $propname => $validator) { - if (isset($done[$propname])) continue; + if (isset($done[$propname])) { + continue; + } $r = $validator->validate($bit, $config, $context); if ($r !== false) { $ret .= $r . ' '; @@ -37,7 +51,6 @@ class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef } return rtrim($ret); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Color.php b/library/HTMLPurifier/AttrDef/CSS/Color.php index 07f95a671..16d2a6b98 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Color.php +++ b/library/HTMLPurifier/AttrDef/CSS/Color.php @@ -6,29 +6,47 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef { - public function validate($color, $config, $context) { - + /** + * @param string $color + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($color, $config, $context) + { static $colors = null; - if ($colors === null) $colors = $config->get('Core.ColorKeywords'); + 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 (isset($colors[$lower])) { + return $colors[$lower]; + } if (strpos($color, 'rgb(') !== false) { // rgb literal handling $length = strlen($color); - if (strpos($color, ')') !== $length - 1) return false; + if (strpos($color, ')') !== $length - 1) { + return false; + } $triad = substr($color, 4, $length - 4 - 1); $parts = explode(',', $triad); - if (count($parts) !== 3) return false; + if (count($parts) !== 3) { + return false; + } $type = false; // to ensure that they're all the same type $new_parts = array(); foreach ($parts as $part) { $part = trim($part); - if ($part === '') return false; + if ($part === '') { + return false; + } $length = strlen($part); if ($part[$length - 1] === '%') { // handle percents @@ -37,9 +55,13 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef } elseif ($type !== 'percentage') { return false; } - $num = (float) substr($part, 0, $length - 1); - if ($num < 0) $num = 0; - if ($num > 100) $num = 100; + $num = (float)substr($part, 0, $length - 1); + if ($num < 0) { + $num = 0; + } + if ($num > 100) { + $num = 100; + } $new_parts[] = "$num%"; } else { // handle integers @@ -48,10 +70,14 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef } elseif ($type !== 'integer') { return false; } - $num = (int) $part; - if ($num < 0) $num = 0; - if ($num > 255) $num = 255; - $new_parts[] = (string) $num; + $num = (int)$part; + if ($num < 0) { + $num = 0; + } + if ($num > 255) { + $num = 255; + } + $new_parts[] = (string)$num; } } $new_triad = implode(',', $new_parts); @@ -65,14 +91,15 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef $color = '#' . $color; } $length = strlen($hex); - if ($length !== 3 && $length !== 6) return false; - if (!ctype_xdigit($hex)) return false; + if ($length !== 3 && $length !== 6) { + return false; + } + if (!ctype_xdigit($hex)) { + return false; + } } - return $color; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Composite.php b/library/HTMLPurifier/AttrDef/CSS/Composite.php index de1289cba..9c1750554 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Composite.php +++ b/library/HTMLPurifier/AttrDef/CSS/Composite.php @@ -13,26 +13,36 @@ class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef { /** - * List of HTMLPurifier_AttrDef objects that may process strings + * List of objects that may process strings. + * @type HTMLPurifier_AttrDef[] * @todo Make protected */ public $defs; /** - * @param $defs List of HTMLPurifier_AttrDef objects + * @param HTMLPurifier_AttrDef[] $defs List of HTMLPurifier_AttrDef objects */ - public function __construct($defs) { + public function __construct($defs) + { $this->defs = $defs; } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { foreach ($this->defs as $i => $def) { $result = $this->defs[$i]->validate($string, $config, $context); - if ($result !== false) return $result; + if ($result !== false) { + return $result; + } } return false; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php index 6599c5b2d..9d77cc9aa 100644 --- a/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php +++ b/library/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php @@ -5,22 +5,38 @@ */ class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef { - public $def, $element; + /** + * @type HTMLPurifier_AttrDef + */ + public $def; + /** + * @type string + */ + public $element; /** - * @param $def Definition to wrap - * @param $element Element to deny + * @param HTMLPurifier_AttrDef $def Definition to wrap + * @param string $element Element to deny */ - public function __construct($def, $element) { + public function __construct($def, $element) + { $this->def = $def; $this->element = $element; } + /** * Checks if CurrentToken is set and equal to $this->element + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string */ - public function validate($string, $config, $context) { + public function validate($string, $config, $context) + { $token = $context->get('CurrentToken', true); - if ($token && $token->name == $this->element) return false; + if ($token && $token->name == $this->element) { + return false; + } return $this->def->validate($string, $config, $context); } } diff --git a/library/HTMLPurifier/AttrDef/CSS/Filter.php b/library/HTMLPurifier/AttrDef/CSS/Filter.php index 147894b86..bde4c3301 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Filter.php +++ b/library/HTMLPurifier/AttrDef/CSS/Filter.php @@ -7,23 +7,37 @@ */ class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef { - + /** + * @type HTMLPurifier_AttrDef_Integer + */ protected $intValidator; - public function __construct() { + public function __construct() + { $this->intValidator = new HTMLPurifier_AttrDef_Integer(); } - public function validate($value, $config, $context) { + /** + * @param string $value + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($value, $config, $context) + { $value = $this->parseCDATA($value); - if ($value === 'none') return $value; + if ($value === 'none') { + return $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; + ) { + return false; + } $cursor = $function_length + 1; $parameters_length = strcspn($value, ')', $cursor); $parameters = substr($value, $cursor, $parameters_length); @@ -32,15 +46,25 @@ class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef $lookup = array(); foreach ($params as $param) { list($key, $value) = explode('=', $param); - $key = trim($key); + $key = trim($key); $value = trim($value); - if (isset($lookup[$key])) continue; - if ($key !== 'opacity') continue; + 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'; + if ($value === false) { + continue; + } + $int = (int)$value; + if ($int > 100) { + $value = '100'; + } + if ($int < 0) { + $value = '0'; + } $ret_params[] = "$key=$value"; $lookup[$key] = true; } @@ -48,7 +72,6 @@ class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef $ret_function = "$function($ret_parameters)"; return $ret_function; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Font.php b/library/HTMLPurifier/AttrDef/CSS/Font.php index 699ee0b70..579b97ef1 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Font.php +++ b/library/HTMLPurifier/AttrDef/CSS/Font.php @@ -7,8 +7,8 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef { /** - * Local copy of component validators. - * + * Local copy of validators + * @type HTMLPurifier_AttrDef[] * @note If we moved specific CSS property definitions to their own * classes instead of having them be assembled at run time by * CSSDefinition, this wouldn't be necessary. We'd instantiate @@ -16,18 +16,28 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef */ protected $info = array(); - public function __construct($config) { + /** + * @param HTMLPurifier_Config $config + */ + public function __construct($config) + { $def = $config->getCSSDefinition(); - $this->info['font-style'] = $def->info['font-style']; + $this->info['font-style'] = $def->info['font-style']; $this->info['font-variant'] = $def->info['font-variant']; - $this->info['font-weight'] = $def->info['font-weight']; - $this->info['font-size'] = $def->info['font-size']; - $this->info['line-height'] = $def->info['line-height']; - $this->info['font-family'] = $def->info['font-family']; + $this->info['font-weight'] = $def->info['font-weight']; + $this->info['font-size'] = $def->info['font-size']; + $this->info['line-height'] = $def->info['line-height']; + $this->info['font-family'] = $def->info['font-family']; } - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { static $system_fonts = array( 'caption' => true, 'icon' => true, @@ -39,7 +49,9 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef // regular pre-processing $string = $this->parseCDATA($string); - if ($string === '') return false; + if ($string === '') { + return false; + } // check if it's one of the keywords $lowercase_string = strtolower($string); @@ -54,15 +66,20 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef $final = ''; // output for ($i = 0, $size = count($bits); $i < $size; $i++) { - if ($bits[$i] === '') continue; + if ($bits[$i] === '') { + continue; + } switch ($stage) { - - // attempting to catch font-style, font-variant or font-weight - case 0: + case 0: // attempting to catch font-style, font-variant or font-weight foreach ($stage_1 as $validator_name) { - if (isset($caught[$validator_name])) continue; + if (isset($caught[$validator_name])) { + continue; + } $r = $this->info[$validator_name]->validate( - $bits[$i], $config, $context); + $bits[$i], + $config, + $context + ); if ($r !== false) { $final .= $r . ' '; $caught[$validator_name] = true; @@ -70,15 +87,17 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef } } // all three caught, continue on - if (count($caught) >= 3) $stage = 1; - if ($r !== false) break; - - // attempting to catch font-size and perhaps line-height - case 1: + if (count($caught) >= 3) { + $stage = 1; + } + if ($r !== false) { + break; + } + case 1: // attempting to catch font-size and perhaps line-height $found_slash = false; if (strpos($bits[$i], '/') !== false) { list($font_size, $line_height) = - explode('/', $bits[$i]); + explode('/', $bits[$i]); if ($line_height === '') { // ooh, there's a space after the slash! $line_height = false; @@ -89,14 +108,19 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef $line_height = false; } $r = $this->info['font-size']->validate( - $font_size, $config, $context); + $font_size, + $config, + $context + ); if ($r !== false) { $final .= $r; // attempt to catch line-height if ($line_height === false) { // we need to scroll forward for ($j = $i + 1; $j < $size; $j++) { - if ($bits[$j] === '') continue; + if ($bits[$j] === '') { + continue; + } if ($bits[$j] === '/') { if ($found_slash) { return false; @@ -116,7 +140,10 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef if ($found_slash) { $i = $j; $r = $this->info['line-height']->validate( - $line_height, $config, $context); + $line_height, + $config, + $context + ); if ($r !== false) { $final .= '/' . $r; } @@ -126,13 +153,14 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef break; } return false; - - // attempting to catch font-family - case 2: + case 2: // attempting to catch font-family $font_family = implode(' ', array_slice($bits, $i, $size - $i)); $r = $this->info['font-family']->validate( - $font_family, $config, $context); + $font_family, + $config, + $context + ); if ($r !== false) { $final .= $r . ' '; // processing completed successfully @@ -143,7 +171,6 @@ class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef } return false; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/FontFamily.php b/library/HTMLPurifier/AttrDef/CSS/FontFamily.php index 42c2054c2..74e24c881 100644 --- a/library/HTMLPurifier/AttrDef/CSS/FontFamily.php +++ b/library/HTMLPurifier/AttrDef/CSS/FontFamily.php @@ -2,12 +2,58 @@ /** * Validates a font family list according to CSS spec - * @todo whitelisting allowed fonts would be nice */ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef { - public function validate($string, $config, $context) { + protected $mask = null; + + public function __construct() + { + $this->mask = '_- '; + for ($c = 'a'; $c <= 'z'; $c++) { + $this->mask .= $c; + } + for ($c = 'A'; $c <= 'Z'; $c++) { + $this->mask .= $c; + } + for ($c = '0'; $c <= '9'; $c++) { + $this->mask .= $c; + } // cast-y, but should be fine + // special bytes used by UTF-8 + for ($i = 0x80; $i <= 0xFF; $i++) { + // We don't bother excluding invalid bytes in this range, + // because the our restriction of well-formed UTF-8 will + // prevent these from ever occurring. + $this->mask .= chr($i); + } + + /* + PHP's internal strcspn implementation is + O(length of string * length of mask), making it inefficient + for large masks. However, it's still faster than + preg_match 8) + for (p = s1;;) { + spanp = s2; + do { + if (*spanp == c || p == s1_end) { + return p - s1; + } + } while (spanp++ < (s2_end - 1)); + c = *++p; + } + */ + // possible optimization: invert the mask. + } + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { static $generic_names = array( 'serif' => true, 'sans-serif' => true, @@ -15,24 +61,33 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef 'fantasy' => true, 'cursive' => true ); + $allowed_fonts = $config->get('CSS.AllowedFonts'); // assume that no font names contain commas in them $fonts = explode(',', $string); $final = ''; - foreach($fonts as $font) { + foreach ($fonts as $font) { $font = trim($font); - if ($font === '') continue; + if ($font === '') { + continue; + } // match a generic name if (isset($generic_names[$font])) { - $final .= $font . ', '; + if ($allowed_fonts === null || isset($allowed_fonts[$font])) { + $final .= $font . ', '; + } continue; } // match a quoted name if ($font[0] === '"' || $font[0] === "'") { $length = strlen($font); - if ($length <= 2) continue; + if ($length <= 2) { + continue; + } $quote = $font[0]; - if ($font[$length - 1] !== $quote) continue; + if ($font[$length - 1] !== $quote) { + continue; + } $font = substr($font, 1, $length - 2); } @@ -40,6 +95,10 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef // $font is a pure representation of the font name + if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) { + continue; + } + if (ctype_alnum($font) && $font !== '') { // very simple font, allow it in unharmed $final .= $font . ', '; @@ -50,20 +109,108 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef // shouldn't show up regardless $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); - // These ugly transforms don't pose a security - // risk (as \\ and \" might). We could try to be clever and - // use single-quote wrapping when there is a double quote - // present, but I have choosen not to implement that. - // (warning: this code relies on the selection of quotation - // mark below) - $font = str_replace('\\', '\\5C ', $font); - $font = str_replace('"', '\\22 ', $font); - - // complicated font, requires quoting - $final .= "\"$font\", "; // note that this will later get turned into " + // Here, there are various classes of characters which need + // to be treated differently: + // - Alphanumeric characters are essentially safe. We + // handled these above. + // - Spaces require quoting, though most parsers will do + // the right thing if there aren't any characters that + // can be misinterpreted + // - Dashes rarely occur, but they fairly unproblematic + // for parsing/rendering purposes. + // The above characters cover the majority of Western font + // names. + // - Arbitrary Unicode characters not in ASCII. Because + // most parsers give little thought to Unicode, treatment + // of these codepoints is basically uniform, even for + // punctuation-like codepoints. These characters can + // show up in non-Western pages and are supported by most + // major browsers, for example: "MS 明朝" is a + // legitimate font-name + // <http://ja.wikipedia.org/wiki/MS_明朝>. See + // the CSS3 spec for more examples: + // <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png> + // You can see live samples of these on the Internet: + // <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック> + // However, most of these fonts have ASCII equivalents: + // for example, 'MS Mincho', and it's considered + // professional to use ASCII font names instead of + // Unicode font names. Thanks Takeshi Terada for + // providing this information. + // The following characters, to my knowledge, have not been + // used to name font names. + // - Single quote. While theoretically you might find a + // font name that has a single quote in its name (serving + // as an apostrophe, e.g. Dave's Scribble), I haven't + // been able to find any actual examples of this. + // Internet Explorer's cssText translation (which I + // believe is invoked by innerHTML) normalizes any + // quoting to single quotes, and fails to escape single + // quotes. (Note that this is not IE's behavior for all + // CSS properties, just some sort of special casing for + // font-family). So a single quote *cannot* be used + // safely in the font-family context if there will be an + // innerHTML/cssText translation. Note that Firefox 3.x + // does this too. + // - Double quote. In IE, these get normalized to + // single-quotes, no matter what the encoding. (Fun + // fact, in IE8, the 'content' CSS property gained + // support, where they special cased to preserve encoded + // double quotes, but still translate unadorned double + // quotes into single quotes.) So, because their + // fixpoint behavior is identical to single quotes, they + // cannot be allowed either. Firefox 3.x displays + // single-quote style behavior. + // - Backslashes are reduced by one (so \\ -> \) every + // iteration, so they cannot be used safely. This shows + // up in IE7, IE8 and FF3 + // - Semicolons, commas and backticks are handled properly. + // - The rest of the ASCII punctuation is handled properly. + // We haven't checked what browsers do to unadorned + // versions, but this is not important as long as the + // browser doesn't /remove/ surrounding quotes (as IE does + // for HTML). + // + // With these results in hand, we conclude that there are + // various levels of safety: + // - Paranoid: alphanumeric, spaces and dashes(?) + // - International: Paranoid + non-ASCII Unicode + // - Edgy: Everything except quotes, backslashes + // - NoJS: Standards compliance, e.g. sod IE. Note that + // with some judicious character escaping (since certain + // types of escaping doesn't work) this is theoretically + // OK as long as innerHTML/cssText is not called. + // We believe that international is a reasonable default + // (that we will implement now), and once we do more + // extensive research, we may feel comfortable with dropping + // it down to edgy. + + // Edgy: alphanumeric, spaces, dashes, underscores and Unicode. Use of + // str(c)spn assumes that the string was already well formed + // Unicode (which of course it is). + if (strspn($font, $this->mask) !== strlen($font)) { + continue; + } + + // Historical: + // In the absence of innerHTML/cssText, these ugly + // transforms don't pose a security risk (as \\ and \" + // might--these escapes are not supported by most browsers). + // We could try to be clever and use single-quote wrapping + // when there is a double quote present, but I have choosen + // not to implement that. (NOTE: you can reduce the amount + // of escapes by one depending on what quoting style you use) + // $font = str_replace('\\', '\\5C ', $font); + // $font = str_replace('"', '\\22 ', $font); + // $font = str_replace("'", '\\27 ', $font); + + // font possibly with spaces, requires quoting + $final .= "'$font', "; } $final = rtrim($final, ', '); - if ($final === '') return false; + if ($final === '') { + return false; + } return $final; } diff --git a/library/HTMLPurifier/AttrDef/CSS/Ident.php b/library/HTMLPurifier/AttrDef/CSS/Ident.php new file mode 100644 index 000000000..973002c17 --- /dev/null +++ b/library/HTMLPurifier/AttrDef/CSS/Ident.php @@ -0,0 +1,32 @@ +<?php + +/** + * Validates based on {ident} CSS grammar production + */ +class HTMLPurifier_AttrDef_CSS_Ident extends HTMLPurifier_AttrDef +{ + + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + $string = trim($string); + + // early abort: '' and '0' (strings that convert to false) are invalid + if (!$string) { + return false; + } + + $pattern = '/^(-?[A-Za-z_][A-Za-z_\-0-9]*)$/'; + if (!preg_match($pattern, $string)) { + return false; + } + return $string; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php index 4e6b35e5a..ffc989fe8 100644 --- a/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php +++ b/library/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php @@ -5,20 +5,34 @@ */ class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef { - public $def, $allow; + /** + * @type HTMLPurifier_AttrDef + */ + public $def; + /** + * @type bool + */ + public $allow; /** - * @param $def Definition to wrap - * @param $allow Whether or not to allow !important + * @param HTMLPurifier_AttrDef $def Definition to wrap + * @param bool $allow Whether or not to allow !important */ - public function __construct($def, $allow = false) { + public function __construct($def, $allow = false) + { $this->def = $def; $this->allow = $allow; } + /** * Intercepts and removes !important if necessary + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string */ - public function validate($string, $config, $context) { + public function validate($string, $config, $context) + { // test for ! and important tokens $string = trim($string); $is_important = false; @@ -32,7 +46,9 @@ class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef } } $string = $this->def->validate($string, $config, $context); - if ($this->allow && $is_important) $string .= ' !important'; + if ($this->allow && $is_important) { + $string .= ' !important'; + } return $string; } } diff --git a/library/HTMLPurifier/AttrDef/CSS/Length.php b/library/HTMLPurifier/AttrDef/CSS/Length.php index a07ec5813..f12453a04 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Length.php +++ b/library/HTMLPurifier/AttrDef/CSS/Length.php @@ -6,42 +6,72 @@ class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef { - protected $min, $max; + /** + * @type HTMLPurifier_Length|string + */ + protected $min; /** - * @param HTMLPurifier_Length $max Minimum length, or null for no bound. String is also acceptable. - * @param HTMLPurifier_Length $max Maximum length, or null for no bound. String is also acceptable. + * @type HTMLPurifier_Length|string */ - public function __construct($min = null, $max = null) { + protected $max; + + /** + * @param HTMLPurifier_Length|string $min Minimum length, or null for no bound. String is also acceptable. + * @param HTMLPurifier_Length|string $max Maximum length, or null for no bound. String is also acceptable. + */ + public function __construct($min = null, $max = null) + { $this->min = $min !== null ? HTMLPurifier_Length::make($min) : null; $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null; } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = $this->parseCDATA($string); // Optimizations - if ($string === '') return false; - if ($string === '0') return '0'; - if (strlen($string) === 1) return false; + if ($string === '') { + return false; + } + if ($string === '0') { + return '0'; + } + if (strlen($string) === 1) { + return false; + } $length = HTMLPurifier_Length::make($string); - if (!$length->isValid()) return false; + if (!$length->isValid()) { + return false; + } if ($this->min) { $c = $length->compareTo($this->min); - if ($c === false) return false; - if ($c < 0) return false; + if ($c === false) { + return false; + } + if ($c < 0) { + return false; + } } if ($this->max) { $c = $length->compareTo($this->max); - if ($c === false) return false; - if ($c > 0) return false; + if ($c === false) { + return false; + } + if ($c > 0) { + return false; + } } - return $length->toString(); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/ListStyle.php b/library/HTMLPurifier/AttrDef/CSS/ListStyle.php index 4406868c0..e74d42654 100644 --- a/library/HTMLPurifier/AttrDef/CSS/ListStyle.php +++ b/library/HTMLPurifier/AttrDef/CSS/ListStyle.php @@ -8,46 +8,72 @@ class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef { /** - * Local copy of component validators. + * Local copy of validators. + * @type HTMLPurifier_AttrDef[] * @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl. */ protected $info; - public function __construct($config) { + /** + * @param HTMLPurifier_Config $config + */ + public function __construct($config) + { $def = $config->getCSSDefinition(); - $this->info['list-style-type'] = $def->info['list-style-type']; + $this->info['list-style-type'] = $def->info['list-style-type']; $this->info['list-style-position'] = $def->info['list-style-position']; $this->info['list-style-image'] = $def->info['list-style-image']; } - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { // regular pre-processing $string = $this->parseCDATA($string); - if ($string === '') return false; + if ($string === '') { + return false; + } // assumes URI doesn't have spaces in it $bits = explode(' ', strtolower($string)); // bits to process $caught = array(); - $caught['type'] = false; + $caught['type'] = false; $caught['position'] = false; - $caught['image'] = false; + $caught['image'] = false; $i = 0; // number of catches $none = false; foreach ($bits as $bit) { - if ($i >= 3) return; // optimization bit - if ($bit === '') continue; + if ($i >= 3) { + return; + } // optimization bit + if ($bit === '') { + continue; + } foreach ($caught as $key => $status) { - if ($status !== false) continue; + if ($status !== false) { + continue; + } $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); - if ($r === false) continue; + if ($r === false) { + continue; + } if ($r === 'none') { - if ($none) continue; - else $none = true; - if ($key == 'image') continue; + if ($none) { + continue; + } else { + $none = true; + } + if ($key == 'image') { + continue; + } } $caught[$key] = $r; $i++; @@ -55,24 +81,32 @@ class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef } } - if (!$i) return false; + if (!$i) { + return false; + } $ret = array(); // construct type - if ($caught['type']) $ret[] = $caught['type']; + if ($caught['type']) { + $ret[] = $caught['type']; + } // construct image - if ($caught['image']) $ret[] = $caught['image']; + if ($caught['image']) { + $ret[] = $caught['image']; + } // construct position - if ($caught['position']) $ret[] = $caught['position']; + if ($caught['position']) { + $ret[] = $caught['position']; + } - if (empty($ret)) return false; + if (empty($ret)) { + return false; + } return implode(' ', $ret); - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Multiple.php b/library/HTMLPurifier/AttrDef/CSS/Multiple.php index 4d62a40d7..9f266cdd1 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Multiple.php +++ b/library/HTMLPurifier/AttrDef/CSS/Multiple.php @@ -13,9 +13,9 @@ */ class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef { - /** * Instance of component definition to defer validation to. + * @type HTMLPurifier_AttrDef * @todo Make protected */ public $single; @@ -27,32 +27,45 @@ class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef public $max; /** - * @param $single HTMLPurifier_AttrDef to multiply - * @param $max Max number of values allowed (usually four) + * @param HTMLPurifier_AttrDef $single HTMLPurifier_AttrDef to multiply + * @param int $max Max number of values allowed (usually four) */ - public function __construct($single, $max = 4) { + public function __construct($single, $max = 4) + { $this->single = $single; $this->max = $max; } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = $this->parseCDATA($string); - if ($string === '') return false; + if ($string === '') { + return false; + } $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n $length = count($parts); $final = ''; for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) { - if (ctype_space($parts[$i])) continue; + if (ctype_space($parts[$i])) { + continue; + } $result = $this->single->validate($parts[$i], $config, $context); if ($result !== false) { $final .= $result . ' '; $num++; } } - if ($final === '') return false; + if ($final === '') { + return false; + } return rtrim($final); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Number.php b/library/HTMLPurifier/AttrDef/CSS/Number.php index 3f99e12ec..8edc159e7 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Number.php +++ b/library/HTMLPurifier/AttrDef/CSS/Number.php @@ -7,32 +7,44 @@ class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef { /** - * Bool indicating whether or not only positive values allowed. + * Indicates whether or not only positive values are allowed. + * @type bool */ protected $non_negative = false; /** - * @param $non_negative Bool indicating whether negatives are forbidden + * @param bool $non_negative indicates whether negatives are forbidden */ - public function __construct($non_negative = false) { + public function __construct($non_negative = false) + { $this->non_negative = $non_negative; } /** + * @param string $number + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string|bool * @warning Some contexts do not pass $config, $context. These * variables should not be used without checking HTMLPurifier_Length */ - public function validate($number, $config, $context) { - + public function validate($number, $config, $context) + { $number = $this->parseCDATA($number); - if ($number === '') return false; - if ($number === '0') return '0'; + if ($number === '') { + return false; + } + if ($number === '0') { + return '0'; + } $sign = ''; switch ($number[0]) { case '-': - if ($this->non_negative) return false; + if ($this->non_negative) { + return false; + } $sign = '-'; case '+': $number = substr($number, 1); @@ -44,14 +56,20 @@ class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef } // Period is the only non-numeric character allowed - if (strpos($number, '.') === false) return false; + if (strpos($number, '.') === false) { + return false; + } list($left, $right) = explode('.', $number, 2); - if ($left === '' && $right === '') return false; - if ($left !== '' && !ctype_digit($left)) return false; + if ($left === '' && $right === '') { + return false; + } + if ($left !== '' && !ctype_digit($left)) { + return false; + } - $left = ltrim($left, '0'); + $left = ltrim($left, '0'); $right = rtrim($right, '0'); if ($right === '') { @@ -59,11 +77,8 @@ class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef } elseif (!ctype_digit($right)) { return false; } - return $sign . $left . '.' . $right; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/Percentage.php b/library/HTMLPurifier/AttrDef/CSS/Percentage.php index c34b8fc3c..f0f25c50a 100644 --- a/library/HTMLPurifier/AttrDef/CSS/Percentage.php +++ b/library/HTMLPurifier/AttrDef/CSS/Percentage.php @@ -7,34 +7,48 @@ class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef { /** - * Instance of HTMLPurifier_AttrDef_CSS_Number to defer number validation + * Instance to defer number validation to. + * @type HTMLPurifier_AttrDef_CSS_Number */ protected $number_def; /** - * @param Bool indicating whether to forbid negative values + * @param bool $non_negative Whether to forbid negative values */ - public function __construct($non_negative = false) { + public function __construct($non_negative = false) + { $this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative); } - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = $this->parseCDATA($string); - if ($string === '') return false; + if ($string === '') { + return false; + } $length = strlen($string); - if ($length === 1) return false; - if ($string[$length - 1] !== '%') return false; + if ($length === 1) { + return false; + } + if ($string[$length - 1] !== '%') { + return false; + } $number = substr($string, 0, $length - 1); $number = $this->number_def->validate($number, $config, $context); - if ($number === false) return false; + if ($number === false) { + return false; + } return "$number%"; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php index 772c922d8..5fd4b7f7b 100644 --- a/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php +++ b/library/HTMLPurifier/AttrDef/CSS/TextDecoration.php @@ -8,8 +8,14 @@ class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef { - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { static $allowed_values = array( 'line-through' => true, 'overline' => true, @@ -18,7 +24,9 @@ class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef $string = strtolower($this->parseCDATA($string)); - if ($string === 'none') return $string; + if ($string === 'none') { + return $string; + } $parts = explode(' ', $string); $final = ''; @@ -28,11 +36,11 @@ class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef } } $final = rtrim($final); - if ($final === '') return false; + if ($final === '') { + return false; + } return $final; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/CSS/URI.php b/library/HTMLPurifier/AttrDef/CSS/URI.php index 1df17dc25..f9434230e 100644 --- a/library/HTMLPurifier/AttrDef/CSS/URI.php +++ b/library/HTMLPurifier/AttrDef/CSS/URI.php @@ -12,25 +12,39 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI { - public function __construct() { + public function __construct() + { parent::__construct(true); // always embedded } - public function validate($uri_string, $config, $context) { + /** + * @param string $uri_string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($uri_string, $config, $context) + { // parse the URI out of the string and then pass it onto // the parent object $uri_string = $this->parseCDATA($uri_string); - if (strpos($uri_string, 'url(') !== 0) return false; + if (strpos($uri_string, 'url(') !== 0) { + return false; + } $uri_string = substr($uri_string, 4); $new_length = strlen($uri_string) - 1; - if ($uri_string[$new_length] != ')') return false; + if ($uri_string[$new_length] != ')') { + return false; + } $uri = trim(substr($uri_string, 0, $new_length)); if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) { $quote = $uri[0]; $new_length = strlen($uri) - 1; - if ($uri[$new_length] !== $quote) return false; + if ($uri[$new_length] !== $quote) { + return false; + } $uri = substr($uri, 1, $new_length - 1); } @@ -38,15 +52,23 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI $result = parent::validate($uri, $config, $context); - if ($result === false) return false; + if ($result === false) { + return false; + } // extra sanity check; should have been done by URI $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result); - return "url(\"$result\")"; + // suspicious characters are ()'; we're going to percent encode + // them for safety. + $result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result); + // there's an extra bug where ampersands lose their escaping on + // an innerHTML cycle, so a very unlucky query parameter could + // then change the meaning of the URL. Unfortunately, there's + // not much we can do about that... + return "url(\"$result\")"; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/Clone.php b/library/HTMLPurifier/AttrDef/Clone.php new file mode 100644 index 000000000..6698a00c0 --- /dev/null +++ b/library/HTMLPurifier/AttrDef/Clone.php @@ -0,0 +1,44 @@ +<?php + +/** + * Dummy AttrDef that mimics another AttrDef, BUT it generates clones + * with make. + */ +class HTMLPurifier_AttrDef_Clone extends HTMLPurifier_AttrDef +{ + /** + * What we're cloning. + * @type HTMLPurifier_AttrDef + */ + protected $clone; + + /** + * @param HTMLPurifier_AttrDef $clone + */ + public function __construct($clone) + { + $this->clone = $clone; + } + + /** + * @param string $v + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($v, $config, $context) + { + return $this->clone->validate($v, $config, $context); + } + + /** + * @param string $string + * @return HTMLPurifier_AttrDef + */ + public function make($string) + { + return clone $this->clone; + } +} + +// vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/Enum.php b/library/HTMLPurifier/AttrDef/Enum.php index 5d603ebcc..8abda7f6e 100644 --- a/library/HTMLPurifier/AttrDef/Enum.php +++ b/library/HTMLPurifier/AttrDef/Enum.php @@ -12,9 +12,10 @@ class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef /** * Lookup table of valid values. + * @type array * @todo Make protected */ - public $valid_values = array(); + public $valid_values = array(); /** * Bool indicating whether or not enumeration is case sensitive. @@ -23,17 +24,23 @@ class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef protected $case_sensitive = false; // values according to W3C spec /** - * @param $valid_values List of valid values - * @param $case_sensitive Bool indicating whether or not case sensitive + * @param array $valid_values List of valid values + * @param bool $case_sensitive Whether or not case sensitive */ - public function __construct( - $valid_values = array(), $case_sensitive = false - ) { + public function __construct($valid_values = array(), $case_sensitive = false) + { $this->valid_values = array_flip($valid_values); $this->case_sensitive = $case_sensitive; } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = trim($string); if (!$this->case_sensitive) { // we may want to do full case-insensitive libraries @@ -45,11 +52,13 @@ class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef } /** - * @param $string In form of comma-delimited list of case-insensitive + * @param string $string In form of comma-delimited list of case-insensitive * valid values. Example: "foo,bar,baz". Prepend "s:" to make * case sensitive + * @return HTMLPurifier_AttrDef_Enum */ - public function make($string) { + public function make($string) + { if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') { $string = substr($string, 2); $sensitive = true; @@ -59,7 +68,6 @@ class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef $values = explode(',', $string); return new HTMLPurifier_AttrDef_Enum($values, $sensitive); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/Bool.php b/library/HTMLPurifier/AttrDef/HTML/Bool.php index e06987eb8..036a240e1 100644 --- a/library/HTMLPurifier/AttrDef/HTML/Bool.php +++ b/library/HTMLPurifier/AttrDef/HTML/Bool.php @@ -6,23 +6,46 @@ class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef { + /** + * @type bool + */ protected $name; + + /** + * @type bool + */ public $minimized = true; - public function __construct($name = false) {$this->name = $name;} + /** + * @param bool $name + */ + public function __construct($name = false) + { + $this->name = $name; + } - public function validate($string, $config, $context) { - if (empty($string)) return false; + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + if (empty($string)) { + return false; + } return $this->name; } /** - * @param $string Name of attribute + * @param string $string Name of attribute + * @return HTMLPurifier_AttrDef_HTML_Bool */ - public function make($string) { + public function make($string) + { return new HTMLPurifier_AttrDef_HTML_Bool($string); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/Class.php b/library/HTMLPurifier/AttrDef/HTML/Class.php index 370068d97..d5013488f 100644 --- a/library/HTMLPurifier/AttrDef/HTML/Class.php +++ b/library/HTMLPurifier/AttrDef/HTML/Class.php @@ -5,7 +5,14 @@ */ class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens { - protected function split($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + protected function split($string, $config, $context) + { // really, this twiddle should be lazy loaded $name = $config->getDefinition('HTML')->doctype->name; if ($name == "XHTML 1.1" || $name == "XHTML 2.0") { @@ -14,13 +21,20 @@ class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens return preg_split('/\s+/', $string); } } - protected function filter($tokens, $config, $context) { + + /** + * @param array $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + protected function filter($tokens, $config, $context) + { $allowed = $config->get('Attr.AllowedClasses'); $forbidden = $config->get('Attr.ForbiddenClasses'); $ret = array(); foreach ($tokens as $token) { - if ( - ($allowed === null || isset($allowed[$token])) && + if (($allowed === null || isset($allowed[$token])) && !isset($forbidden[$token]) && // We need this O(n) check because of PHP's array // implementation that casts -0 to 0. diff --git a/library/HTMLPurifier/AttrDef/HTML/Color.php b/library/HTMLPurifier/AttrDef/HTML/Color.php index d01e20454..946ebb782 100644 --- a/library/HTMLPurifier/AttrDef/HTML/Color.php +++ b/library/HTMLPurifier/AttrDef/HTML/Color.php @@ -6,27 +6,46 @@ class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef { - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { static $colors = null; - if ($colors === null) $colors = $config->get('Core.ColorKeywords'); + if ($colors === null) { + $colors = $config->get('Core.ColorKeywords'); + } $string = trim($string); - if (empty($string)) return false; - if (isset($colors[$string])) return $colors[$string]; - if ($string[0] === '#') $hex = substr($string, 1); - else $hex = $string; + if (empty($string)) { + return false; + } + $lower = strtolower($string); + if (isset($colors[$lower])) { + return $colors[$lower]; + } + if ($string[0] === '#') { + $hex = substr($string, 1); + } else { + $hex = $string; + } $length = strlen($hex); - if ($length !== 3 && $length !== 6) return false; - if (!ctype_xdigit($hex)) return false; - if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2]; - + if ($length !== 3 && $length !== 6) { + return false; + } + if (!ctype_xdigit($hex)) { + return false; + } + if ($length === 3) { + $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; + } return "#$hex"; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php index ae6ea7c01..d79ba12b3 100644 --- a/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php +++ b/library/HTMLPurifier/AttrDef/HTML/FrameTarget.php @@ -6,16 +6,33 @@ class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum { + /** + * @type array + */ public $valid_values = false; // uninitialized value + + /** + * @type bool + */ protected $case_sensitive = false; - public function __construct() {} + public function __construct() + { + } - public function validate($string, $config, $context) { - if ($this->valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets'); + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { + if ($this->valid_values === false) { + $this->valid_values = $config->get('Attr.AllowedFrameTargets'); + } return parent::validate($string, $config, $context); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/ID.php b/library/HTMLPurifier/AttrDef/HTML/ID.php index 81d03762d..3d86efb44 100644 --- a/library/HTMLPurifier/AttrDef/HTML/ID.php +++ b/library/HTMLPurifier/AttrDef/HTML/ID.php @@ -12,42 +12,77 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef { - // ref functionality disabled, since we also have to verify - // whether or not the ID it refers to exists - - public function validate($id, $config, $context) { + // selector is NOT a valid thing to use for IDREFs, because IDREFs + // *must* target IDs that exist, whereas selector #ids do not. + + /** + * Determines whether or not we're validating an ID in a CSS + * selector context. + * @type bool + */ + protected $selector; + + /** + * @param bool $selector + */ + public function __construct($selector = false) + { + $this->selector = $selector; + } - if (!$config->get('Attr.EnableID')) return false; + /** + * @param string $id + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($id, $config, $context) + { + if (!$this->selector && !$config->get('Attr.EnableID')) { + return false; + } $id = trim($id); // trim it first - if ($id === '') return false; + if ($id === '') { + return false; + } $prefix = $config->get('Attr.IDPrefix'); if ($prefix !== '') { $prefix .= $config->get('Attr.IDPrefixLocal'); // prevent re-appending the prefix - if (strpos($id, $prefix) !== 0) $id = $prefix . $id; + if (strpos($id, $prefix) !== 0) { + $id = $prefix . $id; + } } elseif ($config->get('Attr.IDPrefixLocal') !== '') { - trigger_error('%Attr.IDPrefixLocal cannot be used unless '. - '%Attr.IDPrefix is set', E_USER_WARNING); + trigger_error( + '%Attr.IDPrefixLocal cannot be used unless ' . + '%Attr.IDPrefix is set', + E_USER_WARNING + ); } - //if (!$this->ref) { + if (!$this->selector) { $id_accumulator =& $context->get('IDAccumulator'); - if (isset($id_accumulator->ids[$id])) return false; - //} + if (isset($id_accumulator->ids[$id])) { + return false; + } + } // we purposely avoid using regex, hopefully this is faster if (ctype_alpha($id)) { $result = true; } else { - if (!ctype_alpha(@$id[0])) return false; - $trim = trim( // primitive style of regexps, I suppose + if (!ctype_alpha(@$id[0])) { + return false; + } + // primitive style of regexps, I suppose + $trim = trim( $id, 'A..Za..z0..9:-._' - ); + ); $result = ($trim === ''); } @@ -56,15 +91,15 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef return false; } - if (/*!$this->ref && */$result) $id_accumulator->add($id); + if (!$this->selector && $result) { + $id_accumulator->add($id); + } // if no change was made to the ID, return the result // else, return the new id if stripping whitespace made it // valid, or return false. return $result ? $id : false; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/Length.php b/library/HTMLPurifier/AttrDef/HTML/Length.php index a242f9c23..1c4006fbb 100644 --- a/library/HTMLPurifier/AttrDef/HTML/Length.php +++ b/library/HTMLPurifier/AttrDef/HTML/Length.php @@ -10,32 +10,47 @@ class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels { - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = trim($string); - if ($string === '') return false; + if ($string === '') { + return false; + } $parent_result = parent::validate($string, $config, $context); - if ($parent_result !== false) return $parent_result; + if ($parent_result !== false) { + return $parent_result; + } $length = strlen($string); $last_char = $string[$length - 1]; - if ($last_char !== '%') return false; + if ($last_char !== '%') { + return false; + } $points = substr($string, 0, $length - 1); - if (!is_numeric($points)) return false; - - $points = (int) $points; + if (!is_numeric($points)) { + return false; + } - if ($points < 0) return '0%'; - if ($points > 100) return '100%'; - - return ((string) $points) . '%'; + $points = (int)$points; + if ($points < 0) { + return '0%'; + } + if ($points > 100) { + return '100%'; + } + return ((string)$points) . '%'; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php index 76d25ed08..63fa04c15 100644 --- a/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php +++ b/library/HTMLPurifier/AttrDef/HTML/LinkTypes.php @@ -9,26 +9,44 @@ class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef { - /** Name config attribute to pull. */ + /** + * Name config attribute to pull. + * @type string + */ protected $name; - public function __construct($name) { + /** + * @param string $name + */ + public function __construct($name) + { $configLookup = array( 'rel' => 'AllowedRel', 'rev' => 'AllowedRev' ); if (!isset($configLookup[$name])) { - trigger_error('Unrecognized attribute name for link '. - 'relationship.', E_USER_ERROR); + trigger_error( + 'Unrecognized attribute name for link ' . + 'relationship.', + E_USER_ERROR + ); return; } $this->name = $configLookup[$name]; } - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $allowed = $config->get('Attr.' . $this->name); - if (empty($allowed)) return false; + if (empty($allowed)) { + return false; + } $string = $this->parseCDATA($string); $parts = explode(' ', $string); @@ -37,17 +55,18 @@ class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef $ret_lookup = array(); foreach ($parts as $part) { $part = strtolower(trim($part)); - if (!isset($allowed[$part])) continue; + if (!isset($allowed[$part])) { + continue; + } $ret_lookup[$part] = true; } - if (empty($ret_lookup)) return false; + if (empty($ret_lookup)) { + return false; + } $string = implode(' ', array_keys($ret_lookup)); - return $string; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/MultiLength.php b/library/HTMLPurifier/AttrDef/HTML/MultiLength.php index c72fc76e4..bbb20f2f8 100644 --- a/library/HTMLPurifier/AttrDef/HTML/MultiLength.php +++ b/library/HTMLPurifier/AttrDef/HTML/MultiLength.php @@ -9,33 +9,52 @@ class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length { - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = trim($string); - if ($string === '') return false; + if ($string === '') { + return false; + } $parent_result = parent::validate($string, $config, $context); - if ($parent_result !== false) return $parent_result; + if ($parent_result !== false) { + return $parent_result; + } $length = strlen($string); $last_char = $string[$length - 1]; - if ($last_char !== '*') return false; + if ($last_char !== '*') { + return false; + } $int = substr($string, 0, $length - 1); - if ($int == '') return '*'; - if (!is_numeric($int)) return false; - - $int = (int) $int; - - if ($int < 0) return false; - if ($int == 0) return '0'; - if ($int == 1) return '*'; - return ((string) $int) . '*'; - + if ($int == '') { + return '*'; + } + if (!is_numeric($int)) { + return false; + } + + $int = (int)$int; + if ($int < 0) { + return false; + } + if ($int == 0) { + return '0'; + } + if ($int == 1) { + return '*'; + } + return ((string)$int) . '*'; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php b/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php index aa34120bd..f79683b4f 100644 --- a/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php +++ b/library/HTMLPurifier/AttrDef/HTML/Nmtokens.php @@ -6,24 +6,38 @@ class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef { - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = trim($string); // early abort: '' and '0' (strings that convert to false) are invalid - if (!$string) return false; + if (!$string) { + return false; + } $tokens = $this->split($string, $config, $context); $tokens = $this->filter($tokens, $config, $context); - if (empty($tokens)) return false; + if (empty($tokens)) { + return false; + } return implode(' ', $tokens); - } /** * Splits a space separated list of tokens into its constituent parts. + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array */ - protected function split($string, $config, $context) { + protected function split($string, $config, $context) + { // OPTIMIZABLE! // do the preg_match, capture all subpatterns for reformulation @@ -31,9 +45,9 @@ class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef // escaping because I don't know how to do that with regexps // and plus it would complicate optimization efforts (you never // see that anyway). - $pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start - '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'. - '(?:(?=\s)|\z)/'; // look ahead for space or string end + $pattern = '/(?:(?<=\s)|\A)' . // look behind for space or string start + '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)' . + '(?:(?=\s)|\z)/'; // look ahead for space or string end preg_match_all($pattern, $string, $matches); return $matches[1]; } @@ -42,11 +56,15 @@ class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef * Template method for removing certain tokens based on arbitrary criteria. * @note If we wanted to be really functional, we'd do an array_filter * with a callback. But... we're not. + * @param array $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array */ - protected function filter($tokens, $config, $context) { + protected function filter($tokens, $config, $context) + { return $tokens; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/HTML/Pixels.php b/library/HTMLPurifier/AttrDef/HTML/Pixels.php index 4cb2c1b85..a1d019e09 100644 --- a/library/HTMLPurifier/AttrDef/HTML/Pixels.php +++ b/library/HTMLPurifier/AttrDef/HTML/Pixels.php @@ -6,43 +6,71 @@ class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef { + /** + * @type int + */ protected $max; - public function __construct($max = null) { + /** + * @param int $max + */ + public function __construct($max = null) + { $this->max = $max; } - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = trim($string); - if ($string === '0') return $string; - if ($string === '') return false; + if ($string === '0') { + return $string; + } + if ($string === '') { + return false; + } $length = strlen($string); if (substr($string, $length - 2) == 'px') { $string = substr($string, 0, $length - 2); } - if (!is_numeric($string)) return false; - $int = (int) $string; + if (!is_numeric($string)) { + return false; + } + $int = (int)$string; - if ($int < 0) return '0'; + if ($int < 0) { + return '0'; + } // upper-bound value, extremely high values can // crash operating systems, see <http://ha.ckers.org/imagecrash.html> // WARNING, above link WILL crash you if you're using Windows - if ($this->max !== null && $int > $this->max) return (string) $this->max; - - return (string) $int; - + if ($this->max !== null && $int > $this->max) { + return (string)$this->max; + } + return (string)$int; } - public function make($string) { - if ($string === '') $max = null; - else $max = (int) $string; + /** + * @param string $string + * @return HTMLPurifier_AttrDef + */ + public function make($string) + { + if ($string === '') { + $max = null; + } else { + $max = (int)$string; + } $class = get_class($this); return new $class($max); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/Integer.php b/library/HTMLPurifier/AttrDef/Integer.php index d59738d2a..400e707d2 100644 --- a/library/HTMLPurifier/AttrDef/Integer.php +++ b/library/HTMLPurifier/AttrDef/Integer.php @@ -11,17 +11,20 @@ class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef { /** - * Bool indicating whether or not negative values are allowed + * Whether or not negative values are allowed. + * @type bool */ protected $negative = true; /** - * Bool indicating whether or not zero is allowed + * Whether or not zero is allowed. + * @type bool */ protected $zero = true; /** - * Bool indicating whether or not positive values are allowed + * Whether or not positive values are allowed. + * @type bool */ protected $positive = true; @@ -30,44 +33,59 @@ class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef * @param $zero Bool indicating whether or not zero is allowed * @param $positive Bool indicating whether or not positive values are allowed */ - public function __construct( - $negative = true, $zero = true, $positive = true - ) { + public function __construct($negative = true, $zero = true, $positive = true) + { $this->negative = $negative; - $this->zero = $zero; + $this->zero = $zero; $this->positive = $positive; } - public function validate($integer, $config, $context) { - + /** + * @param string $integer + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($integer, $config, $context) + { $integer = $this->parseCDATA($integer); - if ($integer === '') return false; + if ($integer === '') { + return false; + } // we could possibly simply typecast it to integer, but there are // certain fringe cases that must not return an integer. // clip leading sign - if ( $this->negative && $integer[0] === '-' ) { + if ($this->negative && $integer[0] === '-') { $digits = substr($integer, 1); - if ($digits === '0') $integer = '0'; // rm minus sign for zero - } elseif( $this->positive && $integer[0] === '+' ) { + if ($digits === '0') { + $integer = '0'; + } // rm minus sign for zero + } elseif ($this->positive && $integer[0] === '+') { $digits = $integer = substr($integer, 1); // rm unnecessary plus } else { $digits = $integer; } // test if it's numeric - if (!ctype_digit($digits)) return false; + if (!ctype_digit($digits)) { + return false; + } // perform scope tests - if (!$this->zero && $integer == 0) return false; - if (!$this->positive && $integer > 0) return false; - if (!$this->negative && $integer < 0) return false; + if (!$this->zero && $integer == 0) { + return false; + } + if (!$this->positive && $integer > 0) { + return false; + } + if (!$this->negative && $integer < 0) { + return false; + } return $integer; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/Lang.php b/library/HTMLPurifier/AttrDef/Lang.php index 10e6da56d..2a55cea64 100644 --- a/library/HTMLPurifier/AttrDef/Lang.php +++ b/library/HTMLPurifier/AttrDef/Lang.php @@ -7,15 +7,25 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef { - public function validate($string, $config, $context) { - + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $string = trim($string); - if (!$string) return false; + if (!$string) { + return false; + } $subtags = explode('-', $string); $num_subtags = count($subtags); - if ($num_subtags == 0) return false; // sanity check + if ($num_subtags == 0) { // sanity check + return false; + } // process primary subtag : $subtags[0] $length = strlen($subtags[0]); @@ -23,15 +33,15 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef case 0: return false; case 1: - if (! ($subtags[0] == 'x' || $subtags[0] == 'i') ) { + if (!($subtags[0] == 'x' || $subtags[0] == 'i')) { return false; } break; case 2: case 3: - if (! ctype_alpha($subtags[0]) ) { + if (!ctype_alpha($subtags[0])) { return false; - } elseif (! ctype_lower($subtags[0]) ) { + } elseif (!ctype_lower($subtags[0])) { $subtags[0] = strtolower($subtags[0]); } break; @@ -40,17 +50,23 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef } $new_string = $subtags[0]; - if ($num_subtags == 1) return $new_string; + if ($num_subtags == 1) { + return $new_string; + } // process second subtag : $subtags[1] $length = strlen($subtags[1]); if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) { return $new_string; } - if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]); + if (!ctype_lower($subtags[1])) { + $subtags[1] = strtolower($subtags[1]); + } $new_string .= '-' . $subtags[1]; - if ($num_subtags == 2) return $new_string; + if ($num_subtags == 2) { + return $new_string; + } // process all other subtags, index 2 and up for ($i = 2; $i < $num_subtags; $i++) { @@ -63,11 +79,8 @@ class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef } $new_string .= '-' . $subtags[$i]; } - return $new_string; - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/Switch.php b/library/HTMLPurifier/AttrDef/Switch.php index c9e3ed193..c7eb3199a 100644 --- a/library/HTMLPurifier/AttrDef/Switch.php +++ b/library/HTMLPurifier/AttrDef/Switch.php @@ -6,21 +6,41 @@ class HTMLPurifier_AttrDef_Switch { + /** + * @type string + */ protected $tag; - protected $withTag, $withoutTag; + + /** + * @type HTMLPurifier_AttrDef + */ + protected $withTag; + + /** + * @type HTMLPurifier_AttrDef + */ + protected $withoutTag; /** * @param string $tag Tag name to switch upon * @param HTMLPurifier_AttrDef $with_tag Call if token matches tag * @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token */ - public function __construct($tag, $with_tag, $without_tag) { + public function __construct($tag, $with_tag, $without_tag) + { $this->tag = $tag; $this->withTag = $with_tag; $this->withoutTag = $without_tag; } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $token = $context->get('CurrentToken', true); if (!$token || $token->name !== $this->tag) { return $this->withoutTag->validate($string, $config, $context); @@ -28,7 +48,6 @@ class HTMLPurifier_AttrDef_Switch return $this->withTag->validate($string, $config, $context); } } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/Text.php b/library/HTMLPurifier/AttrDef/Text.php index c6216cc53..4553a4ea9 100644 --- a/library/HTMLPurifier/AttrDef/Text.php +++ b/library/HTMLPurifier/AttrDef/Text.php @@ -6,10 +6,16 @@ class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef { - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { return $this->parseCDATA($string); } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/URI.php b/library/HTMLPurifier/AttrDef/URI.php index 01a6d83e9..c1cd89772 100644 --- a/library/HTMLPurifier/AttrDef/URI.php +++ b/library/HTMLPurifier/AttrDef/URI.php @@ -7,31 +7,54 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef { + /** + * @type HTMLPurifier_URIParser + */ protected $parser; + + /** + * @type bool + */ protected $embedsResource; /** - * @param $embeds_resource_resource Does the URI here result in an extra HTTP request? + * @param bool $embeds_resource Does the URI here result in an extra HTTP request? */ - public function __construct($embeds_resource = false) { + public function __construct($embeds_resource = false) + { $this->parser = new HTMLPurifier_URIParser(); - $this->embedsResource = (bool) $embeds_resource; + $this->embedsResource = (bool)$embeds_resource; } - public function make($string) { - $embeds = (bool) $string; + /** + * @param string $string + * @return HTMLPurifier_AttrDef_URI + */ + public function make($string) + { + $embeds = ($string === 'embedded'); return new HTMLPurifier_AttrDef_URI($embeds); } - public function validate($uri, $config, $context) { - - if ($config->get('URI.Disable')) return false; + /** + * @param string $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($uri, $config, $context) + { + if ($config->get('URI.Disable')) { + return false; + } $uri = $this->parseCDATA($uri); // parse the URI $uri = $this->parser->parse($uri); - if ($uri === false) return false; + if ($uri === false) { + return false; + } // add embedded flag to context for validators $context->register('EmbeddedURI', $this->embedsResource); @@ -41,23 +64,35 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef // generic validation $result = $uri->validate($config, $context); - if (!$result) break; + if (!$result) { + break; + } // chained filtering $uri_def = $config->getDefinition('URI'); $result = $uri_def->filter($uri, $config, $context); - if (!$result) break; + if (!$result) { + break; + } // scheme-specific validation $scheme_obj = $uri->getSchemeObj($config, $context); - if (!$scheme_obj) break; - if ($this->embedsResource && !$scheme_obj->browsable) break; + if (!$scheme_obj) { + break; + } + if ($this->embedsResource && !$scheme_obj->browsable) { + break; + } $result = $scheme_obj->validate($uri, $config, $context); - if (!$result) break; + if (!$result) { + break; + } // Post chained filtering $result = $uri_def->postFilter($uri, $config, $context); - if (!$result) break; + if (!$result) { + break; + } // survived gauntlet $ok = true; @@ -65,13 +100,12 @@ class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef } while (false); $context->destroy('EmbeddedURI'); - if (!$ok) return false; - + if (!$ok) { + return false; + } // back to string return $uri->toString(); - } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/URI/Email.php b/library/HTMLPurifier/AttrDef/URI/Email.php index bfee9d166..daf32b764 100644 --- a/library/HTMLPurifier/AttrDef/URI/Email.php +++ b/library/HTMLPurifier/AttrDef/URI/Email.php @@ -5,8 +5,11 @@ abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef /** * Unpacks a mailbox into its display-name and address + * @param string $string + * @return mixed */ - function unpack($string) { + public function unpack($string) + { // needs to be implemented } diff --git a/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php b/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php index 94c715ab4..52c0d5968 100644 --- a/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php +++ b/library/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php @@ -7,15 +7,23 @@ class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email { - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { // no support for named mailboxes i.e. "Bob <bob@example.com>" // that needs more percent encoding to be done - if ($string == '') return false; + if ($string == '') { + return false; + } $string = trim($string); $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string); return $result ? $string : false; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/URI/Host.php b/library/HTMLPurifier/AttrDef/URI/Host.php index 2156c10c6..e7df800b1 100644 --- a/library/HTMLPurifier/AttrDef/URI/Host.php +++ b/library/HTMLPurifier/AttrDef/URI/Host.php @@ -7,56 +7,122 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef { /** - * Instance of HTMLPurifier_AttrDef_URI_IPv4 sub-validator + * IPv4 sub-validator. + * @type HTMLPurifier_AttrDef_URI_IPv4 */ protected $ipv4; /** - * Instance of HTMLPurifier_AttrDef_URI_IPv6 sub-validator + * IPv6 sub-validator. + * @type HTMLPurifier_AttrDef_URI_IPv6 */ protected $ipv6; - public function __construct() { + public function __construct() + { $this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4(); $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6(); } - public function validate($string, $config, $context) { + /** + * @param string $string + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($string, $config, $context) + { $length = strlen($string); - if ($string === '') return ''; - if ($length > 1 && $string[0] === '[' && $string[$length-1] === ']') { + // empty hostname is OK; it's usually semantically equivalent: + // the default host as defined by a URI scheme is used: + // + // If the URI scheme defines a default for host, then that + // default applies when the host subcomponent is undefined + // or when the registered name is empty (zero length). + if ($string === '') { + return ''; + } + if ($length > 1 && $string[0] === '[' && $string[$length - 1] === ']') { //IPv6 $ip = substr($string, 1, $length - 2); $valid = $this->ipv6->validate($ip, $config, $context); - if ($valid === false) return false; - return '['. $valid . ']'; + if ($valid === false) { + return false; + } + return '[' . $valid . ']'; } // need to do checks on unusual encodings too $ipv4 = $this->ipv4->validate($string, $config, $context); - if ($ipv4 !== false) return $ipv4; + if ($ipv4 !== false) { + return $ipv4; + } // A regular domain name. - // This breaks I18N domain names, but we don't have proper IRI support, - // so force users to insert Punycode. If there's complaining we'll - // try to fix things into an international friendly form. + // This doesn't match I18N domain names, but we don't have proper IRI support, + // so force users to insert Punycode. + + // There is not a good sense in which underscores should be + // allowed, since it's technically not! (And if you go as + // far to allow everything as specified by the DNS spec... + // well, that's literally everything, modulo some space limits + // for the components and the overall name (which, by the way, + // we are NOT checking!). So we (arbitrarily) decide this: + // let's allow underscores wherever we would have allowed + // hyphens, if they are enabled. This is a pretty good match + // for browser behavior, for example, a large number of browsers + // cannot handle foo_.example.com, but foo_bar.example.com is + // fairly well supported. + $underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : ''; // The productions describing this are: $a = '[a-z]'; // alpha $an = '[a-z0-9]'; // alphanum - $and = '[a-z0-9-]'; // alphanum | "-" + $and = "[a-z0-9-$underscore]"; // alphanum | "-" // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum - $domainlabel = "$an($and*$an)?"; + $domainlabel = "$an($and*$an)?"; // toplabel = alpha | alpha *( alphanum | "-" ) alphanum - $toplabel = "$a($and*$an)?"; + $toplabel = "$a($and*$an)?"; // hostname = *( domainlabel "." ) toplabel [ "." ] - $match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string); - if (!$match) return false; + if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) { + return $string; + } - return $string; - } + // If we have Net_IDNA2 support, we can support IRIs by + // punycoding them. (This is the most portable thing to do, + // since otherwise we have to assume browsers support + if ($config->get('Core.EnableIDNA')) { + $idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true)); + // we need to encode each period separately + $parts = explode('.', $string); + try { + $new_parts = array(); + foreach ($parts as $part) { + $encodable = false; + for ($i = 0, $c = strlen($part); $i < $c; $i++) { + if (ord($part[$i]) > 0x7a) { + $encodable = true; + break; + } + } + if (!$encodable) { + $new_parts[] = $part; + } else { + $new_parts[] = $idna->encode($part); + } + } + $string = implode('.', $new_parts); + if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) { + return $string; + } + } catch (Exception $e) { + // XXX error reporting + } + } + return false; + } } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/URI/IPv4.php b/library/HTMLPurifier/AttrDef/URI/IPv4.php index ec4cf591b..30ac16c9e 100644 --- a/library/HTMLPurifier/AttrDef/URI/IPv4.php +++ b/library/HTMLPurifier/AttrDef/URI/IPv4.php @@ -8,32 +8,38 @@ class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef { /** - * IPv4 regex, protected so that IPv6 can reuse it + * IPv4 regex, protected so that IPv6 can reuse it. + * @type string */ protected $ip4; - public function validate($aIP, $config, $context) { - - if (!$this->ip4) $this->_loadRegex(); - - if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) - { - return $aIP; + /** + * @param string $aIP + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($aIP, $config, $context) + { + if (!$this->ip4) { + $this->_loadRegex(); } + if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) { + return $aIP; + } return false; - } /** * Lazy load function to prevent regex from being stuffed in * cache. */ - protected function _loadRegex() { + protected function _loadRegex() + { $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255 $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})"; } - } // vim: et sw=4 sts=4 diff --git a/library/HTMLPurifier/AttrDef/URI/IPv6.php b/library/HTMLPurifier/AttrDef/URI/IPv6.php index 9454e9be5..f243793ee 100644 --- a/library/HTMLPurifier/AttrDef/URI/IPv6.php +++ b/library/HTMLPurifier/AttrDef/URI/IPv6.php @@ -9,91 +9,81 @@ class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4 { - public function validate($aIP, $config, $context) { - - if (!$this->ip4) $this->_loadRegex(); + /** + * @param string $aIP + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool|string + */ + public function validate($aIP, $config, $context) + { + if (!$this->ip4) { + $this->_loadRegex(); + } $original = $aIP; $hex = '[0-9a-fA-F]'; $blk = '(?:' . $hex . '{1,4})'; - $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128 + $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128 // prefix check - if (strpos($aIP, '/') !== false) - { - if (preg_match('#' . $pre . '$#s', $aIP, $find)) - { - $aIP = substr($aIP, 0, 0-strlen($find[0])); - unset($find); - } - else - { - return false; - } + if (strpos($aIP, '/') !== false) { + if (preg_match('#' . $pre . '$#s', $aIP, $find)) { + $aIP = substr($aIP, 0, 0 - strlen($find[0])); + unset($find); + } else { + return false; + } } // IPv4-compatiblity check - if (preg_match('#(?<=:'.')' . $this->ip4 . '$#s', $aIP, $find)) - { - $aIP = substr($aIP, 0, 0-strlen($find[0])); - $ip = explode('.', $find[0]); - $ip = array_map('dechex', $ip); - $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3]; - unset($find, $ip); + if (preg_match('#(?<=:' . ')' . $this->ip4 . '$#s', $aIP, $find)) { + $aIP = substr($aIP, 0, 0 - strlen($find[0])); + $ip = explode('.', $find[0]); + $ip = array_map('dechex', $ip); + $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3]; + unset($find, $ip); } // compression check $aIP = explode('::', $aIP); $c = count($aIP); - if ($c > 2) - { + if ($c > 2) { + return false; + } elseif ($c == 2) { + list($first, $second) = $aIP; + $first = explode(':', $first); + $second = explode(':', $second); + + if (count($first) + count($second) > 8) { return false; - } - elseif ($c == 2) - { - list($first, $second) = $aIP; - $first = explode(':', $first); - $second = explode(':', $second); - - if (count($first) + count($second) > 8) - { - return false; - } + } - while(count($first) < 8) - { - array_push($first, '0'); - } + while (count($first) < 8) { + array_push($first, '0'); + } - array_splice($first, 8 - count($second), 8, $second); - $aIP = $first; - unset($first,$second); - } - else - { - $aIP = explode(':', $aIP[0]); + array_splice($first, 8 - count($second), 8, $second); + $aIP = $first; + unset($first, $second); + } else { + $aIP = explode(':', $aIP[0]); } $c = count($aIP); - if ($c != 8) - { - return false; + if ($c != 8) { + return false; } // All the pieces should be 16-bit hex strings. Are they? - foreach ($aIP as $piece) - { - if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) - { - return false; - } + foreach ($aIP as $piece) { + if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) { + return false; + } } - return $original; - } - } // vim: et sw=4 sts=4 |