---
id: 06-security-headers-core-module
epic: security-headers-rate-limit-sbom
title: Security headers core module
type: technical-story
status: done
feature: core-shared
depends-on: []
blocks: [07-security-header-adapters]
created: 2026-05-20T00:00:00Z
updated: 2026-05-20T09:35:46.924Z
---

## Goal

Add the framework-agnostic security headers module to `core-shared/security` — `SecurityHeadersConfig` types, `generateNonce()` cryptographic helper, and `buildSecurityHeaders()` pure builder that returns all six headers with mode-aware CSP and URL validation for allowlisted origins.

## Why

The per-framework adapters (Story 07) and app-wiring stories (08, 09) all depend on this pure builder. Extracting the builder into `core-shared` (must-have) ensures all three template apps and any future framework adapters share a single, tested implementation of the header set, without duplicating CSP string construction or nonce generation logic.

## Done when

- `packages/core-shared/src/security/security-types.ts` exports `SecurityHeadersConfig` and `CspMode`.
- `packages/core-shared/src/security/nonce.ts` exports `generateNonce()` returning a cryptographically random base64-encoded 16-byte string.
- `packages/core-shared/src/security/build-security-headers.ts` exports `buildSecurityHeaders(opts: SecurityHeadersConfig): Record<string, string>` emitting all six headers (HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Content-Security-Policy); prod CSP includes nonce in `script-src 'strict-dynamic' 'nonce-{NONCE}'`; dev CSP is permissive (`'unsafe-inline' 'unsafe-eval' ws: localhost:* 127.0.0.1:*`); `allowedConnectOrigins` entries validated via `URL` constructor, throwing `InvalidSecurityHeadersConfig` on malformed input.
- Unit tests: expected header set per mode, nonce threading into CSP `script-src`, dev vs prod CSP shape, `allowedConnectOrigins` / `allowedImgOrigins` / `allowedFontOrigins` applied to correct CSP directives, URL validation error on malformed origin, `generateNonce` randomness (two calls differ).
- Types exported from `core-shared` barrel.
- `pnpm typecheck && pnpm lint && pnpm test && pnpm conformance && pnpm fallow:audit && pnpm coverage:diff` all pass.

## In scope

- `security-types.ts` — `SecurityHeadersConfig`, `CspMode` types.
- `nonce.ts` — `generateNonce()` using `crypto.randomBytes(16).toString("base64")`.
- `build-security-headers.ts` — pure builder, six headers, prod/dev CSP, `allowedConnectOrigins` / `allowedImgOrigins` / `allowedFontOrigins` applied to CSP directives, URL validation with `InvalidSecurityHeadersConfig` error.
- Unit tests for all three files.
- Barrel export from `core-shared`.

## Out of scope

- Framework-specific adapter subpaths (`core-shared/security/next`, `core-shared/security/tanstack`) — Story 07.
- App middleware wiring — Stories 08 and 09.
- CSP report-uri collector endpoint — deferred, documented in guide.
- Storybook CSP — explicitly out of Epic C scope.

## Tasks

- [x] Create `packages/core-shared/src/security/security-types.ts` with `SecurityHeadersConfig` + `CspMode` types; implement `packages/core-shared/src/security/nonce.ts` with `generateNonce()` using `crypto.randomBytes`; implement `packages/core-shared/src/security/build-security-headers.ts` emitting all six headers with prod/dev CSP mode, nonce threading into `script-src`, `allowedConnectOrigins` / `allowedImgOrigins` / `allowedFontOrigins` applied to CSP, URL validation throwing `InvalidSecurityHeadersConfig` on malformed origins; unit tests covering header set, CSP variants, nonce threading, allowlist CSP directives, URL validation error, nonce randomness; export from `core-shared` barrel; all gates pass.
