[ Index ] |
PHP Cross Reference of phpBB-3.3.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package s9e\TextFormatter 5 * @copyright Copyright (c) 2010-2022 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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Jun 23 12:25:44 2024 | Cross-referenced by PHPXref 0.7.1 |