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");
});
});