diff options
| author | Fuwn <[email protected]> | 2026-02-10 01:59:01 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-10 01:59:01 -0800 |
| commit | 871985bc9eb42c6a088563e7c34db181f603f407 (patch) | |
| tree | 31299597a9f246d332b3bf6d5e2bed177648b577 /apps/web/lib/validate-webhook-url.test.ts | |
| parent | feat: reorder feature grid by attention-grabbing impact (diff) | |
| download | asa.news-871985bc9eb42c6a088563e7c34db181f603f407.tar.xz asa.news-871985bc9eb42c6a088563e7c34db181f603f407.zip | |
fix: harden CI and close remaining test/security gaps
- Make webhook URL tests deterministic with injectable DNS resolver
- Wire tier parity checker into CI and root scripts
- Add rate_limits cleanup cron job (hourly, >1hr retention)
- Change rate limiter to fail closed on RPC error
- Add Go worker tests: parser, SSRF protection, error classification,
authentication, and worker pool (48 test functions)
Diffstat (limited to 'apps/web/lib/validate-webhook-url.test.ts')
| -rw-r--r-- | apps/web/lib/validate-webhook-url.test.ts | 34 |
1 files changed, 31 insertions, 3 deletions
diff --git a/apps/web/lib/validate-webhook-url.test.ts b/apps/web/lib/validate-webhook-url.test.ts index 3375133..78f5b62 100644 --- a/apps/web/lib/validate-webhook-url.test.ts +++ b/apps/web/lib/validate-webhook-url.test.ts @@ -1,5 +1,20 @@ import { describe, it, expect } from "vitest" -import { validateWebhookUrl } from "./validate-webhook-url" +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 () => { @@ -44,13 +59,26 @@ describe("validateWebhookUrl", () => { } }) + 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") + 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 ") + 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") |