102 lines
4.2 KiB
TypeScript
102 lines
4.2 KiB
TypeScript
import { NextRequest } from "next/server";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { proxy } from "./proxy";
|
|
|
|
function response(headers: Record<string, string> = {}) {
|
|
return proxy(new NextRequest("http://localhost:3000/setup", { headers }));
|
|
}
|
|
|
|
describe("deployment-aware content security policy", () => {
|
|
beforeEach(() => {
|
|
vi.stubEnv("NODE_ENV", "production");
|
|
vi.stubEnv("MAGENT_APPLICATION_URL", undefined);
|
|
vi.stubEnv("MAGENT_RUNTIME_MANAGED", undefined);
|
|
});
|
|
|
|
afterEach(() => vi.unstubAllEnvs());
|
|
|
|
it("keeps HTTPS upgrades enabled by default", () => {
|
|
expect(response().headers.get("Content-Security-Policy")).toContain("upgrade-insecure-requests");
|
|
});
|
|
|
|
it("keeps HTTPS upgrades for an explicitly configured HTTPS site", () => {
|
|
vi.stubEnv("MAGENT_APPLICATION_URL", "https://magent.example.com");
|
|
expect(response().headers.get("Content-Security-Policy")).toContain("upgrade-insecure-requests");
|
|
});
|
|
|
|
it("allows an unclaimed managed install to load its wizard on HTTP", () => {
|
|
vi.stubEnv("MAGENT_RUNTIME_MANAGED", "1");
|
|
expect(response().headers.get("Content-Security-Policy")).not.toContain("upgrade-insecure-requests");
|
|
});
|
|
|
|
it.each(["0", "true", "false", ""])('does not activate managed setup for flag "%s"', (flag) => {
|
|
vi.stubEnv("MAGENT_RUNTIME_MANAGED", flag);
|
|
expect(response().headers.get("Content-Security-Policy")).toContain("upgrade-insecure-requests");
|
|
});
|
|
|
|
it.each(["https://magent.example.com", "not-a-url", "http://magent.lan/path"])(
|
|
"does not let managed mode bypass configured HTTPS or invalid origins: %s",
|
|
(origin) => {
|
|
vi.stubEnv("MAGENT_RUNTIME_MANAGED", "1");
|
|
vi.stubEnv("MAGENT_APPLICATION_URL", origin);
|
|
expect(response().headers.get("Content-Security-Policy")).toContain("upgrade-insecure-requests");
|
|
},
|
|
);
|
|
|
|
it("does not accept a caller-provided managed-mode header", () => {
|
|
expect(response({ MAGENT_RUNTIME_MANAGED: "1" }).headers.get("Content-Security-Policy")).toContain(
|
|
"upgrade-insecure-requests",
|
|
);
|
|
});
|
|
|
|
it.each(["http://192.0.2.10:3000", "http://magent.lan:3000/", "http://[fd00::10]:3000"])(
|
|
"supports the operator's explicit HTTP origin %s without upgrading its assets",
|
|
(origin) => {
|
|
vi.stubEnv("MAGENT_APPLICATION_URL", origin);
|
|
expect(response().headers.get("Content-Security-Policy")).not.toContain("upgrade-insecure-requests");
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
"",
|
|
"not-a-url",
|
|
"http:/magent.lan",
|
|
"//magent.lan",
|
|
"ftp://magent.lan",
|
|
"http://user:password@magent.lan",
|
|
"http://magent.lan/path",
|
|
"http://magent.lan?query=1",
|
|
"http://magent.lan#fragment",
|
|
"http://magent.lan\\path",
|
|
"http://magent.\tlan",
|
|
])("does not relax HTTPS upgrades for invalid or non-origin configuration %j", (origin) => {
|
|
vi.stubEnv("MAGENT_APPLICATION_URL", origin);
|
|
expect(response().headers.get("Content-Security-Policy")).toContain("upgrade-insecure-requests");
|
|
});
|
|
|
|
it("does not trust caller-controlled host or forwarding headers to disable upgrades", () => {
|
|
const policy = response({
|
|
Host: "magent.lan:3000",
|
|
"X-Forwarded-Host": "magent.lan:3000",
|
|
"X-Forwarded-Proto": "http",
|
|
Forwarded: "host=magent.lan:3000;proto=http",
|
|
}).headers.get("Content-Security-Policy");
|
|
expect(policy).toContain("upgrade-insecure-requests");
|
|
});
|
|
|
|
it("preserves nonce propagation and strict production script rules on HTTP", () => {
|
|
vi.stubEnv("MAGENT_APPLICATION_URL", "http://magent.lan:3000");
|
|
const first = response();
|
|
const policy = first.headers.get("Content-Security-Policy");
|
|
const nonce = first.headers.get("x-middleware-request-x-nonce");
|
|
expect(nonce).toBeTruthy();
|
|
expect(policy).toContain(`script-src 'self' 'nonce-${nonce}' 'strict-dynamic'`);
|
|
expect(policy).not.toContain("'unsafe-eval'");
|
|
expect(policy).toContain("frame-ancestors 'none'");
|
|
expect(policy).toContain("form-action 'self'");
|
|
expect(policy).toContain("connect-src 'self'");
|
|
expect(first.headers.get("x-middleware-request-content-security-policy")).toBe(policy);
|
|
expect(response().headers.get("x-middleware-request-x-nonce")).not.toBe(nonce);
|
|
});
|
|
});
|