Skip to content

Render the SVG block in a shadow DOM - #328

Merged
peterwilsoncc merged 15 commits into
developfrom
update/shadow-dom
Sep 4, 2026
Merged

peterwilsoncc merged 15 commits into
developfrom
update/shadow-dom

Conversation

@dkotter

@dkotter dkotter commented Sep 1, 2026 •

Copy link
Copy Markdown
Collaborator

Description of the Change

When rendering an SVG in our SVG block, we don't strip out any styles. This can result in someone adding styles that impact other areas of the page and by rendering the SVG, they can impact how the rest of the page looks.

To address this, this PR introduces new handling within the SVG block for any SVG that comes with it's own styles, wrapping those SVGs in a shadow DOM which keeps the styles isolated. This only applies to SVGs that come with styles (though can be turned on/off using the new safe_svg_inline_use_shadow_dom filter) so for majority of use cases, this won't change anything.

Note though for SVGs that do ship with their own styles, theme CSS will no longer be able to target them because of this shadow DOM. That styling will need to be done from within the SVG itself or you can opt out using the safe_svg_inline_use_shadow_dom filter.

In addition, replace the use of ReactSVG with our own custom InlineSvg component that will offer the same protections within the block editor.

We've also introduced the new removeRemoteReferences property of the sanitizer (coming in v1.0.0) and set that to false by default. This can be turned on with the new safe_svg_remove_remote_references filter, which will then strip out any remote references, making the SVG markup even more secure.

How to test the Change

NOTE: The code here is dependent on the update in #327 so that will either need merged first before testing this or you'll need to manually pull that change in locally when testing.

Basic testing across the plugin is desired here to ensure these changes don't break any existing functionality. Main thing to test is the SVG block.

Beyond that, the automated tests this PR ships with contains some SVG files you can use for testing to ensure the styles they load don't leak into the main document.

Changelog Entry

Added - New safe_svg_remove_remote_references filter to strip remote url(), @import and image-set() references, along with remote href targets, from uploaded SVGs. Off by default, because legitimate SVGs reference remote fonts and images but use this filter to turn it on

Added - New safe_svg_inline_use_shadow_dom filter to control which inline SVGs are isolated in a shadow root, and new safe_svg_inline_shadow_styles filter to adjust the CSS injected alongside them

Changed - Theme CSS can no longer target an inline SVG that carries its own <style> element, because stylesheets cannot reach into a shadow root. Style those SVGs from within the SVG itself, or opt out with the safe_svg_inline_use_shadow_dom filter. Inherited properties, including color/currentColor and custom properties, still apply as before, and SVGs without a <style> element are unaffected

Security - The Inline SVG block now renders SVGs that carry their own <style> element inside a shadow root, so their CSS is scoped to the block instead of applying to the whole page

Developer - Replaced the react-svg dependency with a small internal component, so the block editor preview isolates SVG CSS the same way the front end does

Credits

Props @darylldoyle, @dkotter

Checklist:

@dkotter dkotter added this to the 2.5.0 milestone Sep 1, 2026
@dkotter dkotter self-assigned this Sep 1, 2026
@dkotter
dkotter requested a review from jeffpaul as a code owner September 1, 2026 19:04
@github-actions github-actions Bot added the needs:code-review This requires code review. label Sep 1, 2026
@jeffpaul jeffpaul moved this to Code Review in Open Source Practice Sep 1, 2026
@dkotter

dkotter commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

I have the new tests all passing here, we do have some existing test failures coming out of WP 7.1. I think these will be addressed once we can update to the latest Cypress Utils package (which I think is still stuck getting a proper release out). I did rewrite some of the new tests here to work around that, could do the same for the other failures but have left those alone for now

@peterwilsoncc peterwilsoncc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is testing well and looks good.

I've added a few notes inline but there's nothing major that I have picked up.

Comment thread README.md Outdated

Mostly, yes. The Inline SVG block renders an SVG that carries its own `<style>` element inside a shadow root, because CSS inside an inline SVG is otherwise applied to the whole page rather than just the SVG. Stylesheets cannot reach into a shadow root, so theme CSS such as `.entry-content svg { fill: red; }` will not apply to those SVGs.

Inherited properties still cross the boundary, so setting `color` on an ancestor and using `currentColor` inside the SVG works, as do CSS custom properties. SVGs that do not contain a `<style>` element are rendered exactly as before and can be styled normally.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"exactly as before" doesn't really make sense to a new user.

Suggested change
Inherited properties still cross the boundary, so setting `color` on an ancestor and using `currentColor` inside the SVG works, as do CSS custom properties. SVGs that do not contain a `<style>` element are rendered exactly as before and can be styled normally.
Inherited properties still cross the boundary, so setting `color` on an ancestor and using `currentColor` inside the SVG works, as do CSS custom properties. SVGs that do not contain a `<style>` element are rendered without the shadow root and can be styled by theme stylesheets.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 2e9fc51

Comment thread includes/blocks/safe-svg/register.php Outdated
'safe_svg_inline_use_shadow_dom',
svg_has_stylesheet( $contents ),
$contents,
$attributes['imageID']

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let plugin know whether the SVG has a stylesheet.

Suggested change
$attributes['imageID']
$attributes['imageID'],
svg_has_stylesheet( $contents )

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in b74d721 and 555721f

Comment thread tests/unit/test-safe-svg-block.php Outdated
/**
* Test that SVGs carrying a stylesheet are recognised.
*/
public function test_svg_has_stylesheet() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: might be nice to use a data provider here so the test completes for each item even if a previous one fails.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 555721f

Comment thread tests/unit/test-safe-svg-block.php Outdated
/**
* Test that SVGs without a stylesheet are left alone.
*/
public function test_svg_has_no_stylesheet() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above re data provider.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 555721f

Comment thread readme.txt Outdated

Mostly, yes. The Inline SVG block renders an SVG that carries its own `<style>` element inside a shadow root, because CSS inside an inline SVG is otherwise applied to the whole page rather than just the SVG. Stylesheets cannot reach into a shadow root, so theme CSS such as `.entry-content svg { fill: red; }` will not apply to those SVGs.

Inherited properties still cross the boundary, so setting `color` on an ancestor and using `currentColor` inside the SVG works, as do CSS custom properties. SVGs that do not contain a `<style>` element are rendered exactly as before and can be styled normally.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Inherited properties still cross the boundary, so setting `color` on an ancestor and using `currentColor` inside the SVG works, as do CSS custom properties. SVGs that do not contain a `<style>` element are rendered exactly as before and can be styled normally.
Inherited properties still cross the boundary, so setting `color` on an ancestor and using `currentColor` inside the SVG works, as do CSS custom properties. SVGs that do not contain a `<style>` element are rendered without the shadow root and can be styled by theme stylesheets.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 2e9fc51

@dkotter dkotter mentioned this pull request Sep 2, 2026
18 tasks done

@peterwilsoncc peterwilsoncc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me and is testing well.

Testing notes:

  • styled SVGs uploaded on the develop branch are rendered in shadow DOM on this branch
  • styles within SVGs can no longer effect the site styles (I was testing with body {background: grey;}
  • SVGs without style elements render without a shadow DOM on this branch
  • CSS vars and current color are applied to the SVG

@github-project-automation github-project-automation Bot moved this from Code Review to QA Testing in Open Source Practice Sep 2, 2026
@peterwilsoncc
peterwilsoncc merged commit 8c4f029 into develop Sep 4, 2026
15 checks passed
@peterwilsoncc
peterwilsoncc deleted the update/shadow-dom branch September 4, 2026 00:29
@github-project-automation github-project-automation Bot moved this from QA Testing to Done in Open Source Practice Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:code-review This requires code review.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants