源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
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> = {}): OpenworkMcpAppResource {
|
||||
return {
|
||||
serverName: "fixture",
|
||||
toolName: "render",
|
||||
resourceUri: "ui://fixture/view.html",
|
||||
html: "<!doctype html><html><head><title>Fixture</title></head><body>ok</body></html>",
|
||||
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("<title>")
|
||||
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")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user