Skip to content
Source
Sign inSign up

webhookVerification.test.ts

71 lines2.4 KB

1// @effect-diagnostics-next-line nodeBuiltinImport:off -- Node's HMAC is an independent oracle for the Effect implementation.
2import * as NodeCrypto from "node:crypto";
3​
4import * as PlatformNodeCrypto from "@effect/platform-node/NodeCrypto";
5import { assert, describe, it } from "@effect/vitest";
6import * as Effect from "effect/Effect";
7​
8import { verifyWebhookSignature } from "./webhookVerification.ts";
9​
10const body = new TextEncoder().encode('{"action":"opened"}');
11const secret = "shared-secret";
12const hmac = () => NodeCrypto.createHmac("sha256", secret).update(body);
13​
14describe("verifyWebhookSignature", () => {
15 const github = { header: "x-hub-signature-256", encoding: "hex", prefix: "sha256=" } as const;
16​
17 it.effect("rejects a tampered body, a wrong secret, a missing header and a missing prefix", () =>
18 Effect.gen(function* () {
19 const valid = `sha256=${hmac().digest("hex")}`;
20 const tampered = new TextEncoder().encode('{"action":"closed"}');
21 assert.isFalse(
22 yield* verifyWebhookSignature({
23 signature: github,
24 secret,
25 headers: { "x-hub-signature-256": valid },
26 body: tampered,
27 }),
28 );
29 assert.isFalse(
30 yield* verifyWebhookSignature({
31 signature: github,
32 secret: "other",
33 headers: { "x-hub-signature-256": valid },
34 body,
35 }),
36 );
37 assert.isFalse(
38 yield* verifyWebhookSignature({ signature: github, secret, headers: {}, body }),
39 );
40 assert.isFalse(
41 yield* verifyWebhookSignature({
42 signature: github,
43 secret,
44 headers: { "x-hub-signature-256": hmac().digest("hex") },
45 body,
46 }),
47 );
48 }).pipe(Effect.provide(PlatformNodeCrypto.layerMac)),
49 );
50​
51 it.effect("accepts a hex signature with its prefix and a base64 signature without one", () =>
52 Effect.gen(function* () {
53 assert.isTrue(
54 yield* verifyWebhookSignature({
55 signature: github,
56 secret,
57 headers: { "x-hub-signature-256": `sha256=${hmac().digest("hex").toUpperCase()}` },
58 body,
59 }),
60 );
61 assert.isTrue(
62 yield* verifyWebhookSignature({
63 signature: { header: "X-Signature", encoding: "base64", prefix: "" },
64 secret,
65 headers: { "x-signature": hmac().digest("base64") },
66 body,
67 }),
68 );
69 }).pipe(Effect.provide(PlatformNodeCrypto.layerMac)),
70 );
71});