The app frame is a srcdoc document with its own <meta> CSP - #146
Merged
Merged
Conversation
frame-backend.ts no longer navigates the sandboxed iframe to ./frame.html. It fetches the template, ./todomvc-app.css and the bundled ./frame.js as text in the visor's own realm, inlines them, and hands the result to the iframe as `srcdoc`, with a <meta http-equiv="Content-Security-Policy"> inserted as the first child of <head>: `default-src 'none'; script-src 'sha256-<hash of the inline script>'; style-src 'unsafe-inline'; img-src data:`. Why (polyvisor#142, measured in Chromium and Firefox): - A service worker never sees a sandboxed (opaque-origin) frame's navigation, so a real-URL skeleton is served raw by the host, outside the release-integrity path, with whatever headers it happens to send. It cannot be pinned. srcdoc content is pinned by value. - CSP policies compose. The meta's `default-src 'none'` makes the frame network-dead regardless of the visor's inherited policy: zero requests leave the frame, against three (fetch, sendBeacon, <img>) for the meta-less control that is today's shape. The applier's URL validation stops being the sole defense. - The dedicated sandbox origin frame.html's comment argued for is not required for confinement; it would buy only Firefox process isolation. The NOTES mechanics bullet is rewritten as the ruling. Standing constraint, recorded in frame-backend.ts: the inline script must also satisfy the visor's own header `script-src` once the visor ships one, so the hash has to be emitted there from the same frame.js bytes. Today neither consumer sends a header CSP. Gates: deno check (demo, spikes/todomvc); demo/scripts/check-invariants; spikes/todomvc e2e 5/5; a direct Chromium probe of the built spike page (iframe has srcdoc and no src, meta first in <head>, frame location about:srcdoc, in-frame fetch rejected and never reaches the server, a typed todo renders). The demo's own e2e lane needs MinIO, which the engine recipe fetches as linux-amd64 only; not runnable on this arm64 host.
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.
frame-backend.tsstops navigating the sandboxed iframe to./frame.htmland instead assembles the frame document as asrcdocstring — template + stylesheet + bundledframe.jsfetched as text in the visor's realm — with a<meta http-equiv="Content-Security-Policy">inserted first in<head>:default-src 'none'; script-src 'sha256-<hash of the inline script>'; style-src 'unsafe-inline'; img-src data:.Ruling and measurements: #142.
srcdocis pinned by value.default-src 'none'makes the frame network-dead whatever the visor's inherited policy allows. Measured zero requests out; the meta-less control (today's shape) leakedfetch,sendBeaconand<img>.frame.htmlargued for is not required for confinement. NOTES' mechanics bullet is rewritten as the ruling;frame.htmlstays as the template socheck-invariants.sh's greps keep their target.Standing constraint recorded in
frame-backend.ts: once the visor ships a header CSP, the same script hash must be emitted into itsscript-src(the inherited policy must also admit the inline script). Neither consumer sends one today.Gates:
deno check(demo, spikes/todomvc);check-invariants.sh; spikes/todomvc e2e 5/5; a direct Chromium probe of the built spike page — iframe hassrcdoc, nosrc; meta first in<head>;about:srcdoc; in-framefetchrejected and never reaches the server; a typed todo renders. The demo's own e2e lane needs MinIO, fetched linux-amd64 only — not runnable on this arm64 host.Not touched, deliberately:
spikes/todomvc/{web/frame-dioxus.html,host/dioxus-frame.ts}(same real-URL shape, slated for removal when the app instance moves into the frame) and the now-stale CORS rationale forframe.jsinspikes/todomvc/justfile/demo/e2e/run.ts(frame-dioxus.jsstill needs it).Automerge armed.