SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
3.3 KB · 50 lines typescript
Raw Blame History
1import { describe, it, expect } from "vitest";2import { isPrivateIp, isPrivateIpv6, isBlockedHostname, checkEndpointUrlSync, privateEndpointsAllowed } from "@/lib/endpoints/ssrf";34describe("SSRF guard — IP ranges", () => {5  it("flags private, loopback, link-local and special IPv4 ranges", () => {6    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);7    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);8  });9  it("flags IPv6 loopback, unique-local, link-local and mapped IPv4", () => {10    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);11    for (const ip of ["2607:5300:22a:4500::1", "2001:4860:4860::8888", "::ffff:8.8.8.8"]) expect(isPrivateIpv6(ip), ip).toBe(false);12    expect(isPrivateIp("not-an-ip")).toBe(false);13  });14  it("blocks local-only hostnames", () => {15    for (const h of ["localhost", "LOCALHOST", "foo.localhost", "mymac.local", "db.internal", "metadata.google.internal", "ollama", "router.lan"]) expect(isBlockedHostname(h), h).toBe(true);16    for (const h of ["api.openai.com", "my-tunnel.trycloudflare.com", "example.com."]) expect(isBlockedHostname(h), h).toBe(false);17  });18});1920describe("SSRF guard — URL check", () => {21  it("rejects private hosts unless explicitly allowed", () => {22    const r = checkEndpointUrlSync("http://localhost:11434/v1", { allowPrivate: false });23    expect(r.ok).toBe(false);24    expect(r.reason).toBe("PRIVATE_HOST");25    expect(r.isPrivate).toBe(true);26    const ip = checkEndpointUrlSync("http://192.168.2.10:8080/v1", { allowPrivate: false });27    expect(ip.ok).toBe(false);28    expect(ip.reason).toBe("PRIVATE_IP");29    const v6 = checkEndpointUrlSync("http://[::1]:1234/v1", { allowPrivate: false });30    expect(v6.ok).toBe(false);31    const allowed = checkEndpointUrlSync("http://localhost:11434/v1", { allowPrivate: true });32    expect(allowed.ok).toBe(true);33    expect(allowed.isPrivate).toBe(true);34  });35  it("accepts public https and rejects other protocols / embedded credentials", () => {36    expect(checkEndpointUrlSync("https://api.together.xyz/v1", { allowPrivate: false })).toMatchObject({ ok: true, isPrivate: false });37    expect(checkEndpointUrlSync("ftp://example.com/v1", { allowPrivate: false }).reason).toBe("BAD_PROTOCOL");38    expect(checkEndpointUrlSync("file:///etc/passwd", { allowPrivate: false }).reason).toBe("BAD_PROTOCOL");39    expect(checkEndpointUrlSync("http://user:pw@example.com/v1", { allowPrivate: false }).reason).toBe("CREDENTIALS_IN_URL");40    expect(checkEndpointUrlSync("not a url", { allowPrivate: false }).reason).toBe("INVALID_URL");41  });42  it("reads ALLOW_PRIVATE_ENDPOINTS", () => {43    const env = (v?: string) => ({ ...(v === undefined ? {} : { ALLOW_PRIVATE_ENDPOINTS: v }) }) as unknown as NodeJS.ProcessEnv;44    expect(privateEndpointsAllowed(env())).toBe(false);45    expect(privateEndpointsAllowed(env("1"))).toBe(true);46    expect(privateEndpointsAllowed(env("true"))).toBe(true);47    expect(privateEndpointsAllowed(env("0"))).toBe(false);48  });49});50