import assert from 'node:assert/strict'; import { describe, it } from 'node:test'; import { hashPassword, isArgon2idHash, verifyPassword } from '../src/modules/auth/password.js'; import { generateSessionToken, hashSessionToken } from '../src/modules/auth/sessionToken.js'; import { redactObject } from '../src/middleware/requestLogger.js'; describe('password hashing', () => { it('hashes with Argon2id and never stores plaintext', async () => { const password = 'correct-horse-battery'; const hashed = await hashPassword(password); assert.notEqual(hashed, password); assert.equal(isArgon2idHash(hashed), true); assert.equal(await verifyPassword(password, hashed), true); assert.equal(await verifyPassword('wrong-password', hashed), false); }); }); describe('session tokens', () => { it('hashes opaque tokens with SHA-256', () => { const token = generateSessionToken(); const hashed = hashSessionToken(token); assert.notEqual(hashed, token); assert.match(hashed, /^[a-f0-9]{64}$/); assert.equal(hashSessionToken(token), hashed); }); }); describe('log redaction', () => { it('redacts passwords, tokens, and authorization headers', () => { const redacted = redactObject({ password: 'secret', passwordHash: 'hash', authorization: 'Bearer raw-session-token', token: 'raw-session-token', email: 'user@example.com', }); assert.equal(redacted.password, '[REDACTED]'); assert.equal(redacted.passwordHash, '[REDACTED]'); assert.equal(redacted.authorization, '[REDACTED]'); assert.equal(redacted.token, '[REDACTED]'); assert.equal(redacted.email, 'user@example.com'); assert.doesNotMatch(JSON.stringify(redacted), /raw-session-token/); }); });