From 45b389d44d8b1b4e91e952201dd0af203f7ca36e Mon Sep 17 00:00:00 2001 From: Oliver Hader Date: Sun, 11 Jul 2021 13:46:59 +0200 Subject: [PATCH] [TASK] Introduce SVG Sanitizer This change introduces behavior of extension `t3g/svg-sanitizer` into the TYPO3 core. Sanitizing SVG data is actually done by external package `enshrined/svg-sanitize` by Daryll Doyle. The following aspects are introduced: + handle `GeneralUtility::upload_copy_move` invocations + handle FAL action events `file-add`, `file-replace`, `set-content` + provide upgrade wizard, sanitizing all SVG files in storages that are using `LocalDriver` Custom usage: ``` $sanitizer = new \TYPO3\CMS\Core\Resource\Security\SvgSanitizer(); $sanitizer->sanitizeFile($sourcePath, $targetPath); $svg = $sanitizer->sanitizeContent($svg); ``` Basically this change enforces following public service announcements concerning SVG files, to enhance these security aspects per default: + https://typo3.org/security/advisory/typo3-psa-2020-003 + https://typo3.org/security/advisory/typo3-psa-2019-010 Resolves: #94492 Releases: master, 10.4, 9.5 Change-Id: I42c206190d8a335ebaf77b7e5d57b383e3bcbae1 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69817 Tested-by: core-ci Tested-by: Oliver Hader Reviewed-by: Oliver Hader --- composer.json | 1 + composer.lock | 49 ++++- .../Resource/Security/SvgEventListener.php | 72 +++++++ .../Resource/Security/SvgHookHandler.php | 48 +++++ .../Resource/Security/SvgSanitizer.php | 56 +++++ .../Resource/Security/SvgTypeCheck.php | 76 +++++++ typo3/sysext/core/Configuration/Services.yaml | 21 ++ .../Important-94492-IntroduceSVGSanitizer.rst | 39 ++++ .../Fixtures/CleanSVG/TYPO3_Logo_Clean.svg | 7 + .../Fixtures/CleanSVG/TYPO3_Logo_Data.svg | 8 + .../Fixtures/CleanSVG/TYPO3_Logo_Script.svg | 7 + .../Resource/Fixtures/CleanSVG/ariaData.svg | 57 +++++ .../Fixtures/CleanSVG/billion_laughs.svg | 0 .../Resource/Fixtures/CleanSVG/entity.svg | 4 + .../Resource/Fixtures/CleanSVG/entity_2.svg | 4 + .../Resource/Fixtures/CleanSVG/external.svg | 10 + .../Resource/Fixtures/CleanSVG/hrefOne.svg | 13 ++ .../Resource/Fixtures/CleanSVG/hrefTwo.svg | 13 ++ .../Resource/Fixtures/CleanSVG/html.svg | 4 + .../Resource/Fixtures/CleanSVG/simple.svg | 4 + .../Resource/Fixtures/CleanSVG/svgOne.svg | 13 ++ .../Resource/Fixtures/CleanSVG/use.svg | 6 + .../Resource/Fixtures/CleanSVG/useDos.svg | 113 ++++++++++ .../Fixtures/CleanSVG/xlinkLaughs.svg | 71 +++++++ .../Resource/Fixtures/CleanSVG/xlinkLoop.svg | 24 +++ .../Fixtures/CleanSVG/xlink_laughs.svg | 146 +++++++++++++ .../Resource/Fixtures/CleanSVG/xmlOne.xml | 11 + .../Resource/Fixtures/CleanSVG/xss.svg | 4 + .../Fixtures/DirtySVG/TYPO3_Logo_Clean.svg | 7 + .../Fixtures/DirtySVG/TYPO3_Logo_Data.svg | 8 + .../Fixtures/DirtySVG/TYPO3_Logo_Script.svg | 13 ++ .../Resource/Fixtures/DirtySVG/ariaData.svg | 56 +++++ .../Fixtures/DirtySVG/billion_laughs.svg | 16 ++ .../Resource/Fixtures/DirtySVG/entity.svg | 5 + .../Resource/Fixtures/DirtySVG/entity_2.svg | 8 + .../Resource/Fixtures/DirtySVG/external.svg | 11 + .../Resource/Fixtures/DirtySVG/hrefOne.svg | 13 ++ .../Resource/Fixtures/DirtySVG/hrefTwo.svg | 13 ++ .../Resource/Fixtures/DirtySVG/html.svg | 14 ++ .../Resource/Fixtures/DirtySVG/simple.svg | 5 + .../Resource/Fixtures/DirtySVG/svgOne.svg | 15 ++ .../Resource/Fixtures/DirtySVG/use.svg | 5 + .../Resource/Fixtures/DirtySVG/useDos.svg | 129 +++++++++++ .../Fixtures/DirtySVG/xlinkLaughs.svg | 146 +++++++++++++ .../Resource/Fixtures/DirtySVG/xlinkLoop.svg | 31 +++ .../Fixtures/DirtySVG/xlink_laughs.svg | 146 +++++++++++++ .../Resource/Fixtures/DirtySVG/xmlOne.xml | 12 ++ .../Resource/Fixtures/DirtySVG/xss.svg | 8 + .../Resource/Security/SvgSanitizerTest.php | 60 ++++++ typo3/sysext/core/composer.json | 1 + typo3/sysext/core/ext_localconf.php | 1 + .../Classes/Updates/SvgFilesSanitization.php | 200 ++++++++++++++++++ typo3/sysext/install/ext_localconf.php | 2 + 53 files changed, 1804 insertions(+), 2 deletions(-) create mode 100644 typo3/sysext/core/Classes/Resource/Security/SvgEventListener.php create mode 100644 typo3/sysext/core/Classes/Resource/Security/SvgHookHandler.php create mode 100644 typo3/sysext/core/Classes/Resource/Security/SvgSanitizer.php create mode 100644 typo3/sysext/core/Classes/Resource/Security/SvgTypeCheck.php create mode 100644 typo3/sysext/core/Documentation/Changelog/9.5.x/Important-94492-IntroduceSVGSanitizer.rst create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Clean.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Data.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Script.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/ariaData.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/billion_laughs.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity_2.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/external.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefOne.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefTwo.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/html.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/simple.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/svgOne.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/use.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/useDos.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLaughs.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLoop.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlink_laughs.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xmlOne.xml create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xss.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Clean.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Data.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Script.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/ariaData.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/billion_laughs.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity_2.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/external.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefOne.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefTwo.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/html.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/simple.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/svgOne.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/use.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/useDos.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLaughs.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLoop.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlink_laughs.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xmlOne.xml create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xss.svg create mode 100644 typo3/sysext/core/Tests/Functional/Resource/Security/SvgSanitizerTest.php create mode 100644 typo3/sysext/install/Classes/Updates/SvgFilesSanitization.php diff --git a/composer.json b/composer.json index da8c5f7f5182..64aea6282908 100644 --- a/composer.json +++ b/composer.json @@ -44,6 +44,7 @@ "doctrine/instantiator": "^1.1", "doctrine/lexer": "^1.0", "egulias/email-validator": "^2.1", + "enshrined/svg-sanitize": "^0.14.0", "guzzlehttp/guzzle": "^6.3.0", "guzzlehttp/psr7": "^1.4.0", "nikic/php-parser": "^4.10.4", diff --git a/composer.lock b/composer.lock index 41142e064184..293288f8fb89 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "664bdaf86dd7273cd840fa7eaa998893", + "content-hash": "a359b4a71f0ba72147d893bb1fd8278c", "packages": [ { "name": "cogpowered/finediff", @@ -617,6 +617,51 @@ }, "time": "2020-02-13T22:36:52+00:00" }, + { + "name": "enshrined/svg-sanitize", + "version": "0.14.0", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "beff89576a72540ee99476aeb9cfe98222e76fb8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/beff89576a72540ee99476aeb9cfe98222e76fb8", + "reference": "beff89576a72540ee99476aeb9cfe98222e76fb8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*" + }, + "require-dev": { + "codeclimate/php-test-reporter": "^0.1.2", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "enshrined\\svgSanitize\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Daryll Doyle", + "email": "daryll@enshrined.co.uk" + } + ], + "description": "An SVG sanitizer for PHP", + "support": { + "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", + "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.14.0" + }, + "time": "2021-01-21T10:13:20+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "6.4.1", @@ -7528,5 +7573,5 @@ "platform-overrides": { "php": "7.2.5" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "2.1.0" } diff --git a/typo3/sysext/core/Classes/Resource/Security/SvgEventListener.php b/typo3/sysext/core/Classes/Resource/Security/SvgEventListener.php new file mode 100644 index 000000000000..41ddf496da59 --- /dev/null +++ b/typo3/sysext/core/Classes/Resource/Security/SvgEventListener.php @@ -0,0 +1,72 @@ +sanitizer = $sanitizer; + $this->typeCheck = $typeCheck; + } + + public function beforeFileAdded(BeforeFileAddedEvent $event): void + { + $filePath = $event->getSourceFilePath(); + if ($this->typeCheck->forFilePath($filePath)) { + $this->sanitizer->sanitizeFile($filePath); + } + } + + public function beforeFileReplaced(BeforeFileReplacedEvent $event): void + { + $filePath = $event->getLocalFilePath(); + if ($this->typeCheck->forFilePath($filePath)) { + $this->sanitizer->sanitizeFile($filePath); + } + } + + public function afterFileContentsSet(AfterFileContentsSetEvent $event): void + { + $file = $event->getFile(); + if (!$this->typeCheck->forResource($file)) { + return; + } + $content = $event->getContent(); + $sanitizedContent = $this->sanitizer->sanitizeContent($content); + // cave: setting content will trigger calling this handler again + // (having custom-flags on `FileInterface` would allow to mark it as "processed") + if ($sanitizedContent !== $content) { + $file->setContents($sanitizedContent); + } + } +} diff --git a/typo3/sysext/core/Classes/Resource/Security/SvgHookHandler.php b/typo3/sysext/core/Classes/Resource/Security/SvgHookHandler.php new file mode 100644 index 000000000000..26f050c9e5ef --- /dev/null +++ b/typo3/sysext/core/Classes/Resource/Security/SvgHookHandler.php @@ -0,0 +1,48 @@ +sanitizer = $sanitizer; + $this->typeCheck = $typeCheck; + } + + /** + * @param array $parameters + */ + public function processMoveUploadedFile(array $parameters) + { + $filePath = $parameters['source'] ?? null; + if ($filePath !== null && $this->typeCheck->forFilePath($filePath)) { + $this->sanitizer->sanitizeFile($filePath); + } + } +} diff --git a/typo3/sysext/core/Classes/Resource/Security/SvgSanitizer.php b/typo3/sysext/core/Classes/Resource/Security/SvgSanitizer.php new file mode 100644 index 000000000000..373ade783bf1 --- /dev/null +++ b/typo3/sysext/core/Classes/Resource/Security/SvgSanitizer.php @@ -0,0 +1,56 @@ +sanitizeContent($svg); + if ($sanitizedSvg !== $svg) { + file_put_contents($targetPath, $sanitizedSvg); + } + } + + /** + * @param string $svg + * + * @return string + * @throws \BadFunctionCallException + */ + public function sanitizeContent(string $svg): string + { + $sanitizer = new Sanitizer(); + $sanitizer->removeRemoteReferences(true); + return $sanitizer->sanitize($svg) ?: ''; + } +} diff --git a/typo3/sysext/core/Classes/Resource/Security/SvgTypeCheck.php b/typo3/sysext/core/Classes/Resource/Security/SvgTypeCheck.php new file mode 100644 index 000000000000..66509f620564 --- /dev/null +++ b/typo3/sysext/core/Classes/Resource/Security/SvgTypeCheck.php @@ -0,0 +1,76 @@ +mimeTypeDetector = $mimeTypeDetector; + $this->fileExtensions = $this->resolveFileExtensions(); + } + + public function forFilePath(string $filePath): bool + { + $fileInfo = GeneralUtility::makeInstance(FileInfo::class, $filePath); + $fileExtension = $fileInfo->getExtension(); + $mimeType = $fileInfo->getMimeType(); + return in_array($fileExtension, $this->fileExtensions, true) + || in_array($mimeType, self::MIME_TYPES, true); + } + + public function forResource(FileInterface $file): bool + { + $fileExtension = $file->getExtension(); + $mimeType = $file->getMimeType(); + return in_array($fileExtension, $this->fileExtensions, true) + || in_array($mimeType, self::MIME_TYPES, true); + } + + /** + * @return string[] + */ + protected function resolveFileExtensions(): array + { + $fileExtensions = array_map( + function (string $mimeType): array { + return $this->mimeTypeDetector->getFileExtensionsForMimeType($mimeType); + }, + self::MIME_TYPES + ); + $fileExtensions = array_filter($fileExtensions); + return count($fileExtensions) > 0 ? array_unique(array_merge(...$fileExtensions)) : []; + } +} diff --git a/typo3/sysext/core/Configuration/Services.yaml b/typo3/sysext/core/Configuration/Services.yaml index 3441b92e39f7..3d4b023197fc 100644 --- a/typo3/sysext/core/Configuration/Services.yaml +++ b/typo3/sysext/core/Configuration/Services.yaml @@ -342,6 +342,27 @@ services: event: TYPO3\CMS\Core\Resource\Event\AfterFileDeletedEvent + TYPO3\CMS\Core\Resource\Security\SvgEventListener: + tags: + - name: event.listener + identifier: 'svg-resource-storage-listener-before-file-added' + method: 'beforeFileAdded' + event: TYPO3\CMS\Core\Resource\Event\BeforeFileAddedEvent + - name: event.listener + identifier: 'svg-resource-storage-listener-before-file-replaced' + method: 'beforeFileReplaced' + event: TYPO3\CMS\Core\Resource\Event\BeforeFileReplacedEvent + - name: event.listener + identifier: 'svg-resource-storage-listener-after-file-content-set' + method: 'afterFileContentsSet' + event: TYPO3\CMS\Core\Resource\Event\AfterFileContentsSetEvent + + TYPO3\CMS\Core\Resource\Security\SvgHookHandler: + public: true + + TYPO3\CMS\Core\Resource\Security\SvgTypeCheck: + public: true + # Core caches, cache.core and cache.assets are injected as early # entries in TYPO3\CMS\Core\Core\Bootstrap and therefore omitted here cache.hash: diff --git a/typo3/sysext/core/Documentation/Changelog/9.5.x/Important-94492-IntroduceSVGSanitizer.rst b/typo3/sysext/core/Documentation/Changelog/9.5.x/Important-94492-IntroduceSVGSanitizer.rst new file mode 100644 index 000000000000..60813267c3df --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/9.5.x/Important-94492-IntroduceSVGSanitizer.rst @@ -0,0 +1,39 @@ +.. include:: ../../Includes.txt + +=========================================== +Important: #94492 - Introduce SVG Sanitizer +=========================================== + +See :issue:`94492` + +Description +=========== + +SVG sanitization behavior of extension [`t3g/svg-sanitizer`](https://packagist.org/packages/t3g/svg-sanitizer) +has been introduced into TYPO3 core. Actual processing is done by low-level sanitization package +[`enshrined/svg-sanitize`](https://packagist.org/packages/enshrined/svg-sanitize) by Daryll Doyle. + +Introduced aspects +------------------ + +* handle :php:`GeneralUtility::upload_copy_move` invocations +* handle FAL action events `file-add`, `file-replace`, `set-content` +* provide upgrade wizard, sanitizing all SVG files in storages that + are using :php:`\TYPO3\CMS\Core\Resource\Driver\LocalDriver` + +Custom usage +------------ + +.. code-block:: php + + $sanitizer = new \TYPO3\CMS\Core\Resource\Security\SvgSanitizer(); + $sanitizer->sanitizeFile($sourcePath, $targetPath); + $svg = $sanitizer->sanitizeContent($svg); + +Basically this change enforces following public service announcements +concerning SVG files, to enhance these security aspects per default: + +* [TYPO3-PSA-2020-003: Mitigation of Cross-Site Scripting Vulnerabilities in File Upload Handling](https://typo3.org/security/advisory/typo3-psa-2020-003) +* [TYPO3-PSA-2019-010: Cross-Site Scripting Vulnerabilities in File Upload Handling](https://typo3.org/security/advisory/typo3-psa-2019-010) + +.. index:: Backend, FAL, Frontend, ext:core diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Clean.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Clean.svg new file mode 100644 index 000000000000..01db9ad346d1 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Clean.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Data.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Data.svg new file mode 100644 index 000000000000..4e4449894aa2 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Data.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Script.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Script.svg new file mode 100644 index 000000000000..01db9ad346d1 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/TYPO3_Logo_Script.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/ariaData.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/ariaData.svg new file mode 100644 index 000000000000..838e7dc3df9b --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/ariaData.svg @@ -0,0 +1,57 @@ + + + Pixels, My Super-friendly Cat + An illustrated gray cat with bright green blinking eyes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/billion_laughs.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/billion_laughs.svg new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity.svg new file mode 100644 index 000000000000..a7c49b4d49d2 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity.svg @@ -0,0 +1,4 @@ + + + &lab; + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity_2.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity_2.svg new file mode 100644 index 000000000000..cf314616c2ab --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/entity_2.svg @@ -0,0 +1,4 @@ + + + &lab2; + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/external.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/external.svg new file mode 100644 index 000000000000..ef4ab4114867 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/external.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefOne.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefOne.svg new file mode 100644 index 000000000000..b84d2184df75 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefOne.svg @@ -0,0 +1,13 @@ + + + test 1 + test 2 + test 3 + test 4 + + test 5 + test 6 + + test 7 + test 8 + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefTwo.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefTwo.svg new file mode 100644 index 000000000000..2a95507281f2 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/hrefTwo.svg @@ -0,0 +1,13 @@ + + + test 1 + test 2 + test 3 + test 4 + + test 5 + test 6 + + test 7 + test 8 + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/html.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/html.svg new file mode 100644 index 000000000000..c524480099fc --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/html.svg @@ -0,0 +1,4 @@ + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/simple.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/simple.svg new file mode 100644 index 000000000000..c524480099fc --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/simple.svg @@ -0,0 +1,4 @@ + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/svgOne.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/svgOne.svg new file mode 100644 index 000000000000..da8faf5f0702 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/svgOne.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/use.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/use.svg new file mode 100644 index 000000000000..badd6870ba3c --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/use.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/useDos.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/useDos.svg new file mode 100644 index 000000000000..bacbafc2c1fb --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/useDos.svg @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLaughs.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLaughs.svg new file mode 100644 index 000000000000..21661833614a --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLaughs.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLoop.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLoop.svg new file mode 100644 index 000000000000..c6dd6bdaff67 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlinkLoop.svg @@ -0,0 +1,24 @@ + + + + + Ping + + + Pong + + + + 1st + + + 2nd + + + 3rd + + + 4th + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlink_laughs.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlink_laughs.svg new file mode 100644 index 000000000000..ae53de9ab717 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xlink_laughs.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xmlOne.xml b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xmlOne.xml new file mode 100644 index 000000000000..eddcb7b55586 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xmlOne.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xss.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xss.svg new file mode 100644 index 000000000000..7e8106f2285b --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/CleanSVG/xss.svg @@ -0,0 +1,4 @@ + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Clean.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Clean.svg new file mode 100644 index 000000000000..01db9ad346d1 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Clean.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Data.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Data.svg new file mode 100644 index 000000000000..0a014746b779 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Data.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Script.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Script.svg new file mode 100644 index 000000000000..e1170d4f674d --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/TYPO3_Logo_Script.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/ariaData.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/ariaData.svg new file mode 100644 index 000000000000..d09ea5d95413 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/ariaData.svg @@ -0,0 +1,56 @@ + + Pixels, My Super-friendly Cat + An illustrated gray cat with bright green blinking eyes. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/billion_laughs.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/billion_laughs.svg new file mode 100644 index 000000000000..dc9414089ff9 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/billion_laughs.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + +]> + + &lol10; + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity.svg new file mode 100644 index 000000000000..bc2d3defac21 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity.svg @@ -0,0 +1,5 @@ + +]> + + &lab; + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity_2.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity_2.svg new file mode 100644 index 000000000000..19d3a5b057eb --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/entity_2.svg @@ -0,0 +1,8 @@ + + + +]> + + &lab2; + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/external.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/external.svg new file mode 100644 index 000000000000..cd190cb20038 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/external.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefOne.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefOne.svg new file mode 100644 index 000000000000..4f26966a17e5 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefOne.svg @@ -0,0 +1,13 @@ + + + test 1 + test 2 + test 3 + test 4 + + test 5 + test 6 + + test 7 + test 8 + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefTwo.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefTwo.svg new file mode 100644 index 000000000000..3e7be42d7cf3 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/hrefTwo.svg @@ -0,0 +1,13 @@ + + + test 1 + test 2 + test 3 + test 4 + + test 5 + test 6 + + test 7 + test 8 + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/html.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/html.svg new file mode 100644 index 000000000000..31bd3590ce8d --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/html.svg @@ -0,0 +1,14 @@ + + + + + + + +

HTML Injection for phishing

+ +
+
+ diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/simple.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/simple.svg new file mode 100644 index 000000000000..29fc853c7715 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/simple.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/svgOne.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/svgOne.svg new file mode 100644 index 000000000000..f543a84b5f32 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/svgOne.svg @@ -0,0 +1,15 @@ + + + + + + + + + + +shouldn't be here + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/use.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/use.svg new file mode 100644 index 000000000000..888416a20809 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/use.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/useDos.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/useDos.svg new file mode 100644 index 000000000000..3a3248ed553e --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/useDos.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLaughs.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLaughs.svg new file mode 100644 index 000000000000..c344a80ad932 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLaughs.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLoop.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLoop.svg new file mode 100644 index 000000000000..e8de02d74a56 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlinkLoop.svg @@ -0,0 +1,31 @@ + + + + + Ping + + + + Pong + + + + + 1st + + + + 2nd + + + + 3rd + + + + 4th + + + + + diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlink_laughs.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlink_laughs.svg new file mode 100644 index 000000000000..01c676a49dfe --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xlink_laughs.svg @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xmlOne.xml b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xmlOne.xml new file mode 100644 index 000000000000..0566a4500531 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xmlOne.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + <ΓΈ:script src="//0x.lv/" /> + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xss.svg b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xss.svg new file mode 100644 index 000000000000..f6aa93d242e9 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Fixtures/DirtySVG/xss.svg @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Resource/Security/SvgSanitizerTest.php b/typo3/sysext/core/Tests/Functional/Resource/Security/SvgSanitizerTest.php new file mode 100644 index 000000000000..109232378c51 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Resource/Security/SvgSanitizerTest.php @@ -0,0 +1,60 @@ + + */ + public function svgContentIsSanitizedDataProvider(): array + { + $basePath = dirname(__FILE__, 2) . '/Fixtures/'; + $finder = new Finder(); + $finder + ->files() + ->in($basePath . 'DirtySVG/') + ->name('*.svg'); + $data = []; + foreach ($finder as $file) { + $fileName = $file->getFilename(); + $data[$fileName] = ['DirtySVG/' . $fileName, 'CleanSVG/' . $fileName]; + } + return $data; + } + + /** + * @param string $filePath + * @param string $sanitizedFilePath + * @test + * @dataProvider svgContentIsSanitizedDataProvider + */ + public function svgContentIsSanitized($filePath, $sanitizedFilePath) + { + $basePath = dirname(__FILE__, 2) . '/Fixtures/'; + $sanitizer = new SvgSanitizer(); + self::assertStringEqualsFile( + $basePath . $sanitizedFilePath, + $sanitizer->sanitizeContent(file_get_contents($basePath . $filePath)) + ); + } +} diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json index 4000a3756a1b..37f71087395d 100644 --- a/typo3/sysext/core/composer.json +++ b/typo3/sysext/core/composer.json @@ -32,6 +32,7 @@ "doctrine/instantiator": "^1.1", "doctrine/lexer": "^1.0", "egulias/email-validator": "^2.1", + "enshrined/svg-sanitize": "^0.14.0", "guzzlehttp/guzzle": "^6.3.0", "guzzlehttp/psr7": "^1.4.0", "nikic/php-parser": "^4.10.4", diff --git a/typo3/sysext/core/ext_localconf.php b/typo3/sysext/core/ext_localconf.php index 478ea0cf6d1c..2308bdd08c6d 100644 --- a/typo3/sysext/core/ext_localconf.php +++ b/typo3/sysext/core/ext_localconf.php @@ -2,6 +2,7 @@ defined('TYPO3_MODE') or die(); +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\TYPO3\CMS\Core\Utility\GeneralUtility::class]['moveUploadedFile'][] = \TYPO3\CMS\Core\Resource\Security\SvgHookHandler::class . '->processMoveUploadedFile'; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = \TYPO3\CMS\Core\Resource\Security\FileMetadataPermissionsAspect::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = \TYPO3\CMS\Core\Hooks\BackendUserGroupIntegrityCheck::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = \TYPO3\CMS\Core\Hooks\BackendUserPasswordCheck::class; diff --git a/typo3/sysext/install/Classes/Updates/SvgFilesSanitization.php b/typo3/sysext/install/Classes/Updates/SvgFilesSanitization.php new file mode 100644 index 000000000000..4c0b8823d5ba --- /dev/null +++ b/typo3/sysext/install/Classes/Updates/SvgFilesSanitization.php @@ -0,0 +1,200 @@ +storageRepository = GeneralUtility::makeInstance(StorageRepository::class); + $this->confirmation = new Confirmation( + 'Continue sanitizing SVG files?', + $this->getDescription(), + false, + 'sanitize, backup available', + 'cancel', + false + ); + } + + /** + * Return the identifier for this wizard + * This should be the same string as used in the ext_localconf class registration + * + * @return string + */ + public function getIdentifier(): string + { + // needs to be static for exact reference + return 'TYPO3\CMS\Install\Updates\SvgFilesSanitization'; + } + + /** + * Return the speaking name of this wizard + * + * @return string + */ + public function getTitle(): string + { + return 'Sanitize existing SVG files in fileadmin folder'; + } + + /** + * Return the description for this wizard + * + * @return string + */ + public function getDescription(): string + { + return 'This upgrade wizard will sanitize all SVG files located in local file storages. ' + . 'It is very likely that file contents will be changed.' . "\n" + . 'Before continuing, please ensure a proper backup of *.svg and *.svgz files is in place before continuing.'; + } + + /** + * Is an update necessary? + * + * Is used to determine whether a wizard needs to be run. + * Check if data for migration exists. + * + * @return bool + */ + public function updateNecessary(): bool + { + foreach ($this->resolveLocalStorages() as $storage) { + try { + $svgFiles = $this->resolveSvgFiles($storage); + } catch (InsufficientFolderAccessPermissionsException $exception) { + continue; + } + if (count($svgFiles) > 0) { + return true; + } + } + return false; + } + + /** + * Execute the update + * + * Called when a wizard reports that an update is necessary + * + * @return bool + */ + public function executeUpdate(): bool + { + return $this->processSvgFiles(); + } + + /** + * Returns an array of class names of Prerequisite classes + * + * This way a wizard can define dependencies like "database up-to-date" or + * "reference index updated" + * + * @return string[] + */ + public function getPrerequisites(): array + { + return []; + } + + /** + * Return a confirmation message instance + * + * @return Confirmation + */ + public function getConfirmation(): Confirmation + { + return $this->confirmation; + } + + /** + * @return ResourceStorage[] + */ + protected function resolveLocalStorages(): array + { + return array_filter( + $this->storageRepository->findByStorageType('Local'), + function (ResourceStorage $storage) { + return $storage->isWritable(); + } + ); + } + + /** + * @param ResourceStorage $storage + * @return File[] + * @throws InsufficientFolderAccessPermissionsException + */ + protected function resolveSvgFiles(ResourceStorage $storage): array + { + $filter = GeneralUtility::makeInstance(FileExtensionFilter::class); + $filter->setAllowedFileExtensions(['svg', 'svgz']); + return $storage + ->setFileAndFolderNameFilters([[$filter, 'filterFileList']]) + ->getFilesInFolder( + $storage->getRootLevelFolder(), + 0, + 0, + true, + true + ); + } + + protected function processSvgFiles(): bool + { + $successful = true; + $sanitizer = GeneralUtility::makeInstance(SvgSanitizer::class); + foreach ($this->resolveLocalStorages() as $storage) { + try { + $svgFiles = $this->resolveSvgFiles($storage); + } catch (InsufficientFolderAccessPermissionsException $exception) { + // @todo Add notice/warning for this upgrade process + $successful = false; + continue; + } + foreach ($svgFiles as $svgFile) { + $oldFileContent = $svgFile->getContents(); + $newFileContent = $sanitizer->sanitizeContent($oldFileContent); + if ($oldFileContent !== $newFileContent) { + $svgFile->setContents($newFileContent); + } + } + } + return $successful; + } +} diff --git a/typo3/sysext/install/ext_localconf.php b/typo3/sysext/install/ext_localconf.php index bcbea357e9b6..5ec75ef386ed 100644 --- a/typo3/sysext/install/ext_localconf.php +++ b/typo3/sysext/install/ext_localconf.php @@ -45,6 +45,8 @@ = \TYPO3\CMS\Install\Updates\TaskcenterExtractionUpdate::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['sysActionExtension'] = \TYPO3\CMS\Install\Updates\SysActionExtractionUpdate::class; +$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['svgFilesSanitization'] + = \TYPO3\CMS\Install\Updates\SvgFilesSanitization::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['databaseRowsUpdateWizard'] = \TYPO3\CMS\Install\Updates\DatabaseRowsUpdateWizard::class;