Skip to content

TrustedTypes: Indirect sinks#42638

Merged
sideshowbarker merged 4 commits intomdn:mainfrom
hamishwillee:tt_indirect_sinks
Feb 9, 2026
Merged

TrustedTypes: Indirect sinks#42638
sideshowbarker merged 4 commits intomdn:mainfrom
hamishwillee:tt_indirect_sinks

Conversation

@hamishwillee
Copy link
Collaborator

@hamishwillee hamishwillee commented Jan 6, 2026

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

@hamishwillee hamishwillee requested a review from a team as a code owner January 6, 2026 02:18
@hamishwillee hamishwillee requested review from sideshowbarker and removed request for a team January 6, 2026 02:18
@github-actions github-actions bot added Content:WebAPI Web API docs size/s [PR only] 6-50 LoC changed labels Jan 6, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 6, 2026

Preview URLs (1 page)

External URLs (1)

URL: /en-US/docs/Web/API/Trusted_Types_API
Title: Trusted Types API

(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.
Copy link
Contributor

Choose a reason for hiding this comment

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

note: I don't know if "direct" or "indirect" injection sinks are names used in the spec, but ok.

Copy link
Collaborator Author

@hamishwillee hamishwillee Jan 9, 2026

Choose a reason for hiding this comment

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

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

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!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@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.

Copy link
Contributor

Choose a reason for hiding this comment

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

@hamishwillee

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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

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.

@sideshowbarker sideshowbarker requested a review from a team as a code owner January 20, 2026 06:26
@sideshowbarker sideshowbarker requested review from dipikabh and removed request for a team January 20, 2026 06:26
@sideshowbarker sideshowbarker removed the request for review from dipikabh January 20, 2026 06:49
@sideshowbarker
Copy link
Member

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 octo.nvim and some gh cli commands — and using this PR as a sandbox/test-case, and some of it seems to indirectly cause some other actions other than the thing I’m actually trying to do (which is basically just, to review the diff and comment on it)

@hamishwillee hamishwillee mentioned this pull request Jan 27, 2026
8 tasks
@github-actions github-actions bot added size/m [PR only] 51-500 LoC changed and removed size/s [PR only] 6-50 LoC changed labels Jan 30, 2026
@github-actions github-actions bot added size/s [PR only] 6-50 LoC changed and removed size/m [PR only] 51-500 LoC changed labels Feb 1, 2026
@sideshowbarker sideshowbarker merged commit 065e33b into mdn:main Feb 9, 2026
7 checks passed
@hamishwillee hamishwillee deleted the tt_indirect_sinks branch February 9, 2026 05:46
pranjal2004838 pushed a commit to pranjal2004838/content that referenced this pull request Feb 25, 2026
* TrustedTypes: Indirect sinks

* Apply suggestions from code review

* Update following feedback

* Criteria for an operation to make source untrusted
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Content:WebAPI Web API docs size/s [PR only] 6-50 LoC changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants