Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
AbstractDynamicContentCheck.php
001 <?php
002
003 /**
004 * @package s9e\TextFormatter
005 * @copyright Copyright (c) 2010-2022 The s9e authors
006 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
007 */
008 namespace s9e\TextFormatter\Configurator\TemplateChecks;
009
010 use DOMAttr;
011 use DOMElement;
012 use DOMNode;
013 use DOMXPath;
014 use s9e\TextFormatter\Configurator\Exceptions\UnsafeTemplateException;
015 use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
016 use s9e\TextFormatter\Configurator\Items\Attribute;
017 use s9e\TextFormatter\Configurator\Items\Tag;
018 use s9e\TextFormatter\Configurator\TemplateCheck;
019
020 abstract class AbstractDynamicContentCheck extends TemplateCheck
021 {
022 /**
023 * @var bool Whether to ignore unknown attributes
024 */
025 protected $ignoreUnknownAttributes = false;
026
027 /**
028 * Get the nodes targeted by this check
029 *
030 * @param DOMElement $template <xsl:template/> node
031 * @return array Array of DOMElement instances
032 */
033 abstract protected function getNodes(DOMElement $template);
034
035 /**
036 * Return whether an attribute is considered safe
037 *
038 * @param Attribute $attribute Attribute
039 * @return bool
040 */
041 abstract protected function isSafe(Attribute $attribute);
042
043 /**
044 * Look for improperly-filtered dynamic content
045 *
046 * @param DOMElement $template <xsl:template/> node
047 * @param Tag $tag Tag this template belongs to
048 * @return void
049 */
050 public function check(DOMElement $template, Tag $tag)
051 {
052 foreach ($this->getNodes($template) as $node)
053 {
054 // Test this node's safety
055 $this->checkNode($node, $tag);
056 }
057 }
058
059 /**
060 * Configure this template check to detect unknown attributes
061 *
062 * @return void
063 */
064 public function detectUnknownAttributes()
065 {
066 $this->ignoreUnknownAttributes = false;
067 }
068
069 /**
070 * Configure this template check to ignore unknown attributes
071 *
072 * @return void
073 */
074 public function ignoreUnknownAttributes()
075 {
076 $this->ignoreUnknownAttributes = true;
077 }
078
079 /**
080 * Test whether a tag attribute is safe
081 *
082 * @param DOMNode $node Context node
083 * @param Tag $tag Source tag
084 * @param string $attrName Name of the attribute
085 * @return void
086 */
087 protected function checkAttribute(DOMNode $node, Tag $tag, $attrName)
088 {
089 // Test whether the attribute exists
090 if (!isset($tag->attributes[$attrName]))
091 {
092 if ($this->ignoreUnknownAttributes)
093 {
094 return;
095 }
096
097 throw new UnsafeTemplateException("Cannot assess the safety of unknown attribute '" . $attrName . "'", $node);
098 }
099
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 }