diff --git a/apps/pwa/src/lib/crypto.mjs b/apps/pwa/src/lib/crypto.mjs index d09a6f1..e5755b4 100644 --- a/apps/pwa/src/lib/crypto.mjs +++ b/apps/pwa/src/lib/crypto.mjs @@ -32,7 +32,9 @@ export function sign(value) { export function unsign(signed) { if (!signed || typeof signed !== "string" || !signed.includes(".")) return null; - const [payload, mac] = signed.split("."); + const parts = signed.split("."); + if (parts.length !== 2) return null; + const [payload, mac] = parts; const expected = crypto.createHmac("sha256", config.sessionSecret).update(payload).digest("base64url"); const a = Buffer.from(mac); const b = Buffer.from(expected); diff --git a/apps/pwa/test/crypto-cookie.test.mjs b/apps/pwa/test/crypto-cookie.test.mjs new file mode 100644 index 0000000..8143b21 --- /dev/null +++ b/apps/pwa/test/crypto-cookie.test.mjs @@ -0,0 +1,15 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { sign, unsign } from "../src/lib/crypto.mjs"; + +test("unsign accepts an untouched signed cookie value", () => { + const signed = sign({ step: "passkey", nonce: "abc" }); + + assert.deepEqual(unsign(signed), { step: "passkey", nonce: "abc" }); +}); + +test("unsign rejects signed cookie values with extra segments", () => { + const signed = sign({ step: "passkey", nonce: "abc" }); + + assert.equal(unsign(`${signed}.junk`), null); +});