TrustedTypes: Indirect sinks#42638
Conversation
|
Preview URLs (1 page) External URLs (1)URL:
(comment last updated: 2026-02-06 05:31:29) |
|
|
||
| ### Indirect injection sinks | ||
|
|
||
| Indirect injection sinks are those where untrusted strings are injected into the DOM via an intermediate mechanism that doesn't trigger script execution, and then evaluated. |
There was a problem hiding this comment.
note: I don't know if "direct" or "indirect" injection sinks are names used in the spec, but ok.
There was a problem hiding this comment.
I didn't find a specific section in the spec explain all this or using terms I could steal. This seems like a reasonable term/distinction to me though. Happy to accept a better one!
| Because a text node isn't always used in a context where the text might be used as an injection sink, it does enforce trusted types. | ||
|
|
||
| Instead, browsers that are enforcing trusted types will run trusted type checks when indirect injection sinks are is injected into the DOM (in the example above, when `appendChild()` is called). | ||
| This will cause an exception if the text node was constructed with a string rather than an appropriate trusted type. |
There was a problem hiding this comment.
Note sure I understand this either. document.createTextNode always creates the text node from a string. The condition is whether the script element's text was modified via a TrustedScript injection sinks (such as HTMLScriptElement.text, etc) or by other APIs (like appending a text node).
Incidentally, note that HTMLScriptElement.innerHTML is a TrustedHTML injection sinks, so enforcement would still be needed when the script is executed.
There was a problem hiding this comment.
Thanks @fred-wang .
I think you are saying that
- the condition to know that the script element might contain untrustworthy text/code is when the script element text changes through a mechanism that doesn't take a TrustedScript
- The trusted type when the script is about to be executed (and would check that trustworthiness).
Hence the exception occurs when the script is added to the DOM.
With respect to this sentence, it is based on what is probably a false assumption - that the developer can do something to prevent the exception when trusted types are enforced. I.e. I was assuming that if you passed a TrustedScript when creating the textNode then you would not get an exception.
I think you are saying that if TT is enforced, there is no way to write code like this.
If not, what am I missing!
There was a problem hiding this comment.
PS Is this discussion entirely related to the script element, or are there other cases (do I need to discuss this mor generically)
I am away on holiday now for a few weeks. Appreciate your help.
There was a problem hiding this comment.
@fred-wang Please see my responses above https://github.com/mdn/content/pull/42638/files#r2674942952
Specifically, I think you are saying that " if TT is enforced, there is no TT-safe way to write code like this.". In other words, the check is run when potentially unsafe code is about to be executed (added to the DOM) and will result in an exception if TT is enabled.
Presumably in this case even a default transform is not enough.
There was a problem hiding this comment.
I think essentially yes, but just to be sure here are the details of what's happening, so you can compare:
const script = document.createElement("script");
This creates a script element. Firefox internally uses a boolean saying that this script is trusted (initially set to "trusted"), while Chromium/WebKit internally caches the latest trusted string seen (initially set to an empty string).
script.textContent = input1;
textContent setter accepts TrustedScript of DOMString per the WebIDL. The script source is either modified immediately if input1 is a TrustedScript or goes through trusted type enforcement if input1 is a string (either rejecting or obtaining a TrustedScript from a default policy). In any case, Firefox internally sets its boolean to "trusted" while Chromium/WebKit updates the latest trusted string to what the script text is finally set.
const textNode = document.createTextNode(input2);
document.createTextNode() accepts as argument a DOMString per the WebIDL. If input2 is a TrustedScript, it will just be serialized before being passed to the method, there is no way we could distinguish it from a raw string. Anyway, at this point there is no reason to perform TrustedType enforcement, we are just creating a text node and don't know what we are going to use it for.
script.appendChild(textNode);
We append the textNode to the script, but again it is just a node with some raw string, there is no hint that "this is trusted script source". At this point, Firefox internally sets its boolean to "untrusted" while Chromium/WebKit don't modify the latest trusted string used for the script.
document.body.appendChild(script);
The script is inserted into the document and that will trigger its execution later. We need to make sure it is trusted script when that happens. Firefox internally checks the boolean associated to the script: if it's "trusted" it just goes ahead normally with script execution, if it's "untrusted" then it will perform trusted type enforcement on the script text first (either rejecting or obtaining a TrustedScript from a default policy and then serializing it again). Chromium/WebKit internally compare the script text against the latest trusted string, if these strings match they just go ahead normally with script execution, otherwise they will perform trusted type enforcement on the script text first (either rejecting or obtaining a TrustedScript from a default policy and then serializing it again).
In the spec textContent, is called a trusted type sink. Chrome/WebKit behavior is the one described in the spec, but there is a discussion to use Firefox's one instead (w3c/trusted-types#579). The main differences are that Firefox has a stricter policy (it can run script enforcement even if the source is not modified at all e.g. if input2 is an empty string in the example) but more memory efficient (Chromium/WebKit use extra memory to cache the latest trusted string).
https://wpt.fyi/results/trusted-types/script-enforcement-001.html contains tests for some DOM APIs on script elements, checking whether they will cause trusted type enforcement to be triggered before execution.
There was a problem hiding this comment.
Thanks so much @fred-wang.
I'm glad you did provide that level of detail because I was missing a couple of important details. I get it now. This has been updated and should now be correct.
Feel free to check again.
562bccd to
218a504
Compare
|
Y’all can safely ignore any notifications you received today about activity on this PR. I’ve been testing out some tooling for doing PR reviews — specifically |
218a504 to
2d8ce74
Compare
97136e6 to
daecff0
Compare
daecff0 to
15db67d
Compare
* TrustedTypes: Indirect sinks * Apply suggestions from code review * Update following feedback * Criteria for an operation to make source untrusted
This expands on the concept of Indirect injection sinks with a real example. Note that I haven't tried to list all the alternatives, just explained that TT does deal with these cases.
@fred-wang If you have bandwidth, would appreciate your sanity check. This fall out of our discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1928932#c22
Related docs work in #37518 and #41507