import { describe, expect, mock, test } from "bun:test"; mock.module("cloudflare:workers", () => ({ env: { PUBLIC_URL: "https://gitfor.example.com", INTEGRATIONS_ALLOW_LOOPBACK: "false", SECRETS_KEY: "test" }, waitUntil: () => undefined, WorkflowEntrypoint: class {}, DurableObject: class {}, WorkerEntrypoint: class {}, })); const { assertResolvesPublic, isNonPublicAddress } = await import("../net/resolve-guard"); const { screenMirrorPush, withoutHiddenRefs, rewritableUpdates, isPolicyRefusal } = await import("./mirror-screen"); const { reportRefName } = await import("./github-sync"); const { protectionVerdicts } = await import("../git/proxy"); const { postToTargets } = await import("./deliver"); const { stableId } = await import("./queue"); type Resolver = Parameters[1]; type Answer = { type: number; data: string }; function fakeDns(records: Record): Resolver { return async (name, type) => ({ Status: records[name] ? 0 : 3, Answer: records[name]?.[type] ?? [] }); } const A = (data: string): Answer => ({ type: 1, data }); const AAAA = (data: string): Answer => ({ type: 28, data }); const CNAME = (data: string): Answer => ({ type: 5, data }); describe("DNS resolution guard", () => { test("classifies non-public addresses", () => { for (const address of [ "127.0.0.1", "10.1.2.3", "172.16.0.1", "172.31.255.255", "192.168.1.1", "169.254.169.254", "100.64.0.1", "0.0.0.0", "224.0.0.1", "255.255.255.255", "::1", "::", "fe80::1", "fd00::1", "::ffff:127.0.0.1", "::ffff:10.0.0.1", "64:ff9b::a9fe:a9fe", "2002:c0a8:0101::1", "192.0.2.10", "198.51.100.7", "203.0.113.9", "198.18.0.1", "fec0::1", "2001:0:4136:e378::1", "64:ff9b:1::1", "100::1", "::ffff:0:10.0.0.1", "64:ff9b:0:1::808:808", "4000::1", "2001:10::1", "2001:2::1", "not-an-ip", ]) { expect(isNonPublicAddress(address)).toBe(true); } for (const address of ["8.8.8.8", "172.32.0.1", "1.1.1.1", "2606:4700:4700::1111", "::ffff:8.8.8.8"]) { expect(isNonPublicAddress(address)).toBe(false); } }); test("rejects DNS-rebinding and private-resolving names", async () => { const resolver = fakeDns({ "hooks.example.com": { A: [A("93.184.216.34")], AAAA: [AAAA("2606:2800:220:1::1")] }, "rebind.example.com": { A: [A("127.0.0.1")] }, "mixed.example.com": { A: [A("93.184.216.34"), A("10.0.0.5")] }, "v6.example.com": { AAAA: [AAAA("::ffff:169.254.169.254")] }, "alias.example.com": { A: [CNAME("inner.example.net.")] }, "inner.example.net": { A: [A("192.168.0.10")] }, "empty.example.com": {}, }); await assertResolvesPublic("https://hooks.example.com/x", resolver); for (const host of ["rebind", "mixed", "v6", "alias", "empty", "missing"]) { await expect(assertResolvesPublic(`https://${host}.example.com/x`, resolver)).rejects.toThrow(); } await expect(assertResolvesPublic("https://10.0.0.1/x", resolver)).rejects.toThrow(); }); test("DoH outages are retryable, not treated as public", async () => { const failing: Resolver = async () => { throw Object.assign(new Error("Could not verify the source address; try again"), { status: 503 }); }; await expect(assertResolvesPublic("https://hooks.example.com/x", failing)).rejects.toThrow(/try again/); }); }); const sha = (char: string) => char.repeat(40); const repo = { id: "r1" } as Parameters[0]; const emptyBody = () => new ReadableStream({ start: (controller) => controller.close() }); const protectedMain = [ { id: "p1", pattern: "main", requirePullRequest: false, requiredApprovals: 0, requiredChecks: [], dismissStaleReviews: false, allowDeletions: false, allowForcePushes: false, enforceAdmins: false, requireCodeOwnerReview: false, }, ]; /** Gates built on the real protection rules, with a fake history where every rewrite is non-fast-forward. */ const scanCalls: Array<{ expectsPack: boolean; allowUnscanned: boolean }> = []; function gates(rules: typeof protectedMain, scanRejects = false) { const nonFastForward = async (_repo: unknown, updates: Array<{ ref: string }>, body: ReadableStream) => ({ rejected: new Map(updates.map((update) => [update.ref, "protected branch: force pushes are not allowed"])), body, }); return { checkProtections: async ( _repo: unknown, permission: "write", commands: Array<{ oldSha: string; newSha: string; ref: string }>, body: ReadableStream, ) => { const verdicts = protectionVerdicts(rules, permission, commands); const violations = new Map(verdicts.violations); if (verdicts.needsFastForward.length > 0) { for (const [ref, reason] of (await nonFastForward(null, verdicts.needsFastForward, body)).rejected) { violations.set(ref, reason); } } return { violations, body }; }, findForcePushes: nonFastForward, scanPushBody: async ( _repo: unknown, _pusher: unknown, body: ReadableStream, _gzip: boolean, expectsPack: boolean, allowUnscanned: boolean, ) => { scanCalls.push({ expectsPack, allowUnscanned }); return { body, rejection: scanRejects ? "secret found" : null }; }, } as unknown as Parameters[4]; } describe("mirror screening", () => { const rewriteMain = [{ name: "refs/heads/main", sha: sha("b"), oldSha: sha("a") }]; const rewriteFeature = [{ name: "refs/heads/feature", sha: sha("b"), oldSha: sha("a") }]; const createBranch = [{ name: "refs/heads/new", sha: sha("c"), oldSha: sha("0") }]; test("hidden refs are never mirrored", () => { const result = withoutHiddenRefs([ { name: "refs/pull/1/head", sha: sha("a") }, { name: "refs/gitfor/pull/x/1/base", sha: sha("a") }, { name: "refs/heads/main", sha: sha("b") }, ]); expect(result.commands.map((command) => command.name)).toEqual(["refs/heads/main"]); expect(result.rejected.size).toBe(2); expect(rewritableUpdates([...rewriteMain, ...createBranch]).map((command) => command.name)).toEqual([ "refs/heads/main", ]); }); test("protected branches refuse non-fast-forward mirrors even with force mirroring", async () => { const policy = { force: true, pusherId: "u1" }; const result = await screenMirrorPush(repo, rewriteMain, emptyBody(), policy, gates(protectedMain)); expect(result.rejected.get("refs/heads/main")).toMatch(/force pushes are not allowed/); const allowed = [{ ...protectedMain[0], allowForcePushes: true }]; const ok = await screenMirrorPush(repo, rewriteMain, emptyBody(), policy, gates(allowed)); expect(ok.rejected.size).toBe(0); }); test("without force mirroring, any rewrite is refused; creations pass", async () => { const policy = { force: false, pusherId: "u1" }; const refused = await screenMirrorPush(repo, rewriteFeature, emptyBody(), policy, gates([])); expect(refused.rejected.get("refs/heads/feature")).toMatch(/fast-forward only/); const created = await screenMirrorPush(repo, createBranch, emptyBody(), policy, gates([])); expect(created.rejected.size).toBe(0); const forced = await screenMirrorPush(repo, rewriteFeature, emptyBody(), { ...policy, force: true }, gates([])); expect(forced.rejected.size).toBe(0); }); test("push protection refuses the whole mirror when secrets are found", async () => { const result = await screenMirrorPush( repo, createBranch, emptyBody(), { force: true, pusherId: "u1" }, gates([], true), ); expect(result.rejected.get("refs/heads/new")).toMatch(/secrets/); }); }); describe("mirror scanning and reporting", () => { test("force mirroring never relaxes the fail-closed secret scan; the fast path expects no pack", async () => { scanCalls.length = 0; const commands = [{ name: "refs/tags/v1", sha: sha("c"), oldSha: sha("0") }]; await screenMirrorPush(repo, commands, emptyBody(), { force: true, pusherId: "u1" }, gates([])); await screenMirrorPush(repo, commands, emptyBody(), { force: true, pusherId: "u1" }, gates([]), false); expect(scanCalls).toEqual([ { expectsPack: true, allowUnscanned: false }, { expectsPack: false, allowUnscanned: false }, ]); }); test("receive-pack refusals map back to ref names and policy refusals are recognized", () => { expect(reportRefName("refs/heads/main pre-receive hook declined")).toBe("refs/heads/main"); expect(reportRefName("refs/heads/main")).toBe("refs/heads/main"); expect(isPolicyRefusal("refs/heads/main: protected branch: force pushes are not allowed")).toBe(true); expect(isPolicyRefusal("refs/heads/x: mirror is fast-forward only; an admin can enable force mirroring")).toBe( true, ); expect(isPolicyRefusal("refs/heads/x: protected branch: gitfor could not verify this is a fast-forward")).toBe( false, ); expect(isPolicyRefusal("refs/heads/x: not applied after retries; will retry")).toBe(false); }); }); describe("delivery dedupe", () => { test("idempotency keys are stable per (event, integration)", async () => { const first = await stableId("evt-1", "int-1"); expect(first).toHaveLength(20); expect(await stableId("evt-1", "int-1")).toBe(first); expect(await stableId("evt-1", "int-2")).not.toBe(first); expect(await stableId("evt-2", "int-1")).not.toBe(first); }); test("a retried multi-channel post only resends channels that failed", async () => { const done = new Set(); const posts: string[] = []; let failB = true; const post = async (channel: string) => { posts.push(channel); if (channel === "B" && failB) return { status: 200, body: "", error: "Slack: ratelimited", durationMs: 0 }; return { status: 200, body: "", error: null, durationMs: 0 }; }; const mark = async (channel: string) => { done.add(channel); }; const first = await postToTargets(["A", "B", "C"], done, post, mark); expect(first.error).toBe("Slack: ratelimited"); failB = false; const second = await postToTargets(["A", "B", "C"], done, post, mark); expect(second.error).toBeNull(); expect(posts).toEqual(["A", "B", "C", "B"]); }); test("a retryable failure wins over a permanent one so the delivery is retried", async () => { const result = await postToTargets( ["A", "B"], new Set(), async (channel) => channel === "A" ? { status: 200, body: "", error: "Slack: channel_not_found", durationMs: 0, permanent: true } : { status: 200, body: "", error: "Slack: ratelimited", durationMs: 0 }, async () => undefined, ); expect(result.permanent).toBeFalsy(); }); });