Skip to content

fix(basic-host): accept Safari same-origin event.source in sandbox relay#632

Open
ochafik wants to merge 1 commit intomainfrom
fix/basic-host-safari-source-identity
Open

fix(basic-host): accept Safari same-origin event.source in sandbox relay#632
ochafik wants to merge 1 commit intomainfrom
fix/basic-host-safari-source-identity

Conversation

@ochafik
Copy link
Copy Markdown
Contributor

@ochafik ochafik commented Apr 21, 2026

Summary

Safari/WebKit can surface inner-iframe messages at the sandbox relay with event.source === window (the sandbox's own window) rather than inner.contentWindow. The strict source-identity check at sandbox.ts:115 then drops the View's ui/initialize, and the handshake never completes — Chromium-based hosts render fine, Safari desktop / iOS WebKit / mobile-native do not.

Accept that case when event.origin === OWN_ORIGIN. The sandbox runs on its own dedicated origin, so origin is the actual security boundary; source-identity was a belt-and-braces check that turns out to be brittle on WebKit. The existing inner event.origin !== OWN_ORIGIN rejection still applies.

Diagnosis and fix by @kentcdodds — see the runtime evidence in #542 (comment).

This is separate from the #542 srcdoc construction-timing race (host attaches its listener after the View has already posted), which is being addressed via host-construction-order docs/helper instead of a deferred-target transport (see discussion on #543).

Test plan

  • npm run --workspace examples/basic-host build — compiles
  • Manual Safari verification: load basic-host in Safari, confirm an example View completes ui/initialize and renders
  • Spot-check Chromium still works (no regression in existing e2e)

Safari/WebKit can surface inner-iframe messages at the sandbox relay
with event.source === window (the sandbox's own window) rather than
inner.contentWindow. The strict source-identity check then drops the
View's ui/initialize and the handshake never completes.

Accept that case when event.origin === OWN_ORIGIN — the sandbox runs on
its own dedicated origin, so origin is the real security boundary here.
Source-identity remains the primary check on Chromium where it works.
@ochafik
Copy link
Copy Markdown
Contributor Author

ochafik commented Apr 21, 2026

@mel-anthropic — could you sanity-check this on Safari/iOS when you get a chance? Kent's runtime evidence (linked above) shows WebKit reporting the inner iframe's event.source as window rather than inner.contentWindow in the sandbox relay, which drops ui/initialize. You've been closest to the iOS/mobile rendering path (#525/#555/#567) so a quick confirm-or-deny on a real device would be great before this lands.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 21, 2026

Open in StackBlitz

@modelcontextprotocol/ext-apps

npm i https://pkg.pr.new/@modelcontextprotocol/ext-apps@632

@modelcontextprotocol/server-basic-preact

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-preact@632

@modelcontextprotocol/server-basic-react

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-react@632

@modelcontextprotocol/server-basic-solid

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-solid@632

@modelcontextprotocol/server-basic-svelte

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-svelte@632

@modelcontextprotocol/server-basic-vanillajs

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vanillajs@632

@modelcontextprotocol/server-basic-vue

npm i https://pkg.pr.new/@modelcontextprotocol/server-basic-vue@632

@modelcontextprotocol/server-budget-allocator

npm i https://pkg.pr.new/@modelcontextprotocol/server-budget-allocator@632

@modelcontextprotocol/server-cohort-heatmap

npm i https://pkg.pr.new/@modelcontextprotocol/server-cohort-heatmap@632

@modelcontextprotocol/server-customer-segmentation

npm i https://pkg.pr.new/@modelcontextprotocol/server-customer-segmentation@632

@modelcontextprotocol/server-debug

npm i https://pkg.pr.new/@modelcontextprotocol/server-debug@632

@modelcontextprotocol/server-map

npm i https://pkg.pr.new/@modelcontextprotocol/server-map@632

@modelcontextprotocol/server-pdf

npm i https://pkg.pr.new/@modelcontextprotocol/server-pdf@632

@modelcontextprotocol/server-scenario-modeler

npm i https://pkg.pr.new/@modelcontextprotocol/server-scenario-modeler@632

@modelcontextprotocol/server-shadertoy

npm i https://pkg.pr.new/@modelcontextprotocol/server-shadertoy@632

@modelcontextprotocol/server-sheet-music

npm i https://pkg.pr.new/@modelcontextprotocol/server-sheet-music@632

@modelcontextprotocol/server-system-monitor

npm i https://pkg.pr.new/@modelcontextprotocol/server-system-monitor@632

@modelcontextprotocol/server-threejs

npm i https://pkg.pr.new/@modelcontextprotocol/server-threejs@632

@modelcontextprotocol/server-transcript

npm i https://pkg.pr.new/@modelcontextprotocol/server-transcript@632

@modelcontextprotocol/server-video-resource

npm i https://pkg.pr.new/@modelcontextprotocol/server-video-resource@632

@modelcontextprotocol/server-wiki-explorer

npm i https://pkg.pr.new/@modelcontextprotocol/server-wiki-explorer@632

commit: 2158eab

@kentcdodds
Copy link
Copy Markdown

I'm not very familiar with the implementation of this project but it is a little disappointing that this fix is in an example because that means that all implementers must also implement this fix. Is there not a library that implementers use so that this fix can be automatically applied to them?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants