[threads] Validate message origin in ThreadWindow, add "ancestor" mode#968
Merged
Conversation
`ThreadWindow`'s `targetOrigin` previously only set the origin for outgoing `postMessage()` calls; inbound messages were accepted from any origin as long as they came from the expected window. `windowToThreadTarget` now also rejects messages whose `origin` does not match `targetOrigin`, so a concrete origin authenticates both directions. The default stays `'*'` (unchanged behaviour). Adds a `targetOrigin: 'ancestor'` mode that pins to the origin that framed the current window (from `location.ancestorOrigins`), falling back to the origin of the first received message on platforms without that API (e.g. Firefox) and buffering outgoing messages until it is known. This is the common "I'm in an iframe and trust whoever embedded me, but don't know their origin" case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
ThreadWindow'stargetOriginonly controlled the outgoingpostMessage()origin. Inbound messages were accepted from any origin as long asevent.sourcematched the expected window — so a concretetargetOrigindid not actually authenticate the other side, and there was no built-in way to safely talk to "whoever framed me" without hard-coding their origin.This came up wiring a cross-origin live-preview thread between an admin editor and the iframe it embeds: to get an authenticated channel I had to hand-roll a
ThreadMessageTargetthat (a) validatesevent.originand (b) discovers + pins the framing origin. That logic belongs in the library.What changed
windowToThreadTarget(and thereforeThreadWindow/.iframe/.parent/.openervia thetargetOriginoption) now treatstargetOriginas a two-way restriction:'*'(default) — post to, and accept messages from, any origin. Unchanged, so this is non-breaking.'https://embed.my-app.com') — post only to it, and drop inbound messages whoseevent.origindoesn't match.'ancestor'(new) — pin to the origin that framed the current window, read fromlocation.ancestorOrigins. When that API is unavailable (notably Firefox), the origin of the first received message is trusted instead, and outbound messages are buffered until the origin is known.A
frame-ancestorsCSP directive remains the right complement for restricting who may frame you; this adds the per-message origin check on top.Interface
ThreadWindowOptions.targetOrigin(packages/threads/source/threads/ThreadWindow.ts)Tests
packages/threads/source/tests/window.test.tscovers the default permissive behaviour, concrete-origin pinning + inbound rejection,event.sourcefiltering,'ancestor'viaancestorOrigins, and the buffer-then-pin-to-first-sender fallback. All green.Changeset
@quilted/threads: minor (additive; default behaviour unchanged).🤖 Generated with Claude Code