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 6efb1c300f..1b64114575 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
@@ -35,7 +35,7 @@ describe("HtmlSvgRenderer", () => {
expect(iframe.getAttribute("srcdoc") ?? iframe.srcdoc).toContain("hello");
});
- it("renders an SVG preview and strips malicious
@@ -43,15 +43,22 @@ describe("HtmlSvgRenderer", () => {
render();
- const previewHost = screen.getByTestId("html-svg-renderer-svg-preview");
- // The circle survives, but every script tag and on* handler must be
- // stripped by DOMPurify before the SVG is inserted into the DOM.
- const circle = previewHost.querySelector("circle");
- expect(circle).not.toBeNull();
- expect(circle?.getAttribute("onclick")).toBeNull();
- expect(previewHost.querySelector("script")).toBeNull();
- expect(previewHost.innerHTML.toLowerCase()).not.toContain("onclick");
- expect(previewHost.innerHTML.toLowerCase()).not.toContain("alert");
+ const iframe = screen.getByTestId(
+ "html-svg-renderer-svg-preview",
+ ) as HTMLIFrameElement;
+ expect(iframe.tagName).toBe("IFRAME");
+ // SECURITY: SVG iframe must NEVER allow scripts or same-origin -- those
+ // would re-introduce the host-page-leak / XSS regressions the iframe
+ // boundary is here to prevent.
+ expect(iframe.getAttribute("sandbox")).toBe("");
+ const srcdoc = (iframe.getAttribute("srcdoc") ?? iframe.srcdoc).toLowerCase();
+ expect(srcdoc).toContain(" {
@@ -164,4 +171,28 @@ describe("sanitizeSvgSource", () => {
expect(clean.startsWith(" blocks so SVG CSS cannot retarget host selectors", () => {
+ const svg = ``;
+ const clean = sanitizeSvgSource(svg).toLowerCase();
+ expect(clean).not.toContain("",
+ safeSvg,
+ ].join("");
+}
+
function SvgPreview({ source }: { source: string }) {
- const safe = useMemo(() => sanitizeSvgSource(source), [source]);
+ const srcDoc = useMemo(() => buildSvgSrcDoc(sanitizeSvgSource(source)), [
+ source,
+ ]);
return (
-
);
}