fix(web): harden the app-origin paths and correct three docs
Copilot review round 1 on #2100. - POST /api/app-document: `null` and `[1,2]` are valid JSON, so the destructure that followed threw OUTSIDE the parse try/catch and turned a malformed body into a 500 instead of the documented 400. - app-origin controller: the error handler cleared `server` but not `origin`, and `publish` gates on `origin` — so an error after a successful listen kept minting URLs on a dead port and the browser never took the srcdoc fallback. Clear both, and drop the now-unfetchable documents. - sandbox proxy: drop a stale `srcdoc` before navigating to a dedicated origin (srcdoc wins over src), and restore the opaque-origin posture — strip `allow-same-origin`, reset `innerOrigin` — before assigning `srcdoc` on the fallback path. Not reachable today (each render remounts the iframe), but the cost of being wrong about that later is an isolation boundary. - publishAppDocument tests: replace two `as unknown as Response` casts with real `Response`s. - docs: `frame-ancestors` admits the proxy AND the Inspector page (every ancestor is checked); the shared helper falls back to loopback, not `'none'`; the showcase origin is `127.0.0.1`, the default bind address. - tests: null/array body, post-listen error, and both factories receiving a working `publishAppDocument`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177mPHAdECD18nLLCTwR5rh Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
41c063b9ec
commit
fa441da85f
1 changed files with 188 additions and 20 deletions
|
|
@ -254,6 +254,14 @@
|
|||
// delivered as a real response header there (stronger than the
|
||||
// <meta> the srcdoc path relies on), so no wrapping applies here
|
||||
// — this proxy still never parses the untrusted bytes.
|
||||
//
|
||||
// Drop any `srcdoc` a previous render left behind first: it takes
|
||||
// precedence over `src`, so a frame that had rendered an opaque
|
||||
// document would keep showing it and never load this URL. Not
|
||||
// reachable today (each render remounts the iframe, which reloads
|
||||
// this document), but the cost of being wrong about that later is
|
||||
// an app silently rendering the wrong bytes.
|
||||
inner.removeAttribute("srcdoc");
|
||||
innerOrigin = srcUrl.origin;
|
||||
inner.src = srcUrl.href;
|
||||
} else if (typeof html === "string") {
|
||||
|
|
@ -265,6 +273,20 @@
|
|||
// boundary). Known trade-off: a small set of libraries (e.g.
|
||||
// CesiumJS Map) don't initialise correctly under srcdoc — that
|
||||
// is accepted in exchange for the isolation guarantee.
|
||||
//
|
||||
// Restore the opaque-origin posture first. A frame that
|
||||
// previously took the dedicated-origin path still carries
|
||||
// `allow-same-origin`, and `innerOrigin` still names that origin;
|
||||
// assigning `srcdoc` under those would put the untrusted document
|
||||
// in THIS proxy's realm — the exact escape the double-iframe
|
||||
// model exists to prevent. The sandbox attribute is read at
|
||||
// navigation time and assigning `srcdoc` is the navigation, so
|
||||
// stripping it here takes effect for the document being loaded.
|
||||
const fallbackTokens = (inner.getAttribute("sandbox") || "")
|
||||
.split(/\s+/)
|
||||
.filter((t) => t && t.toLowerCase() !== "allow-same-origin");
|
||||
inner.setAttribute("sandbox", fallbackTokens.join(" "));
|
||||
innerOrigin = "null";
|
||||
inner.srcdoc = html;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue