SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
2.6 KB · 62 lines typescript
Raw Blame History
1import { beforeAll, describe, expect, it } from 'vitest';2import { hashPassword, verifyPassword, encryptSecret, decryptSecret, signPayload, verifyPayload, numericCode, recoveryCode, normalizeRecoveryCode, hmacToken } from './crypto';3import { newTotpSecret, verifyTotp, currentTotp, totpUri } from './totp';4import { passwordStrength } from './password-strength';56beforeAll(() => {7  process.env.SESSION_SECRET = 'test-secret-test-secret-test-secret';8});910describe('password hashing', () => {11  it('hashes and verifies with scrypt', async () => {12    const h = await hashPassword('correct horse battery');13    expect(h.startsWith('scrypt$')).toBe(true);14    expect(await verifyPassword('correct horse battery', h)).toBe(true);15    expect(await verifyPassword('wrong', h)).toBe(false);16    expect(await verifyPassword('x', null)).toBe(false);17  });18  it('strength meter', () => {19    expect(passwordStrength('short').score).toBe(0);20    expect(passwordStrength('password12').score).toBe(0);21    expect(passwordStrength('aaaaaaaaaaaa').score).toBe(0);22    expect(passwordStrength('Tr0ub4dor&3xyz!!').score).toBeGreaterThanOrEqual(2);23    expect(passwordStrength('correct horse battery staple hymn').score).toBe(4);24  });25});2627describe('codes & secrets', () => {28  it('numeric codes are 6 digits and hmac is stable', () => {29    for (let i = 0; i < 50; i++) expect(numericCode(6)).toMatch(/^\d{6}$/);30    expect(hmacToken('a', 'x')).toBe(hmacToken('a', 'x'));31    expect(hmacToken('a', 'x')).not.toBe(hmacToken('a', 'y'));32  });33  it('recovery codes normalise', () => {34    const c = recoveryCode();35    expect(c).toMatch(/^[A-Z2-9]{5}-[A-Z2-9]{5}$/);36    expect(normalizeRecoveryCode(c.toLowerCase().replace('-', ' '))).toBe(c);37  });38  it('aes-gcm roundtrip', () => {39    const enc = encryptSecret('JBSWY3DPEHPK3PXP');40    expect(enc).not.toContain('JBSWY3DPEHPK3PXP');41    expect(decryptSecret(enc)).toBe('JBSWY3DPEHPK3PXP');42  });43  it('signed payloads expire and detect tampering', () => {44    const t = signPayload({ uid: 'usr_1', stage: 'mfa' }, 60);45    expect(verifyPayload<{ uid: string }>(t)?.uid).toBe('usr_1');46    expect(verifyPayload(`${t}x`)).toBeNull();47    const expired = signPayload({ uid: 'usr_1' }, -1);48    expect(verifyPayload(expired)).toBeNull();49  });50});5152describe('totp', () => {53  it('generates and validates', () => {54    const s = newTotpSecret();55    expect(totpUri(s, 'a@b.c')).toContain('otpauth://totp/RareIndex:a%40b.c');56    const code = currentTotp(s);57    expect(verifyTotp(s, code)).not.toBeNull();58    expect(verifyTotp(s, '000000') === null || code === '000000').toBe(true);59    expect(verifyTotp(s, 'abc')).toBeNull();60  });61});62