diff --git a/composer.json b/composer.json index aceda9e71..72f2fd76e 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,6 @@ }, "require": { "php": ">=7.4", - "composer": "< 2.3", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", @@ -51,6 +50,7 @@ "ext-tidy": "*", "ext-tokenizer": "*", "ext-xml": "*", + "composer": "< 2.3", "babdev/pagerfanta-bundle": "^2.5", "bdunogier/guzzle-site-authenticator": "^1.0.0", "craue/config-bundle": "^2.3.0", @@ -59,6 +59,7 @@ "doctrine/doctrine-cache-bundle": "^1.3", "doctrine/doctrine-migrations-bundle": "^1.3", "doctrine/orm": "^2.6", + "enshrined/svg-sanitize": "^0.15.4", "friendsofsymfony/jsrouting-bundle": "^2.2", "friendsofsymfony/oauth-server-bundle": "^1.5", "friendsofsymfony/rest-bundle": "~2.1", diff --git a/composer.lock b/composer.lock index 803bfdb64..5411a79cf 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": "0d0c51bee0ced9698321d0fd2ad33aca", + "content-hash": "5aec7c6af1a3c05510db2e1243da8db5", "packages": [ { "name": "babdev/pagerfanta-bundle", @@ -2060,6 +2060,51 @@ ], "time": "2022-06-18T20:57:19+00:00" }, + { + "name": "enshrined/svg-sanitize", + "version": "0.15.4", + "source": { + "type": "git", + "url": "https://github.com/darylldoyle/svg-sanitizer.git", + "reference": "e50b83a2f1f296ca61394fe88fbfe3e896a84cf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/e50b83a2f1f296ca61394fe88fbfe3e896a84cf4", + "reference": "e50b83a2f1f296ca61394fe88fbfe3e896a84cf4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^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.15.4" + }, + "time": "2022-02-21T09:13:59+00:00" + }, { "name": "fig/link-util", "version": "1.1.2", @@ -12997,7 +13042,6 @@ "prefer-lowest": false, "platform": { "php": ">=7.4", - "composer": "< 2.3", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", @@ -13013,7 +13057,8 @@ "ext-simplexml": "*", "ext-tidy": "*", "ext-tokenizer": "*", - "ext-xml": "*" + "ext-xml": "*", + "composer": "< 2.3" }, "platform-dev": [], "platform-overrides": { diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php index a8a6ccad8..b15b87dfa 100644 --- a/src/Wallabag/CoreBundle/Helper/DownloadImages.php +++ b/src/Wallabag/CoreBundle/Helper/DownloadImages.php @@ -2,6 +2,7 @@ namespace Wallabag\CoreBundle\Helper; +use enshrined\svgSanitize\Sanitizer; use GuzzleHttp\Psr7\Uri; use GuzzleHttp\Psr7\UriResolver; use Http\Client\Common\HttpMethodsClient; @@ -146,6 +147,32 @@ class DownloadImages $hashImage = hash('crc32', $absolutePath); $localPath = $folderPath . '/' . $hashImage . '.' . $ext; + $urlPath = $this->wallabagUrl . '/assets/images/' . $relativePath . '/' . $hashImage . '.' . $ext; + + // custom case for SVG (because GD doesn't support SVG) + if ('svg' === $ext) { + try { + $sanitizer = new Sanitizer(); + $sanitizer->minify(true); + $sanitizer->removeRemoteReferences(true); + $cleanSVG = $sanitizer->sanitize((string) $res->getBody()); + + // add an extra validation by checking about `logger->error('DownloadImages: Bad SVG given', ['path' => $imagePath]); + + return false; + } + + file_put_contents($localPath, $cleanSVG); + + return $urlPath; + } catch (\Exception $e) { + $this->logger->error('DownloadImages: Error while sanitize SVG', ['path' => $imagePath, 'message' => $e->getMessage()]); + + return false; + } + } try { $im = imagecreatefromstring((string) $res->getBody()); @@ -196,7 +223,7 @@ class DownloadImages imagedestroy($im); - return $this->wallabagUrl . '/assets/images/' . $relativePath . '/' . $hashImage . '.' . $ext; + return $urlPath; } /** @@ -351,7 +378,7 @@ class DownloadImages $this->logger->debug('DownloadImages: Checking extension (alternative)', ['ext' => $ext]); } - if (!\in_array($ext, ['jpeg', 'jpg', 'gif', 'png', 'webp'], true)) { + if (!\in_array($ext, ['jpeg', 'jpg', 'gif', 'png', 'webp', 'svg'], true)) { $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping: ' . $imagePath); return false; diff --git a/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php b/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php index 0732b4375..f0735719d 100644 --- a/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php +++ b/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php @@ -220,4 +220,32 @@ class DownloadImagesTest extends TestCase $this->assertSame('', $res); } + + public function testProcessSingleImageWithSvg() + { + $httpMockClient = new HttpMockClient(); + $httpMockClient->addResponse(new Response(200, ['content-type' => 'image/svg+xml'], file_get_contents(__DIR__ . '/../fixtures/modal-content.svg'))); + + $logHandler = new TestHandler(); + $logger = new Logger('test', [$logHandler]); + + $download = new DownloadImages($httpMockClient, sys_get_temp_dir() . '/wallabag_test', 'http://wallabag.io/', $logger); + $res = $download->processSingleImage(123, 'modal-content.svg', 'http://imgur.com/gallery/WxtWY'); + + $this->assertStringContainsString('/assets/images/9/b/9b0ead26/400e29f9.svg', $res); + } + + public function testProcessSingleImageWithBadSvg() + { + $httpMockClient = new HttpMockClient(); + $httpMockClient->addResponse(new Response(200, ['content-type' => 'image/svg+xml'], file_get_contents(__DIR__ . '/../fixtures/unnamed.png'))); + + $logHandler = new TestHandler(); + $logger = new Logger('test', [$logHandler]); + + $download = new DownloadImages($httpMockClient, sys_get_temp_dir() . '/wallabag_test', 'http://wallabag.io/', $logger); + $res = $download->processSingleImage(123, 'modal-content.svg', 'http://imgur.com/gallery/WxtWY'); + + $this->assertFalse($res); + } } diff --git a/tests/Wallabag/CoreBundle/fixtures/modal-content.svg b/tests/Wallabag/CoreBundle/fixtures/modal-content.svg new file mode 100644 index 000000000..530e43492 --- /dev/null +++ b/tests/Wallabag/CoreBundle/fixtures/modal-content.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +