forked from farhoodlabs/paperclip
feat(server): add github_hmac and none webhook signing modes
Adds two new webhook trigger signing modes for external provider compatibility: - github_hmac: accepts X-Hub-Signature-256 header with HMAC-SHA256(secret, rawBody), no timestamp prefix. Compatible with GitHub, Sentry, and services following the same standard. - none: no authentication; the 24-char hex publicId in the URL acts as the shared secret. For services that cannot add auth headers. The replay window UI field is hidden when these modes are selected since neither uses timestamp-based replay protection. Closes #1892 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -617,4 +617,72 @@ describeEmbeddedPostgres("routine service live-execution coalescing", () => {
|
||||
expect(run.status).toBe("issue_created");
|
||||
expect(run.linkedIssueId).toBeTruthy();
|
||||
});
|
||||
|
||||
it("accepts GitHub-style X-Hub-Signature-256 with github_hmac signing mode", async () => {
|
||||
const { routine, svc } = await seedFixture();
|
||||
const { trigger, secretMaterial } = await svc.createTrigger(
|
||||
routine.id,
|
||||
{
|
||||
kind: "webhook",
|
||||
signingMode: "github_hmac",
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const payload = { action: "opened", pull_request: { number: 1 } };
|
||||
const rawBody = Buffer.from(JSON.stringify(payload));
|
||||
const signature = `sha256=${createHmac("sha256", secretMaterial!.webhookSecret)
|
||||
.update(rawBody)
|
||||
.digest("hex")}`;
|
||||
|
||||
const run = await svc.firePublicTrigger(trigger.publicId!, {
|
||||
hubSignatureHeader: signature,
|
||||
rawBody,
|
||||
payload,
|
||||
});
|
||||
|
||||
expect(run.source).toBe("webhook");
|
||||
expect(run.status).toBe("issue_created");
|
||||
});
|
||||
|
||||
it("rejects invalid signature for github_hmac signing mode", async () => {
|
||||
const { routine, svc } = await seedFixture();
|
||||
const { trigger } = await svc.createTrigger(
|
||||
routine.id,
|
||||
{
|
||||
kind: "webhook",
|
||||
signingMode: "github_hmac",
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const rawBody = Buffer.from(JSON.stringify({ ok: true }));
|
||||
|
||||
await expect(
|
||||
svc.firePublicTrigger(trigger.publicId!, {
|
||||
hubSignatureHeader: "sha256=0000000000000000000000000000000000000000000000000000000000000000",
|
||||
rawBody,
|
||||
payload: { ok: true },
|
||||
}),
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
it("accepts any request with none signing mode", async () => {
|
||||
const { routine, svc } = await seedFixture();
|
||||
const { trigger } = await svc.createTrigger(
|
||||
routine.id,
|
||||
{
|
||||
kind: "webhook",
|
||||
signingMode: "none",
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const run = await svc.firePublicTrigger(trigger.publicId!, {
|
||||
payload: { event: "error.created" },
|
||||
});
|
||||
|
||||
expect(run.source).toBe("webhook");
|
||||
expect(run.status).toBe("issue_created");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user