From 4b6742f1e4e6892b8b6b47f73304b8aa32343bf9 Mon Sep 17 00:00:00 2001 From: Maksim Soldatjonok Date: Fri, 27 Mar 2026 10:44:07 +0200 Subject: [PATCH] fix: Add path traversal protection to DirectivePlugin (APSB26-05) Magento 2.4.6-p14 security patch (APSB26-05) added path traversal protection to Cms\Controller\Adminhtml\Wysiwyg\Directive::execute() via DirectoryResolver::validatePath(). However, the DirectivePlugin uses aroundExecute() and returns early for SVG files, completely bypassing this security validation. Changes: - Add DirectoryResolver to validate file path is within media directory - Add Filesystem for proper path resolution via getAbsolutePath() - Normalize path separators (backslash to forward slash) - Replace file_get_contents() with Magento filesystem abstraction --- .../Adminhtml/Wysiwyg/DirectivePlugin.php | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/Plugin/Controller/Adminhtml/Wysiwyg/DirectivePlugin.php b/Plugin/Controller/Adminhtml/Wysiwyg/DirectivePlugin.php index c599946..d567451 100644 --- a/Plugin/Controller/Adminhtml/Wysiwyg/DirectivePlugin.php +++ b/Plugin/Controller/Adminhtml/Wysiwyg/DirectivePlugin.php @@ -8,10 +8,14 @@ use Magento\Cms\Controller\Adminhtml\Wysiwyg\Directive; use Magento\Cms\Model\Template\Filter; +use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\Framework\App\Filesystem\DirectoryResolver; use Magento\Framework\Controller\Result\Raw; use Magento\Framework\Controller\Result\RawFactory; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Url\DecoderInterface;use MagestyApps\WebImages\Helper\ImageHelper; +use Magento\Framework\Filesystem; +use Magento\Framework\Url\DecoderInterface; +use MagestyApps\WebImages\Helper\ImageHelper; class DirectivePlugin { @@ -35,23 +39,39 @@ class DirectivePlugin */ private $imageHelper; + /** + * @var Filesystem + */ + private $filesystem; + + /** + * @var DirectoryResolver + */ + private $directoryResolver; + /** * DirectivePlugin constructor. * @param DecoderInterface $urlDecoder * @param Filter $filter * @param RawFactory $resultRawFactory * @param ImageHelper $imageHelper + * @param Filesystem $filesystem + * @param DirectoryResolver $directoryResolver */ public function __construct( DecoderInterface $urlDecoder, Filter $filter, RawFactory $resultRawFactory, - ImageHelper $imageHelper + ImageHelper $imageHelper, + Filesystem $filesystem, + DirectoryResolver $directoryResolver ) { $this->urlDecoder = $urlDecoder; $this->filter = $filter; $this->resultRawFactory = $resultRawFactory; $this->imageHelper = $imageHelper; + $this->filesystem = $filesystem; + $this->directoryResolver = $directoryResolver; } /** @@ -67,19 +87,29 @@ public function aroundExecute(Directive $subject, callable $proceed) $directive = $subject->getRequest()->getParam('___directive'); $directive = $this->urlDecoder->decode($directive); $imagePath = $this->filter->filter($directive); + $imagePath = str_replace('\\', '/', $imagePath); if (!$this->imageHelper->isVectorImage($imagePath)) { throw new LocalizedException(__('This is not a vector image')); } + $urlPath = $this->filesystem->getUri(DirectoryList::MEDIA); + $relativeFilePath = str_replace(rtrim($urlPath, '/') . '/', '', $imagePath); + $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); + $absolutePath = $mediaDirectory->getAbsolutePath($relativeFilePath); + + if (!$this->directoryResolver->validatePath($absolutePath, DirectoryList::MEDIA)) { + throw new LocalizedException(__('Invalid Path')); + } + /** @var Raw $resultRaw */ $resultRaw = $this->resultRawFactory->create(); $resultRaw->setHeader('Content-Type', 'image/svg+xml'); - $resultRaw->setContents(file_get_contents($imagePath)); + $resultRaw->setContents($mediaDirectory->readFile($relativeFilePath)); return $resultRaw; } catch (\Exception $e) { return $proceed(); } } -} +} \ No newline at end of file