Skip to content

feat(media): render shared HTML files in a sandboxed lightbox preview - #850

Merged
selfcontained merged 1 commit into
mainfrom
agt_5b4e6d9ee746/agent-9ee746
Jul 30, 2026
Merged

feat(media): render shared HTML files in a sandboxed lightbox preview#850
selfcontained merged 1 commit into
mainfrom
agt_5b4e6d9ee746/agent-9ee746

Conversation

@lukebrevoort

@lukebrevoortlukebrevoort commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Overview

Thought it was nice since we render .md files to begin rendering HTML files as well. mostly a convenience thing since nowadays I flip between .md and .html and its nice to be able to view it without having to leave dispatch entirely!


AGENT

Human

Shared .html media used to show up in the media/pin sidebar as syntax-highlighted source with no way to see the actual page. Now the lightbox renders it, and there's a one-click escape hatch to a full browser tab.

Lightbox rendering an agent-shared HTML prototype (styles applied, inline script executed inside the sandbox), with the new Open in tab action in the header:

image

(Screenshot is in the Dispatch media panel for agent agt_5b4e6d9ee746html-lightbox-preview-2026-07-29-21-43-34-619.png; drop it here before marking ready.)

Agent

Intent..md media got a rendered preview but .html fell through media-lightbox.tsx's type dispatch to TextFileViewer (escaped source). This adds a third branch instead of widening the markdown one, plus an "open in browser" affordance, per the original request.

What changed

  • apps/web/src/components/app/media-lightbox.tsx.html items render in <iframe sandbox="allow-scripts allow-popups">. Deliberately no allow-same-origin: the document gets an opaque origin and cannot reach the Dispatch API, cookies, or storage (verified live: contentDocument is null from the parent).
  • apps/web/src/components/app/media-lightbox-actions.tsx — new "Open in tab" action (target="_blank" rel="noopener noreferrer"), HTML-only; Copy label becomes "Copy source" for HTML like markdown.
  • apps/server/src/routes/media.ts — media responses now send X-Content-Type-Options: nosniff, and every non-passive content type (anything but image/*, video/mp4, application/pdf) gets Content-Security-Policy: sandbox allow-scripts allow-popups. This is what makes "Open in tab" safe: even as a top-level document, agent-authored HTML runs in an opaque origin.

Constraints / edge cases

  • The CSP covers XML too: .xml is an allowed upload and browsers render XHTML-namespace roots (and XSLT) actively — without the header that was a same-origin script bypass. Caught by the backend-security-review persona (review Add ghost-primary, ghost-info, ghost-destructive button variants #82, item fix: pack-release SIGPIPE on Linux + remove gh-auth test #348, resolved); the exempt-list inversion keeps future renderable types safe by default.
  • Popups can't escape the sandbox (allow-popups-to-escape-sandbox intentionally absent).
  • Sandboxed pages get an opaque origin, so localStorage access inside shared HTML throws — pages must guard it. Accepted tradeoff.
  • The PDF iframe (no sandbox attr) is pre-existing and untouched; PDFs are excluded from the CSP header because Chrome blocks sandboxed PDF rendering.

Out of scope. Upload validation, media storage layout, pins panel rendering, the media-card list (HTML still shows as a text chip there — the divergence was only in the lightbox).

Validation

  • pnpm run check ✓, finalize:web
  • Server vitest: full suite green (2364 passed); 4 new tests in media-routes.test.ts (HTML CSP, evil.xml CSP, passive-type exemption, nosniff)
  • Playwright e2e: full suite green (175 passed / 12 skipped / 0 failed) including new renders shared HTML in a sandboxed lightbox preview
  • Live validation on an isolated dev stack: iframe renders with scripts executing, opaque origin confirmed, headers verified via curl

🤖 Generated with Claude Code

Shared .html media previously fell through to the syntax-highlighted
source viewer with no way to see the rendered page. The lightbox now
renders HTML in an iframe with sandbox="allow-scripts allow-popups"
(no allow-same-origin) and gains an "Open in tab" action.
Server-side, media responses get X-Content-Type-Options: nosniff, and
every non-passive type (anything but image/*, video/mp4, and pdf) is
served with CSP "sandbox allow-scripts allow-popups" so agent-authored
HTML/XML opened as a top-level document runs in an opaque origin and
cannot act same-origin against the Dispatch API.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lukebrevoort
lukebrevoort marked this pull request as ready for review July 29, 2026 22:02
@selfcontained
selfcontained merged commit 6e5bc25 into mainJul 30, 2026
1 check passed
@selfcontained
selfcontained deleted the agt_5b4e6d9ee746/agent-9ee746 branch July 30, 2026 03:41
selfcontained added a commit that referenced this pull request Jul 30, 2026
…#852)
Media & Sharing deep-dive against the consolidated shared
media-file-types module (#844): the format examples, upload accepted-type
claims, clipboard-paste wording, and inject delivery claims all verified
accurate. One gap: the sandboxed HTML lightbox preview from #850 had no
docs coverage — added a Lightbox previews section (per-type rendering,
sandbox isolation, Open in tab, copy-source behavior) and html to the
dispatch_share format examples.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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.

3 participants

@lukebrevoort@selfcontained@lukebrevoort-mytra