import { describe, expect, it } from "bun:test";
import { readFileSync } from "fs";
const SRC = readFileSync("src/lib/webhook-delivery.ts", "utf8");
const fn = SRC.slice(
SRC.indexOf("async function deliverFollowingRedirects"),
SRC.indexOf("// ---", SRC.indexOf("async function deliverFollowingRedirects"))
);
describe("redirects cannot escape the guard", () => {
it("never lets fetch follow redirects itself", () => {
expect(fn).toContain('redirect: "manual"');
});
it("no bare fetch(row.url) with default redirect handling remains", () => {
expect(SRC).not.toMatch(/await fetch\(row\.url, \{\s*\n\s*method: "POST"/);
});
it("re-validates the URL on every hop, not just the first", () => {
const loopStart = fn.indexOf("for (let hop");
const guardCall = fn.indexOf("assertPublicUrl(url)");
expect(loopStart).toBeGreaterThan(-1);
expect(guardCall).toBeGreaterThan(loopStart);
});
it("bounds the hop count so a redirect cycle cannot spin", () => {
expect(SRC).toContain("MAX_REDIRECTS");
expect(fn).toContain("hop <= MAX_REDIRECTS");
expect(fn).toContain("more than ${MAX_REDIRECTS} redirects");
});
it("resolves a relative Location against the current hop", () => {
expect(fn).toContain("new URL(location, url)");
});
it("treats a 3xx with no Location as a terminal response", () => {
expect(fn).toContain("if (!location) return { status: res.status }");
});
});
describe("the DNS layer is actually wired in", () => {
it("webhook delivery calls resolvesToPrivate", () => {
expect(SRC).toContain("resolvesToPrivate");
expect(fn).toContain("await resolvesToPrivate(guard.url.hostname)");
});
it("checks DNS before issuing the request", () => {
const dns = fn.indexOf("resolvesToPrivate");
const req = fn.indexOf("await fetch(url");
expect(dns).toBeLessThan(req);
});
it("honours the same allow-private escape hatch as assertPublicUrl", () => {
expect(fn).toContain("ssrfPrivateAllowed()");
expect(fn).toContain("enforcing &&");
});
});
describe("blocked deliveries are reported as blocked", () => {
it("surfaces an SSRF block distinctly from an HTTP error", () => {
expect(SRC).toContain("blockedReason");
expect(SRC).toContain("(SSRF protection)");
});
});
|