PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.10.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.10.7
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / phpoffice / phpspreadsheet / src / PhpSpreadsheet / Reader / Security / XmlScanner.php

XmlScanner.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.10.7, at vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php

155 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace PhpOffice\PhpSpreadsheet\Reader\Security;
4
5 use PhpOffice\PhpSpreadsheet\Reader;
6 use PhpOffice\PhpSpreadsheet\Settings;
7
8 class XmlScanner
9 {
10 /**
11 * String used to identify risky xml elements.
12 *
13 * @var string
14 */
15 private $pattern;
16
17 private $callback;
18
19 private static $libxmlDisableEntityLoaderValue;
20
21 public function __construct($pattern = '<!DOCTYPE')
22 {
23 $this->pattern = $pattern;
24
25 $this->disableEntityLoaderCheck();
26
27 // A fatal error will bypass the destructor, so we register a shutdown here
28 register_shutdown_function([__CLASS__, 'shutdown']);
29 }
30
31 public static function getInstance(Reader\IReader $reader)
32 {
33 switch (true) {
34 case $reader instanceof Reader\Html:
35 return new self('<!ENTITY');
36 case $reader instanceof Reader\Xlsx:
37 case $reader instanceof Reader\Xml:
38 case $reader instanceof Reader\Ods:
39 case $reader instanceof Reader\Gnumeric:
40 return new self('<!DOCTYPE');
41 default:
42 return new self('<!DOCTYPE');
43 }
44 }
45
46 public static function threadSafeLibxmlDisableEntityLoaderAvailability()
47 {
48 if (PHP_MAJOR_VERSION == 7) {
49 switch (PHP_MINOR_VERSION) {
50 case 2:
51 return PHP_RELEASE_VERSION >= 1;
52 case 1:
53 return PHP_RELEASE_VERSION >= 13;
54 case 0:
55 return PHP_RELEASE_VERSION >= 27;
56 }
57
58 return true;
59 }
60
61 return false;
62 }
63
64 private function disableEntityLoaderCheck()
65 {
66 if (Settings::getLibXmlDisableEntityLoader()) {
67 $libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
68
69 if (self::$libxmlDisableEntityLoaderValue === null) {
70 self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
71 }
72 }
73 }
74
75 public static function shutdown()
76 {
77 if (self::$libxmlDisableEntityLoaderValue !== null) {
78 libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
79 self::$libxmlDisableEntityLoaderValue = null;
80 }
81 }
82
83 public function __destruct()
84 {
85 self::shutdown();
86 }
87
88 public function setAdditionalCallback(callable $callback)
89 {
90 $this->callback = $callback;
91 }
92
93 private function toUtf8($xml)
94 {
95 $pattern = '/encoding="(.*?)"/';
96 $result = preg_match($pattern, $xml, $matches);
97 $charset = strtoupper($result ? $matches[1] : 'UTF-8');
98
99 if ($charset !== 'UTF-8') {
100 $xml = mb_convert_encoding($xml, 'UTF-8', $charset);
101
102 $result = preg_match($pattern, $xml, $matches);
103 $charset = strtoupper($result ? $matches[1] : 'UTF-8');
104 if ($charset !== 'UTF-8') {
105 throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
106 }
107 }
108
109 return $xml;
110 }
111
112 /**
113 * Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks.
114 *
115 * @param mixed $xml
116 *
117 * @throws Reader\Exception
118 *
119 * @return string
120 */
121 public function scan($xml)
122 {
123 $this->disableEntityLoaderCheck();
124
125 $xml = $this->toUtf8($xml);
126
127 // Don't rely purely on libxml_disable_entity_loader()
128 $pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
129
130 if (preg_match($pattern, $xml)) {
131 throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
132 }
133
134 if ($this->callback !== null && is_callable($this->callback)) {
135 $xml = call_user_func($this->callback, $xml);
136 }
137
138 return $xml;
139 }
140
141 /**
142 * Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks.
143 *
144 * @param string $filestream
145 *
146 * @throws Reader\Exception
147 *
148 * @return string
149 */
150 public function scanFile($filestream)
151 {
152 return $this->scan(file_get_contents($filestream));
153 }
154 }
155