Safe SVG Uploads
Enables SVG uploads in WordPress for trusted roles and strips scripting from the file before it is stored
by Alex Mochulskyi · github.com/alexskybrain/wp-safe-svg-uploads
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/alexskybrain/wp-safe-svg-uploads/archive/refs/heads/main.zipWordPress blocks SVG uploads for a good reason: an SVG is XML that the browser renders inside your own origin, so an uploaded file can carry script that runs as your site. Most "enable SVG" snippets solve this by turning the check off, which trades a small inconvenience for stored cross-site scripting.
This plugin enables SVG uploads and removes the dangerous parts of the file before it is written to disk.
Requirements
- WordPress 6.4+
- PHP 8.2+ with the DOM extension. Without DOM the plugin refuses to enable SVG uploads at all and says so in the admin, rather than falling back to an unchecked upload.
Installation
Copy the folder into wp-content/plugins/ and activate it. Administrators and editors can then upload SVG files. Nothing else changes.
What the sanitiser removes
The file is parsed as XML and rebuilt, so anything that is not valid SVG never survives the round trip. On top of that:
- A DOCTYPE is rejected outright. It is the entry point for entity expansion and external entity attacks, and an uploaded asset has no legitimate use for one.
- Executable elements are dropped:
script,handler,foreignObject,iframe,embed,object,audio,video. - *Every `on
attribute is dropped**, which is whereonloadandonclick` payloads live. - URL attributes are checked for dangerous schemes —
javascript:,data:,vbscript:andfile:— inhref,xlink:href,src, and also infrom,to,valuesandby. Those last four matter:<animate to="javascript:…">is how a payload survives a sanitiser that only inspectshref. - Scheme matching ignores whitespace and case, because browsers accept
java\nscript:andJAVASCRIPT :just as happily as the plain form.
The cleaned markup is what gets stored. Rejecting only the obviously hostile files would still leave event handlers sitting in the uploads directory.
Why the capability check stays
Uploading remains limited to manage_options and edit_others_posts, adjustable through safe_svg_user_can_upload. A sanitiser is a filter, not a proof, and there is no reason for a subscriber-level account on a shop to be able to upload vector files at all.
Other behaviour
wp_check_filetype_and_ext is overridden so WordPress stops rejecting SVGs whose sniffed MIME type comes back as plain XML — but only after the file has been confirmed to parse as an SVG document, so the override cannot be used to smuggle other content through by renaming it.
SVG attachments get a width and height in their metadata, read from the width and height attributes or from the viewBox, so blocks and galleries can reserve layout space instead of collapsing the image.
Tests
The sanitiser is deliberately free of WordPress dependencies, so it can be exercised with plain PHP:
php tests/sanitizer-test.php
The suite covers each attack shape listed above plus a clean icon that must survive untouched.
Filters
| Filter | Default | Purpose |
|---|---|---|
safe_svg_user_can_upload |
administrators and editors | Who may upload SVG files |
License
GPL-2.0-or-later — see LICENSE.