Skip to content
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

Spec / implementation mismatch with document.write/writeln #510

Closed
lukewarlow opened this issue Apr 30, 2024 · 8 comments
Closed

Spec / implementation mismatch with document.write/writeln #510

lukewarlow opened this issue Apr 30, 2024 · 8 comments
Labels
Milestone

Comments

@lukewarlow
Copy link
Member

lukewarlow commented Apr 30, 2024

Chromium implementation's IDL:

image

Spec IDL:

image

These IDL differences + an implementation detail about when to run the default policy lead to numerous differences between the spec (and WebKit's current implementation) and Chromium's implementation.

This issue is to discuss the path forwards here.

cc @otherdaniel @koto @annevk @mbrodesser-Igalia

@lukewarlow lukewarlow added this to the v1 milestone Apr 30, 2024
@lukewarlow
Copy link
Member Author

lukewarlow commented Apr 30, 2024

I propose an IDL of (TrustedHTML or DOMString)... with behaviour similar to that of the Function constructor. e.g. if you have any DOMStrings it calls the default policy with the final constructed string.

This is observably the same as Chrome's implementation in that the default policy is called on the final string (whereas the current spec would call it on each string param), the difference is that you can pass multiple TrustedHTML params, this keeps it consistent with Function constructor, and also makes for a simpler implementation as there's no method variants.

Of course the alternative is we just spec Chromium's behaviour.

@annevk
Copy link
Member

annevk commented Apr 30, 2024

I guess you already have to be sure that repeated document.write() calls can be made so a single TrustedHTML or several shouldn't matter in terms of safety. And given that Function does a very similar thing per your comment (I haven't verified) I guess I agree that consistency with that makes sense.

@annevk
Copy link
Member

annevk commented Apr 30, 2024

Also, given that the idea behind supporting document.write() and such in the first place is to require legacy code to change as little as possible Chromium's approach seems a little suspect as legacy code with multiple arguments would have to refactor more.

@otherdaniel
Copy link
Member

If I recall correctly, the motivation was that we were trying to avoid any operations on "trusted" strings, either explicit or implicit, and that you shouldn't be able to construct an effectively-trusted strings out of trusted parts. E.g. if you have a trusted string that references a vairable "hello", and one that calls "world()", you still shouldn't be able to synthesize a trusted call to a different function "helloworld()" out of it (without going through the TrustedScript constructor again).

I'm honestly not sure whether the same logic would strictly apply to document.write; but avoiding implicit operations on TrustedXXXs still sounds like a good idea.

The Function constructor is just plain weird, and I think we just couldn't come up with a satisfying solution for it, given how ECMAScript specifies it. I don't think we should model anything off of it. That said, Function() at least specifies a seperator being added between the constituent parts, which makes it harder to just glue together parts into a new meaningful string.

@lukewarlow
Copy link
Member Author

@otherdaniel thanks for the explanation that makes sense. One issue I have with that reasoning is that document write would allow you to do that regardless? You could half open a script Element in one call and carry it on in a follow up right?

For something like document.write you inherently have to just assume the worst and reject anything that looks close to script execution OR assume a trusted input in which case the author doing something silly/malicious is out of scope for trusted types?

Apologies if I've missed something obvious.

@otherdaniel
Copy link
Member

@otherdaniel thanks for the explanation that makes sense. One issue I have with that reasoning is that document write would allow you to do that regardless? You could half open a script Element in one call and carry it on in a follow up right?

I think you're right: For document.write, it makes no difference whether you call document.write with N individually trusted strings, or N times with one trusted string each. (I suspect in practice, finding an exploitable gadget that you can call N times is harder, but that surely isn't a principled difference in threat level.)

@lukewarlow
Copy link
Member Author

lukewarlow commented May 2, 2024

So just to check I'm understanding correctly the top one here should be fine. Is the bottom one acceptable to Chromium? I think we should keep these methods consistent with each other.

Both would only call the default policy once on the constructed string (which matches Chromium and the Function constructor)

If this is okay I'll make a PR to the html spec with the relevant changes and update WebKit's implementation to match.

[CEReactions] undefined write((TrustedHTML or DOMString)... text);
[CEReactions] undefined writeln((TrustedHTML or DOMString)... text);

@lukewarlow
Copy link
Member Author

I've opened whatwg/html#10328 to update the spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants