diff --git a/composer.json b/composer.json index b7518b0e..cbcf92fc 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,8 @@ "php": "^8.2", "astrotomic/laravel-translatable": "^11.13", "doctrine/dbal": "^4.2", + "enshrined/svg-sanitize": "^0.16", + "ezyang/htmlpurifier": "^4.16", "hassankhan/config": "^2.2", "intervention/image": "^3.11", "ip2location/ip2location-php": "^9.7", @@ -83,6 +85,10 @@ "test-coverage": "vendor/bin/phpunit --coverage" }, "config": { - "sort-packages": true + "sort-packages": true, + "audit": { + "abandoned": "ignore", + "block-insecure": false + } } } diff --git a/composer.lock b/composer.lock index 2e3ca0f6..20542f85 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": "0adc420e3fbefba6772d5be49b669087", + "content-hash": "52bfb1c2bbaa01e1a31818c38bc51165", "packages": [ { "name": "astrotomic/laravel-translatable", @@ -966,6 +966,52 @@ ], "time": "2025-03-06T22:45:56+00:00" }, + { + "name": "enshrined/svg-sanitize", + "version": "0.16.0", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "239e257605e2141265b429e40987b2ee51bba4b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/239e257605e2141265b429e40987b2ee51bba4b4", + "reference": "239e257605e2141265b429e40987b2ee51bba4b4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ezyang/htmlpurifier": "^4.16", + "php": "^5.6 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.5 || ^8.5" + }, + "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.16.0" + }, + "time": "2023-03-20T10:51:12+00:00" + }, { "name": "ezyang/htmlpurifier", "version": "v4.19.0", diff --git a/src/FileManager/MediaRepository.php b/src/FileManager/MediaRepository.php index fccdba34..4daef17e 100644 --- a/src/FileManager/MediaRepository.php +++ b/src/FileManager/MediaRepository.php @@ -159,5 +159,22 @@ public function validateUploadedFile(UploadedFile $file, string $disk): void if (($maxSize = config("media.disks.{$disk}.max_size", [])) && $file->getSize() > $maxSize) { throw MediaException::maxFileSizeExceeded($maxSize); } + + if ($this->isSvg($file)) { + $this->sanitizeSvg($file); + } + } + + protected function isSvg(UploadedFile $file): bool + { + return in_array($file->getMimeType(), ['image/svg+xml', 'image/svg']) || + $file->getClientOriginalExtension() === 'svg'; + } + + protected function sanitizeSvg(UploadedFile $file): void + { + $sanitizer = new \enshrined\svgSanitize\Sanitizer; + $cleanSvg = $sanitizer->sanitize(file_get_contents($file->getRealPath())); + file_put_contents($file->getRealPath(), $cleanSvg); } } diff --git a/tests/Unit/SvgUploadTest.php b/tests/Unit/SvgUploadTest.php new file mode 100644 index 00000000..23051163 --- /dev/null +++ b/tests/Unit/SvgUploadTest.php @@ -0,0 +1,27 @@ +'; + + $file = UploadedFile::fake()->createWithContent('malicious.svg', $svgContent); + + $upload = MediaUploader::make($file)->upload(); + + $this->assertTrue(Storage::disk('public')->exists($upload->getPath())); + + $uploadedContent = Storage::disk('public')->get($upload->getPath()); + $this->assertStringNotContainsString('assertDatabaseHas('media', ['id' => $upload->id]); + } +}