import { describe, it, expect } from "vitest" import { validateWebhookUrl, type DnsResolver } from "./validate-webhook-url" const publicResolver: DnsResolver = { resolve4: async () => ["93.184.216.34"], resolve6: async () => [], } const privateResolver: DnsResolver = { resolve4: async () => ["192.168.1.1"], resolve6: async () => [], } const failingResolver: DnsResolver = { resolve4: async () => { throw new Error("ENOTFOUND") }, resolve6: async () => { throw new Error("ENOTFOUND") }, } describe("validateWebhookUrl", () => { it("rejects empty urls", async () => { const result = await validateWebhookUrl("") expect(result.valid).toBe(false) }) it("rejects non-https urls", async () => { const result = await validateWebhookUrl("http://example.com/webhook") expect(result.valid).toBe(false) if (!result.valid) { expect(result.error).toContain("https") } }) it("rejects invalid url format", async () => { const result = await validateWebhookUrl("not-a-url") expect(result.valid).toBe(false) }) it("rejects localhost", async () => { const result = await validateWebhookUrl("https://localhost/webhook") expect(result.valid).toBe(false) }) it("rejects ipv6 loopback", async () => { const result = await validateWebhookUrl("https://[::1]/webhook") expect(result.valid).toBe(false) }) it("rejects private ipv4 addresses", async () => { const privateAddresses = [ "https://10.0.0.1/webhook", "https://172.16.0.1/webhook", "https://192.168.1.1/webhook", "https://127.0.0.1/webhook", ] for (const address of privateAddresses) { const result = await validateWebhookUrl(address) expect(result.valid).toBe(false) } }) it("rejects hostnames that resolve to private addresses", async () => { const result = await validateWebhookUrl("https://internal.example.com/webhook", privateResolver) expect(result.valid).toBe(false) }) it("rejects hostnames that fail to resolve", async () => { const result = await validateWebhookUrl("https://nonexistent.example.com/webhook", failingResolver) expect(result.valid).toBe(false) if (!result.valid) { expect(result.error).toContain("could not be resolved") } }) it("accepts valid public https urls", async () => { const result = await validateWebhookUrl("https://example.com/webhook", publicResolver) expect(result.valid).toBe(true) }) it("trims whitespace from urls", async () => { const result = await validateWebhookUrl(" https://example.com/webhook ", publicResolver) expect(result.valid).toBe(true) if (result.valid) { expect(result.url).toBe("https://example.com/webhook") } }) })