1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It is free software; you can redistribute it and/or modify it under 9 * the terms of the GNU General Public License, either version 2 10 * of the License, or any later version. 11 * 12 * For the full copyright and license information, please read the 13 * LICENSE.txt file that was distributed with this source code. 14 * 15 * The TYPO3 project - inspiring people to share! 16 */ 17 18namespace TYPO3\CMS\Install\Updates; 19 20use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException; 21use TYPO3\CMS\Core\Resource\File; 22use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter; 23use TYPO3\CMS\Core\Resource\ResourceStorage; 24use TYPO3\CMS\Core\Resource\Security\SvgSanitizer; 25use TYPO3\CMS\Core\Resource\StorageRepository; 26use TYPO3\CMS\Core\Utility\GeneralUtility; 27 28class SvgFilesSanitization implements UpgradeWizardInterface, ConfirmableInterface 29{ 30 /** 31 * @var StorageRepository 32 */ 33 protected $storageRepository; 34 35 /** 36 * @var Confirmation 37 */ 38 protected $confirmation; 39 40 public function __construct() 41 { 42 $this->storageRepository = GeneralUtility::makeInstance(StorageRepository::class); 43 $this->confirmation = new Confirmation( 44 'Continue sanitizing SVG files?', 45 $this->getDescription(), 46 false, 47 'sanitize, backup available', 48 'cancel', 49 false 50 ); 51 } 52 53 /** 54 * Return the identifier for this wizard 55 * This should be the same string as used in the ext_localconf class registration 56 * 57 * @return string 58 */ 59 public function getIdentifier(): string 60 { 61 return 'svgFilesSanitization'; 62 } 63 64 /** 65 * Return the speaking name of this wizard 66 * 67 * @return string 68 */ 69 public function getTitle(): string 70 { 71 return 'Sanitize existing SVG files in fileadmin folder'; 72 } 73 74 /** 75 * Return the description for this wizard 76 * 77 * @return string 78 */ 79 public function getDescription(): string 80 { 81 return 'This upgrade wizard will sanitize all SVG files located in local file storages. ' 82 . 'It is very likely that file contents will be changed.' . "\n" 83 . 'Before continuing, please ensure a proper backup of *.svg and *.svgz files is in place before continuing.'; 84 } 85 86 /** 87 * To avoid timeout issues, no check is performed in advance 88 * 89 * @return bool 90 */ 91 public function updateNecessary(): bool 92 { 93 return true; 94 } 95 96 /** 97 * Execute the update 98 * 99 * Called when a wizard reports that an update is necessary 100 * 101 * @return bool 102 */ 103 public function executeUpdate(): bool 104 { 105 return $this->processSvgFiles(); 106 } 107 108 /** 109 * Returns an array of class names of Prerequisite classes 110 * 111 * This way a wizard can define dependencies like "database up-to-date" or 112 * "reference index updated" 113 * 114 * @return string[] 115 */ 116 public function getPrerequisites(): array 117 { 118 return []; 119 } 120 121 /** 122 * Return a confirmation message instance 123 * 124 * @return Confirmation 125 */ 126 public function getConfirmation(): Confirmation 127 { 128 return $this->confirmation; 129 } 130 131 /** 132 * @return ResourceStorage[] 133 */ 134 protected function resolveLocalStorages(): array 135 { 136 return array_filter( 137 $this->storageRepository->findByStorageType('Local'), 138 static function (ResourceStorage $storage) { 139 return $storage->isWritable(); 140 } 141 ); 142 } 143 144 /** 145 * @param ResourceStorage $storage 146 * @return File[] 147 * @throws InsufficientFolderAccessPermissionsException 148 */ 149 protected function resolveSvgFiles(ResourceStorage $storage): array 150 { 151 $filter = GeneralUtility::makeInstance(FileExtensionFilter::class); 152 $filter->setAllowedFileExtensions(['svg', 'svgz']); 153 $files = $storage 154 ->setFileAndFolderNameFilters([ 155 [$filter, 'filterFileList'], 156 ]) 157 ->getFilesInFolder( 158 $storage->getRootLevelFolder(), 159 0, 160 0, 161 true, 162 true 163 ); 164 $storage->resetFileAndFolderNameFiltersToDefault(); 165 return $files; 166 } 167 168 protected function processSvgFiles(): bool 169 { 170 $successful = true; 171 $sanitizer = GeneralUtility::makeInstance(SvgSanitizer::class); 172 foreach ($this->resolveLocalStorages() as $storage) { 173 try { 174 $svgFiles = $this->resolveSvgFiles($storage); 175 } catch (InsufficientFolderAccessPermissionsException $exception) { 176 // @todo Add notice/warning for this upgrade process 177 $successful = false; 178 continue; 179 } 180 foreach ($svgFiles as $svgFile) { 181 $oldFileContent = $svgFile->getContents(); 182 $newFileContent = $sanitizer->sanitizeContent($oldFileContent); 183 if ($oldFileContent !== $newFileContent) { 184 $svgFile->setContents($newFileContent); 185 } 186 } 187 } 188 return $successful; 189 } 190} 191