import { describe, expect, test } from "bun:test" import { createOpenworkServerClient, normalizeMcpAppHostOrigin, OpenworkServerError, type OpenworkMcpAppResource, } from "../src/app/lib/openwork-server" import { formatMcpAppDiagnostic, safeMcpAppDiagnosticMessage } from "../src/components/chat/mcp-app-diagnostics" import { buildMcpAppCsp, gatewayMcpAppLaunch, isActionableMcpAppResolutionError, secureMcpAppHtml, } from "../src/components/chat/mcp-app-frame" function fixture(overrides: Partial = {}): OpenworkMcpAppResource { return { serverName: "fixture", toolName: "render", resourceUri: "ui://fixture/view.html", html: "Fixtureok", csp: { connectDomains: [], resourceDomains: [], frameDomains: [], baseUriDomains: [], }, prefersBorder: true, ...overrides, } } describe("MCP App iframe policy", () => { test("accepts a namespaced gateway launch reference without exposing credentials", () => { expect(gatewayMcpAppLaunch({ source: "provider", "openwork/mcpApp": { connectionId: "emc_01atlas", toolName: "open_project_atlas", resourceUri: "ui://atlas/1/index.html", arguments: { query: "migration" }, }, })).toEqual({ connectionId: "emc_01atlas", toolName: "open_project_atlas", resourceUri: "ui://atlas/1/index.html", arguments: { query: "migration" }, }) expect(gatewayMcpAppLaunch({ "openwork/mcpApp": { connectionId: "emc_01atlas", toolName: "open_project_atlas", resourceUri: "ui://atlas/1/index.html", }, })).toBeNull() }) test("accepts a same-server generated App launch without a connection reference", () => { expect(gatewayMcpAppLaunch({ "openwork/mcpApp": { toolName: "render_artifact_view", resourceUri: "ui://openwork/artifacts/atlas/views/1/index.html", arguments: { input: { query: "migration" } }, }, })).toEqual({ toolName: "render_artifact_view", resourceUri: "ui://openwork/artifacts/atlas/views/1/index.html", arguments: { input: { query: "migration" } }, }) }) test("uses the opaque message origin for packaged file hosts", () => { expect(normalizeMcpAppHostOrigin("file://")).toBe("null") expect(normalizeMcpAppHostOrigin("null")).toBe("null") expect(normalizeMcpAppHostOrigin("https://desktop.example")).toBe("https://desktop.example") const client = createOpenworkServerClient({ baseUrl: "http://localhost:61856" }) const sandbox = client.mcpAppSandbox(fixture(), "file://") expect(new URL(sandbox.url).searchParams.get("hostOrigin")).toBe("null") }) test("keeps ordinary tools silent while surfacing advertised resource failures", () => { expect(isActionableMcpAppResolutionError(new OpenworkServerError(503, "mcp_unreachable", "offline"))).toBe(false) expect(isActionableMcpAppResolutionError(new OpenworkServerError(404, "resource_read_failed", "missing"))).toBe(true) expect(isActionableMcpAppResolutionError(new Error("generic failure"))).toBe(false) }) test("formats safe, copyable handshake diagnostics", () => { const details = formatMcpAppDiagnostic({ code: "MCP_APP_INITIALIZE_TIMEOUT", causeCode: "mcp_unreachable", stage: "app-initialization", message: "The HTML document loaded, but initialization did not complete.", toolName: "artifact_render_card", resourceUri: "ui://openwork/artifacts/arv_1/views/avr_2/index.html", sandboxOrigin: "http://127.0.0.1:4321", elapsedMs: 10_025, checkpoints: ["resource-resolved+0ms", "resource-document-loaded+24ms"], sandboxDocument: { readyState: "complete", hasHtmlRoot: true, scriptCount: 1 }, }) expect(details).toContain("Code: MCP_APP_INITIALIZE_TIMEOUT") expect(details).toContain("Cause code: mcp_unreachable") expect(details).toContain("Stage: app-initialization") expect(details).toContain("Resource: ui://openwork/artifacts/arv_1/views/avr_2/index.html") expect(details).toContain("Document: readyState=complete, htmlRoot=true, scripts=1") expect(details).toContain("resource-document-loaded+24ms") }) test("redacts credentials from diagnostic messages", () => { expect(safeMcpAppDiagnosticMessage( new Error("request failed: Bearer secret-value https://example.com?access_token=also-secret"), "fallback", )).toBe("request failed: Bearer [redacted] https://example.com?access_token=[redacted]") }) test("defaults every ambient capability closed", () => { const csp = buildMcpAppCsp(fixture()) expect(csp).toContain("default-src 'none'") expect(csp).toContain("connect-src 'none'") expect(csp).toContain("frame-src 'none'") expect(csp).toContain("base-uri 'none'") expect(csp).toContain("form-action 'none'") }) test("injects the host-enforced CSP before resource markup runs", () => { const html = secureMcpAppHtml(fixture()) const policy = html.indexOf('http-equiv="Content-Security-Policy"') const title = html.indexOf("") expect(policy).toBeGreaterThan(-1) expect(policy).toBeLessThan(title) }) test("creates a valid policy-bearing head when the resource omits one", () => { const html = secureMcpAppHtml(fixture({ html: "<html><body>headless resource</body></html>" })) expect(html).toContain('<html><head><meta http-equiv="Content-Security-Policy"') expect(html.indexOf("Content-Security-Policy")).toBeLessThan(html.indexOf("<body>")) const fragment = secureMcpAppHtml(fixture({ html: "<main>fragment resource</main>" })) expect(fragment).toStartWith('<!doctype html><html><head><meta http-equiv="Content-Security-Policy"') expect(fragment).toContain("<body><main>fragment resource</main></body>") }) test("rejects executable markup before an existing document policy", () => { expect(() => secureMcpAppHtml(fixture({ html: "<script>globalThis.beforePolicy = true</script><html><head></head><body>bad</body></html>", }))).toThrow("executable markup before its HTML root") expect(() => secureMcpAppHtml(fixture({ html: "<html><script>globalThis.beforePolicy = true</script><head></head><body>bad</body></html>", }))).toThrow("markup before its policy-bearing head") }) test("allows only the server-declared origins in each directive", () => { const csp = buildMcpAppCsp(fixture({ csp: { connectDomains: ["https://api.example.com"], resourceDomains: ["https://static.example.com"], frameDomains: ["https://embed.example.com"], baseUriDomains: [], }, })) expect(csp).toContain("connect-src https://api.example.com") expect(csp).toContain("script-src 'unsafe-inline' https://static.example.com") expect(csp).toContain("frame-src https://embed.example.com") }) })