// author: simon-pierre boucher import { describe, expect, it } from "vitest"; import { validateEgress, type Resolver } from "./ssrf.js"; const publicResolver: Resolver = async () => ["93.184.216.34"]; const privateResolver: Resolver = async () => ["10.0.0.5"]; describe("validateEgress", () => { it("accepts a public https URL on an allowed port", async () => { const r = await validateEgress("https://example.com/x", publicResolver); expect(r.ok).toBe(true); if (r.ok) { expect(r.value.port).toBe(443); expect(r.value.addresses).toEqual(["93.184.216.34"]); } }); it("rejects non-http schemes", async () => { const r = await validateEgress("ftp://example.com", publicResolver); expect(r.ok).toBe(false); if (!r.ok) expect(r.error.code).toBe("ERR_INVALID_URL"); }); it("rejects disallowed ports before resolving", async () => { const r = await validateEgress("http://example.com:22/", publicResolver); expect(r.ok).toBe(false); if (!r.ok) expect(r.error.code).toBe("ERR_SSRF_BLOCKED"); }); it("rejects a literal private host without DNS", async () => { const r = await validateEgress("http://127.0.0.1/", publicResolver); expect(r.ok).toBe(false); if (!r.ok) expect(r.error.code).toBe("ERR_SSRF_BLOCKED"); }); it("rejects when DNS resolves to a private address (rebinding defense)", async () => { const r = await validateEgress("https://evil.example/", privateResolver); expect(r.ok).toBe(false); if (!r.ok) expect(r.error.code).toBe("ERR_SSRF_BLOCKED"); }); it("rejects if any resolved address is private", async () => { const mixed: Resolver = async () => ["93.184.216.34", "10.0.0.5"]; const r = await validateEgress("https://example.com/", mixed); expect(r.ok).toBe(false); }); it("allows port 8080 and 8443", async () => { for (const p of [8080, 8443]) { const r = await validateEgress(`http://example.com:${p}/`, publicResolver); expect(r.ok).toBe(true); } }); });