import { expect, test } from "@playwright/test"; import { buildSurfaceFrameDocument } from "../packages/views/plugins/surface-document"; test.describe("plugin surface browser boundary", () => { test("a normal hosted document relays exactly one guest-created port", async ({ page }) => { await page.route("https://plugin-content.example.test/**", async (route) => { await route.fulfill({ contentType: "text/html", headers: { "Content-Security-Policy": "default-src 'none'; script-src 'unsafe-inline'" }, body: ``, }); }); const wrapper = buildSurfaceFrameDocument({ url: "https://plugin-content.example.test/plugin-surfaces/opaque", bridgeToken: "proof", }); await page.setContent(``); await page.locator("#host").evaluate((frame, srcdoc) => { (frame as HTMLIFrameElement).srcdoc = srcdoc as string; }, wrapper); await expect.poll(() => page.evaluate(() => (window as unknown as { bridgeCount: number }).bridgeCount)).toBe(1); }); test("a first-line external navigation sends no request and cannot retain a bridge", async ({ page }) => { let attackerRequests = 0; await page.route("https://plugin-content.example.test/**", async (route) => { await route.fulfill({ contentType: "text/html", headers: { "Content-Security-Policy": "default-src 'none'; script-src 'unsafe-inline'", }, body: ``, }); }); await page.route("https://attacker.example.test/**", async (route) => { attackerRequests += 1; await route.fulfill({ body: "should never load" }); }); const wrapper = buildSurfaceFrameDocument({ url: "https://plugin-content.example.test/plugin-surfaces/opaque", bridgeToken: "proof", }); await page.setContent(``); await page.locator("#host").evaluate((frame, srcdoc) => { (frame as HTMLIFrameElement).srcdoc = srcdoc as string; }, wrapper); await expect.poll(() => page.evaluate(() => (window as unknown as { blockedCount: number }).blockedCount)).toBe(1); await page.evaluate(() => (window as unknown as { bridgePort?: MessagePort }).bridgePort?.postMessage("ping")); await page.waitForTimeout(100); expect(await page.evaluate(() => (window as unknown as { bridgeReply: boolean }).bridgeReply)).toBe(false); expect(attackerRequests).toBe(0); }); test("two script-capable plugin frames cannot inspect each other", async ({ page }) => { await page.setContent(` `); await expect.poll(() => page.evaluate(() => (window as unknown as { results: string[] }).results)).toContain("SecurityError"); expect(await page.evaluate(() => (window as unknown as { results: string[] }).results)).not.toContain("leaked"); }); });