30 private $notAllowedTags = [
'script',
'img',
'embed',
'iframe',
'video',
'source',
'object',
'audio'];
35 private $allowedAttributes = [
'id',
'class',
'href',
'target',
'title',
'style'];
40 private static $xssFiltrationPattern =
41 '/((javascript(\\\\x3a|:|%3A))|(data(\\\\x3a|:|%3A))|(vbscript:))|' 42 .
'((\\\\x6A\\\\x61\\\\x76\\\\x61\\\\x73\\\\x63\\\\x72\\\\x69\\\\x70\\\\x74(\\\\x3a|:|%3A))|' 43 .
'(\\\\x64\\\\x61\\\\x74\\\\x61(\\\\x3a|:|%3A)))/i';
48 private $escapeAsUrlAttributes = [
'href'];
62 if (!is_array(
$data)) {
66 if (is_array(
$data)) {
72 if (is_array($allowedTags) && !empty($allowedTags)) {
73 $allowedTags = $this->filterProhibitedTags($allowedTags);
74 $wrapperElementId = uniqid();
75 $domDocument = new \DOMDocument(
'1.0',
'UTF-8');
77 function ($errorNumber, $errorString) {
78 throw new \Exception($errorString, $errorNumber);
81 $string = mb_convert_encoding(
$data,
'HTML-ENTITIES',
'UTF-8');
83 $domDocument->loadHTML(
84 '<html><body id="' . $wrapperElementId .
'">' . $string .
'</body></html>' 86 }
catch (\Exception $e) {
87 restore_error_handler();
88 $this->getLogger()->critical($e);
90 restore_error_handler();
92 $this->removeNotAllowedTags($domDocument, $allowedTags);
93 $this->removeNotAllowedAttributes($domDocument);
94 $this->escapeText($domDocument);
95 $this->escapeAttributeValues($domDocument);
97 $result = mb_convert_encoding($domDocument->saveHTML(),
'UTF-8',
'HTML-ENTITIES');
98 preg_match(
'/<body id="' . $wrapperElementId .
'">(.+)<\/body><\/html>$/si',
$result, $matches);
99 return !empty($matches) ? $matches[1] :
'';
101 $result = htmlspecialchars(
$data, ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8',
false);
116 private function removeNotAllowedTags(\DOMDocument $domDocument, array $allowedTags)
118 $xpath = new \DOMXPath($domDocument);
119 $nodes = $xpath->query(
120 '//node()[name() != \'' 121 . implode(
'\' and
name() != \
'', array_merge($allowedTags, [
'html',
'body']))
124 foreach ($nodes as $node) { 125 if ($node->nodeName != '#text
' && $node->nodeName != '#comment
') { 126 $node->parentNode->replaceChild($domDocument->createTextNode($node->textContent), $node); 137 private function removeNotAllowedAttributes(\DOMDocument $domDocument) 139 $xpath = new \DOMXPath($domDocument); 140 $nodes = $xpath->query( 143 foreach ($nodes as $node) {
144 $node->parentNode->removeAttribute($node->nodeName);
154 private function escapeText(\DOMDocument $domDocument)
156 $xpath = new \DOMXPath($domDocument);
157 $nodes = $xpath->query(
'//text()');
158 foreach ($nodes as $node) {
159 $node->textContent = $this->
escapeHtml($node->textContent);
169 private function escapeAttributeValues(\DOMDocument $domDocument)
171 $xpath = new \DOMXPath($domDocument);
172 $nodes = $xpath->query(
'//@*');
173 foreach ($nodes as $node) {
174 $value = $this->escapeAttributeValue(
176 $node->parentNode->getAttribute($node->nodeName)
178 $node->parentNode->setAttribute($node->nodeName,
$value);
189 private function escapeAttributeValue(
$name,
$value)
204 if ($escapeSingleQuote) {
205 return $this->getEscaper()->escapeHtmlAttr((
string) $string);
207 return htmlspecialchars((
string)$string, ENT_COMPAT,
'UTF-8',
false);
230 return $this->getEscaper()->escapeUrl($string);
242 if ($string ===
'' || ctype_digit($string)) {
246 return preg_replace_callback(
247 '/[^a-z0-9,\._]/iSu',
248 function ($matches) {
250 if (strlen($chr) != 1) {
251 $chr = mb_convert_encoding($chr,
'UTF-16BE',
'UTF-8');
252 $chr = ($chr ===
false) ?
'' : $chr;
254 return sprintf(
'\\u%04s', strtoupper(bin2hex($chr)));
269 return $this->getEscaper()->escapeCss($string);
282 if (is_array(
$data)) {
302 return htmlspecialchars(
303 $this->escapeScriptIdentifiers((
string)
$data),
304 ENT_COMPAT | ENT_HTML5 | ENT_HTML401,
316 private function escapeScriptIdentifiers(
string $data): string
318 $filteredData = preg_replace(self::$xssFiltrationPattern,
':',
$data) ?:
'';
319 if (preg_match(self::$xssFiltrationPattern, $filteredData)) {
320 $filteredData = $this->escapeScriptIdentifiers($filteredData);
323 return $filteredData;
338 if ($addSlashes ===
true) {
341 return htmlspecialchars(
$data, ENT_QUOTES,
null,
false);
350 private function getEscaper()
352 if ($this->escaper ==
null) {
354 ->get(\
Magento\Framework\ZendEscaper::class);
356 return $this->escaper;
365 private function getLogger()
367 if ($this->logger ==
null) {
369 ->get(\Psr\Log\LoggerInterface::class);
371 return $this->logger;
380 private function filterProhibitedTags(array $allowedTags): array
382 $notAllowedTags = array_intersect(
383 array_map(
'strtolower', $allowedTags),
384 $this->notAllowedTags
387 if (!empty($notAllowedTags)) {
388 $this->getLogger()->critical(
389 'The following tag(s) are not allowed: ' . implode(
', ', $notAllowedTags)
391 $allowedTags = array_diff($allowedTags, $this->notAllowedTags);
elseif(isset( $params[ 'redirect_parent']))
escapeJsQuote($data, $quote='\'')
escapeHtml($data, $allowedTags=null)
escapeQuote($data, $addSlashes=false)
escapeHtmlAttr($string, $escapeSingleQuote=true)
if(!isset($_GET['name'])) $name