WP Manifestindependent plugin directory
manifest / security / wp-safe-svg-uploads

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

★ 0stars
0forks

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.zip

WordPress 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 `onattribute is dropped**, which is whereonloadandonclick` payloads live.
  • URL attributes are checked for dangerous schemes — javascript:, data:, vbscript: and file: — in href, xlink:href, src, and also in from, to, values and by. Those last four matter: <animate to="javascript:…"> is how a payload survives a sanitiser that only inspects href.
  • Scheme matching ignores whitespace and case, because browsers accept java\nscript: and JAVASCRIPT : 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.