import { describe, it, expect } from 'vitest' import { buildApp } from '../src/app.js' import { makeConfig, fakeCpClient, fakeMinter } from './helpers.js' async function makeApp() { return buildApp({ config: makeConfig(), cpClient: fakeCpClient(), minter: fakeMinter(), staticRoot: null }) } const HARDENING: Readonly> = { 'x-content-type-options': 'nosniff', 'x-frame-options': 'DENY', 'referrer-policy': 'no-referrer', } describe('security response headers', () => { it('stamps CSP + hardening headers on a matched route', async () => { const app = await makeApp() const res = await app.inject({ method: 'GET', url: '/api/session' }) expect(res.statusCode).toBe(200) for (const [name, value] of Object.entries(HARDENING)) expect(res.headers[name]).toBe(value) const csp = String(res.headers['content-security-policy']) expect(csp).toContain("default-src 'self'") expect(csp).toContain("img-src 'self' data:") // pairing QR is a data: image expect(csp).toContain("style-src 'self' 'unsafe-inline'") expect(csp).toContain("object-src 'none'") expect(csp).toContain("base-uri 'none'") expect(csp).toContain("frame-ancestors 'none'") await app.close() }) it('stamps the headers even on an unmatched (404) response', async () => { const app = await makeApp() const res = await app.inject({ method: 'GET', url: '/definitely-not-a-route' }) expect(res.statusCode).toBe(404) expect(res.headers['x-frame-options']).toBe('DENY') expect(String(res.headers['content-security-policy'])).toContain("default-src 'self'") await app.close() }) })