docs(website): correct the CSP delta to one directive and document the report sink #51

Merged
whitlocktech merged 1 commits from feature/csp-report-only into main 2026-07-27 20:22:30 +00:00
Member

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.md claimed two directives, one of which was adding form-action 'self' as "currently absent". It was not absent: the directives object in app.js doesn't list it, but the middleware runs useDefaults: true and 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: a frame-ancestors report 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-report in BACKEND_DESIGN.md § 6 and API_V2_PLAN.md § Phase 1 — why same-origin, why outside /api/v1, both wire formats, the Reporting-Endpoints requirement that keeps report-to from being inert, and the properties that make an unauthenticated public POST safe (always-204, 16 KB cap, rate limit, field allowlist, truncation).
  • The sink is scoped to the soak. The enforce PR must decide explicitly whether to retire it with the report-only twin or keep a report-to group on the enforced policy — written down so it isn't left behind as an orphan route.
  • The [csp] log tag in § 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 — marked as such.

api-route-inventory.json: 199 → 200

The 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

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally.
  • I have added or updated tests/docs where it makes sense.
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • No AI tools were used to produce this contribution.
  • AI tools were used. Tool(s): Claude Code (Opus). I have reviewed and understand
    every change, and take responsibility for it. AI-authored commits are
    marked with a Co-Authored-By / Assisted-By trailer.

License

  • I agree that my contribution is licensed under this project's license
    (GNU GPL v3.0 or later), and I have the right to contribute it.
## 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.md` claimed two directives, one of which was adding **`form-action 'self'`** as "currently absent". It was not absent: the directives object in `app.js` doesn't list it, but the middleware runs `useDefaults: true` and 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: a `frame-ancestors` report 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-report`** in `BACKEND_DESIGN.md` § 6 and `API_V2_PLAN.md` § Phase 1 — why same-origin, why outside `/api/v1`, both wire formats, the `Reporting-Endpoints` requirement that keeps `report-to` from being inert, and the properties that make an unauthenticated public POST safe (always-204, 16 KB cap, rate limit, field allowlist, truncation). - **The sink is scoped to the soak.** The enforce PR must decide explicitly whether to retire it with the report-only twin or keep a `report-to` group on the enforced policy — written down so it isn't left behind as an orphan route. - **The `[csp]` log tag** in § 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 — marked as such. ### `api-route-inventory.json`: 199 → 200 The 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 - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. - [x] I have added or updated tests/docs where it makes sense. - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [ ] No AI tools were used to produce this contribution. - [x] AI tools were used. Tool(s): `Claude Code (Opus)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` / `Assisted-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it.
wtclaude added 1 commit 2026-07-27 20:21:38 +00:00
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>
whitlocktech approved these changes 2026-07-27 20:22:24 +00:00
whitlocktech merged commit 8c46af7e54 into main 2026-07-27 20:22:30 +00:00
whitlocktech deleted branch feature/csp-report-only 2026-07-27 20:22:30 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/docs#51
No description provided.