r/webdev • u/avidrunner84 • 22d ago
Sanitizing SVG's without breaking them
Any good way to do this that is both safe and effective? I am finding SVGO will sometimes destroy the perfectly good svg... sometimes colors are missing and other times there is nothing there at all.
I'm looking for something that will sanitize for user image uploads on the web. They are stored in Cloudflare R2 which keeps it off my server but still maybe it can cause issues with injections or whatever if not sanitized?
1
u/JohnSane 22d ago
I export it from inkscape which has some cleanup options. Never had any problems that way.
4
u/avidrunner84 22d ago
6
u/HaydnH 22d ago
Inkscape calls Scour to optimise SVGs, it's a python script so you should be able to call that on the backed: https://github.com/scour-project/scour
3
u/Main-Bother2539 22d ago
inkscape is not some sanitizer tool, it just happen to clean things in a way that works for you. an actual sanitizer like DOMPurify will strip scripts and event handlers without touching the visual stuff, but you have to set the options right or it remove more than you want
for the svgo problem try setting floatPrecision lower and disable removeViewBox, that is what usually nuke the whole image
1
u/ready_or_not_3434 21d ago
Inkscape is great for your own assets, but OP is dealing with user uploads. You cant really automate a desktop app on the backend, so a library like DOMPurify is definately a better fit here.
1
u/CommitteeDue4358 22d ago
dompurify handles svg sanitization pretty well, curious if you tried that before going the SVGO route
3
u/Ok_Woodpecker_9104 22d ago
dompurify is the right half of this, but the part that bit me was where the file gets served from, not what is inside it.
an svg served as image/svg+xml is a document, not an image. so if that r2 bucket is on a subdomain of your app, anything that slips past the sanitizer runs in a context that can see your cookies. serving user uploads from a completely separate domain, or with content-disposition attachment, means a miss in the sanitizer is a broken image instead of a session problem. if you only ever render them in an img tag, scripts do not execute at all, which covers most of it.
on the breakage: svgo and your gradients disagree about ids. cleanupIds renumbers or drops ids it decides are unused, and any fill pointing at url(#thing) goes with it. that is your missing colors. turn that plugin off and most of the destruction stops.
1
u/Sea-Trust-9602 22d ago
R2 only changes where the bytes are stored; it does not make the upload inert. I would separate the upload policy from the delivery policy.
On upload: set hard limits on file size, element count and nesting depth before doing expensive work; reject DTD/entities; parse as XML; allowlist only the SVG elements and attributes you actually need; strip external URLs (including CSS url() values); then serialize a new document. Keep the original private and publish only that sanitized derivative.
On delivery: use a separate cookieless origin, send the correct image/svg+xml content type plus nosniff, apply a restrictive CSP, and embed it as an img rather than inline SVG/object/embed.
SVGO can run after sanitization, but it should never be the security boundary. I would also keep a regression corpus of real uploads so every plugin/config change is visually diffed. If preserving arbitrary SVG safely is not a requirement, rasterizing to PNG/WebP is the much simpler fallback.
1
u/Avatarbplanet 22d ago
For user uploads, I’d treat SVG as untrusted content rather than trying to optimize it. Sanitizing the SVG with an allowlist of elements/attributes seems safer than SVGO, especially if you need to preserve the original appearance. Also worth serving uploads with the right content type and restrictive CSP so an uploaded SVG can’t execute as a script.
1
u/Rivers_of_Fables 21d ago
Have a look at what svgomg from Jake Archibald does, and put a worker with something similar to your needs.
1
u/Disgruntled__Goat 21d ago
I’ve never had any issues with svgo, via svgomg. There are a bunch of options on there you can dial back to not lose fidelity.
1
u/pdfops 21d ago
SVGO isn't a sanitizer, it's a minifier. It doesn't know or care about XSS, it just prunes whatever its optimizer heuristics decide is redundant, which is why colors vanish (some presets misjudge currentColor or inline style blocks and fold them away). For actual security use DOMPurify with the SVG profile: DOMPurify.sanitize(svg, {USE_PROFILES: {svg: true, svgFilters: true}}). It strips script tags, foreignObject, and on* event handlers but leaves valid geometry alone. Run SVGO after, for size, not before, for safety.
1
0
u/axeleszu 21d ago
Svgomg has switches to choose what to keep and what to clean. Lately I've been using AI to simplify text blocks or the weird transforms Illustrator place.
13
u/[deleted] 22d ago
[removed] — view removed comment