From a18b45f0f81a0adf230268a523a80eed0ba246ab Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 20 Feb 2026 21:40:37 +0900 Subject: [PATCH 1/4] Strip namespace prefixes before checking dangerous tags in SVG --- common/framework/filters/FileContentFilter.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/framework/filters/FileContentFilter.php b/common/framework/filters/FileContentFilter.php index 272fe3477..6bfa5cf53 100644 --- a/common/framework/filters/FileContentFilter.php +++ b/common/framework/filters/FileContentFilter.php @@ -44,7 +44,7 @@ class FileContentFilter $skip_xml = preg_match('/^(hwpx)$/', $ext); // Check SVG files. - if (($ext === 'svg' || $is_xml) && !self::_checkSVG($fp, 0, $filesize)) + if (($ext === 'svg' || $is_xml) && !self::_checkSVG($fp, 0, $filesize, $ext)) { fclose($fp); return false; @@ -89,11 +89,12 @@ class FileContentFilter * @param resource $fp * @param int $from * @param int $to + * @param string $ext * @return bool */ - protected static function _checkSVG($fp, $from, $to) + protected static function _checkSVG($fp, $from, $to, $ext) { - if (self::_matchStream('/(?:<|<)(?:script|iframe|foreignObject|object|embed|handler)|javascript:|xlink:href\s*=\s*"(?!data:)/i', $fp, $from, $to)) + if (self::_matchStream('/(?:<|<|:)(?:script|iframe|foreignObject|object|embed|handler)|javascript:|(?:\s|:)href\s*=\s*"(?!data:)/i', $fp, $from, $to)) { return false; } From bf2df84d0f2209313a50348e01b90c89fb1e78d5 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 20 Feb 2026 21:55:29 +0900 Subject: [PATCH 2/4] Use enshrined\svgSanitize to clean SVG file content --- common/framework/Security.php | 6 ++++++ modules/file/file.controller.php | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/common/framework/Security.php b/common/framework/Security.php index d5e0802cd..6871af61e 100644 --- a/common/framework/Security.php +++ b/common/framework/Security.php @@ -38,6 +38,12 @@ class Security if (!utf8_check($input)) return false; return Filters\FilenameFilter::clean($input); + // Clean up SVG content to prevent various attacks. + case 'svg': + if (!utf8_check($input)) return false; + $sanitizer = new \enshrined\svgSanitize\Sanitizer(); + return strval($sanitizer->sanitize($input)); + // Unknown filters. default: throw new Exception('Unknown filter type for sanitize: ' . $type); diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index 1e482ce9e..c42aaf6dc 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -936,6 +936,14 @@ class FileController extends File } } + // Sanitize SVG + if(!$manual_insert && !$this->user->isAdmin() && ($file_info['type'] === 'image/svg+xml' || $file_info['extension'] === 'svg')) + { + $dirty_svg = Rhymix\Framework\Storage::read($file_info['tmp_name']); + $clean_svg = Rhymix\Framework\Security::sanitize($dirty_svg, 'svg'); + Rhymix\Framework\Storage::write($file_info['tmp_name'], $clean_svg); + } + // Adjust if(!$manual_insert) { From 91744ec87cafb77cd3afc9dcb94b64ffd4bbf171 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 20 Feb 2026 21:57:07 +0900 Subject: [PATCH 3/4] Always download SVG as attachment --- modules/file/file.controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index c42aaf6dc..3f24cae0c 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -551,7 +551,7 @@ class FileController extends File { $download_type = 'inline'; } - if (Context::get('force_download') === 'Y') + if ($mime_type === 'image/svg+xml' || Context::get('force_download') === 'Y') { $download_type = 'attachment'; } From e4c60b56d47df1554adc6d978a391e28627b8f84 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 20 Feb 2026 21:57:35 +0900 Subject: [PATCH 4/4] Add unit tests for Security::sanitize() supporting SVG --- tests/unit/framework/SecurityTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index 4246316be..9fb35a2b0 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -15,6 +15,16 @@ class SecurityTest extends \Codeception\Test\Unit // Filename (more thorough tests in FilenameFilterTest) $this->assertEquals('foo(bar).xls', Rhymix\Framework\Security::sanitize('foo.xls', 'filename')); + + // SVG #1 + $source = 'Test'; + $target = '' . "\n\n \n Test\n \n\n"; + $this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'svg')); + + // SVG #2 + $source = ''; + $target = '' . "\n\n \n\n"; + $this->assertEquals($target, Rhymix\Framework\Security::sanitize($source, 'svg')); } public function testEncryption()