Skip to content

Commit b0517d3

Browse files
Altahrimbackportbot[bot]
authored andcommitted
feat(previews): add file signature check before opening files
Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
1 parent a0a5df3 commit b0517d3

10 files changed

Lines changed: 118 additions & 54 deletions

File tree

lib/private/Preview/Bitmap.php

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ abstract class Bitmap extends ProviderV2 {
3333
abstract protected function getAllowedMimeTypes(): string;
3434

3535
/**
36-
* {@inheritDoc}
36+
* @return list<string>
3737
*/
38+
abstract protected function getMagicStrings(): array;
39+
40+
abstract protected function getImagickFormatHint(): string;
41+
3842
#[\Override]
3943
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
4044
$tmpPath = $this->getLocalFile($file);
@@ -65,7 +69,7 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
6569
//new bitmap image object
6670
$image = new Image();
6771
$image->loadFromData((string)$bp);
68-
//check if image object is valid
72+
// Check if image object is valid
6973
return $image->valid() ? $image : null;
7074
}
7175

@@ -88,15 +92,19 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
8892
private function getResizedPreview($tmpPath, $maxX, $maxY) {
8993
$bp = new Imagick();
9094

95+
if (!$this->isMagicStringSupported($tmpPath)) {
96+
throw new \Exception('Invalid image type: magic string not recognized');
97+
}
98+
9199
// Validate mime type
92-
$bp->pingImage($tmpPath . '[0]');
100+
$bp->pingImage($this->getImagickFormatHint() . ':' . $tmpPath . '[0]');
93101
$mimeType = $bp->getImageMimeType();
94102
if (!preg_match($this->getAllowedMimeTypes(), $mimeType)) {
95103
throw new \Exception('File mime type does not match the preview provider: ' . $mimeType);
96104
}
97105

98106
// Layer 0 contains either the bitmap or a flat representation of all vector layers
99-
$bp->readImage($tmpPath . '[0]');
107+
$bp->readImage($this->getImagickFormatHint() . ':' . $tmpPath . '[0]');
100108

101109
$bp = $this->resize($bp, $maxX, $maxY);
102110

@@ -105,6 +113,22 @@ private function getResizedPreview($tmpPath, $maxX, $maxY) {
105113
return $bp;
106114
}
107115

116+
private function isMagicStringSupported(string $filepath): bool {
117+
$signatures = $this->getMagicStrings();
118+
if (empty($signatures)) {
119+
return true;
120+
}
121+
$length = array_reduce($signatures, static fn (int $carry, string $signature) => max($carry, strlen($signature)), 0);
122+
$firstBytes = file_get_contents($filepath, false, null, 0, $length);
123+
foreach ($signatures as $signature) {
124+
if (str_starts_with($firstBytes, $signature)) {
125+
return true;
126+
}
127+
}
128+
129+
return false;
130+
}
131+
108132
/**
109133
* Returns a resized \Imagick object
110134
*

lib/private/Preview/Font.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,26 @@
1111

1212
// .otf, .ttf and .pfb
1313
class Font extends Bitmap {
14-
/**
15-
* {@inheritDoc}
16-
*/
1714
#[\Override]
1815
public function getMimeType(): string {
1916
return '/application\/(?:font-sfnt|x-font$)/';
2017
}
2118

22-
/**
23-
* {@inheritDoc}
24-
*/
2519
#[\Override]
2620
protected function getAllowedMimeTypes(): string {
2721
return '/(application|image)\/(?:font-sfnt|x-font|x-otf|x-ttf|x-pfb$)/';
2822
}
23+
24+
#[\Override]
25+
protected function getMagicStrings(): array {
26+
return [
27+
"\x00\x01\x00\x00\x00", // TTF
28+
'OTTO', // OTF
29+
];
30+
}
31+
32+
#[\Override]
33+
protected function getImagickFormatHint(): string {
34+
return 'ttf';
35+
}
2936
}

lib/private/Preview/HEIC.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,14 @@ private function getResizedPreview($tmpPath, $maxX, $maxY) {
101101

102102
// Some HEIC files just contain (or at least are identified as) other formats
103103
// like JPEG. We just need to check if the image is safe to process.
104-
$bp->pingImage($tmpPath . '[0]');
104+
$bp->pingImage('heic:' . $tmpPath . '[0]');
105105
$mimeType = $bp->getImageMimeType();
106106
if (!preg_match('/^image\/(x-)?(png|jpeg|gif|bmp|tiff|webp|hei(f|c)|avif)$/', $mimeType)) {
107107
throw new \Exception('File mime type does not match the preview provider: ' . $mimeType);
108108
}
109109

110110
// Layer 0 contains either the bitmap or a flat representation of all vector layers
111-
$bp->readImage($tmpPath . '[0]');
111+
$bp->readImage('heic:' . $tmpPath . '[0]');
112112

113113
// Fix orientation from EXIF
114114
$bp->autoOrient();

lib/private/Preview/Illustrator.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@
1111

1212
//.ai
1313
class Illustrator extends Bitmap {
14-
/**
15-
* {@inheritDoc}
16-
*/
1714
#[\Override]
1815
public function getMimeType(): string {
1916
return '/application\/illustrator/';
2017
}
2118

22-
/**
23-
* {@inheritDoc}
24-
*/
2519
#[\Override]
2620
protected function getAllowedMimeTypes(): string {
2721
return '/application\/(illustrator|pdf)/';
2822
}
23+
24+
#[\Override]
25+
protected function getMagicStrings(): array {
26+
return ["\x25\x50\x44\x46"];
27+
}
28+
29+
#[\Override]
30+
protected function getImagickFormatHint(): string {
31+
return 'ai';
32+
}
2933
}

lib/private/Preview/PDF.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@
1111

1212
//.pdf
1313
class PDF extends Bitmap {
14-
/**
15-
* {@inheritDoc}
16-
*/
1714
#[\Override]
1815
public function getMimeType(): string {
1916
return '/application\/pdf/';
2017
}
2118

22-
/**
23-
* {@inheritDoc}
24-
*/
2519
#[\Override]
2620
protected function getAllowedMimeTypes(): string {
2721
return '/application\/pdf/';
2822
}
23+
24+
#[\Override]
25+
protected function getMagicStrings(): array {
26+
return ['%PDF-'];
27+
}
28+
29+
#[\Override]
30+
protected function getImagickFormatHint(): string {
31+
return 'pdf';
32+
}
2933
}

lib/private/Preview/Photoshop.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@
1111

1212
//.psd
1313
class Photoshop extends Bitmap {
14-
/**
15-
* {@inheritDoc}
16-
*/
1714
#[\Override]
1815
public function getMimeType(): string {
1916
return '/application\/x-photoshop/';
2017
}
2118

22-
/**
23-
* {@inheritDoc}
24-
*/
2519
#[\Override]
2620
protected function getAllowedMimeTypes(): string {
2721
return '/(application|image)\/(x-photoshop|x-psd)/';
2822
}
23+
24+
#[\Override]
25+
protected function getMagicStrings(): array {
26+
return ['8BPS'];
27+
}
28+
29+
#[\Override]
30+
protected function getImagickFormatHint(): string {
31+
return 'psd';
32+
}
2933
}

lib/private/Preview/Postscript.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@
1111

1212
//.eps
1313
class Postscript extends Bitmap {
14-
/**
15-
* {@inheritDoc}
16-
*/
1714
#[\Override]
1815
public function getMimeType(): string {
1916
return '/application\/postscript/';
2017
}
2118

22-
/**
23-
* {@inheritDoc}
24-
*/
2519
#[\Override]
2620
protected function getAllowedMimeTypes(): string {
2721
return '/(application\/postscript|image\/x-eps)/';
2822
}
23+
24+
#[\Override]
25+
protected function getMagicStrings(): array {
26+
return ['%!PS'];
27+
}
28+
29+
#[\Override]
30+
protected function getImagickFormatHint(): string {
31+
return 'ps';
32+
}
2933
}

lib/private/Preview/SGI.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,23 @@
1010

1111
//.sgi
1212
class SGI extends Bitmap {
13-
/**
14-
* {@inheritDoc}
15-
*/
1613
#[\Override]
1714
public function getMimeType(): string {
1815
return '/image\/(x-)?sgi/';
1916
}
2017

21-
/**
22-
* {@inheritDoc}
23-
*/
2418
#[\Override]
2519
protected function getAllowedMimeTypes(): string {
2620
return '/image\/(x-)?sgi/';
2721
}
22+
23+
#[\Override]
24+
protected function getMagicStrings(): array {
25+
return ["\x01\xDA"];
26+
}
27+
28+
#[\Override]
29+
protected function getImagickFormatHint(): string {
30+
return 'sgi';
31+
}
2832
}

lib/private/Preview/TGA.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,23 @@
1010

1111
//.tga
1212
class TGA extends Bitmap {
13-
/**
14-
* {@inheritDoc}
15-
*/
1613
#[\Override]
1714
public function getMimeType(): string {
1815
return '/image\/(x-)?t(ar)?ga/';
1916
}
2017

21-
/**
22-
* {@inheritDoc}
23-
*/
2418
#[\Override]
2519
protected function getAllowedMimeTypes(): string {
2620
return '/image\/(x-)?t(ar)?ga/';
2721
}
22+
23+
#[\Override]
24+
protected function getMagicStrings(): array {
25+
return [];
26+
}
27+
28+
#[\Override]
29+
protected function getImagickFormatHint(): string {
30+
return 'tga';
31+
}
2832
}

lib/private/Preview/TIFF.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,28 @@
1111

1212
//.tiff
1313
class TIFF extends Bitmap {
14-
/**
15-
* {@inheritDoc}
16-
*/
1714
#[\Override]
1815
public function getMimeType(): string {
1916
return '/image\/tiff/';
2017
}
2118

22-
/**
23-
* {@inheritDoc}
24-
*/
2519
#[\Override]
2620
protected function getAllowedMimeTypes(): string {
2721
return '/image\/tiff/';
2822
}
23+
24+
#[\Override]
25+
protected function getMagicStrings(): array {
26+
return [
27+
"II*\x00",
28+
"MM\x00*",
29+
"II+\x00",
30+
"MM\x00+",
31+
];
32+
}
33+
34+
#[\Override]
35+
protected function getImagickFormatHint(): string {
36+
return 'tiff';
37+
}
2938
}

0 commit comments

Comments
 (0)