From fc28f8486b7cac1ecdfa8d60e506456c47eb6b92 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Mon, 25 May 2026 07:38:17 +0000 Subject: [PATCH] Studio: let HTML preview links open a new tab Codex review on b21717120c flagged that the previous commit's ```` combined with sandbox flags that omitted ``allow-popups`` silently dropped every link click in the preview -- before that change links at least navigated inside the frame; after it they were no-ops. Add ``allow-popups`` and ``allow-popups-to-escape-sandbox`` so a target=_blank link opens a regular browser tab (rather than an opaque-origin sandboxed one that most docs sites would render broken). The popup is then equivalent to the user clicking the same URL anywhere else. allow-same-origin and allow-top-navigation are still NOT granted, so the iframe still cannot read parent.document or navigate the host page. Test updated to assert the new sandbox tokens explicitly. --- .../__tests__/html-svg-renderer.test.tsx | 10 ++++++-- .../assistant-ui/html-svg-renderer.tsx | 23 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx b/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx index 4de79e7b7c..6cbb635d1b 100644 --- a/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx +++ b/studio/frontend/src/components/assistant-ui/__tests__/html-svg-renderer.test.tsx @@ -27,10 +27,16 @@ describe("HtmlSvgRenderer", () => { expect(iframe.tagName).toBe("IFRAME"); // SECURITY: allow-scripts + allow-modals leave script / alert / // confirm operative IF the inherited host CSP ever permits inline; - // NEVER allow-same-origin or allow-top-navigation -- those would let + // allow-popups + allow-popups-to-escape-sandbox so a `` link does not silently no-op; NEVER + // allow-same-origin or allow-top-navigation -- those would let // the preview read parent.document and exfiltrate session data. const sandbox = iframe.getAttribute("sandbox") ?? ""; - expect(sandbox.split(/\s+/)).toContain("allow-scripts"); + const sandboxTokens = sandbox.split(/\s+/); + expect(sandboxTokens).toContain("allow-scripts"); + expect(sandboxTokens).toContain("allow-modals"); + expect(sandboxTokens).toContain("allow-popups"); + expect(sandboxTokens).toContain("allow-popups-to-escape-sandbox"); expect(sandbox).not.toContain("allow-same-origin"); expect(sandbox).not.toContain("allow-top-navigation"); // srcdoc carries the assistant HTML plus a defense-in-depth meta CSP diff --git a/studio/frontend/src/components/assistant-ui/html-svg-renderer.tsx b/studio/frontend/src/components/assistant-ui/html-svg-renderer.tsx index 8664e0469f..612f258916 100644 --- a/studio/frontend/src/components/assistant-ui/html-svg-renderer.tsx +++ b/studio/frontend/src/components/assistant-ui/html-svg-renderer.tsx @@ -346,12 +346,23 @@ function HtmlPreview({ data-testid="html-svg-renderer-iframe" title="HTML preview" srcDoc={srcDoc} - // SECURITY: allow-scripts (in case the host CSP ever loosens to - // permit inline) + allow-modals (so alert/confirm do not silently - // no-op when scripts do run). We do NOT grant allow-same-origin or - // allow-top-navigation, so the iframe cannot read parent.document, - // navigate the host page, or escape its origin. - sandbox="allow-scripts allow-modals" + // SECURITY: + // allow-scripts -- in case the host CSP ever loosens + // to permit inline (today it does not) + // allow-modals -- so alert/confirm/prompt do not no-op + // when scripts do run + // allow-popups + -- so the ```` we + // allow-popups-to- set above can open a new tab without + // escape-sandbox being silently dropped; escape- + // sandbox makes the popup a regular + // browser tab rather than an opaque- + // origin sandboxed one (the popup is + // equivalent to the user clicking the + // same URL in any other web app) + // We do NOT grant allow-same-origin or allow-top-navigation, + // so the iframe cannot read parent.document, navigate the host + // page, or escape its origin. + sandbox="allow-scripts allow-modals allow-popups allow-popups-to-escape-sandbox" style={{ width: "100%", height: iframeHeight,