Skip to content

feat(desktop): render HTML file previews as a sandboxed page - #25

Merged
jmfcamp merged 1 commit into
mainfrom
cursor/html-preview-rendered-3c99
Aug 21, 2026
Merged

jmfcamp merged 1 commit into
mainfrom
cursor/html-preview-rendered-3c99

Conversation

@jmfcamp

@jmfcamp jmfcamp commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up to #23 (Slack-style file preview) and #24 (10 MiB cap). HTML attachments previously opened as inert syntax-highlighted source. They now open as a laid-out page by default, like Slack file preview.

Hard security constraints are unchanged and not weakened:

  • Bytes still come only from the authenticated Tauri fetch_media_bytes path. The iframe src is never the relay /media/ URL, and Download is still a button that invokes download_file.
  • The main Buzz webview never navigates to the blob.
  • HTML renders in an <iframe> with srcDoc from the fetched bytes and an empty sandbox. The plan type locks allowScripts: false, iframeSrc: null, and sandbox: "" — there is no token list a caller can upgrade to include scripts or same-origin.
  • No Eruda, no injected debugger, no allow-scripts. Static HTML/CSS may display; JS must not run.
  • Download and the 10 MiB FILE_PREVIEW_MAX_BYTES cap stay.
  • Images/video lightbox, relay serve_inline / MIME deny lists, and Hula branding (productName, com.huladesk.buzz, hulabuzz://) are untouched.

An optional Source tab still shows the highlighted markup. Default tab is the rendered page.

Related issue

Follow-up to #23 / #24. None found for rendered HTML specifically.

Testing

Locally verified:

  • pnpm typecheck (desktop) passed
  • node --import ./test-loader.mjs --experimental-strip-types --test src/shared/ui/filePreview.test.mjs — 28 passed (HTML kind is html with a rendered/sandboxed srcdoc plan; 3.3 MiB HTML is under the cap; iframe src cannot be /media/; sandbox cannot include allow-scripts)
  • pnpm exec playwright test tests/e2e/file-attachment.spec.ts --project=smoke — 19 passed, including:
    • HTML preview is a sandboxed iframe (empty sandbox, no /media/ src)
    • <h1>Live?</h1> renders inside the frame, not in the parent webview
    • window.__XSS__ stays unset in the parent and frame
    • Source tab still shows the script as text
    • 10 MiB cap still refuses oversized files; 3 MiB markdown still previews
cd desktop
pnpm typecheck
node --import ./test-loader.mjs --experimental-strip-types --test \
  src/shared/ui/filePreview.test.mjs
pnpm exec playwright test tests/e2e/file-attachment.spec.ts --project=smoke
Open in Web Open in Cursor 

HTML attachments now open as a laid-out page in the Slack-style preview
dialog. Bytes still come only from fetch_media_bytes; the iframe is
srcdoc with an empty sandbox so scripts and same-origin cannot be
enabled. Download and the 10 MiB cap are unchanged.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: jmfcamp <jmfcamp@users.noreply.github.com>
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.

2 participants