feat(webhooks): add GitHub webhook signature verifier
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import {
|
||||
timingSafeEqualHex,
|
||||
verifyGithubSignature,
|
||||
} from '../../convex/githubWebhookVerify';
|
||||
|
||||
const SECRET = "It's a Secret to Everybody";
|
||||
const BODY = 'Hello, World!';
|
||||
const VALID_SIGNATURE =
|
||||
'sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17';
|
||||
|
||||
describe('verifyGithubSignature', () => {
|
||||
test('verifies the GitHub known vector', async () => {
|
||||
expect(await verifyGithubSignature(SECRET, BODY, VALID_SIGNATURE)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects a tampered body', async () => {
|
||||
expect(
|
||||
await verifyGithubSignature(SECRET, 'Hello, World?', VALID_SIGNATURE),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects a wrong secret', async () => {
|
||||
expect(
|
||||
await verifyGithubSignature('wrong secret', BODY, VALID_SIGNATURE),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects a header missing the sha256= prefix', async () => {
|
||||
expect(
|
||||
await verifyGithubSignature(
|
||||
SECRET,
|
||||
BODY,
|
||||
'757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17',
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects a null header', async () => {
|
||||
expect(await verifyGithubSignature(SECRET, BODY, null)).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects an empty header', async () => {
|
||||
expect(await verifyGithubSignature(SECRET, BODY, '')).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects a correct-format but wrong-length signature', async () => {
|
||||
expect(
|
||||
await verifyGithubSignature(SECRET, BODY, 'sha256=deadbeef'),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
test('rejects an empty secret', async () => {
|
||||
expect(await verifyGithubSignature('', BODY, VALID_SIGNATURE)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('timingSafeEqualHex', () => {
|
||||
test('returns true for equal strings', () => {
|
||||
expect(timingSafeEqualHex('abcd', 'abcd')).toBe(true);
|
||||
});
|
||||
|
||||
test('returns false for equal-length but differing strings', () => {
|
||||
expect(timingSafeEqualHex('abcd', 'abce')).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false for different lengths', () => {
|
||||
expect(timingSafeEqualHex('abcd', 'abcde')).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user