From b0517d372117efb6138bbe7dadc03b404db98abc Mon Sep 17 00:00:00 2001 From: Benjamin Gaussorgues Date: Wed, 8 Jul 2026 15:58:28 +0200 Subject: [PATCH 1/2] feat(previews): add file signature check before opening files Signed-off-by: Benjamin Gaussorgues --- lib/private/Preview/Bitmap.php | 32 +++++++++++++++++++++++++---- lib/private/Preview/Font.php | 19 +++++++++++------ lib/private/Preview/HEIC.php | 4 ++-- lib/private/Preview/Illustrator.php | 16 +++++++++------ lib/private/Preview/PDF.php | 16 +++++++++------ lib/private/Preview/Photoshop.php | 16 +++++++++------ lib/private/Preview/Postscript.php | 16 +++++++++------ lib/private/Preview/SGI.php | 16 +++++++++------ lib/private/Preview/TGA.php | 16 +++++++++------ lib/private/Preview/TIFF.php | 21 +++++++++++++------ 10 files changed, 118 insertions(+), 54 deletions(-) diff --git a/lib/private/Preview/Bitmap.php b/lib/private/Preview/Bitmap.php index a9f5004201bec..eb07b094cf02c 100644 --- a/lib/private/Preview/Bitmap.php +++ b/lib/private/Preview/Bitmap.php @@ -33,8 +33,12 @@ abstract class Bitmap extends ProviderV2 { abstract protected function getAllowedMimeTypes(): string; /** - * {@inheritDoc} + * @return list */ + abstract protected function getMagicStrings(): array; + + abstract protected function getImagickFormatHint(): string; + #[\Override] public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { $tmpPath = $this->getLocalFile($file); @@ -65,7 +69,7 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { //new bitmap image object $image = new Image(); $image->loadFromData((string)$bp); - //check if image object is valid + // Check if image object is valid return $image->valid() ? $image : null; } @@ -88,15 +92,19 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { private function getResizedPreview($tmpPath, $maxX, $maxY) { $bp = new Imagick(); + if (!$this->isMagicStringSupported($tmpPath)) { + throw new \Exception('Invalid image type: magic string not recognized'); + } + // Validate mime type - $bp->pingImage($tmpPath . '[0]'); + $bp->pingImage($this->getImagickFormatHint() . ':' . $tmpPath . '[0]'); $mimeType = $bp->getImageMimeType(); if (!preg_match($this->getAllowedMimeTypes(), $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($this->getImagickFormatHint() . ':' . $tmpPath . '[0]'); $bp = $this->resize($bp, $maxX, $maxY); @@ -105,6 +113,22 @@ private function getResizedPreview($tmpPath, $maxX, $maxY) { return $bp; } + private function isMagicStringSupported(string $filepath): bool { + $signatures = $this->getMagicStrings(); + if (empty($signatures)) { + return true; + } + $length = array_reduce($signatures, static fn (int $carry, string $signature) => max($carry, strlen($signature)), 0); + $firstBytes = file_get_contents($filepath, false, null, 0, $length); + foreach ($signatures as $signature) { + if (str_starts_with($firstBytes, $signature)) { + return true; + } + } + + return false; + } + /** * Returns a resized \Imagick object * diff --git a/lib/private/Preview/Font.php b/lib/private/Preview/Font.php index 312d5de2bb8b3..998d2dc8f52bd 100644 --- a/lib/private/Preview/Font.php +++ b/lib/private/Preview/Font.php @@ -11,19 +11,26 @@ // .otf, .ttf and .pfb class Font extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/application\/(?:font-sfnt|x-font$)/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/(application|image)\/(?:font-sfnt|x-font|x-otf|x-ttf|x-pfb$)/'; } + + #[\Override] + protected function getMagicStrings(): array { + return [ + "\x00\x01\x00\x00\x00", // TTF + 'OTTO', // OTF + ]; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'ttf'; + } } diff --git a/lib/private/Preview/HEIC.php b/lib/private/Preview/HEIC.php index 413a3ef9e1e0e..26c3a0e9488fc 100644 --- a/lib/private/Preview/HEIC.php +++ b/lib/private/Preview/HEIC.php @@ -101,14 +101,14 @@ private function getResizedPreview($tmpPath, $maxX, $maxY) { // 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]'); + $bp->pingImage('heic:' . $tmpPath . '[0]'); $mimeType = $bp->getImageMimeType(); if (!preg_match('/^image\/(x-)?(png|jpeg|gif|bmp|tiff|webp|hei(f|c)|avif)$/', $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/lib/private/Preview/Illustrator.php b/lib/private/Preview/Illustrator.php index fdbe68542c726..2fc092715a192 100644 --- a/lib/private/Preview/Illustrator.php +++ b/lib/private/Preview/Illustrator.php @@ -11,19 +11,23 @@ //.ai class Illustrator extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/application\/illustrator/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/application\/(illustrator|pdf)/'; } + + #[\Override] + protected function getMagicStrings(): array { + return ["\x25\x50\x44\x46"]; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'ai'; + } } diff --git a/lib/private/Preview/PDF.php b/lib/private/Preview/PDF.php index 2aab2385569df..ad5e03f766e02 100644 --- a/lib/private/Preview/PDF.php +++ b/lib/private/Preview/PDF.php @@ -11,19 +11,23 @@ //.pdf class PDF extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/application\/pdf/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/application\/pdf/'; } + + #[\Override] + protected function getMagicStrings(): array { + return ['%PDF-']; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'pdf'; + } } diff --git a/lib/private/Preview/Photoshop.php b/lib/private/Preview/Photoshop.php index fea1bcf9a0d0b..b19219a6eef02 100644 --- a/lib/private/Preview/Photoshop.php +++ b/lib/private/Preview/Photoshop.php @@ -11,19 +11,23 @@ //.psd class Photoshop extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/application\/x-photoshop/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/(application|image)\/(x-photoshop|x-psd)/'; } + + #[\Override] + protected function getMagicStrings(): array { + return ['8BPS']; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'psd'; + } } diff --git a/lib/private/Preview/Postscript.php b/lib/private/Preview/Postscript.php index 49b3ff5af22b8..2194d01820bca 100644 --- a/lib/private/Preview/Postscript.php +++ b/lib/private/Preview/Postscript.php @@ -11,19 +11,23 @@ //.eps class Postscript extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/application\/postscript/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/(application\/postscript|image\/x-eps)/'; } + + #[\Override] + protected function getMagicStrings(): array { + return ['%!PS']; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'ps'; + } } diff --git a/lib/private/Preview/SGI.php b/lib/private/Preview/SGI.php index b27a270034da1..e21bd65268da9 100644 --- a/lib/private/Preview/SGI.php +++ b/lib/private/Preview/SGI.php @@ -10,19 +10,23 @@ //.sgi class SGI extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/image\/(x-)?sgi/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/image\/(x-)?sgi/'; } + + #[\Override] + protected function getMagicStrings(): array { + return ["\x01\xDA"]; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'sgi'; + } } diff --git a/lib/private/Preview/TGA.php b/lib/private/Preview/TGA.php index 98ae85ad865a8..d73b604f51712 100644 --- a/lib/private/Preview/TGA.php +++ b/lib/private/Preview/TGA.php @@ -10,19 +10,23 @@ //.tga class TGA extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/image\/(x-)?t(ar)?ga/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/image\/(x-)?t(ar)?ga/'; } + + #[\Override] + protected function getMagicStrings(): array { + return []; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'tga'; + } } diff --git a/lib/private/Preview/TIFF.php b/lib/private/Preview/TIFF.php index f04f56c334354..4ed65ca34d35c 100644 --- a/lib/private/Preview/TIFF.php +++ b/lib/private/Preview/TIFF.php @@ -11,19 +11,28 @@ //.tiff class TIFF extends Bitmap { - /** - * {@inheritDoc} - */ #[\Override] public function getMimeType(): string { return '/image\/tiff/'; } - /** - * {@inheritDoc} - */ #[\Override] protected function getAllowedMimeTypes(): string { return '/image\/tiff/'; } + + #[\Override] + protected function getMagicStrings(): array { + return [ + "II*\x00", + "MM\x00*", + "II+\x00", + "MM\x00+", + ]; + } + + #[\Override] + protected function getImagickFormatHint(): string { + return 'tiff'; + } } From 1db2c833e1cbef0b24eb614df9fb59582fec8e90 Mon Sep 17 00:00:00 2001 From: Benjamin Gaussorgues Date: Tue, 28 Jul 2026 10:23:46 +0200 Subject: [PATCH 2/2] Revert "fix: Disable imagick preview providers" This reverts commit 706fe8f8179e288b43800e88f022d68357673f00. Signed-off-by: Benjamin Gaussorgues --- lib/private/Preview/IMagickSupport.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/private/Preview/IMagickSupport.php b/lib/private/Preview/IMagickSupport.php index bb23ef4fd54e1..8225b1e7644d7 100644 --- a/lib/private/Preview/IMagickSupport.php +++ b/lib/private/Preview/IMagickSupport.php @@ -24,12 +24,10 @@ public function __construct(ICacheFactory $cacheFactory) { } public function hasExtension(): bool { - return false; return !is_null($this->imagick); } public function supportsFormat(string $format): bool { - return false; if (is_null($this->imagick)) { return false; }