# Task 01 — The pure-ASGI header middleware + unit suite **Phase:** `82_security_headers` · **Story:** n/a (security hardening — audit SEC-04) ## Objective `app/core/security_headers.py` exists exactly per the phase design block, and `tests/unit/test_security_headers.py` pins the header values, the 404 path, the SSE streaming passthrough (byte-identical body + headers present), and the non-http scope passthrough. ## Work 1. `app/core/security_headers.py` (new) — implement per the phase overview's design block: the `CSP` constant (the exact A1 string), `SecurityHeadersMiddleware` as a pure-ASGI class wrapping `send` (headers written on `http.response.start` via `starlette.datastructures.MutableHeaders(scope=message)`), the `scope["type"] != "http"` fast path, and the module docstring content listed in the design block (audit basis, the No-CDN verification, the pure-ASGI/SSE rationale). 2. `tests/unit/test_security_headers.py` (new): - a tiny ASGI `test_app` (a plain 200 `Response`-like dict sequence, or starlette's `Response` wrapped in an ASGI callable) + the middleware: assert all three headers with exact values; - a 404-shaped response (status 404) → same three headers; - a streaming response (an async generator yielding `data: a\n\n` then `data: b\n\n`, `media_type="text/event-stream"`) → consume the full body via the ASGI `receive`/`send` protocol (drive it with a small async client loop, or `httpx.AsyncClient(transport=ASGITransport(...))` if already used in the unit suite) → body is exactly the two frames in order AND the three headers are present (the SSE pin); - a `websocket` scope → the app is called, no crash, no header injection attempt. ## Testing & Quality - `uv run pytest tests/unit/test_security_headers.py -v` green. - Coverage: **>90%** on `app/core/security_headers.py` (every branch: http start, http other messages, non-http scope). ## Completion Criteria - [ ] `uv run pytest tests/unit/test_security_headers.py -v` — all tests green, including the SSE byte-identity pin. - [ ] `uv run ruff check app/core/security_headers.py tests/unit/test_security_headers.py && uv run pyright` clean. - [ ] `git diff --stat` for this task: exactly the two new files.