[ Index ]

PHP Cross Reference of phpBB-3.3.7-deutsch

title

Body

[close]

/vendor/s9e/text-formatter/src/Configurator/TemplateChecks/ -> AbstractDynamicContentCheck.php (source)

   1  <?php
   2  
   3  /**
   4  * @package   s9e\TextFormatter
   5  * @copyright Copyright (c) 2010-2021 The s9e authors
   6  * @license   http://www.opensource.org/licenses/mit-license.php The MIT License
   7  */
   8  namespace s9e\TextFormatter\Configurator\TemplateChecks;
   9  
  10  use DOMAttr;
  11  use DOMElement;
  12  use DOMNode;
  13  use DOMXPath;
  14  use s9e\TextFormatter\Configurator\Exceptions\UnsafeTemplateException;
  15  use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
  16  use s9e\TextFormatter\Configurator\Items\Attribute;
  17  use s9e\TextFormatter\Configurator\Items\Tag;
  18  use s9e\TextFormatter\Configurator\TemplateCheck;
  19  
  20  abstract class AbstractDynamicContentCheck extends TemplateCheck
  21  {
  22      /**
  23      * @var bool Whether to ignore unknown attributes
  24      */
  25      protected $ignoreUnknownAttributes = false;
  26  
  27      /**
  28      * Get the nodes targeted by this check
  29      *
  30      * @param  DOMElement $template <xsl:template/> node
  31      * @return array             Array of DOMElement instances
  32      */
  33      abstract protected function getNodes(DOMElement $template);
  34  
  35      /**
  36      * Return whether an attribute is considered safe
  37      *
  38      * @param  Attribute $attribute Attribute
  39      * @return bool
  40      */
  41      abstract protected function isSafe(Attribute $attribute);
  42  
  43      /**
  44      * Look for improperly-filtered dynamic content
  45      *
  46      * @param  DOMElement $template <xsl:template/> node
  47      * @param  Tag        $tag      Tag this template belongs to
  48      * @return void
  49      */
  50  	public function check(DOMElement $template, Tag $tag)
  51      {
  52          foreach ($this->getNodes($template) as $node)
  53          {
  54              // Test this node's safety
  55              $this->checkNode($node, $tag);
  56          }
  57      }
  58  
  59      /**
  60      * Configure this template check to detect unknown attributes
  61      *
  62      * @return void
  63      */
  64  	public function detectUnknownAttributes()
  65      {
  66          $this->ignoreUnknownAttributes = false;
  67      }
  68  
  69      /**
  70      * Configure this template check to ignore unknown attributes
  71      *
  72      * @return void
  73      */
  74  	public function ignoreUnknownAttributes()
  75      {
  76          $this->ignoreUnknownAttributes = true;
  77      }
  78  
  79      /**
  80      * Test whether a tag attribute is safe
  81      *
  82      * @param  DOMNode $node     Context node
  83      * @param  Tag     $tag      Source tag
  84      * @param  string  $attrName Name of the attribute
  85      * @return void
  86      */
  87  	protected function checkAttribute(DOMNode $node, Tag $tag, $attrName)
  88      {
  89          // Test whether the attribute exists
  90          if (!isset($tag->attributes[$attrName]))
  91          {
  92              if ($this->ignoreUnknownAttributes)
  93              {
  94                  return;
  95              }
  96  
  97              throw new UnsafeTemplateException("Cannot assess the safety of unknown attribute '" . $attrName . "'", $node);
  98          }
  99  
 100          // Test whether the attribute is safe to be used in this content type
 101          if (!$this->tagFiltersAttributes($tag) || !$this->isSafe($tag->attributes[$attrName]))
 102          {
 103              throw new UnsafeTemplateException("Attribute '" . $attrName . "' is not properly sanitized to be used in this context", $node);
 104          }
 105      }
 106  
 107      /**
 108      * Test whether an attribute expression is safe
 109      *
 110      * @param  DOMNode $node Context node
 111      * @param  Tag     $tag  Source tag
 112      * @param  string  $expr XPath expression that evaluates to one or multiple named attributes
 113      * @return void
 114      */
 115  	protected function checkAttributeExpression(DOMNode $node, Tag $tag, $expr)
 116      {
 117          preg_match_all('(@([-\\w]+))', $expr, $matches);
 118          foreach ($matches[1] as $attrName)
 119          {
 120              $this->checkAttribute($node, $tag, $attrName);
 121          }
 122      }
 123  
 124      /**
 125      * Test whether an attribute node is safe
 126      *
 127      * @param  DOMAttr $attribute Attribute node
 128      * @param  Tag     $tag       Reference tag
 129      * @return void
 130      */
 131  	protected function checkAttributeNode(DOMAttr $attribute, Tag $tag)
 132      {
 133          // Parse the attribute value for XPath expressions and assess their safety
 134          foreach (AVTHelper::parse($attribute->value) as $token)
 135          {
 136              if ($token[0] === 'expression')
 137              {
 138                  $this->checkExpression($attribute, $token[1], $tag);
 139              }
 140          }
 141      }
 142  
 143      /**
 144      * Test whether a node's context can be safely assessed
 145      *
 146      * @param  DOMNode $node Source node
 147      * @return void
 148      */
 149  	protected function checkContext(DOMNode $node)
 150      {
 151          // Test whether we know in what context this node is used. An <xsl:for-each/> ancestor would // change this node's context
 152          $xpath     = new DOMXPath($node->ownerDocument);
 153          $ancestors = $xpath->query('ancestor::xsl:for-each', $node);
 154  
 155          if ($ancestors->length)
 156          {
 157              throw new UnsafeTemplateException("Cannot assess context due to '" . $ancestors->item(0)->nodeName . "'", $node);
 158          }
 159      }
 160  
 161      /**
 162      * Test whether an <xsl:copy-of/> node is safe
 163      *
 164      * @param  DOMElement $node <xsl:copy-of/> node
 165      * @param  Tag        $tag  Reference tag
 166      * @return void
 167      */
 168  	protected function checkCopyOfNode(DOMElement $node, Tag $tag)
 169      {
 170          $this->checkSelectNode($node->getAttributeNode('select'), $tag);
 171      }
 172  
 173      /**
 174      * Test whether an element node is safe
 175      *
 176      * @param  DOMElement $element Element
 177      * @param  Tag        $tag     Reference tag
 178      * @return void
 179      */
 180  	protected function checkElementNode(DOMElement $element, Tag $tag)
 181      {
 182          $xpath = new DOMXPath($element->ownerDocument);
 183  
 184          // If current node is not an <xsl:attribute/> element, we exclude descendants
 185          // with an <xsl:attribute/> ancestor so that content such as:
 186          //   <script><xsl:attribute name="id"><xsl:value-of/></xsl:attribute></script>
 187          // would not trigger a false-positive due to the presence of an <xsl:value-of/>
 188          // element in a <script>
 189          $predicate = ($element->localName === 'attribute') ? '' : '[not(ancestor::xsl:attribute)]';
 190  
 191          // Test the select expression of <xsl:value-of/> nodes
 192          $query = './/xsl:value-of' . $predicate;
 193          foreach ($xpath->query($query, $element) as $valueOf)
 194          {
 195              $this->checkSelectNode($valueOf->getAttributeNode('select'), $tag);
 196          }
 197  
 198          // Reject all <xsl:apply-templates/> nodes
 199          $query = './/xsl:apply-templates' . $predicate;
 200          foreach ($xpath->query($query, $element) as $applyTemplates)
 201          {
 202              throw new UnsafeTemplateException('Cannot allow unfiltered data in this context', $applyTemplates);
 203          }
 204      }
 205  
 206      /**
 207      * Test the safety of an XPath expression
 208      *
 209      * @param  DOMNode $node Source node
 210      * @param  string  $expr XPath expression
 211      * @param  Tag     $tag  Source tag
 212      * @return void
 213      */
 214  	protected function checkExpression(DOMNode $node, $expr, Tag $tag)
 215      {
 216          $this->checkContext($node);
 217  
 218          if (preg_match('/^\\$(\\w+)$/', $expr, $m))
 219          {
 220              // Either this expression came from a variable that is considered safe, or it's a
 221              // stylesheet parameters, which are considered safe by default
 222              $this->checkVariable($node, $tag, $m[1]);
 223          }
 224          elseif (preg_match('/^@[-\\w]+(?:\\s*\\|\\s*@[-\\w]+)*$/', $expr))
 225          {
 226              $this->checkAttributeExpression($node, $tag, $expr);
 227          }
 228          elseif (!$this->isExpressionSafe($expr))
 229          {
 230              throw new UnsafeTemplateException("Cannot assess the safety of expression '" . $expr . "'", $node);
 231          }
 232      }
 233  
 234      /**
 235      * Test whether a node is safe
 236      *
 237      * @param  DOMNode $node Source node
 238      * @param  Tag     $tag  Reference tag
 239      * @return void
 240      */
 241  	protected function checkNode(DOMNode $node, Tag $tag)
 242      {
 243          if ($node instanceof DOMAttr)
 244          {
 245              $this->checkAttributeNode($node, $tag);
 246          }
 247          elseif ($node instanceof DOMElement)
 248          {
 249              if ($node->namespaceURI === self::XMLNS_XSL && $node->localName === 'copy-of')
 250              {
 251                  $this->checkCopyOfNode($node, $tag);
 252              }
 253              else
 254              {
 255                  $this->checkElementNode($node, $tag);
 256              }
 257          }
 258      }
 259  
 260      /**
 261      * Check whether a variable is safe in context
 262      *
 263      * @param  DOMNode $node  Context node
 264      * @param  Tag     $tag   Source tag
 265      * @param  string  $qname Name of the variable
 266      * @return void
 267      */
 268  	protected function checkVariable(DOMNode $node, $tag, $qname)
 269      {
 270          // Test whether this variable comes from a previous xsl:param or xsl:variable element
 271          $this->checkVariableDeclaration($node, $tag, 'xsl:param[@name="' . $qname . '"]');
 272          $this->checkVariableDeclaration($node, $tag, 'xsl:variable[@name="' . $qname . '"]');
 273      }
 274  
 275      /**
 276      * Check whether a variable declaration is safe in context
 277      *
 278      * @param  DOMNode $node  Context node
 279      * @param  Tag     $tag   Source tag
 280      * @param  string  $query XPath query
 281      * @return void
 282      */
 283  	protected function checkVariableDeclaration(DOMNode $node, $tag, $query)
 284      {
 285          $query = 'ancestor-or-self::*/preceding-sibling::' . $query . '[@select]';
 286          $xpath = new DOMXPath($node->ownerDocument);
 287          foreach ($xpath->query($query, $node) as $varNode)
 288          {
 289              // Intercept the UnsafeTemplateException and change the node to the one we're
 290              // really checking before rethrowing it
 291              try
 292              {
 293                  $this->checkExpression($varNode, $varNode->getAttribute('select'), $tag);
 294              }
 295              catch (UnsafeTemplateException $e)
 296              {
 297                  $e->setNode($node);
 298  
 299                  throw $e;
 300              }
 301          }
 302      }
 303  
 304      /**
 305      * Test whether a select attribute of a node is safe
 306      *
 307      * @param  DOMAttr $select Select attribute node
 308      * @param  Tag     $tag    Reference tag
 309      * @return void
 310      */
 311  	protected function checkSelectNode(DOMAttr $select, Tag $tag)
 312      {
 313          $this->checkExpression($select, $select->value, $tag);
 314      }
 315  
 316      /**
 317      * Test whether given expression is safe in context
 318      *
 319      * @param  string $expr XPath expression
 320      * @return bool         Whether the expression is safe in context
 321      */
 322  	protected function isExpressionSafe($expr)
 323      {
 324          return false;
 325      }
 326  
 327      /**
 328      * Test whether given tag filters attribute values
 329      *
 330      * @param  Tag  $tag
 331      * @return bool
 332      */
 333  	protected function tagFiltersAttributes(Tag $tag)
 334      {
 335          return $tag->filterChain->containsCallback('s9e\\TextFormatter\\Parser\\FilterProcessing::filterAttributes');
 336      }
 337  }


Generated: Thu Mar 24 21:31:15 2022 Cross-referenced by PHPXref 0.7.1