fix: prevent arbitrary file write via unsanitized SVG/MVG bitmap previews [10.16] (OC10-164) - #41828
Draft
kw-tmueller wants to merge 4 commits into
Draft
kw-tmueller wants to merge 4 commits into
kw-tmueller wants to merge 4 commits into
Conversation
…iews (OC10-164)
Bitmap::getResizedPreview() sanitized SVG content before handing it to
Imagick::readImageBlob(), but fell back to the ORIGINAL, unsanitized bytes
whenever the sanitizer returned an empty string - which it does for any
content libxml cannot parse, not just for genuinely malformed SVG. A
malformed SVG (or any non-XML payload such as a raw MVG script) therefore
reached ImageMagick unsanitized, where an <image xlink:href="MSL:..."> or
an MVG "fill 'url(...)'" primitive can execute an MSL script that reads and
writes arbitrary files as the web user (CVSS 8.8).
Bitmap providers (PDF, Font, Postscript, ...) only ever need to decode real
bitmap/vector image formats, never SVG or script-shaped text content - that
belongs exclusively to the dedicated SVG provider. getResizedPreview() now
rejects any content whose libmagic-detected media type is text/*,
image/svg+xml, application/xml, or image/x-mvg before ever calling into
Imagick, instead of trying to sanitize and falling back on failure. It also
now goes through ImagickFactory::create() so the svg:sanitize/svg:embed/
svg:decode hardening options apply here as they already did in the SVG
provider.
SVG::sanitizeSVGContent() return type changes from string to ?string so it
can report "could not sanitize" (null) separately from "sanitized to an
empty document" (''); its own provider now bails out on null instead of
silently passing empty content to Imagick.
The removal of the sanitize-with-fallback path in Bitmap changes the
behaviour asserted by SanitizeTest: SVG content fed to a Bitmap provider
(PDF, Font) now yields false instead of a rendered PNG, since Bitmap
providers no longer attempt to handle SVG-shaped content at all. Added
regression cases for a malformed SVG with an MSL xlink:href, a raw MVG
script, and a well-formed SVG - all must return false from a Bitmap
provider.
Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com>
Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com>
Confirmed while testing this backport: PHP 7.4's bundled fileinfo extension reports the same SVG content as "image/svg", not "image/svg+xml" - the exact-match check silently let it through on this runtime while still catching it on PHP 8.3. Match by prefix instead so the gate is not dependent on which libmagic build a given PHP runtime happens to link. Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com>
Signed-off-by: Thomas Müller <323649642+oc-tmueller@users.noreply.github.com>
4 tasks
This was referenced Sep 14, 2026
oc-tmueller
marked this pull request as draft
September 16, 2026 11:48
This was referenced Sep 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Backport of #41827 to the
10.16maintenance line.Bitmap::getResizedPreview()sanitized SVG content before handing it toImagick::readImageBlob(), but fell back to the original, unsanitized bytes whenever the sanitizer returned an empty string.SVG::sanitizeSVGContent()returns''for any content libxml cannot parse, not just genuinely malformed SVG - so a malformed SVG (or any non-XML payload such as a raw MVG script) reached ImageMagick unsanitized. There, an<image xlink:href="MSL:...">or an MVGfill 'url(...)'primitive can execute an MSL script that reads and writes arbitrary files as the web user (CVSS 8.8).Bitmap::getResizedPreview()no longer sanitizes-then-falls-back. It now rejects any content whose libmagic-detected media type istext/*,image/svg(with or without the+xmlsuffix - see note below),application/xml, orimage/x-mvgbefore ever calling into Imagick (isDangerousToDecode()), and goes throughImagickFactory::create()so thesvg:sanitize/svg:embed/svg:decodehardening options apply here too.SVG::sanitizeSVGContent()'s return type changes fromstringto?string, returningnullwhen the underlying sanitizer does not return a string, so callers can distinguish "could not sanitize" from "sanitized to an empty document". The SVG provider's own call site now bails out onnullinstead of silently passing empty content to Imagick.SanitizeTestis updated: SVG content fed to a Bitmap provider (PDF, Font) now must returnfalseinstead of a rendered PNG. Added regression cases for a malformed SVG with an MSLxlink:href, a raw MVG script, and a well-formed SVG - all must returnfalsefrom a Bitmap provider.Two notes specific to what surfaced while verifying this backport on PHP 7.4:
image/x-mvgis in the deny-list because libmagic classifies a raw MVG script asimage/x-mvg, not as anytext/*/xml type.image/svg), not exact equality againstimage/svg+xml: PHP 7.4's bundled fileinfo build reports the identical SVG content asimage/svg(no+xml), while PHP 8.3 reportsimage/svg+xml. An exact match would have silently passed SVG content through unblocked on this branch's PHP 7.4 target while still blocking it on master's PHP 8.3 - confirmed by running the regression suite in both aowncloudci/php:7.4andowncloudci/php:8.3container before settling on the prefix check.Not in scope for this PR (flagging separately):
Bitmap::getThumbnail()leaks$streamwhengetResizedPreview()throws - it returns at line 54 before thefclose()at line 57. That's a resource leak, not a security defect, and deserves its own focused PR.Test plan
make test-php-stylemake test-php-unit TEST_PHP_SUITE=tests/lib/Preview/onowncloudci/php:7.4- 44 tests, 8 skipped (unrelated missing Movie/Office providers), 0 failurestests/lib/Preview/PDFTest.php(testimage.pdf) andtests/lib/Preview/BitmapTest.php(testimage.eps) still produce previews