|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* @package s9e\TextFormatter |
|
5
|
|
|
* @copyright Copyright (c) 2010-2019 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 DOMElement; |
|
11
|
|
|
use DOMXPath; |
|
12
|
|
|
use s9e\TextFormatter\Configurator\Exceptions\UnsafeTemplateException; |
|
13
|
|
|
use s9e\TextFormatter\Configurator\Helpers\AVTHelper; |
|
14
|
|
|
use s9e\TextFormatter\Configurator\Items\Tag; |
|
15
|
|
|
use s9e\TextFormatter\Configurator\TemplateCheck; |
|
16
|
|
|
|
|
17
|
|
|
class DisallowXPathFunction extends TemplateCheck |
|
18
|
|
|
{ |
|
19
|
|
|
/** |
|
20
|
|
|
* @var string Name of the disallowed function |
|
21
|
|
|
*/ |
|
22
|
|
|
public $funcName; |
|
23
|
|
|
|
|
24
|
|
|
/** |
|
25
|
|
|
* Constructor |
|
26
|
|
|
* |
|
27
|
|
|
* @param string $funcName Name of the disallowed function |
|
28
|
|
|
*/ |
|
29
|
7 |
|
public function __construct($funcName) |
|
30
|
|
|
{ |
|
31
|
7 |
|
$this->funcName = $funcName; |
|
32
|
|
|
} |
|
33
|
|
|
|
|
34
|
|
|
/** |
|
35
|
|
|
* Test for the presence of given XPath function |
|
36
|
|
|
* |
|
37
|
|
|
* @param DOMElement $template <xsl:template/> node |
|
38
|
|
|
* @param Tag $tag Tag this template belongs to |
|
39
|
|
|
* @return void |
|
40
|
|
|
*/ |
|
41
|
7 |
|
public function check(DOMElement $template, Tag $tag) |
|
42
|
|
|
{ |
|
43
|
|
|
// Regexp that matches the function call |
|
44
|
7 |
|
$regexp = '#(?!<\\pL)' . preg_quote($this->funcName, '#') . '\\s*\\(#iu'; |
|
45
|
|
|
|
|
46
|
|
|
// Allow whitespace around colons (NOTE: colons are unnecessarily escaped by preg_quote()) |
|
47
|
7 |
|
$regexp = str_replace('\\:', '\\s*:\\s*', $regexp); |
|
48
|
|
|
|
|
49
|
7 |
|
foreach ($this->getExpressions($template) as $expr => $node) |
|
50
|
|
|
{ |
|
51
|
|
|
// Remove string literals from the expression |
|
52
|
7 |
|
$expr = preg_replace('#([\'"]).*?\\1#s', '', $expr); |
|
53
|
|
|
|
|
54
|
|
|
// Test whether the expression contains a document() call |
|
55
|
7 |
|
if (preg_match($regexp, $expr)) |
|
56
|
|
|
{ |
|
57
|
7 |
|
throw new UnsafeTemplateException('An XPath expression uses the ' . $this->funcName . '() function', $node); |
|
58
|
|
|
} |
|
59
|
|
|
} |
|
60
|
|
|
} |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* Get all the potential XPath expressions used in given template |
|
64
|
|
|
* |
|
65
|
|
|
* @param DOMElement $template <xsl:template/> node |
|
66
|
|
|
* @return array XPath expression as key, reference node as value |
|
67
|
|
|
*/ |
|
68
|
7 |
|
protected function getExpressions(DOMElement $template) |
|
69
|
|
|
{ |
|
70
|
7 |
|
$xpath = new DOMXPath($template->ownerDocument); |
|
71
|
7 |
|
$exprs = []; |
|
72
|
|
|
|
|
73
|
7 |
|
foreach ($xpath->query('//@*') as $attribute) |
|
74
|
|
|
{ |
|
75
|
7 |
|
if ($attribute->parentNode->namespaceURI === self::XMLNS_XSL) |
|
76
|
|
|
{ |
|
77
|
|
|
// Attribute of an XSL element. May or may not use XPath, but it shouldn't produce |
|
78
|
|
|
// false-positives |
|
79
|
3 |
|
$expr = $attribute->value; |
|
80
|
3 |
|
$exprs[$expr] = $attribute; |
|
81
|
|
|
} |
|
82
|
|
|
else |
|
83
|
|
|
{ |
|
84
|
|
|
// Attribute of an HTML (or otherwise) element -- Look for inline expressions |
|
85
|
4 |
|
foreach (AVTHelper::parse($attribute->value) as $token) |
|
86
|
|
|
{ |
|
87
|
4 |
|
if ($token[0] === 'expression') |
|
88
|
|
|
{ |
|
89
|
4 |
|
$exprs[$token[1]] = $attribute; |
|
90
|
|
|
} |
|
91
|
|
|
} |
|
92
|
|
|
} |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
7 |
|
return $exprs; |
|
96
|
|
|
} |
|
97
|
|
|
} |