3 Commits
Author SHA1 Message Date
dokterdok bd78ca4afa fix(webapp): preserve SandboxedFrame guest parity
Dispatch each guest load on the outer iframe as its own marked load
event, so onLoad gets a real React event and ancestors see guest loads.
The shell's own load is taken in the document's capture phase, before
React or any consumer handler, and the document is sent from there, also
while an Activity or Suspense boundary hides the frame; a frame mounted
hidden starts when first shown. Inside a shadow root, where a load never
reaches the document, the frame's own handler takes the shell load. Guest loads are routed by the shell's
window, so they arrive while hidden too.

Relay nested transferables both ways with an iterative walk, retry plain
once, and close ports when a message cannot be delivered, including one
that arrives before the guest frame exists.

Remount the shell when sanitized sandbox tokens change.

Mark a document sent only once it reached a shell, and send it again
only when it changed.
2026-10-28 07:25:35 -07:00
Teknium 764fa746ae fix(webapp): keep scripted SandboxedFrame data: HTML running under the page CSP
The Webapp page policy authorizes only the app's own inline scripts, and a
data: document inherits it, so a plugin's documented SandboxedFrame
`data:text/html` embed stopped running its script in browser tabs.

In the browser host only, SandboxedFrame now decodes HTML data: URLs and
hands the markup to a separately served guest shell
(/assets/webapp-guest.html), the same parent-messaged shell file previews
use, served with its own policy and the SDK sandbox allowlist as its
ceiling. The frame's sandbox attribute narrows that to the caller's
sanitized tokens (plus allow-scripts for the shell); the nested guest
document gets the caller's tokens exactly, so a script-less embed stays
inert and allow-same-origin can never apply. The shell relays messages
both ways, so `ref.current.contentWindow.postMessage` and
`event.source === ref.current.contentWindow` keep working, and onLoad
fires once the guest document (not the shell) has loaded. Native Desktop,
http(s) and non-HTML data: embeds keep the direct frame; the page script
policy is unchanged.

Live (built Webapp, real gateway, scratch HOME, Chromium): a SandboxedFrame
data: guest posts ready and answers a ping after onLoad on main and with
this commit; on the previous head it never ran.
2026-10-27 20:44:23 -07:00
dokterdok e0a3c60055 fix(webapp): isolate preview documents from the renderer policy
`::preview` widgets, HTML artifacts and file previews render in srcdoc
frames, which inherit the policy of the page that embeds them, so a
strict Webapp policy blocked their CDN libraries (why #144721 reverted
its CSP).

Serve a fixed, credential-free sandbox shell (/assets/webapp-preview.html)
with its own policy and send it the HTML in memory. Its inner frame keeps
the caller's script permission, so file previews stay inert. Previews
keep the reach they have today (any http(s) library, image, fetch,
worker or embed; inline script; string evaluation for D3's CSV parser):
their isolation is the opaque sandbox, not a source list. "Open in
browser" loads the same shell from its blob wrapper. Native Desktop keeps
its direct srcdoc frames.

Refs #144648 (WA-11)
2026-10-27 20:44:23 -07:00