Some checks are pending
CI / Lint bridge (Biome) (push) Waiting to run
CI / Type-check bridge (push) Blocked by required conditions
CI / Tests unit bridge (push) Blocked by required conditions
CI / Tests integration bridge (push) Blocked by required conditions
CI / Security scan (push) Waiting to run
CI / Docker build + healthcheck (push) Blocked by required conditions
69 lines
2.2 KiB
TypeScript
69 lines
2.2 KiB
TypeScript
import { createHmac } from 'node:crypto';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { computeHmacSha256Hex, verifyHmacSha256 } from '../../src/webhooks/signature.js';
|
|
|
|
const SECRET = 'super-secret-key-32chars-min-len';
|
|
const BODY = JSON.stringify({ event_id: 'abc', event_type: 'rows.created' });
|
|
|
|
function refSig(body: string, secret: string): string {
|
|
return createHmac('sha256', secret).update(body, 'utf8').digest('hex');
|
|
}
|
|
|
|
describe('computeHmacSha256Hex', () => {
|
|
it('match Node crypto reference', () => {
|
|
expect(computeHmacSha256Hex(BODY, SECRET)).toBe(refSig(BODY, SECRET));
|
|
});
|
|
|
|
it('different secrets -> different output', () => {
|
|
expect(computeHmacSha256Hex(BODY, SECRET)).not.toBe(
|
|
computeHmacSha256Hex(BODY, `${SECRET}-other`),
|
|
);
|
|
});
|
|
|
|
it('different body -> different output', () => {
|
|
expect(computeHmacSha256Hex(BODY, SECRET)).not.toBe(computeHmacSha256Hex(`${BODY}x`, SECRET));
|
|
});
|
|
|
|
it('hex length is 64 (sha256)', () => {
|
|
expect(computeHmacSha256Hex(BODY, SECRET)).toHaveLength(64);
|
|
});
|
|
});
|
|
|
|
describe('verifyHmacSha256', () => {
|
|
it('accepte hex pur valide', () => {
|
|
const sig = refSig(BODY, SECRET);
|
|
expect(verifyHmacSha256(BODY, SECRET, sig)).toBe(true);
|
|
});
|
|
|
|
it('accepte format prefixe sha256=<hex>', () => {
|
|
const sig = `sha256=${refSig(BODY, SECRET)}`;
|
|
expect(verifyHmacSha256(BODY, SECRET, sig)).toBe(true);
|
|
});
|
|
|
|
it('rejette signature null', () => {
|
|
expect(verifyHmacSha256(BODY, SECRET, null)).toBe(false);
|
|
});
|
|
|
|
it('rejette signature longueur differente', () => {
|
|
expect(verifyHmacSha256(BODY, SECRET, 'tooshort')).toBe(false);
|
|
});
|
|
|
|
it('rejette signature meme longueur mais wrong digest', () => {
|
|
const wrong = 'a'.repeat(64);
|
|
expect(verifyHmacSha256(BODY, SECRET, wrong)).toBe(false);
|
|
});
|
|
|
|
it('rejette si body modifie', () => {
|
|
const sig = refSig(BODY, SECRET);
|
|
expect(verifyHmacSha256(`${BODY}x`, SECRET, sig)).toBe(false);
|
|
});
|
|
|
|
it('rejette si secret different', () => {
|
|
const sig = refSig(BODY, SECRET);
|
|
expect(verifyHmacSha256(BODY, `${SECRET}-other`, sig)).toBe(false);
|
|
});
|
|
|
|
it('signatures non-ascii ne crashent pas', () => {
|
|
expect(verifyHmacSha256(BODY, SECRET, 'é'.repeat(64))).toBe(false);
|
|
});
|
|
});
|