feat(media): render shared HTML files in a sandboxed lightbox preview - #850
Merged
Conversation
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
marked this pull request as ready for review
July 29, 2026 22:02
Uh oh!
There was an error while loading. Please reload this page.
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>
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 freeto 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.
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
.htmlmedia 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:
(Screenshot is in the Dispatch media panel for agent
agt_5b4e6d9ee746—html-lightbox-preview-2026-07-29-21-43-34-619.png; drop it here before marking ready.)Agent
Intent.
.mdmedia got a rendered preview but.htmlfell throughmedia-lightbox.tsx's type dispatch toTextFileViewer(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—.htmlitems render in<iframe sandbox="allow-scripts allow-popups">. Deliberately noallow-same-origin: the document gets an opaque origin and cannot reach the Dispatch API, cookies, or storage (verified live:contentDocumentis 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 sendX-Content-Type-Options: nosniff, and every non-passive content type (anything butimage/*,video/mp4,application/pdf) getsContent-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
.xmlis 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.allow-popups-to-escape-sandboxintentionally absent).localStorageaccess inside shared HTML throws — pages must guard it. Accepted tradeoff.sandboxattr) 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✓media-routes.test.ts(HTML CSP, evil.xml CSP, passive-type exemption, nosniff)renders shared HTML in a sandboxed lightbox preview🤖 Generated with Claude Code