Skip to content

fix(liveview): white screen — frame-ancestors must name the dashboard origin - #407

Merged
jrothrock merged 1 commit into
mainfrom
fix/liveview-frame-ancestors-origin
Sep 8, 2026
Merged

jrothrock merged 1 commit into
mainfrom
fix/liveview-frame-ancestors-origin

Conversation

@jrothrock

Copy link
Copy Markdown
Collaborator

THE white-screen root cause

Every verification (the verify-live-view endpoint I just shipped, and the conductor's exhaustive manual curls in conv 145) reported the page serves 200 with real content — because they all loaded the URL directly, where frame-ancestors never applies. In the real dashboard the page is loaded in a cross-origin iframe, and that's where it breaks.

The live proxy set Content-Security-Policy: frame-ancestors 'self' on the framed app. But:

  • the iframe is served from localhost:<livePort>
  • the dashboard that frames it is 127.0.0.1:<dashPort>

Browsers treat 127.0.0.1 and localhost as different origins, so 'self' (= the iframe's own origin) did not permit the dashboard to frame it → the browser refuses the embed → white screen. (The separate-origin design is deliberate — it keeps the untrusted app off the dashboard's origin — but 'self' contradicted it.)

Fix

Name the dashboard's real origins:

frame-ancestors http://127.0.0.1:<dashPort> http://localhost:<dashPort>

Both loopback hosts (either may be how the dashboard was opened). Thread the dashboard port (server.dashPort = *port) through to hardenLiveResponse.

Security (asked during review)

This is narrower than a wildcard, not more open — only the loopback dashboard origins at the specific dashboard port may frame the app; no external site can ('self' was just the wrong single origin). The untrusted-app isolation (separate origin + iframe sandbox) is untouched.

Verified

The isolation test now asserts the CSP names the dashboard origins; live-view / credential / isolation suites are green. This is the fix that makes Live View actually render in the browser instead of a white screen.

🤖 Generated with Claude Code

… origin

THE white-screen cause. Every verification (the verify-live-view endpoint, and
the conductor's exhaustive manual curls) showed the page serves 200 with real
content — because they all loaded the URL DIRECTLY, where frame-ancestors never
applies. In the actual dashboard it's loaded in a cross-origin IFRAME, and that's
where it breaks.

The live proxy set `Content-Security-Policy: frame-ancestors 'self'` on the
framed app. But the iframe is served from localhost:<livePort> while the
dashboard that frames it is 127.0.0.1:<dashPort> — a DIFFERENT origin (browsers
treat 127.0.0.1 and localhost as distinct). So 'self' (= the iframe's own origin)
did NOT permit the dashboard to frame it → the browser refused the embed → white
screen. The separate-origin design was deliberate (keeps the untrusted app off
the dashboard origin), but 'self' contradicted it.

Name the dashboard's real origins instead:
`frame-ancestors http://127.0.0.1:<dashPort> http://localhost:<dashPort>` (both
loopback hosts, since either may be how it was opened). Thread the dashboard port
(server.dashPort = *port) through to hardenLiveResponse.

Security: this is NARROWER than a wildcard, not more open — only the loopback
dashboard origins at the specific dashboard port may frame the app; no external
site can. The untrusted-app isolation (separate origin, iframe sandbox) is
untouched.

Verified: isolation test asserts the new CSP names the dashboard origins;
live-view/cred/isolation suites green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jrothrock
jrothrock merged commit 20c2786 into main Sep 8, 2026
1 check passed
@jrothrock
jrothrock deleted the fix/liveview-frame-ancestors-origin branch September 8, 2026 22:09
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.

1 participant