[VS-NfD] Add security response headers and an explicitly restrictive CORS policy #197

Closed
opened 2026-07-30 01:43:34 +02:00 by fable-5 · 1 comment
Collaborator

Plan reference: 20-massnahmenplan.md -> Phase 2
ADR: n/a
Effort: S (1 AT)
Depends on:

Context

Response headers are the cheapest verifiable hardening evidence there is,
and their absence is the first thing an automated assessment reports. CORS
must be a stated decision, not an implicit default.

Current state

  • helmet is not a dependency of apps/api and appears nowhere in
    apps/api/src; no CORS configuration was found in the api bootstrap.
  • The web tier already ships a strict CSP (default-src 'self',
    script-src 'self') — the api's own responses are the gap.

Acceptance criteria

  • The api sends HSTS, X-Content-Type-Options, Referrer-Policy,
    X-Frame-Options/frame-ancestors, and a Permissions-Policy, each
    value chosen deliberately.
  • CORS is configured explicitly and restrictively (APP_BASE_URL
    origin only, credentials rules stated); a cross-origin request from
    another origin is rejected by test.
  • The plugin sandbox's framing requirements (ADR 0008) are verified not
    to break — covered by an existing or new plugin e2e assertion.
  • A test asserts the header set on a representative api response, so
    regressions are caught.
  • docs/architecture/security.md lists the headers and their reasons.

Out of scope

Changing the web tier's CSP, and certificate/TLS termination (operator's
reverse proxy).

**Plan reference:** `20-massnahmenplan.md` -> Phase 2 **ADR:** n/a **Effort:** S (1 AT) **Depends on:** — ## Context Response headers are the cheapest verifiable hardening evidence there is, and their absence is the first thing an automated assessment reports. CORS must be a stated decision, not an implicit default. ## Current state - `helmet` is not a dependency of `apps/api` and appears nowhere in `apps/api/src`; no CORS configuration was found in the api bootstrap. - The web tier already ships a strict CSP (`default-src 'self'`, `script-src 'self'`) — the api's own responses are the gap. ## Acceptance criteria - [ ] The api sends HSTS, `X-Content-Type-Options`, `Referrer-Policy`, `X-Frame-Options`/frame-ancestors, and a `Permissions-Policy`, each value chosen deliberately. - [ ] CORS is configured explicitly and restrictively (`APP_BASE_URL` origin only, credentials rules stated); a cross-origin request from another origin is rejected by test. - [ ] The plugin sandbox's framing requirements (ADR 0008) are verified not to break — covered by an existing or new plugin e2e assertion. - [ ] A test asserts the header set on a representative api response, so regressions are caught. - [ ] `docs/architecture/security.md` lists the headers and their reasons. ## Out of scope Changing the web tier's CSP, and certificate/TLS termination (operator's reverse proxy).
fable-5 added this to the M24 — VS-NfD: security quick wins milestone 2026-07-30 01:43:34 +02:00
fable-5 added the
area:ops
effort:S
vs-nfd
labels 2026-07-30 01:43:34 +02:00
Author
Collaborator

Done in PR #252 (commit 3c62b7b, CI run 504 green, fast-forward merge).All acceptance criteria met: header set (HSTS, nosniff, Referrer-Policy no-referrer, X-Frame-Options SAMEORIGIN, minimal Permissions-Policy) via a hand-rolled middleware wired through the AppModule MiddlewareConsumer; CORS explicitly restrictive (only the APP_BASE_URL origin is echoed, Vary: Origin, foreign origin gets no ACAO by test); plugin-sandbox framing verified by a new frame assertion in plugins.e2e.db.test.ts; header set fenced by security-headers.e2e.test.ts; rationale table in docs/architecture/security.md.

Done in PR #252 (commit 3c62b7b, CI run 504 green, fast-forward merge).All acceptance criteria met: header set (HSTS, nosniff, Referrer-Policy no-referrer, X-Frame-Options SAMEORIGIN, minimal Permissions-Policy) via a hand-rolled middleware wired through the AppModule MiddlewareConsumer; CORS explicitly restrictive (only the APP_BASE_URL origin is echoed, Vary: Origin, foreign origin gets no ACAO by test); plugin-sandbox framing verified by a new frame assertion in plugins.e2e.db.test.ts; header set fenced by security-headers.e2e.test.ts; rationale table in docs/architecture/security.md.
Sign in to join this conversation.
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stwaidele/dorfteich#197
No description provided.