You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Should sanitizer be allowed to remove attributes that create a declarative shadow root? #12539
However, those attributes are handled at parse time, and the sanitizer doesn't recognize them as "attributes". This means that in the current spec, removeAttributes: ["shadowrootmode"] doesn't prevent declarative shadow roots, and likewise removeElements: ["template"].
Is this WAI, or an oversight?
(Note, this is different from WICG/sanitizer-api#45 which was about allowing the sanitizer to "see into" the shadow root)
I think we should seriously consider switching to a streaming model where we sanitize during parsing rather than keep having these special post-processing cases.
We need streaming sanitization anyway for streamHTML and the different parser insertion points are much safer places to perform this kind of operation, e.g. we have an actual template element at that point.
I agree we should, but as far as I can tell that would not solve this problem. This happens as part of processing the "template" start tag which is not an element.
I agree we should, but as far as I can tell that would not solve this problem. This happens as part of processing the "template" start tag which is not an element.
Right, it still needs a special case, but at least there is some concept of this being a "template" at this stage so it can feel a bit more natural than reverse-engineering a ShadowRoot inside a DocumentFragment into a template with attributes (or delaying transforming the template into a ShadowRoot).
Declarative shadow roots are processed in the parser. So by the time the sanitizer gets to see this, the <template> element has already been turned into a shadow root. There is no config that would block this. The root gets recursed into and sanitized; but blocking the shadow root (e.g. by removing <template>) doesn't work.
This will require some sort of special treatment. Question is, is this exposed to the user (e.g. with an allowShadowRoot option or somesuch), or can we make it work as if it were a regular element (e.g., making removeElements: ["template"] work).
Options:
Add a shadow DOM specific flag to the config.
Modify the fragment parser, to call out to the parser before the <template> element is processed / inserted.
Call the fragment parser with a flag to not process shadow roots; then sanitize; then post-process and attach all remaining shadow roots.
Parse + sanitize as before, but fix up the result. E.g., when the sanitizer finds an element that is a shadow host, call into the sanitizer and check if <template> would have been allowed and remove the shadow root if not. Similar processing for shadow root related attributes.
Opinions voiced, in no particular order:
Option 1 changes the developer experience. Probably not a good idea.
Option 2 might pave a way for or help with streaming parsing.
Option 3 spec support (parser flag) seems to mostly already be there.
Option 4 seems error prone. Also, no spec support for some operations (like removing a shadow root).
General opinion was drifting towards option 2. 1 + 4 were disliked.
Activity
annevk commentedon Mar 31, 2026
I would consider this an oversight similar to
is="". Addressing this will require a special case unfortunately, but so be it.noamr commentedon Mar 31, 2026
I think we should seriously consider switching to a streaming model where we sanitize during parsing rather than keep having these special post-processing cases.
We need streaming sanitization anyway for
streamHTMLand the different parser insertion points are much safer places to perform this kind of operation, e.g. we have an actualtemplateelement at that point.annevk commentedon Mar 31, 2026
I agree we should, but as far as I can tell that would not solve this problem. This happens as part of processing the "template" start tag which is not an element.
noamr commentedon Mar 31, 2026
Right, it still needs a special case, but at least there is some concept of this being a "template" at this stage so it can feel a bit more natural than reverse-engineering a
ShadowRootinside aDocumentFragmentinto a template with attributes (or delaying transforming the template into aShadowRoot).otherdaniel commentedon Apr 1, 2026
Summary of discussion in the meeting:
<template>element has already been turned into a shadow root. There is no config that would block this. The root gets recursed into and sanitized; but blocking the shadow root (e.g. by removing<template>) doesn't work.allowShadowRootoption or somesuch), or can we make it work as if it were a regular element (e.g., makingremoveElements: ["template"]work).<template>element is processed / inserted.<template>would have been allowed and remove the shadow root if not. Similar processing for shadow root related attributes.