Skip to content

Commit af70600

Browse files
committed
fix(preview): properly handle encoded content
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent 7430bf3 commit af70600

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

lib/private/Preview/SVG.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ public function getMimeType(): string {
3030
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
3131
try {
3232
$content = stream_get_contents($file->fopen('r'));
33+
$content = ltrim($content);
3334
if (substr($content, 0, 5) !== '<?xml') {
3435
$content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content;
3536
}
3637

37-
// Do not parse SVG files with references
38-
if (preg_match('/["\s](xlink:)?href\s*=/i', $content)) {
38+
if (!$this->canBeProcessed($content)) {
3939
return null;
4040
}
4141

@@ -72,4 +72,26 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
7272
}
7373
return null;
7474
}
75+
76+
/**
77+
* Check if the file can be processed by this provider,
78+
* meaning the SVG is safe to be processed and does not contain any external references.
79+
*/
80+
protected function canBeProcessed(string $content): bool {
81+
// check for allowed encodings and convert if necessary
82+
$encoding = mb_detect_encoding($content, ['UTF-8', 'ISO-2022-JP', 'ISO-8859-1'], true);
83+
if ($encoding === false) {
84+
return false;
85+
} elseif ($encoding !== 'UTF-8') {
86+
$content = mb_convert_encoding($content, 'UTF-8', $encoding);
87+
}
88+
89+
// Strip all non-printable/control characters except newlines/tabs
90+
$content = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $content);
91+
// check for any potential external reference (include custom namespace prefix)
92+
if (preg_match('/["\s\']([a-z_][a-z0-9_.]*:)?href\s*=/i', $content)) {
93+
return false;
94+
}
95+
return true;
96+
}
7597
}

tests/lib/Preview/SVGTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,38 @@ public function testGetThumbnailSVGHref(string $content): void {
6161

6262
self::assertNull($this->provider->getThumbnail($file, 512, 512));
6363
}
64+
65+
#[\PHPUnit\Framework\Attributes\RequiresPhpExtension('imagick')]
66+
public function testGetThumbnailSvgHrefNamespace(): void {
67+
$handle = fopen('php://temp', 'w+');
68+
fwrite($handle, '<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:foobar="http://www.w3.org/1999/xlink">
69+
<image x="0" y="0" foobar:href="fxlogo.png" height="100" width="100" />
70+
</svg>');
71+
rewind($handle);
72+
73+
$file = $this->createMock(File::class);
74+
$file->method('fopen')
75+
->willReturn($handle);
76+
77+
self::assertNull($this->provider->getThumbnail($file, 512, 512));
78+
}
79+
80+
#[\PHPUnit\Framework\Attributes\DataProvider('dataGetThumbnailSvgEncoded')]
81+
#[\PHPUnit\Framework\Attributes\RequiresPhpExtension('imagick')]
82+
public function testGetThumbnailSvgEncoded(string $content): void {
83+
$handle = fopen('php://temp', 'w+');
84+
fwrite($handle, $content);
85+
rewind($handle);
86+
87+
$file = $this->createMock(File::class);
88+
$file->method('fopen')
89+
->willReturn($handle);
90+
self::assertNull($this->provider->getThumbnail($file, 512, 512));
91+
}
92+
93+
public static function dataGetThumbnailSvgEncoded(): array {
94+
return [
95+
'iso-2022-jp' => ["<?xml version=\"1.0\" encoding=\"ISO-2022-JP\"?>\n<svg width=\"700\" height=\"700\" xmlns=\"http://www.w3.org/2000/svg\">\n<i\x1b(Bmage width=\"700\" height=\"700\" h\x1b(Bref=\"text:/proc/cpuinfo\" />\n</svg>"],
96+
];
97+
}
6498
}

0 commit comments

Comments
 (0)