diff --git a/vendor/magento/module-translation/Model/Inline/Parser.php b/vendor/magento/module-translation/Model/Inline/Parser.php index 2d223a6464d2c..a9d2d02739912 100644 --- a/vendor/magento/module-translation/Model/Inline/Parser.php +++ b/vendor/magento/module-translation/Model/Inline/Parser.php @@ -7,8 +7,11 @@ namespace Magento\Translation\Model\Inline; +use DOMDocument; +use Normalizer; use Laminas\Filter\FilterInterface; use Magento\Backend\App\Area\FrontNameResolver; +use Magento\Framework\App\ObjectManager; use Magento\Framework\Translate\Inline\ParserInterface; use Magento\Translation\Model\ResourceModel\StringFactory; use Magento\Translation\Model\ResourceModel\StringUtils; @@ -116,6 +119,9 @@ class Parser implements ParserInterface * @var array */ private $filterTagsExpression = [ + '/<\/?(script|meta|link|frame|iframe|object|embed|form|input|style)[^&]*>/i', + '/<[^&]*(ondblclick|onclick|onkeydown|onkeypress|onkeyup|onmousedown|onmousemove|' . + 'onmouseout|onmouseover|onmouseup|onload|onunload|onerror)[^&]*>/i', '/\b(alert|eval|setTimeout|setInterval|Function|setImmediate|requestAnimationFrame|document\.(write' . '|writeln)|innerHTML|outerHTML|insertAdjacentHTML|console\.(log|error|warn|info|debug))\s*[=(]/i', '/\b(window|this|self)\s*\[\s*[\'"]?(alert|eval)[\'"]?\s*\]/i', @@ -169,5 +175,10 @@ class Parser implements ParserInterface private $relatedCacheTypes; + /** + * @var Normalizer + */ + private $normalizer; + /** * Initialize base inline translation model * @@ -180,6 +191,7 @@ class Parser implements ParserInterface * @param Escaper $escaper * @param CacheManager $cacheManager * @param array $relatedCacheTypes + * @param Normalizer|null $normalizer */ public function __construct( StringUtilsFactory $resource, @@ -190,7 +203,8 @@ public function __construct( InlineInterface $translateInline, Escaper $escaper, CacheManager $cacheManager, - array $relatedCacheTypes = [] + array $relatedCacheTypes = [], + ?Normalizer $normalizer = null ) { $this->_resourceFactory = $resource; $this->_storeManager = $storeManager; @@ -201,6 +215,8 @@ public function __construct( $this->escaper = $escaper; $this->cacheManager = $cacheManager; $this->relatedCacheTypes = $relatedCacheTypes; + $this->normalizer = $normalizer + ?? ObjectManager::getInstance()->get(Normalizer::class); } /** @@ -222,6 +238,7 @@ public function processAjaxPost(array $translateParams) $this->_validateTranslationParams($translateParams); $this->_filterTranslationParams($translateParams, ['custom']); + $this->filterExternalLinks($translateParams); /** @var $validStoreId int */ $validStoreId = $this->_storeManager->getStore()->getId(); @@ -285,12 +302,101 @@ protected function _filterTranslationParams(array &$translateParams, array $fiel } } + /** + * Sanitize external links in translations + * + * @param array $translateParams + * @return void + */ + private function filterExternalLinks(array &$translateParams): void + { + foreach ($translateParams as &$param) { + if (isset($param['custom']) && is_string($param['custom'])) { + $param['custom'] = $this->removeExternalLinks($param['custom']); + } + } + } + + /** + * Remove external links from HTML content, converting them to plain text + * + * @param string $content + * @return string + */ + private function removeExternalLinks(string $content): string + { + $content = trim($content); + if ($content === '') { + return $content; + } + $dom = new DOMDocument('1.0', 'UTF-8'); + libxml_use_internal_errors(true); + $wrapper = '