Webhooks for the AT Protocol airglow.run
atproto atprotocol automation webhook
9
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 119 lines 4.1 kB view raw
1import { describe, it, expect, vi, beforeEach } from "vitest"; 2import { assertPublicUrl, UrlGuardError } from "./url-guard.js"; 3 4// Mock DNS resolution to control test behavior without network 5vi.mock("node:dns/promises", () => ({ 6 resolve4: vi.fn(), 7 resolve6: vi.fn(), 8})); 9 10import { resolve4, resolve6 } from "node:dns/promises"; 11 12const mockResolve4 = vi.mocked(resolve4); 13const mockResolve6 = vi.mocked(resolve6); 14 15function resolveAs(ipv4: string[] = [], ipv6: string[] = []) { 16 mockResolve4.mockResolvedValue(ipv4); 17 mockResolve6.mockResolvedValue(ipv6); 18} 19 20function resolveNothing() { 21 mockResolve4.mockRejectedValue(new Error("ENOTFOUND")); 22 mockResolve6.mockRejectedValue(new Error("ENOTFOUND")); 23} 24 25describe("assertPublicUrl", () => { 26 beforeEach(() => { 27 mockResolve4.mockReset(); 28 mockResolve6.mockReset(); 29 }); 30 31 it("accepts a public URL", async () => { 32 resolveAs(["93.184.216.34"]); 33 const url = await assertPublicUrl("https://example.com/hook"); 34 expect(url.hostname).toBe("example.com"); 35 }); 36 37 it("rejects non-HTTP schemes", async () => { 38 await expect(assertPublicUrl("ftp://example.com")).rejects.toThrow(UrlGuardError); 39 await expect(assertPublicUrl("file:///etc/passwd")).rejects.toThrow(UrlGuardError); 40 }); 41 42 it("rejects invalid URLs", async () => { 43 await expect(assertPublicUrl("not a url")).rejects.toThrow(); 44 }); 45 46 it("rejects localhost IP literal", async () => { 47 await expect(assertPublicUrl("http://127.0.0.1/hook")).rejects.toThrow(UrlGuardError); 48 }); 49 50 it("rejects private IP 10.x.x.x", async () => { 51 await expect(assertPublicUrl("http://10.0.0.1/hook")).rejects.toThrow(UrlGuardError); 52 }); 53 54 it("rejects private IP 192.168.x.x", async () => { 55 await expect(assertPublicUrl("http://192.168.1.1/hook")).rejects.toThrow(UrlGuardError); 56 }); 57 58 it("rejects private IP 172.16.x.x", async () => { 59 await expect(assertPublicUrl("http://172.16.0.1/hook")).rejects.toThrow(UrlGuardError); 60 }); 61 62 it("rejects cloud metadata IP", async () => { 63 await expect(assertPublicUrl("http://169.254.169.254/latest")).rejects.toThrow(UrlGuardError); 64 }); 65 66 it("rejects localhost hostname", async () => { 67 await expect(assertPublicUrl("http://localhost/hook")).rejects.toThrow(UrlGuardError); 68 }); 69 70 it("rejects .local hostname", async () => { 71 await expect(assertPublicUrl("http://myhost.local/hook")).rejects.toThrow(UrlGuardError); 72 }); 73 74 it("rejects when DNS resolves to private IP", async () => { 75 resolveAs(["127.0.0.1"]); 76 await expect(assertPublicUrl("https://evil.com/hook")).rejects.toThrow(UrlGuardError); 77 }); 78 79 it("rejects when DNS resolves to 10.x.x.x", async () => { 80 resolveAs(["10.0.0.5"]); 81 await expect(assertPublicUrl("https://evil.com/hook")).rejects.toThrow(UrlGuardError); 82 }); 83 84 it("rejects when any resolved IP is private", async () => { 85 resolveAs(["93.184.216.34", "10.0.0.1"]); 86 await expect(assertPublicUrl("https://evil.com/hook")).rejects.toThrow(UrlGuardError); 87 }); 88 89 it("rejects unresolvable hostnames", async () => { 90 resolveNothing(); 91 await expect(assertPublicUrl("https://nonexistent.invalid/hook")).rejects.toThrow( 92 UrlGuardError, 93 ); 94 }); 95 96 it("rejects IPv6 loopback", async () => { 97 await expect(assertPublicUrl("http://[::1]/hook")).rejects.toThrow(UrlGuardError); 98 }); 99 100 it("rejects when DNS resolves to IPv6 link-local", async () => { 101 resolveAs([], ["fe80::1"]); 102 await expect(assertPublicUrl("https://evil.com/hook")).rejects.toThrow(UrlGuardError); 103 }); 104 105 it("rejects when DNS resolves to IPv6 unique local", async () => { 106 resolveAs([], ["fd12::1"]); 107 await expect(assertPublicUrl("https://evil.com/hook")).rejects.toThrow(UrlGuardError); 108 }); 109 110 it("rejects CGN/Tailscale IP 100.64.x.x", async () => { 111 resolveAs(["100.64.0.1"]); 112 await expect(assertPublicUrl("https://evil.com/hook")).rejects.toThrow(UrlGuardError); 113 }); 114 115 it("rejects benchmarking IP 198.18.x.x", async () => { 116 resolveAs(["198.18.0.1"]); 117 await expect(assertPublicUrl("https://evil.com/hook")).rejects.toThrow(UrlGuardError); 118 }); 119});