← back to Letsbegin
__tests__/session-token.test.mjs
69 lines
/**
* session-token.test.mjs — PROOF for the TK-11776 follow-up (Letsbegin local session).
*
* The local `letsbegin_session` cookie used to be an UNSIGNED base64(JSON) blob that
* middleware.ts + /api/auth/session trusted on its `authenticated` flag alone, so
* `btoa('{"authenticated":true,"loginTime":<now>}')` was a valid session. This proves the
* new HMAC signer REJECTS that forgery, ACCEPTS a validly-signed session, and fails closed
* on wrong-secret / tampered / unset-secret. Runnable with plain
* `node __tests__/session-token.test.mjs` (Node 23.6+/26 imports the .ts lib directly).
*/
import assert from 'node:assert/strict';
import { signSession, verifySession } from '../lib/session-token.ts';
const FIXTURE = 'hmac-test-fixture';
// the exact live forgery: an unsigned base64(JSON) cookie claiming authenticated:true
const forge = (extra = {}) =>
Buffer.from(JSON.stringify({ authenticated: true, loginTime: Date.now(), ...extra })).toString('base64');
// the vulnerable OLD logic, verbatim — used only to prove this test is a real detector
function oldVerify(token) {
try {
const d = JSON.parse(Buffer.from(token, 'base64').toString());
return d.authenticated === true;
} catch {
return false;
}
}
let pass = 0;
const check = async (name, fn) => { await fn(); pass++; console.log(` ✓ ${name}`); };
console.log('TK-11776 follow-up — forgeable letsbegin_session is closed');
await check('detector sanity: OLD logic ACCEPTS the forged authenticated cookie', async () => {
assert.equal(oldVerify(forge()), true);
});
await check('REJECTS the forged unsigned base64(JSON) cookie (the live exploit) -> null', async () => {
assert.equal(await verifySession(forge(), FIXTURE), null);
});
await check('ACCEPTS a validly-signed session and returns its fields', async () => {
const token = await signSession({ authenticated: true, username: 'steve', loginTime: Date.now() }, FIXTURE);
const s = await verifySession(token, FIXTURE);
assert.ok(s && s.authenticated === true, 'authenticated should be true');
assert.equal(s.username, 'steve');
assert.equal(typeof s.loginTime, 'number');
});
await check('REJECTS a valid token under the WRONG secret -> null', async () => {
const token = await signSession({ authenticated: true, loginTime: Date.now() }, FIXTURE);
assert.equal(await verifySession(token, 'a-different-secret'), null);
});
await check('REJECTS a tampered payload with a stolen signature -> null', async () => {
const good = await signSession({ authenticated: false, loginTime: Date.now() }, FIXTURE);
const sig = good.slice(good.lastIndexOf('.'));
const tampered = `${Buffer.from(JSON.stringify({ authenticated: true, loginTime: Date.now() })).toString('base64')}${sig}`;
assert.equal(await verifySession(tampered, FIXTURE), null);
});
await check('FAILS CLOSED when the secret is unset -> verify null, sign throws', async () => {
const token = await signSession({ authenticated: true, loginTime: Date.now() }, FIXTURE);
assert.equal(await verifySession(token, undefined), null);
await assert.rejects(() => signSession({ authenticated: true }, undefined));
});
await check('REJECTS junk / signatureless / empty tokens -> null', async () => {
for (const t of ['', 'x', 'not.base64.parts', forge(), null, undefined]) {
assert.equal(await verifySession(t, FIXTURE), null);
}
});
console.log(`\n${pass} passed`);