diff --git a/REUSE.toml b/REUSE.toml index f9c27b0cc8f97..3661ca7f3f1c9 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -261,6 +261,12 @@ precedence = "aggregate" SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors" SPDX-License-Identifier = "AGPL-3.0-or-later" +[[annotations]] +path = ["tests/data/testimage-disguised-svg.heic"] +precedence = "aggregate" +SPDX-FileCopyrightText = "2026 Nextcloud GmbH and Nextcloud contributors" +SPDX-License-Identifier = "AGPL-3.0-or-later" + [[annotations]] path = ["composer.json", "composer.lock", ".github/CODEOWNERS", "__tests__/tsconfig.json", "tsconfig.json", "apps/**/composer/composer.json", "apps/**/composer/composer.lock", "apps/**/composer/composer/installed.json"] precedence = "aggregate" diff --git a/lib/private/Preview/HEIC.php b/lib/private/Preview/HEIC.php index e56101e8e4019..ef4e8771bad63 100644 --- a/lib/private/Preview/HEIC.php +++ b/lib/private/Preview/HEIC.php @@ -100,16 +100,15 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { private function getResizedPreview($tmpPath, $maxX, $maxY) { $bp = new \Imagick(); - // Some HEIC files just contain (or at least are identified as) other formats - // like JPEG. We just need to check if the image is safe to process. - $bp->pingImage($tmpPath . '[0]'); + // Force Imagick to only accept HEIC or HEIF images + $bp->pingImage('heic:' . $tmpPath . '[0]'); $mimeType = $bp->getImageMimeType(); - if (!preg_match('/^image\/(x-)?(png|jpeg|gif|bmp|tiff|webp|hei(f|c)|avif)$/', $mimeType)) { + if (!preg_match('/^image\/(x-)?hei(f|c)$/', $mimeType)) { throw new \Exception('File mime type does not match the preview provider: ' . $mimeType); } // Layer 0 contains either the bitmap or a flat representation of all vector layers - $bp->readImage($tmpPath . '[0]'); + $bp->readImage('heic:' . $tmpPath . '[0]'); // Fix orientation from EXIF $bp->autoOrient(); diff --git a/tests/data/testimage-disguised-svg.heic b/tests/data/testimage-disguised-svg.heic new file mode 100644 index 0000000000000..28b5988122a80 --- /dev/null +++ b/tests/data/testimage-disguised-svg.heic @@ -0,0 +1,10 @@ + + + + diff --git a/tests/lib/Preview/HEICDisguisedSVGTest.php b/tests/lib/Preview/HEICDisguisedSVGTest.php new file mode 100644 index 0000000000000..41f238141c837 --- /dev/null +++ b/tests/lib/Preview/HEICDisguisedSVGTest.php @@ -0,0 +1,61 @@ +markTestSkipped('ImageMagick is not HEIC aware. Skipping tests'); + } else { + parent::setUp(); + + $this->width = 1680; + $this->height = 1050; + $this->provider = new HEIC; + } + } + + /** + * Launches all the tests we have + * + * + * @param int $widthAdjustment + * @param int $heightAdjustment + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dimensionsDataProvider')] + #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('imagick')] + public function testGetThumbnail($widthAdjustment, $heightAdjustment): void { + # HEIC->getThumbnail will always return null if there is an exception, be we want to check why getResizedPreview fails + $reflection = new \ReflectionClass($this->provider); + $method = $reflection->getMethod('getResizedPreview'); + $method->setAccessible(true); + + $absolutePath = \OC::$SERVERROOT . '/tests/data/' . 'testimage-disguised-svg.heic'; + + try { + $method->invoke( + $this->provider, + $absolutePath, + $this->width, + $this->height + ); + $this->fail('Expected ImagickException was not thrown.'); + } catch (\ImagickException $e) { + $this->assertStringStartsWith('ImageTypeNotSupported', $e->getMessage()); + } + } +}