Bots can now show a concept as a visual: create_graphic (with update_graphic, read_graphic and list_graphics, on every run, no computer needed) stores one HTML document in bot_graphics, per user like the conversation, and the chat renders its [graphic:ID] ref as an inline card sized to fit, with a large view and a page of its own (no rail). Graphic HTML is treated as hostile: it is only served at /graphics/<id> with a CSP sandbox allowing scripts under an origin of its own and no network, loading only the vendored Chart.js and Mermaid from /graphics-lib/. The document carries the app's theme vars and scrollbars, reports its height to the thread, and a frame that navigates away is closed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
30 lines
1.3 KiB
TypeScript
30 lines
1.3 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import graphicHeaders from "./graphic-headers";
|
|
|
|
describe("graphicHeaders", () => {
|
|
const csp = graphicHeaders({ origin: "https://app.test" })["Content-Security-Policy"]!;
|
|
const directives = csp.split("; ");
|
|
|
|
test("sandboxes scripts away from the app's origin", () => {
|
|
expect(directives).toContain("sandbox allow-scripts");
|
|
expect(csp).not.toContain("allow-same-origin");
|
|
expect(csp).not.toContain("allow-top-navigation");
|
|
expect(csp).not.toContain("allow-popups");
|
|
});
|
|
|
|
test("allows no network beyond the app's graphic libraries", () => {
|
|
expect(directives).toContain("default-src 'none'");
|
|
expect(directives).toContain("connect-src 'none'");
|
|
expect(directives).toContain("script-src 'unsafe-inline' https://app.test/graphics-lib/");
|
|
expect(directives).toContain("img-src data: blob:");
|
|
});
|
|
|
|
test("is only framed by the app and never cached", () => {
|
|
const headers = graphicHeaders({ origin: "https://app.test" });
|
|
|
|
expect(directives).toContain("frame-ancestors 'self'");
|
|
expect(headers["Cache-Control"]).toBe("private, no-store");
|
|
expect(headers["X-Content-Type-Options"]).toBe("nosniff");
|
|
});
|
|
});
|