import { describe, expect, mock, test } from "bun:test"; import { generateKeyPairSync } from "node:crypto"; import { hmacSha256Hex } from "../crypto"; import { signPayload, verifyLinearSignature, verifySha256Signature, verifySlackSignature } from "./signatures"; import { escapeSlack, googleChatDigest, googleChatMessage, slackDigest, slackMessage } from "./format"; import { branchMatches, passesFilters, summarizeEvent } from "./summarize"; import type { EventSummary } from "./summarize"; import { extractIssueKeys } from "./linear-keys"; import { DEFAULT_FILTERS } from "~/lib/integrations"; const fakeEnv = { PUBLIC_URL: "https://gitfor.example.com", INTEGRATIONS_ALLOW_LOOPBACK: "false", SECRETS_KEY: "test" }; mock.module("cloudflare:workers", () => ({ env: fakeEnv, waitUntil: () => undefined, WorkflowEntrypoint: class {}, DurableObject: class {}, WorkerEntrypoint: class {}, })); const repo = { fullName: "acme/web", url: "https://gitfor.example.com/acme/web", defaultBranch: "main" }; describe("signatures", () => { test("outbound HMAC round-trips and rejects tampering", async () => { const header = await signPayload("s3cret", '{"a":1}'); expect(header).toBe(`sha256=${await hmacSha256Hex("s3cret", '{"a":1}')}`); expect(await verifySha256Signature("s3cret", '{"a":1}', header)).toBe(true); expect(await verifySha256Signature("s3cret", '{"a":2}', header)).toBe(false); expect(await verifySha256Signature("other", '{"a":1}', header)).toBe(false); expect(await verifySha256Signature("s3cret", '{"a":1}', null)).toBe(false); expect(await verifySha256Signature("", '{"a":1}', header)).toBe(false); }); test("Slack v0 signatures are verified with a replay window", async () => { const nowMs = 1_700_000_000_000; const timestamp = String(nowMs / 1000); const body = "token=x&team_id=T1&text=status+acme%2Fweb"; const signature = `v0=${await hmacSha256Hex("signing", `v0:${timestamp}:${body}`)}`; const base = { signingSecret: "signing", body, timestamp, signature, nowMs }; expect(await verifySlackSignature(base)).toBe(true); expect(await verifySlackSignature({ ...base, body: `${body}&x=1` })).toBe(false); expect(await verifySlackSignature({ ...base, signingSecret: "wrong" })).toBe(false); expect(await verifySlackSignature({ ...base, nowMs: nowMs + 6 * 60_000 })).toBe(false); expect(await verifySlackSignature({ ...base, timestamp: "abc" })).toBe(false); expect(await verifySlackSignature({ ...base, signature: null })).toBe(false); }); test("Linear signatures require a fresh webhookTimestamp", async () => { const nowMs = 1_700_000_000_000; const body = JSON.stringify({ type: "Issue", action: "update", webhookTimestamp: nowMs - 5_000 }); const signature = await hmacSha256Hex("lin", body); expect(await verifyLinearSignature({ secret: "lin", body, signature, nowMs })).toBe(true); expect(await verifyLinearSignature({ secret: "lin", body, signature: signature.toUpperCase(), nowMs })).toBe(true); expect(await verifyLinearSignature({ secret: "nope", body, signature, nowMs })).toBe(false); expect(await verifyLinearSignature({ secret: "lin", body, signature, nowMs: nowMs + 120_000 })).toBe(false); const stale = JSON.stringify({ type: "Issue" }); expect( await verifyLinearSignature({ secret: "lin", body: stale, signature: await hmacSha256Hex("lin", stale), nowMs }), ).toBe(false); }); }); function pullMerged(): EventSummary { const summary = summarizeEvent( "pull_request", { action: "closed", number: 7, pull_request: { number: 7, merged: true, title: "Fix