Add security-headers middleware with strict CSP (TDD)
Exports buildSecurityHeaders() (pure, testable) and wires it into the Astro onRequest middleware. Adds astro:middleware alias in vitest config so the unit tests run outside Astro's build context. 14 tests pass (7 existing + 7 new). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
52
src/middleware.test.ts
Normal file
52
src/middleware.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildSecurityHeaders } from './middleware';
|
||||
|
||||
describe('buildSecurityHeaders', () => {
|
||||
const headers = buildSecurityHeaders();
|
||||
|
||||
it('sets a strict Content-Security-Policy', () => {
|
||||
const csp = headers['Content-Security-Policy'];
|
||||
expect(csp).toContain("default-src 'self'");
|
||||
expect(csp).toContain("script-src 'self'");
|
||||
expect(csp).toContain("frame-ancestors 'none'");
|
||||
expect(csp).toContain("form-action 'none'");
|
||||
expect(csp).toContain("base-uri 'self'");
|
||||
expect(csp).toContain("object-src 'none'");
|
||||
});
|
||||
|
||||
it('denies framing', () => {
|
||||
expect(headers['X-Frame-Options']).toBe('DENY');
|
||||
});
|
||||
|
||||
it('disables MIME sniffing', () => {
|
||||
expect(headers['X-Content-Type-Options']).toBe('nosniff');
|
||||
});
|
||||
|
||||
it('sets a strict referrer policy', () => {
|
||||
expect(headers['Referrer-Policy']).toBe('strict-origin-when-cross-origin');
|
||||
});
|
||||
|
||||
it('disables sensitive browser features', () => {
|
||||
const pp = headers['Permissions-Policy'];
|
||||
expect(pp).toContain('geolocation=()');
|
||||
expect(pp).toContain('microphone=()');
|
||||
expect(pp).toContain('camera=()');
|
||||
expect(pp).toContain('payment=()');
|
||||
});
|
||||
|
||||
it('sets HSTS with long max-age, subdomains, and preload', () => {
|
||||
const hsts = headers['Strict-Transport-Security'];
|
||||
expect(hsts).toContain('max-age=31536000');
|
||||
expect(hsts).toContain('includeSubDomains');
|
||||
expect(hsts).toContain('preload');
|
||||
});
|
||||
|
||||
it('does not allow inline scripts', () => {
|
||||
expect(headers['Content-Security-Policy']).not.toContain("'unsafe-inline' 'nonce-");
|
||||
const scriptDirective = headers['Content-Security-Policy']
|
||||
.split(';')
|
||||
.map(s => s.trim())
|
||||
.find(s => s.startsWith('script-src')) ?? '';
|
||||
expect(scriptDirective).not.toContain("'unsafe-inline'");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user