import { describe, expect, it } from "vitest"; import { applyCockpitCorsHeaders, formatCockpitListenUrl, hasValidBearerToken, isLoopbackHost, isPrivilegedCockpitRoute, isPublicCockpitRoute, resolveAllowedCorsOrigin, resolveCockpitConfig, resolveCockpitCorsConfig, } from "./server-config.js"; describe("resolveCockpitConfig", () => { it("binds to loopback by default without requiring a token", () => { expect(resolveCockpitConfig({})).toEqual({ port: 3088, host: "127.0.0.1", token: null, requiresAuth: false, }); }); it("honours explicit loopback hosts", () => { for (const host of ["127.0.0.1", "localhost", "::1"]) { expect(resolveCockpitConfig({ EVOO_COCKPIT_HOST: host })).toMatchObject({ host, token: null, requiresAuth: false, }); } }); it("requires a shared token before binding beyond loopback", () => { expect(() => resolveCockpitConfig({ EVOO_COCKPIT_HOST: "0.0.0.0" })).toThrow( /EVOO_COCKPIT_TOKEN/, ); }); it("allows a non-loopback bind when a token is configured", () => { expect( resolveCockpitConfig({ EVOO_COCKPIT_HOST: "0.0.0.0", EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", }), ).toEqual({ port: 3088, host: "0.0.0.0", token: "cockpit-review-token-000000000000", requiresAuth: true, }); }); it("rejects invalid ports", () => { expect(() => resolveCockpitConfig({ PORT: "0" })).toThrow(/PORT/); expect(() => resolveCockpitConfig({ PORT: "abc" })).toThrow(/PORT/); }); }); describe("cockpit auth helpers", () => { const token = "cockpit-review-token-000000000000"; it("treats health routes as public when auth is enabled", () => { expect(isPublicCockpitRoute("/health")).toBe(true); expect(isPublicCockpitRoute("/api/health")).toBe(true); expect(isPublicCockpitRoute("/api/status")).toBe(false); }); it("accepts only bearer tokens with constant-time comparison", () => { expect(hasValidBearerToken(`Bearer ${token}`, token)).toBe(true); expect(hasValidBearerToken(`Bearer ${`${token}x`}`, token)).toBe(false); expect(hasValidBearerToken(token, token)).toBe(false); expect(hasValidBearerToken(undefined, token)).toBe(false); }); it("formats listen URLs with bracketed IPv6 hosts", () => { expect(formatCockpitListenUrl("127.0.0.1", 3088)).toBe("http://127.0.0.1:3088"); expect(formatCockpitListenUrl("::1", 3088)).toBe("http://[::1]:3088"); }); }); describe("isLoopbackHost", () => { it("recognises loopback hosts only", () => { expect(isLoopbackHost("127.0.0.1")).toBe(true); expect(isLoopbackHost("localhost")).toBe(true); expect(isLoopbackHost("::1")).toBe(true); expect(isLoopbackHost("0.0.0.0")).toBe(false); expect(isLoopbackHost("192.168.1.10")).toBe(false); }); }); describe("isPrivilegedCockpitRoute", () => { it("marks API routes except health as privileged", () => { expect(isPrivilegedCockpitRoute("/health")).toBe(false); expect(isPrivilegedCockpitRoute("/api/health")).toBe(false); expect(isPrivilegedCockpitRoute("/api/status")).toBe(true); expect(isPrivilegedCockpitRoute("/api/trading-bot/mode")).toBe(true); expect(isPrivilegedCockpitRoute("/index.html")).toBe(false); }); }); describe("resolveCockpitCorsConfig", () => { const loopbackCockpit = resolveCockpitConfig({ EVOO_COCKPIT_HOST: "127.0.0.1" }); const publicCockpit = resolveCockpitConfig({ EVOO_COCKPIT_HOST: "0.0.0.0", EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", }); it("defaults to local web origins on loopback binds", () => { const cors = resolveCockpitCorsConfig({}, loopbackCockpit); expect(cors.loopbackBind).toBe(true); expect(cors.usesDefaultLoopbackOrigins).toBe(true); expect(cors.allowedOrigins).toContain("http://127.0.0.1:5173"); }); it("requires an explicit allowlist off loopback", () => { const cors = resolveCockpitCorsConfig({}, publicCockpit); expect(cors.allowedOrigins).toEqual([]); expect(cors.usesDefaultLoopbackOrigins).toBe(false); }); it("honours EVOO_COCKPIT_CORS_ORIGIN when set", () => { const cors = resolveCockpitCorsConfig( { EVOO_COCKPIT_CORS_ORIGIN: "https://ops.example.com, https://ops.example.com" }, publicCockpit, ); expect(cors.allowedOrigins).toEqual(["https://ops.example.com"]); }); }); describe("resolveAllowedCorsOrigin", () => { const loopbackCors = resolveCockpitCorsConfig({}, resolveCockpitConfig({})); const publicCors = resolveCockpitCorsConfig( { EVOO_COCKPIT_CORS_ORIGIN: "https://ops.example.com" }, resolveCockpitConfig({ EVOO_COCKPIT_HOST: "0.0.0.0", EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", }), ); const blockedPublicCors = resolveCockpitCorsConfig( {}, resolveCockpitConfig({ EVOO_COCKPIT_HOST: "0.0.0.0", EVOO_COCKPIT_TOKEN: "cockpit-review-token-000000000000", }), ); it("never returns a wildcard and omits headers without an Origin", () => { expect(resolveAllowedCorsOrigin(undefined, loopbackCors, "/api/status")).toBeNull(); expect(resolveAllowedCorsOrigin("", loopbackCors, "/api/status")).toBeNull(); }); it("reflects allowlisted origins on privileged routes", () => { expect(resolveAllowedCorsOrigin("http://127.0.0.1:5173", loopbackCors, "/api/status")).toBe( "http://127.0.0.1:5173", ); expect(resolveAllowedCorsOrigin("https://evil.example", loopbackCors, "/api/status")).toBe( false, ); }); it("blocks cross-origin privileged access off loopback without an allowlist", () => { expect( resolveAllowedCorsOrigin("https://ops.example.com", blockedPublicCors, "/api/status"), ).toBe(false); }); it("allows configured origins off loopback", () => { expect( resolveAllowedCorsOrigin("https://ops.example.com", publicCors, "/api/trading-bot"), ).toBe("https://ops.example.com"); }); }); describe("applyCockpitCorsHeaders", () => { it("sets reflected origins and never sets *", () => { const cors = resolveCockpitCorsConfig({}, resolveCockpitConfig({})); const headers = {}; const res = { setHeader: (key, value) => { headers[key] = value; }, }; const allowed = applyCockpitCorsHeaders( { headers: { origin: "http://127.0.0.1:5173" } }, res, "/api/status", cors, ); expect(allowed).toBe("http://127.0.0.1:5173"); expect(headers["Access-Control-Allow-Origin"]).toBe("http://127.0.0.1:5173"); expect(headers["Access-Control-Allow-Origin"]).not.toBe("*"); expect(headers["Access-Control-Allow-Headers"]).toContain("Authorization"); }); it("omits Access-Control-Allow-Origin for disallowed cross-origin requests", () => { const cors = resolveCockpitCorsConfig({}, resolveCockpitConfig({})); const headers = {}; const res = { setHeader: (key, value) => { headers[key] = value; }, }; const allowed = applyCockpitCorsHeaders( { headers: { origin: "https://evil.example" } }, res, "/api/status", cors, ); expect(allowed).toBe(false); expect(headers["Access-Control-Allow-Origin"]).toBeUndefined(); }); });