Sanitizeresw_svg_allowed_tags

esw_svg_allowed_tags

Filter the SVG element names Easy SVG keeps when it cleans a file: add a drawing element your files need or strip one you never want stored.

Filter · Easy SVG · Status: Stable

Easy SVG cleans every SVG before it is stored and throws away every element whose name is not on an allow-list. This filter hands you that list so you can add an element your drawings need, or take one out that you never want on your site. It changes what every upload, every icon and every add-on on the site keeps.

Usage

apply_filters( 'esw_svg_allowed_tags', array $tags )
add_filter( 'esw_svg_allowed_tags', 'myplugin_svg_tags' );

When to use it

  • A design tool exports an element that is missing from the default list, so it disappears on upload. The typical case is a filter primitive such as feDropShadow, which the bundled library does not list.
  • Your site policy is stricter than the default and you want an element gone from every SVG, for example embedded bitmaps (image).
  • An add-on cleans markup through easy_svg_sanitizer() and must keep the same element as your uploads do. It gets your change automatically, there is nothing extra to wire.

Do not use it:

  • To allow scripting or embedded HTML. script, foreignObject, animate, set and iframe stay off the list. See the danger box below.
  • To fix a single file. Export the file again without the element instead of widening the list for the whole site.
  • To allow an attribute. Attributes have their own list, see esw_svg_allowed_attributes.

Parameters

body
$tagsstring[]
Required

A flat, numerically indexed array of element names, for example array( 'a', 'font', 'image', 'style', 'svg', ... ). On a site without other callbacks it is the default list of the bundled enshrined/svg-sanitize library (70 entries in version 1.0.0). Names are matched case-insensitively against the element name as written in the file, including any namespace prefix: svg:rect is not rect.

Return value

returnstring[]

The element names the sanitizer may keep. It must be an array. Anything else (for example null from a callback that forgot its return) makes the library throw a TypeError while the sanitizer is built, which stops the upload with a fatal error.

Default

The AllowedTags list of enshrined/svg-sanitize 1.0.0, unchanged. It already contains:

  • HTML style names: a, font, image, style
  • Shapes and structure: svg, g, defs, symbol, use, view, switch, path, rect, circle, ellipse, line, polyline, polygon, marker, mask, pattern, clippath
  • Text: text, tspan, textpath, tref, title, desc, metadata, the old SVG font elements (glyph, hkern, vkern, altglyph and relatives)
  • Paint and effects: lineargradient, radialgradient, stop, filter and 23 filter primitives from feBlend to feTurbulence
  • Animation: animatecolor, animatemotion, animatetransform, mpath

Not on the list, so removed by default: script, foreignObject, animate, set, iframe, object, embed, feDropShadow, feImage, and anything from editor namespaces such as sodipodi:namedview.

Examples

Allow a missing filter primitive

/**
 * Keep drop shadows exported as feDropShadow.
 *
 * @param string[] $tags Allowed element names.
 * @return string[]
 */
function myplugin_allow_drop_shadow( $tags ) {
	$tags[] = 'feDropShadow';
	return $tags;
}
add_filter( 'esw_svg_allowed_tags', 'myplugin_allow_drop_shadow' );

The attributes feDropShadow uses (dx, dy, stdDeviation, flood-color, flood-opacity) are already on the attribute list, so this one line is enough. Without it the filter element survives empty and the shadow silently vanishes.

Remove elements your site never wants

/**
 * Strip embedded bitmaps and legacy font markup from every SVG.
 *
 * @param string[] $tags Allowed element names.
 * @return string[]
 */
function myplugin_strict_svg_tags( $tags ) {
	$blocked = array( 'image', 'font' );

	return array_values(
		array_filter(
			$tags,
			static function ( $tag ) use ( $blocked ) {
				return ! in_array( strtolower( $tag ), $blocked, true );
			}
		)
	);
}
add_filter( 'esw_svg_allowed_tags', 'myplugin_strict_svg_tags' );

Compare in lower case. The default list mixes spellings (feBlend, lineargradient), so a plain array_diff( $tags, array( 'feblend' ) ) matches nothing and removes nothing.

Defensive version for a shared plugin

/**
 * Add elements once, keep the value an array whatever ran before us.
 *
 * @param mixed $tags Allowed element names.
 * @return string[]
 */
function myplugin_svg_tags_defensive( $tags ) {
	$tags  = is_array( $tags ) ? $tags : array();
	$known = array_map( 'strtolower', $tags );

	foreach ( array( 'feDropShadow' ) as $tag ) {
		if ( ! in_array( strtolower( $tag ), $known, true ) ) {
			$tags[] = $tag;
		}
	}

	return $tags;
}

// Only worth hooking when Easy SVG is the plugin that will read the list.
if ( defined( 'EASY_SVG_API' ) || function_exists( 'easy_svg_sanitizer' ) ) {
	add_filter( 'esw_svg_allowed_tags', 'myplugin_svg_tags_defensive' );
}

Hooking unconditionally is harmless too: without Easy SVG nobody applies the filter. The guard only documents intent. Place the guard inside a plugins_loaded callback if your plugin may load before Easy SVG, because a defined() check at file load runs before Easy SVG has defined anything.

Never add script, foreignObject, animate, set or iframe. The allow-list only decides which names may appear. It does not look at what an element does: an animate element can set an href to javascript: through its attributeName and values, and foreignObject embeds HTML the SVG rules were never meant to check. An uploaded file is opened by your administrators in the media library, so a widened list is a way to run code in their browsers.

How it works

The filter is applied inside esw_svg_tags::getTags() (easy-svg.php, line 74). easy_svg_sanitizer() creates a new sanitizer on every call and passes new esw_svg_tags() to setAllowedTags(), which calls getTags(), lower-cases every entry and stores the result. So your callback runs once per sanitizer, and every path that cleans SVG builds its own:

While cleaning, the library walks the document and removes every element whose lower-cased name is not on the list, together with everything inside it. Then it checks attributes against esw_svg_allowed_attributes.

Further steps depend on the path:

  • Uploads and new attachments: after sanitizing, esw_svg_file_checker() looks for animation elements. If your list lets animate, set or animate* through, easy_svg_neutralize_href_animation() removes the ones whose attributeName targets href or an on* handler. This runs on the file paths only, not inside easy_svg_sanitizer().
  • wp_upload_bits (XML-RPC and plugins writing bytes): the filter cannot rewrite bytes, so the file is refused unless the sanitizer would leave it unchanged. Removing a tag that a file contains makes that upload fail on this path.
  • Icons: after sanitizing, easy_svg_harden_icon_markup() removes the elements in EASY_SVG_ICON_UNSAFE_ELEMENTS (among them style, script, foreignobject and every animation element) whatever your list says, because the icon is inlined into pages.

The upload filters are only registered while Allow SVG uploads in the media library is switched on in Icons, Settings. The new-attachment check is always on.

Pitfalls

  • Removing an element removes its children. Take out g or a and every shape inside a group or link goes too, often the whole drawing.
  • Return an array, always. A callback that returns null or a string causes a fatal error during the upload, not a quiet fallback.
  • Case-insensitive matching, case-sensitive removal. Matching is lower case, but your own array_diff() is not. Compare with strtolower().
  • Prefixes count. A file that writes svg:rect needs svg:rect on the list. A file whose root is svg:svg comes out without a drawing.
  • The readme example is a no-op. view is already on the default list. Adding it changes nothing.
  • Old files are not re-checked. The list is applied when a file arrives. Files already in the media library keep whatever the list allowed back then.
  • The legacy global cannot change the list. Calling setAllowedTags() on the old global $sanitizer has no effect, because easy_svg_sanitizer() sets the list again on its copy. Use this filter.
  • No context argument. The callback gets only the list. Uploads made by WP-CLI, cron or importers may run without a logged-in user, so do not tie the list to current_user_can() unless you accept that those paths see a different list.