Skip to content

Commit 55cfccb

Browse files
authored
Merge pull request #62661 from nextcloud/backport/62641/stable26
[stable26] chore(previews): add file signature check before opening files
2 parents 42240fb + 877e24d commit 55cfccb

11 files changed

Lines changed: 191 additions & 7 deletions

File tree

lib/private/Preview/Bitmap.php

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,25 @@
3737
* @package OC\Preview
3838
*/
3939
abstract class Bitmap extends ProviderV2 {
40+
/**
41+
* List of MIME types that this preview provider is allowed to process.
42+
*
43+
* These should correspond to the MIME types *identified* by Imagemagick
44+
* for files to be processed by this provider. These do / will not
45+
* necessarily need to match the MIME types stored in the database
46+
* (which are identified by IMimeTypeDetector).
47+
*
48+
* @return string Regular expression
49+
*/
50+
abstract protected function getAllowedMimeTypes(): string;
51+
52+
/**
53+
* @return list<string>
54+
*/
55+
abstract protected function getMagicStrings(): array;
56+
57+
abstract protected function getImagickFormatHint(): string;
58+
4059
/**
4160
* {@inheritDoc}
4261
*/
@@ -86,12 +105,25 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
86105
* @param int $maxY
87106
*
88107
* @return \Imagick
108+
*
109+
* @throws \Exception
89110
*/
90111
private function getResizedPreview($tmpPath, $maxX, $maxY) {
91112
$bp = new Imagick();
92113

114+
if (!$this->isMagicStringSupported($tmpPath)) {
115+
throw new \Exception('Invalid image type: magic string not recognized');
116+
}
117+
118+
// Validate mime type
119+
$bp->pingImage($this->getImagickFormatHint() . ':' . $tmpPath . '[0]');
120+
$mimeType = $bp->getImageMimeType();
121+
if (!preg_match($this->getAllowedMimeTypes(), $mimeType)) {
122+
throw new \Exception('File mime type does not match the preview provider: ' . $mimeType);
123+
}
124+
93125
// Layer 0 contains either the bitmap or a flat representation of all vector layers
94-
$bp->readImage($tmpPath . '[0]');
126+
$bp->readImage($this->getImagickFormatHint() . ':' . $tmpPath . '[0]');
95127

96128
$bp = $this->resize($bp, $maxX, $maxY);
97129

@@ -100,6 +132,22 @@ private function getResizedPreview($tmpPath, $maxX, $maxY) {
100132
return $bp;
101133
}
102134

135+
private function isMagicStringSupported(string $filepath): bool {
136+
$signatures = $this->getMagicStrings();
137+
if (empty($signatures)) {
138+
return true;
139+
}
140+
$length = array_reduce($signatures, static fn (int $carry, string $signature) => max($carry, strlen($signature)), 0);
141+
$firstBytes = file_get_contents($filepath, false, null, 0, $length);
142+
foreach ($signatures as $signature) {
143+
if (str_starts_with($firstBytes, $signature)) {
144+
return true;
145+
}
146+
}
147+
148+
return false;
149+
}
150+
103151
/**
104152
* Returns a resized \Imagick object
105153
*

lib/private/Preview/Font.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,22 @@ class Font extends Bitmap {
3030
public function getMimeType(): string {
3131
return '/application\/(?:font-sfnt|x-font$)/';
3232
}
33+
34+
/**
35+
* {@inheritDoc}
36+
*/
37+
protected function getAllowedMimeTypes(): string {
38+
return '/(application|image)\/(?:font-sfnt|x-font|x-otf|x-ttf|x-pfb$)/';
39+
}
40+
41+
protected function getMagicStrings(): array {
42+
return [
43+
"\x00\x01\x00\x00\x00", // TTF
44+
'OTTO', // OTF
45+
];
46+
}
47+
48+
protected function getImagickFormatHint(): string {
49+
return 'ttf';
50+
}
3351
}

lib/private/Preview/HEIC.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class HEIC extends ProviderV2 {
4444
* {@inheritDoc}
4545
*/
4646
public function getMimeType(): string {
47-
return '/image\/hei(f|c)/';
47+
return '/image\/(x-)?hei(f|c)/';
4848
}
4949

5050
/**
@@ -108,12 +108,22 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
108108
* @param int $maxY
109109
*
110110
* @return \Imagick
111+
*
112+
* @throws \Exception
111113
*/
112114
private function getResizedPreview($tmpPath, $maxX, $maxY) {
113115
$bp = new \Imagick();
114116

117+
// Some HEIC files just contain (or at least are identified as) other formats
118+
// like JPEG. We just need to check if the image is safe to process.
119+
$bp->pingImage('heic:' . $tmpPath . '[0]');
120+
$mimeType = $bp->getImageMimeType();
121+
if (!preg_match('/^image\/(x-)?(png|jpeg|gif|bmp|tiff|webp|hei(f|c)|avif)$/', $mimeType)) {
122+
throw new \Exception('File mime type does not match the preview provider: ' . $mimeType);
123+
}
124+
115125
// Layer 0 contains either the bitmap or a flat representation of all vector layers
116-
$bp->readImage($tmpPath . '[0]');
126+
$bp->readImage('heic:' . $tmpPath . '[0]');
117127

118128
// Fix orientation from EXIF
119129
$bp->autoOrient();

lib/private/Preview/IMagickSupport.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,10 @@ public function __construct(ICacheFactory $cacheFactory) {
2020
}
2121

2222
public function hasExtension(): bool {
23-
return false;
2423
return !is_null($this->imagick);
2524
}
2625

2726
public function supportsFormat(string $format): bool {
28-
return false;
2927
if (is_null($this->imagick)) {
3028
return false;
3129
}

lib/private/Preview/Illustrator.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ class Illustrator extends Bitmap {
3131
public function getMimeType(): string {
3232
return '/application\/illustrator/';
3333
}
34+
35+
/**
36+
* {@inheritDoc}
37+
*/
38+
protected function getAllowedMimeTypes(): string {
39+
return '/application\/(illustrator|pdf)/';
40+
}
41+
42+
protected function getMagicStrings(): array {
43+
return ["\x25\x50\x44\x46"];
44+
}
45+
46+
protected function getImagickFormatHint(): string {
47+
return 'ai';
48+
}
3449
}

lib/private/Preview/PDF.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ class PDF extends Bitmap {
3131
public function getMimeType(): string {
3232
return '/application\/pdf/';
3333
}
34+
35+
/**
36+
* {@inheritDoc}
37+
*/
38+
protected function getAllowedMimeTypes(): string {
39+
return '/application\/pdf/';
40+
}
41+
42+
protected function getMagicStrings(): array {
43+
return ['%PDF-'];
44+
}
45+
46+
protected function getImagickFormatHint(): string {
47+
return 'pdf';
48+
}
3449
}

lib/private/Preview/Photoshop.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ class Photoshop extends Bitmap {
3131
public function getMimeType(): string {
3232
return '/application\/x-photoshop/';
3333
}
34+
35+
/**
36+
* {@inheritDoc}
37+
*/
38+
protected function getAllowedMimeTypes(): string {
39+
return '/(application|image)\/(x-photoshop|x-psd)/';
40+
}
41+
42+
protected function getMagicStrings(): array {
43+
return ['8BPS'];
44+
}
45+
46+
protected function getImagickFormatHint(): string {
47+
return 'psd';
48+
}
3449
}

lib/private/Preview/Postscript.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ class Postscript extends Bitmap {
3131
public function getMimeType(): string {
3232
return '/application\/postscript/';
3333
}
34+
35+
/**
36+
* {@inheritDoc}
37+
*/
38+
protected function getAllowedMimeTypes(): string {
39+
return '/application\/postscript/';
40+
}
41+
42+
protected function getMagicStrings(): array {
43+
return ['%!PS'];
44+
}
45+
46+
protected function getImagickFormatHint(): string {
47+
return 'ps';
48+
}
3449
}

lib/private/Preview/SGI.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ class SGI extends Bitmap {
2828
* {@inheritDoc}
2929
*/
3030
public function getMimeType(): string {
31-
return '/image\/sgi/';
31+
return '/image\/(x-)?sgi/';
32+
}
33+
34+
/**
35+
* {@inheritDoc}
36+
*/
37+
protected function getAllowedMimeTypes(): string {
38+
return '/image\/(x-)?sgi/';
39+
}
40+
41+
protected function getMagicStrings(): array {
42+
return ["\x01\xDA"];
43+
}
44+
45+
protected function getImagickFormatHint(): string {
46+
return 'sgi';
3247
}
3348
}

lib/private/Preview/TGA.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ class TGA extends Bitmap {
2828
* {@inheritDoc}
2929
*/
3030
public function getMimeType(): string {
31-
return '/image\/t(ar)?ga/';
31+
return '/image\/(x-)?t(ar)?ga/';
32+
}
33+
34+
/**
35+
* {@inheritDoc}
36+
*/
37+
protected function getAllowedMimeTypes(): string {
38+
return '/image\/(x-)?t(ar)?ga/';
39+
}
40+
41+
protected function getMagicStrings(): array {
42+
return [];
43+
}
44+
45+
protected function getImagickFormatHint(): string {
46+
return 'tga';
3247
}
3348
}

0 commit comments

Comments
 (0)