-
Notifications
You must be signed in to change notification settings - Fork 296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Declarative Shadow DOM] How should we build the "opt-in" for fragment parsing of declarative Shadow DOM? #912
Comments
Anyway I will not further disturb you on this discussion. I hope you come to a sane conclusion. |
Maybe I misunderstand, but don't all of these opt-in suggestions require JavaScript, which would be contrary to "compelling use cases such enabling scoped styles without requiring Javascript"? |
This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc
This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc
This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc
This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Commit-Queue: Mason Freed <masonfreed@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Cr-Commit-Position: refs/heads/master@{#824591}
For both of these questions, the answer is that here, we're talking about the fragment parser. I.e. for things like |
This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Commit-Queue: Mason Freed <masonfreed@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Cr-Commit-Position: refs/heads/master@{#824591}
So I've modified the Chromium implementation to disable declarative Shadow DOM, unless opted-in, for all six of the parser entry points listed in the OP here. I also added WPT tests for each of these. In essence, the API is mostly just an const div = document.createElement('div');
const html = '<div><template shadowroot=open></template></div>';
div.innerHTML = html; // No Shadow DOM here!
document.allowDeclarativeShadowDom = true;
div.innerHTML = html; // Declarative Shadow DOM enabled! The implementation seems pretty clean, and I'm guessing (hoping?) that the spec changes should be similarly straightforward. Mostly, this is a new flag on the |
Should be
In general I don't think we're adding new sandboxing flags; instead document policy seems to be the right replacement? That also gives you the right header-based opt-in for the main document, I believe. @clelland would know more. |
Also when at all possible it'd be better to avoid global toggles in favor of method parameters. So e.g. instead of |
@domenic thanks for the feedback!
Somehow I always get that wrong, even when I try to get it right. Thanks - will change.
Ok, I wasn't sure what the situation was there. It doesn't seem like Document Policy is quite available everywhere (anywhere?). That's why I went with the more supported sandbox attribute, which does seem to have a natural linkage over to Document Policy when available, no? This issue just needs an imperative way to enable/disable declarative Shadow DOM for use by sanitizers, and doesn't need a header-based opt-in. (That's #913, still up for debate.) I'll read up on Document Policy more, but is there a way to implement this that would be supported today, without requiring other implementers to also implement Document Policy? I suppose they could just not support DSD for
Ok, I avoided this because I didn't want to invent a new API ( |
In general shared mutable global state is problematic and makes code harder to reason about. E.g., you can set it, then call a framework method which isn't prepared to deal with the consequences. (Not security consequences; just, it was written before DSD, and it doesn't know how to deal with some nodes getting swept into a shadow tree.) You can think of it similarly to how we try to avoid global variables in C++ that control the behavior of methods, and instead we pass arguments to methods. |
Yep, totally fair. Ok, I’ll make that change. How do you feel about using properties on |
Re: Document Policy, which is available in Chromium as of M86, and is currently used for the opt-out for scroll-to-text. In general, I'd much rather see features like this defined in terms of Document Policy than Sandbox; sandboxing has some very un-ergonomic properties. Adding this to sandbox means that it is automatically disabled by default in every sandboxed frame; it also means that a developer wanting to disable just this feature would have to use <iframe sandbox> and then specifically re-enable every other sandbox feature. (And keep that list up to date if other sandbox flags are added). One question to ask right now is whether frames should be allowed to set this flag independently of their parent frames; sandbox does not allow that, ever -- if disabled in one frame, it is necessarily disabled in all of its subframes. Document policy allows a frame to disable a feature, without necessarily imposing the same restrictions on embedded content. (The spec does include a way to impose restrictions like that on subframes, like sandbox does, but that mode is not shipped yet.) |
@clelland thanks for this context - that's very helpful. I can check out the scroll-to-text spec to use as a template for this change.
Ok, this sounds promising. I'll take a look at Document Policy for this feature. Side note, it actually sounds like the sandbox feature itself runs afoul of #913 in some way. But I'll leave that as a pure side note for now. |
I don't think we should call it "shadow DOM" in APIs. Using "DOM" in API names is a legacy construct. |
Ok, makes sense to me. Anything to avoid an uppercased acronym is a win for me. |
It'd be better to use an options argument for DOMParser IMO. For XHR, I can't see anywhere nice to slot it in, and everything uses properties anyway. Although maybe you'd want it to be |
Yeah, let's not add this feature to XHR. |
…arsing to be opt-in, a=testonly Automatic update from web-platform-tests Change declarative Shadow DOM fragment parsing to be opt-in This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Commit-Queue: Mason Freed <masonfreed@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Cr-Commit-Position: refs/heads/master@{#824591} -- wpt-commits: b13461b9a46b46eb1b092a58bde2b10418e7a73d wpt-pr: 26398
…arsing to be opt-in, a=testonly Automatic update from web-platform-tests Change declarative Shadow DOM fragment parsing to be opt-in This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Commit-Queue: Mason Freed <masonfreed@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Cr-Commit-Position: refs/heads/master@{#824591} -- wpt-commits: b13461b9a46b46eb1b092a58bde2b10418e7a73d wpt-pr: 26398
The issue thread [1] has had more discussion, after the initial draft of declarative Shadow DOM opt-in landed [2]. This CL implements those bits of feedback. In particular: - There is no public allowDeclarativeShadowDom state available on Document or DocumentFragment. - All APIs use call parameters to avoid state, with the exception of DOMParser. - innerHTML no longer supports Declarative Shadow DOM. - A new setInnerHTML() function allows opt-in access to DSD. - Several of the more obscure APIs do not have an opt-in for declarative Shadow DOM, such as XHR, createContextualFragment, and document.write. - The sandbox flag has been removed from iframes completely. The new plan is to use DocumentPolicy to enable declarative Shadow DOM for iframes. For now, iframes do not support declarative Shadow DOM. - allowDeclarativeShadowDOM has become allowShadowRoot. [1] whatwg/dom#912 (comment) [2] https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Bug: 1042130 Change-Id: I3a2becf2a113cc8647b29077d2efea1c990d4547
The issue thread [1] has had more discussion, after the initial draft of declarative Shadow DOM opt-in landed [2]. This CL implements those bits of feedback. In particular: - There is no public allowDeclarativeShadowDom state available on Document or DocumentFragment. - All APIs use call parameters to avoid state, with the exception of DOMParser. - innerHTML no longer supports Declarative Shadow DOM. - A new setInnerHTML() function allows opt-in access to DSD. - Several of the more obscure APIs do not have an opt-in for declarative Shadow DOM, such as XHR, createContextualFragment, and document.write. - The sandbox flag has been removed from iframes completely. The new plan is to use DocumentPolicy to enable declarative Shadow DOM for iframes. For now, iframes always support declarative Shadow DOM. - 'allowDeclarativeShadowDOM' has become 'allowShadowRoot'. [1] whatwg/dom#912 (comment) [2] https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Bug: 1042130 Change-Id: I3a2becf2a113cc8647b29077d2efea1c990d4547
The issue thread [1] has had more discussion, after the initial draft of declarative Shadow DOM opt-in landed [2]. This CL implements those bits of feedback. In particular: - There is no public allowDeclarativeShadowDom state available on Document or DocumentFragment. - All APIs use call parameters to avoid state, with the exception of DOMParser. - innerHTML no longer supports Declarative Shadow DOM. - A new setInnerHTML() function allows opt-in access to DSD. - Several of the more obscure APIs do not have an opt-in for declarative Shadow DOM, such as XHR, createContextualFragment, and document.write. - The sandbox flag has been removed from iframes completely. The new plan is to use DocumentPolicy to enable declarative Shadow DOM for iframes. For now, iframes always support declarative Shadow DOM. - 'allowDeclarativeShadowDOM' has become 'allowShadowRoot'. [1] whatwg/dom#912 (comment) [2] https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Bug: 1042130 Change-Id: I3a2becf2a113cc8647b29077d2efea1c990d4547 Cq-Do-Not-Cancel-Tryjobs: true
I'm thinking that given the issues here, it might be best to just not add a Element.prototype.setInnerHTML = function(content) {
const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`,
'text/html', {includeShadowRoots: true});
this.replaceChildren(...fragment.body.firstChild.childNodes);
}; I'm going to move forward with this approach for now, but please feel free to chime in with comments. |
The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c
The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#830501}
The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#830501}
The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#830501}
Automatic update from web-platform-tests Remove setInnerHTML completely The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#830501} -- wpt-commits: 60d87a5d19f5cf033f96b26f9597b32ad2732792 wpt-pr: 26631
Automatic update from web-platform-tests Remove setInnerHTML completely The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#830501} -- wpt-commits: 60d87a5d19f5cf033f96b26f9597b32ad2732792 wpt-pr: 26631
Automatic update from web-platform-tests Remove setInnerHTML completely The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreedchromium.org> Commit-Queue: Kouhei Ueno <kouheichromium.org> Reviewed-by: Kouhei Ueno <kouheichromium.org> Cr-Commit-Position: refs/heads/master{#830501} -- wpt-commits: 60d87a5d19f5cf033f96b26f9597b32ad2732792 wpt-pr: 26631 UltraBlame original commit: 1046b32a6c9d31383f782745ed88a9d1fd4bc71e
Automatic update from web-platform-tests Remove setInnerHTML completely The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreedchromium.org> Commit-Queue: Kouhei Ueno <kouheichromium.org> Reviewed-by: Kouhei Ueno <kouheichromium.org> Cr-Commit-Position: refs/heads/master{#830501} -- wpt-commits: 60d87a5d19f5cf033f96b26f9597b32ad2732792 wpt-pr: 26631 UltraBlame original commit: 1046b32a6c9d31383f782745ed88a9d1fd4bc71e
Automatic update from web-platform-tests Remove setInnerHTML completely The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreedchromium.org> Commit-Queue: Kouhei Ueno <kouheichromium.org> Reviewed-by: Kouhei Ueno <kouheichromium.org> Cr-Commit-Position: refs/heads/master{#830501} -- wpt-commits: 60d87a5d19f5cf033f96b26f9597b32ad2732792 wpt-pr: 26631 UltraBlame original commit: 1046b32a6c9d31383f782745ed88a9d1fd4bc71e
This concern could be discharged by adding a method |
Automatic update from web-platform-tests Remove setInnerHTML completely The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#830501} -- wpt-commits: 60d87a5d19f5cf033f96b26f9597b32ad2732792 wpt-pr: 26631
This CL implements most of the suggestions from [1], which effectively block declarative Shadow DOM from being used by any fragment parser entry point, unless an explicit opt-in is toggled. The opt-ins include: - DOMParser.allowDeclarativeShadowDom = true; - HTMLTemplateElement.allowDeclarativeShadowDom = true; - XMLHttpRequest.allowDeclarativeShadowDom = true; - DocumentFragment.allowDeclarativeShadowDom = true; - Document.allowDeclarativeShadowDom = true; // For innerHTML - A new <iframe> sandbox flag: allow-declarative-shadow-dom This mitigates the potential client-side XSS sanitizer bypass detailed in the explainer and at [1]. Assuming these changes are functional, and mitigate the issue, this new behavior will be folded into the spec PRs at [2] and [3]. But given the security implications of the existing code, I'd like to get this landed first. [1] whatwg/dom#912 (comment) [2] whatwg/html#5465 [3] whatwg/dom#892 Bug: 1042130 Change-Id: I088f28f63078a0d26e354a4442494c0132b47ffc Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Commit-Queue: Mason Freed <masonfreed@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Cr-Commit-Position: refs/heads/master@{#824591} GitOrigin-RevId: 83080e44d00b0a9321b54bcca7bbe0eb2f9d5e43
The issue thread [1] has had more discussion, after the initial draft of declarative Shadow DOM opt-in landed [2]. This CL implements those bits of feedback. In particular: - There is no public allowDeclarativeShadowDom state available on Document or DocumentFragment. - All APIs use call parameters to avoid state, with the exception of DOMParser. - innerHTML no longer supports Declarative Shadow DOM. - A new setInnerHTML() function allows opt-in access to DSD. - Several of the more obscure APIs do not have an opt-in for declarative Shadow DOM, such as XHR, createContextualFragment, and document.write. - The sandbox flag has been removed from iframes completely. The new plan is to use DocumentPolicy to enable declarative Shadow DOM for iframes. For now, iframes always support declarative Shadow DOM. - 'allowDeclarativeShadowDOM' has become 'allowShadowRoot'. [1] whatwg/dom#912 (comment) [2] https://chromium-review.googlesource.com/c/chromium/src/+/2513525 Bug: 1042130 Change-Id: I3a2becf2a113cc8647b29077d2efea1c990d4547 Cq-Do-Not-Cancel-Tryjobs: true Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2530222 Auto-Submit: Mason Freed <masonfreed@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Commit-Queue: Mason Freed <masonfreed@chromium.org> Cr-Commit-Position: refs/heads/master@{#826643} GitOrigin-RevId: d5bf4401a47f04cbfbcca5d7fb4be5f99fa41e19
Per more feedback [1] on the issue thread, it would be preferred to remove the state from DOMParser and add it instead to the parseFromString method. [1] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: I316c008b80436bdacf7d0548e4ccd891a5c15411 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2533848 Commit-Queue: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Auto-Submit: Mason Freed <masonfreed@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#826693} GitOrigin-RevId: f5504c3fcaea2608e33fa834a243905c869621dc
The conversation [1] about the recent changes to setInnerHTML have led to the conclusion [2] that perhaps we shouldn't add a new XSS sink method at all. That would "fix" the declarative Shadow DOM problem, but would create a new sink that all security libraries would need to know about and handle. Seems like not a good trade. In the meantime, a polyfill can stand in for setInnerHTML: Element.prototype.setInnerHTML = function(content) { const fragment = (new DOMParser()).parseFromString(`<pre>${content}</pre>`, 'text/html', {includeShadowRoots: true}); this.replaceChildren(...fragment.body.firstChild.childNodes); }; [1] whatwg/dom#912 [2] whatwg/dom#912 (comment) Bug: 1042130 Change-Id: Ibaf15a3edf86be9a720225dea2ba2741f2882b8c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2555589 Auto-Submit: Mason Freed <masonfreed@chromium.org> Commit-Queue: Kouhei Ueno <kouhei@chromium.org> Reviewed-by: Kouhei Ueno <kouhei@chromium.org> Cr-Commit-Position: refs/heads/master@{#830501} GitOrigin-RevId: af023967d0ff024b3008265de09a68d5041f919c
In #831, there is a rough consensus that, to protect against client-side XSS, the entry points to the fragment parser need to be guarded by a declarative Shadow DOM "opt-in". If some script doesn't enable this opt-in, then any declarative Shadow DOM (
<template shadowroot>
) in the markup being parsed will be treated as a "normal"<template>
that happens to have an attribute called "shadowroot".How should this opt-in work? There are multiple entry points to the parser, some of which are "good" for sanitization, in that they parse into an isolated document fragment that doesn't execute script:
DOMParser.parseFromString()
<template>.innerHTML
XMLHttpRequest
with an HTML MIME type and a data URL<iframe>
using srcdoc, src, document open/write, etc.createHTMLDocument
and then usecreateContextualFragment()
createHTMLDocument
and then use body.innerHTMLAre there others?
Of the list above, the most straightforward for most would seem to be just adding an opt-in attribute to the relevant object:
1. DOMParser:
let parser = new DOMParser(); parser.allowDeclarativeShadowDOM = true;
3. XMLHttpRequest:
let client = new XMLHttpRequest(); client.allowDeclarativeShadowDOM = true;
4. HTMLIframeElement:
let iframe = document.createElement('iframe'); iframe.allowDeclarativeShadowDOM = true;
For createContextualFragment, perhaps just add an options bag? Seems cleaner than an attribute on Range():
5. createContextualFragment:
createContextualFragment(fragment, {allowDeclarativeShadowDOM: true});
The most difficult, it would seem, is the
innerHTML
attribute. Perhaps an attribute on the owning document?2 and 6. innerHTML:
element.ownerDocument.allowDeclarativeShadowDOM = true; element.innerHTML = html;
The text was updated successfully, but these errors were encountered: