1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
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")
}
})
})
|