Skip to content
File

Blob: tests/worker/lib/spa-shell.workers.test.ts

typescript46 lines
1import { env } from "cloudflare:workers";
2import { afterEach, describe, expect, it } from "vitest";
3import { createPublicClientConfigScript, renderSpaShell, resetShellHintsCacheForTests } from "@/worker/lib/spa-shell";
4 
5afterEach(() => {
6 resetShellHintsCacheForTests();
7});
8 
9describe("createPublicClientConfigScript", () => {
10 it("escapes unsafe html sequences inside the injected JSON", () => {
11 const script = createPublicClientConfigScript(
12 {
13 SENTRY_DSN: "https://public@example.ingest.sentry.io/1?x=</script><script>alert(1)</script>",
14 } as Pick<Env, "SENTRY_DSN">,
15 "nonce-test",
16 );
17 
18 expect(script).toContain("window.__BLAND_PUBLIC_CONFIG__=");
19 expect(script).toContain('window.__BLAND_CSP_NONCE__="nonce-test";');
20 expect(script).toContain("\\u003c/script>\\u003cscript>alert(1)\\u003c/script>");
21 expect(script).not.toContain("</script><script>");
22 });
23});
24 
25describe("renderSpaShell", () => {
26 it("injects bootstrap config, applies one nonce to all scripts, and appends shell hint link headers", async () => {
27 const response = await renderSpaShell(
28 new Request("https://bland.tools/acme/page-1"),
29 env as Pick<Env, "ASSETS" | "SENTRY_DSN">,
30 );
31 
32 const responseHtml = await response.text();
33 expect(responseHtml).toContain("window.__BLAND_PUBLIC_CONFIG__=");
34 expect(responseHtml).toContain("window.__BLAND_CSP_NONCE__=");
35 
36 const nonceMatches = [...responseHtml.matchAll(/nonce="([^"]+)"/g)].map((match) => match[1]);
37 expect(new Set(nonceMatches).size).toBe(1);
38 expect(nonceMatches.length).toBeGreaterThanOrEqual(3);
39 expect(response.headers.get("Content-Security-Policy")).toContain(`'nonce-${nonceMatches[0]}'`);
40 expect(response.headers.get("Link")).toContain("</app-assets/index-test.js>; rel=preload; as=script; crossorigin");
41 expect(response.headers.get("Link")).toContain("</app-assets/index-test.css>; rel=preload; as=style; crossorigin");
42 expect(response.headers.get("X-Frame-Options")).toBe("DENY");
43 expect(response.headers.get("Referrer-Policy")).toBe("strict-origin-when-cross-origin");
44 });
45});