From 0a78cb2b633a618ac514eadef2c19ef78b1e12f2 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 17 Sep 2012 13:10:59 -0500 Subject: [PATCH 1/9] Patch Zend\Tag\Cloud\Decorator\HtmlCloud to use Escaper - Modified HtmlCloud to use Escaper internally (and to allow composing an Escaper instance) - Validate HTML element names and attribute names before using them. --- .../Zend/Tag/Cloud/Decorator/HtmlCloud.php | 76 ++++++++++++++++++- .../InvalidAttributeNameException.php | 16 ++++ .../Exception/InvalidElementNameException.php | 16 ++++ library/Zend/Tag/composer.json | 5 +- tests/ZendTest/Tag/Cloud/CloudTest.php | 4 +- .../Tag/Cloud/Decorator/HtmlCloudTest.php | 61 ++++++++++++++- 6 files changed, 169 insertions(+), 9 deletions(-) create mode 100644 library/Zend/Tag/Exception/InvalidAttributeNameException.php create mode 100644 library/Zend/Tag/Exception/InvalidElementNameException.php diff --git a/library/Zend/Tag/Cloud/Decorator/HtmlCloud.php b/library/Zend/Tag/Cloud/Decorator/HtmlCloud.php index b0429469563..6f017519668 100644 --- a/library/Zend/Tag/Cloud/Decorator/HtmlCloud.php +++ b/library/Zend/Tag/Cloud/Decorator/HtmlCloud.php @@ -10,6 +10,9 @@ namespace Zend\Tag\Cloud\Decorator; +use Zend\Escaper\Escaper; +use Zend\Tag\Exception; + /** * Simple HTML decorator for clouds * @@ -23,6 +26,11 @@ class HtmlCloud extends AbstractCloud */ protected $encoding = 'UTF-8'; + /** + * @var Escaper + */ + protected $escaper; + /** * List of HTML tags * @@ -61,6 +69,33 @@ public function setEncoding($value) return $this; } + /** + * Set Escaper instance + * + * @param Escaper $escaper + * @return HtmlCloud + */ + public function setEscaper($escaper) + { + $this->escaper = $escaper; + return $this; + } + + /** + * Retrieve Escaper instance + * + * If none registered, instantiates and registers one using current encoding. + * + * @return Escaper + */ + public function getEscaper() + { + if (null === $this->escaper) { + $this->setEscaper(new Escaper($this->getEncoding())); + } + return $this->escaper; + } + /** * Set the HTML tags surrounding all tags * @@ -122,17 +157,20 @@ public function render($tags) } $cloudHTML = implode($this->getSeparator(), $tags); - $enc = $this->getEncoding(); + $escaper = $this->getEscaper(); foreach ($this->getHTMLTags() as $key => $data) { if (is_array($data)) { $htmlTag = $key; + $this->validateElementName($htmlTag); $attributes = ''; foreach ($data as $param => $value) { - $attributes .= ' ' . $param . '="' . htmlspecialchars($value, ENT_COMPAT, $enc) . '"'; + $this->validateAttributeName($param); + $attributes .= ' ' . $param . '="' . $escaper->escapeHtmlAttr($value) . '"'; } } else { $htmlTag = $data; + $this->validateElementName($htmlTag); $attributes = ''; } @@ -141,4 +179,38 @@ public function render($tags) return $cloudHTML; } + + /** + * Validate an HTML element name + * + * @param string $name + * @throws Exception\InvalidElementNameException + */ + protected function validateElementName($name) + { + if (!preg_match('/^[a-z0-9]+$/i', $name)) { + throw new Exception\InvalidElementNameException(sprintf( + '%s: Invalid element name "%s" provided; please provide valid HTML element names', + __METHOD__, + $this->getEscaper()->escapeHtml($name) + )); + } + } + + /** + * Validate an HTML attribute name + * + * @param string $name + * @throws Exception\InvalidAttributeNameException + */ + protected function validateAttributeName($name) + { + if (!preg_match('/^[a-z_:][-a-z0-9_:.]*$/i', $name)) { + throw new Exception\InvalidAttributeNameException(sprintf( + '%s: Invalid HTML attribute name "%s" provided; please provide valid HTML attribute names', + __METHOD__, + $this->getEscaper()->escapeHtml($name) + )); + } + } } diff --git a/library/Zend/Tag/Exception/InvalidAttributeNameException.php b/library/Zend/Tag/Exception/InvalidAttributeNameException.php new file mode 100644 index 00000000000..41288292adc --- /dev/null +++ b/library/Zend/Tag/Exception/InvalidAttributeNameException.php @@ -0,0 +1,16 @@ +=5.3.3" + "php": ">=5.3.3", + "zendframework/zend-escaper": "self.version" } -} \ No newline at end of file +} diff --git a/tests/ZendTest/Tag/Cloud/CloudTest.php b/tests/ZendTest/Tag/Cloud/CloudTest.php index b22690bb6a9..0b8b07438bc 100644 --- a/tests/ZendTest/Tag/Cloud/CloudTest.php +++ b/tests/ZendTest/Tag/Cloud/CloudTest.php @@ -208,7 +208,7 @@ public function testSkipOptions() public function testRender() { $cloud = $this->_getCloud(array('tags' => array(array('title' => 'foo', 'weight' => 1), array('title' => 'bar', 'weight' => 3)))); - $expected = '