Files
summercms/admin/tests/app/sessionKey.test.ts
Jakub Zych d5494faa81 test(12.2-05): SPA unit tests for the phase and the check-phase12.2 gate
- sessionKey, dateFormat, FileuploadField (protected thumbnails and
  keyboard reorder backstops), RelationChildModal, RelationPivotModal,
  RelationManager row actions and create-screen deferral, date and time
  list cells; typed deferred relation-schema and file-list fixtures
- scripts/check-phase12.2.sh: go, security (named tests, refuses missing
  or skipped), spa, openapi, dist, docs, hygiene and app stages, a
  detector self-test, one PASS or FAIL line per stage under --all
2026-10-02 20:51:36 +02:00

47 lines
1.7 KiB
TypeScript

// Form session keys (Phase 12.2, D-02): 32 random bytes from
// crypto.getRandomValues as unpadded base64url, 43 characters, never the
// same twice, and only ever sent in the two headers.
import { describe, expect, it, vi } from 'vitest'
import { CHILD_SESSION_HEADER, SESSION_HEADER, newSessionKey } from '../../src/app/sessionKey'
const SERVER_PATTERN = /^[A-Za-z0-9_-]{32,128}$/
describe('newSessionKey', () => {
it('is 43 base64url characters the server accepts', () => {
for (let i = 0; i < 50; i++) {
const key = newSessionKey()
expect(key).toHaveLength(43)
expect(key).toMatch(/^[A-Za-z0-9_-]+$/)
expect(key).toMatch(SERVER_PATTERN)
expect(key).not.toContain('=')
}
})
it('draws 32 bytes from crypto.getRandomValues and encodes them', () => {
const spy = vi.spyOn(globalThis.crypto, 'getRandomValues').mockImplementation(<T extends ArrayBufferView | null>(array: T): T => {
const bytes = array as unknown as Uint8Array
bytes.fill(0xfb)
bytes[31] = 0xff
return array
})
const key = newSessionKey()
expect(spy).toHaveBeenCalledTimes(1)
const arg = spy.mock.calls[0]![0] as unknown as Uint8Array
expect(arg).toBeInstanceOf(Uint8Array)
expect(arg.byteLength).toBe(32)
// 0xfb bytes give "+" and "/" in standard base64: base64url turns them
// into "-" and "_".
expect(key).toBe('-_v7-_v7-_v7-_v7-_v7-_v7-_v7-_v7-_v7-_v7-_8')
})
it('differs on every call', () => {
const keys = new Set(Array.from({ length: 200 }, () => newSessionKey()))
expect(keys.size).toBe(200)
})
it('names the two headers', () => {
expect(SESSION_HEADER).toBe('X-Session-Key')
expect(CHILD_SESSION_HEADER).toBe('X-Child-Session-Key')
})
})