import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { validateCorsOrigin, parseCorsAllowlist, } from "~/server/lib/cors-validation"; describe("validateCorsOrigin", () => { describe("accepted origins", () => { it("accepts valid HTTPS origins", () => { expect(validateCorsOrigin("https://app.kordant.com")).toBe(true); expect(validateCorsOrigin("https://admin.kordant.com")).toBe(true); expect(validateCorsOrigin("https://localhost:3000")).toBe(true); }); it("accepts valid HTTP origins", () => { expect(validateCorsOrigin("http://localhost:3000")).toBe(true); expect(validateCorsOrigin("http://localhost:3001")).toBe(true); expect(validateCorsOrigin("http://127.0.0.1:8080")).toBe(true); }); it("accepts origins with ports", () => { expect(validateCorsOrigin("https://app.kordant.com:8443")).toBe(true); expect(validateCorsOrigin("http://localhost:5173")).toBe(true); }); it("accepts origins with paths", () => { expect(validateCorsOrigin("https://app.kordant.com/api")).toBe(true); }); }); describe("rejected origins", () => { it("rejects wildcard", () => { expect(validateCorsOrigin("*")).toBe(false); }); it("rejects missing scheme", () => { expect(validateCorsOrigin("evil.com")).toBe(false); expect(validateCorsOrigin("localhost")).toBe(false); expect(validateCorsOrigin("app.kordant.com")).toBe(false); }); it("rejects non-HTTP schemes", () => { expect(validateCorsOrigin("ftp://evil.com")).toBe(false); expect(validateCorsOrigin("file:///etc/passwd")).toBe(false); expect(validateCorsOrigin("javascript:alert(1)")).toBe(false); expect(validateCorsOrigin("data:text/html,test")).toBe(false); }); it("rejects empty and whitespace strings", () => { expect(validateCorsOrigin("")).toBe(false); expect(validateCorsOrigin(" ")).toBe(false); expect(validateCorsOrigin("\t")).toBe(false); }); it("rejects malformed URLs", () => { expect(validateCorsOrigin("not a url")).toBe(false); expect(validateCorsOrigin("://missing-protocol")).toBe(false); }); }); }); describe("parseCorsAllowlist", () => { beforeEach(() => { vi.spyOn(console, "warn").mockImplementation(() => {}); }); afterEach(() => { vi.restoreAllMocks(); }); it("returns empty array for undefined/null/empty input", () => { expect(parseCorsAllowlist(undefined)).toEqual([]); expect(parseCorsAllowlist(null)).toEqual([]); expect(parseCorsAllowlist("")).toEqual([]); expect(parseCorsAllowlist(" ")).toEqual([]); }); it("parses and validates a comma-separated list of origins", () => { const result = parseCorsAllowlist( "https://app.kordant.com,https://admin.kordant.com", ); expect(result).toEqual([ "https://app.kordant.com", "https://admin.kordant.com", ]); }); it("filters out invalid origins and warns", () => { const result = parseCorsAllowlist( "https://app.kordant.com,evil.com,*,ftp://bad.com", ); expect(result).toEqual(["https://app.kordant.com"]); expect(console.warn).toHaveBeenCalledTimes(3); }); it("rejects http://localhost:9999 when not in the allowlist", () => { // This origin is not in the configured list const result = parseCorsAllowlist("https://app.kordant.com"); expect(result).not.toContain("http://localhost:9999"); expect(result).toEqual(["https://app.kordant.com"]); }); it("handles whitespace around commas", () => { const result = parseCorsAllowlist( " https://app.kordant.com , https://admin.kordant.com ", ); expect(result).toEqual([ "https://app.kordant.com", "https://admin.kordant.com", ]); }); });