docs(website): correct the CSP delta to one directive and document the report sink #51
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/csp-report-only"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What & why
Documentation companion to RunicGateway/website#100 (
feat(security): soak the tightened CSP on report-only, with a same-origin sink).The plan's Phase 1 delta was wrong, and the correction is recorded in place
API_V2_PLAN.mdclaimed two directives, one of which was addingform-action 'self'as "currently absent". It was not absent: the directives object inapp.jsdoesn't list it, but the middleware runsuseDefaults: trueand helmet's defaults already supply it — production has served it all along. The plan was written from the config rather than from the live header.Corrected in place, with a strikethrough and the reason, rather than quietly edited — so a future reader doesn't repeat the mistake.
That leaves
frame-ancestors 'self'→'none'as the entire behavioural delta — and the doc now notes why that is still worth a soak rather than a straight flip: aframe-ancestorsreport comes from the browser of whoever framed the site, which is the only way to discover a legitimate embed before an enforcing policy breaks it.Newly documented
POST /api/csp-reportinBACKEND_DESIGN.md§ 6 andAPI_V2_PLAN.md§ Phase 1 — why same-origin, why outside/api/v1, both wire formats, theReporting-Endpointsrequirement that keepsreport-tofrom being inert, and the properties that make an unauthenticated public POST safe (always-204, 16 KB cap, rate limit, field allowlist, truncation).report-togroup on the enforced policy — written down so it isn't left behind as an orphan route.[csp]log tag in § 7.5 as the thing to watch during the soak, and what silence across one release means.client/vite.config.jsalready setsmodulePreload: { polyfill: false }, so the plan's inline-polyfill prerequisite was already satisfied — marked as such.api-route-inventory.json: 199 → 200The first manifest diff since the PR 0 baseline, and a deliberate reviewed one. That is the freeze working as designed, not an exception to it.
How it was tested
Markdown plus the regenerated inventory mirror. Every claim cross-checked against the code in website#100 — the before/after live headers, the sink's guards, the wire-format handling, and the route count.
Checklist
AI-assisted contributions (required)
Claude Code (Opus). I have reviewed and understandevery change, and take responsibility for it. AI-authored commits are
marked with a
Co-Authored-By/Assisted-Bytrailer.License
(GNU GPL v3.0 or later), and I have the right to contribute it.
Companion to website "feat(security): soak the tightened CSP on report-only". The plan's Phase 1 claimed a two-directive delta, one of which was adding `form-action 'self'` as "currently absent". It was not absent. The directives object in app.js does not list it, but the middleware runs `useDefaults: true` and helmet's default set already supplies it, so production has been serving it all along. The plan was written from the config rather than from the live header; the correction, and how it was caught, are now recorded in place rather than quietly fixed. That leaves `frame-ancestors 'self'` -> `'none'` as the entire behavioural delta of the phase. Worth noting that this is also the directive that most justifies a soak: a frame-ancestors violation is reported by the browser of whoever framed the site, so it is the only available way to discover a legitimate embed before an enforcing policy breaks it. Also documented: * POST /api/csp-report -- the same-origin sink report-to/report-uri point at, why it is same-origin, why it lives outside /api/v1, both wire formats, the Reporting-Endpoints header requirement, and the properties that make an unauthenticated public POST safe (always-204, caps, truncation, rate limit). * That the sink is scoped to the soak, so the enforce PR must decide explicitly whether to retire it or keep a report-to group on the enforced policy -- rather than leaving an orphan route behind. * The `[csp]` log tag in section 7.5 as the thing to watch during the soak, and what silence across one release means. * client/vite.config.js already sets `modulePreload: { polyfill: false }`, so the plan's inline-polyfill prerequisite was already satisfied. api-route-inventory.json moves 199 -> 200 for the new route. That is the PR 0 freeze working as intended: the first manifest diff since the baseline is a deliberate, reviewed one. Co-Authored-By: Claude <noreply@anthropic.com>