import { describe, it, expect } from "vitest"; import { isPrivateIp, isPrivateIpv6, isBlockedHostname, checkEndpointUrlSync, privateEndpointsAllowed } from "@/lib/endpoints/ssrf"; describe("SSRF guard — IP ranges", () => { it("flags private, loopback, link-local and special IPv4 ranges", () => { for (const ip of ["127.0.0.1", "10.0.0.5", "172.16.0.1", "172.31.255.254", "192.168.1.10", "169.254.169.254", "0.0.0.0", "100.64.1.1", "224.0.0.1", "255.255.255.255", "198.18.0.1"]) expect(isPrivateIp(ip), ip).toBe(true); for (const ip of ["8.8.8.8", "1.1.1.1", "172.32.0.1", "172.15.0.1", "51.161.112.61", "100.63.255.255"]) expect(isPrivateIp(ip), ip).toBe(false); }); it("flags IPv6 loopback, unique-local, link-local and mapped IPv4", () => { for (const ip of ["::1", "::", "fc00::1", "fd12:3456::1", "fe80::1", "fe80::1%en0", "::ffff:127.0.0.1", "::ffff:10.0.0.1", "::ffff:c0a8:0101", "64:ff9b::7f00:1", "ff02::1"]) expect(isPrivateIpv6(ip), ip).toBe(true); for (const ip of ["2607:5300:22a:4500::1", "2001:4860:4860::8888", "::ffff:8.8.8.8"]) expect(isPrivateIpv6(ip), ip).toBe(false); expect(isPrivateIp("not-an-ip")).toBe(false); }); it("blocks local-only hostnames", () => { for (const h of ["localhost", "LOCALHOST", "foo.localhost", "mymac.local", "db.internal", "metadata.google.internal", "ollama", "router.lan"]) expect(isBlockedHostname(h), h).toBe(true); for (const h of ["api.openai.com", "my-tunnel.trycloudflare.com", "example.com."]) expect(isBlockedHostname(h), h).toBe(false); }); }); describe("SSRF guard — URL check", () => { it("rejects private hosts unless explicitly allowed", () => { const r = checkEndpointUrlSync("http://localhost:11434/v1", { allowPrivate: false }); expect(r.ok).toBe(false); expect(r.reason).toBe("PRIVATE_HOST"); expect(r.isPrivate).toBe(true); const ip = checkEndpointUrlSync("http://192.168.2.10:8080/v1", { allowPrivate: false }); expect(ip.ok).toBe(false); expect(ip.reason).toBe("PRIVATE_IP"); const v6 = checkEndpointUrlSync("http://[::1]:1234/v1", { allowPrivate: false }); expect(v6.ok).toBe(false); const allowed = checkEndpointUrlSync("http://localhost:11434/v1", { allowPrivate: true }); expect(allowed.ok).toBe(true); expect(allowed.isPrivate).toBe(true); }); it("accepts public https and rejects other protocols / embedded credentials", () => { expect(checkEndpointUrlSync("https://api.together.xyz/v1", { allowPrivate: false })).toMatchObject({ ok: true, isPrivate: false }); expect(checkEndpointUrlSync("ftp://example.com/v1", { allowPrivate: false }).reason).toBe("BAD_PROTOCOL"); expect(checkEndpointUrlSync("file:///etc/passwd", { allowPrivate: false }).reason).toBe("BAD_PROTOCOL"); expect(checkEndpointUrlSync("http://user:pw@example.com/v1", { allowPrivate: false }).reason).toBe("CREDENTIALS_IN_URL"); expect(checkEndpointUrlSync("not a url", { allowPrivate: false }).reason).toBe("INVALID_URL"); }); it("reads ALLOW_PRIVATE_ENDPOINTS", () => { const env = (v?: string) => ({ ...(v === undefined ? {} : { ALLOW_PRIVATE_ENDPOINTS: v }) }) as unknown as NodeJS.ProcessEnv; expect(privateEndpointsAllowed(env())).toBe(false); expect(privateEndpointsAllowed(env("1"))).toBe(true); expect(privateEndpointsAllowed(env("true"))).toBe(true); expect(privateEndpointsAllowed(env("0"))).toBe(false); }); });