Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Merge branch 'security/escaper-usage'
Browse files Browse the repository at this point in the history
Fixes a number of components that were not using Zend\Escaper to escape HTML,
HTML attributes, and/or URLs.
  • Loading branch information
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 232 deletions.
52 changes: 1 addition & 51 deletions src/Cloud/Decorator/AbstractCloud.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,62 +10,12 @@

namespace Zend\Tag\Cloud\Decorator;

use Traversable;
use Zend\Stdlib\ArrayUtils;
use Zend\Tag\Cloud\Decorator\DecoratorInterface as Decorator;

/**
* Abstract class for cloud decorators
*
* @category Zend
* @package Zend_Tag
*/
abstract class AbstractCloud implements Decorator
abstract class AbstractCloud extends AbstractDecorator
{
/**
* Option keys to skip when calling setOptions()
*
* @var array
*/
protected $skipOptions = array(
'options',
'config',
);

/**
* Create a new cloud decorator with options
*
* @param array|Traversable $options
*/
public function __construct($options = null)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (is_array($options)) {
$this->setOptions($options);
}
}

/**
* Set options from array
*
* @param array $options Configuration for the decorator
* @return AbstractCloud
*/
public function setOptions(array $options)
{
foreach ($options as $key => $value) {
if (in_array(strtolower($key), $this->skipOptions)) {
continue;
}

$method = 'set' . $key;
if (method_exists($this, $method)) {
$this->$method($value);
}
}

return $this;
}
}
190 changes: 190 additions & 0 deletions src/Cloud/Decorator/AbstractDecorator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_Tag
*/

namespace Zend\Tag\Cloud\Decorator;

use Traversable;
use Zend\Escaper\Escaper;
use Zend\Stdlib\ArrayUtils;
use Zend\Tag\Cloud\Decorator\DecoratorInterface as Decorator;
use Zend\Tag\Exception;

/**
* Abstract class for decorators
*
* @category Zend
* @package Zend_Tag
*/
abstract class AbstractDecorator implements Decorator
{
/**
* @var string Encoding to use
*/
protected $encoding = 'UTF-8';

/**
* @var Escaper
*/
protected $escaper;

/**
* Option keys to skip when calling setOptions()
*
* @var array
*/
protected $skipOptions = array(
'options',
'config',
);

/**
* Create a new decorator with options
*
* @param array|Traversable $options
*/
public function __construct($options = null)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (is_array($options)) {
$this->setOptions($options);
}
}

/**
* Set options from array
*
* @param array $options Configuration for the decorator
* @return AbstractTag
*/
public function setOptions(array $options)
{
foreach ($options as $key => $value) {
if (in_array(strtolower($key), $this->skipOptions)) {
continue;
}

$method = 'set' . $key;
if (method_exists($this, $method)) {
$this->$method($value);
}
}

return $this;
}

/**
* Get encoding
*
* @return string
*/
public function getEncoding()
{
return $this->encoding;
}

/**
* Set encoding
*
* @param string
* @return HTMLCloud
*/
public function setEncoding($value)
{
$this->encoding = (string) $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;
}

/**
* 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)
));
}
}

protected function wrapTag($html)
{
$escaper = $this->getEscaper();
foreach ($this->getHTMLTags() as $key => $data) {
if (is_array($data)) {
$attributes = '';
$htmlTag = $key;
$this->validateElementName($htmlTag);

foreach ($data as $param => $value) {
$this->validateAttributeName($param);
$attributes .= ' ' . $param . '="' . $escaper->escapeHtmlAttr($value) . '"';
}
} else {
$attributes = '';
$htmlTag = $data;
$this->validateElementName($htmlTag);
}

$html = sprintf('<%1$s%3$s>%2$s</%1$s>', $htmlTag, $html, $attributes);
}
return $html;
}
}
52 changes: 1 addition & 51 deletions src/Cloud/Decorator/AbstractTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,62 +10,12 @@

namespace Zend\Tag\Cloud\Decorator;

use Traversable;
use Zend\Stdlib\ArrayUtils;
use Zend\Tag\Cloud\Decorator\DecoratorInterface as Decorator;

/**
* Abstract class for tag decorators
*
* @category Zend
* @package Zend_Tag
*/
abstract class AbstractTag implements Decorator
abstract class AbstractTag extends AbstractDecorator
{
/**
* Option keys to skip when calling setOptions()
*
* @var array
*/
protected $skipOptions = array(
'options',
'config',
);

/**
* Create a new cloud decorator with options
*
* @param array|Traversable $options
*/
public function __construct($options = null)
{
if ($options instanceof Traversable) {
$options = ArrayUtils::iteratorToArray($options);
}
if (is_array($options)) {
$this->setOptions($options);
}
}

/**
* Set options from array
*
* @param array $options Configuration for the decorator
* @return AbstractTag
*/
public function setOptions(array $options)
{
foreach ($options as $key => $value) {
if (in_array(strtolower($key), $this->skipOptions)) {
continue;
}

$method = 'set' . $key;
if (method_exists($this, $method)) {
$this->$method($value);
}
}

return $this;
}
}
46 changes: 1 addition & 45 deletions src/Cloud/Decorator/HtmlCloud.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@
*/
class HtmlCloud extends AbstractCloud
{
/**
* @var string Encoding to use
*/
protected $encoding = 'UTF-8';

/**
* List of HTML tags
*
Expand All @@ -39,28 +34,6 @@ class HtmlCloud extends AbstractCloud
*/
protected $separator = ' ';

/**
* Get encoding
*
* @return string
*/
public function getEncoding()
{
return $this->encoding;
}

/**
* Set encoding
*
* @param string
* @return HTMLCloud
*/
public function setEncoding($value)
{
$this->encoding = (string) $value;
return $this;
}

/**
* Set the HTML tags surrounding all tags
*
Expand Down Expand Up @@ -121,24 +94,7 @@ public function render($tags)
));
}
$cloudHTML = implode($this->getSeparator(), $tags);

$enc = $this->getEncoding();
foreach ($this->getHTMLTags() as $key => $data) {
if (is_array($data)) {
$htmlTag = $key;
$attributes = '';

foreach ($data as $param => $value) {
$attributes .= ' ' . $param . '="' . htmlspecialchars($value, ENT_COMPAT, $enc) . '"';
}
} else {
$htmlTag = $data;
$attributes = '';
}

$cloudHTML = sprintf('<%1$s%3$s>%2$s</%1$s>', $htmlTag, $cloudHTML, $attributes);
}

$cloudHTML = $this->wrapTag($cloudHTML);
return $cloudHTML;
}
}
Loading

0 comments on commit 2817c6f

Please # to comment.