Context
The API accepts POST/PUT/DELETE JSON with no CSRF defences. Currently tolerable because (a) no auth/session cookies are issued and (b) the app is behind a trusted proxy. Once auth (#1) lands, CSRF becomes a real concern.
Proposal
- Add `flask-wtf` or roll a lightweight CSRF token tied to `SECRET_KEY`.
- Token issued by `GET /` template (meta tag), sent as `X-CSRF-Token` header by `app.js` on state-changing calls.
- Skip CSRF for `/healthz` and `/readyz`.
- Alternatively: require `Sec-Fetch-Site: same-origin` + `Content-Type: application/json` (simple but browser-dependent).
Acceptance
- Cross-origin POST without token is rejected 403.
- Existing tests updated or get an exemption path.
Blocks: #1 (only meaningful once there's an authenticated session).
Context
The API accepts POST/PUT/DELETE JSON with no CSRF defences. Currently tolerable because (a) no auth/session cookies are issued and (b) the app is behind a trusted proxy. Once auth (#1) lands, CSRF becomes a real concern.
Proposal
Acceptance
Blocks: #1 (only meaningful once there's an authenticated session).