fix(security): drop 'unsafe-eval' from CSP script-src (follow-up to #112) #114

Open
KnallerKalle wants to merge 4 commits from feat/csp-unsafe-eval into dev
KnallerKalle commented 2026-08-18 22:41:13 +00:00 (Migrated from github.com)

Plan

Den dokumentierten Follow-up aus PR #112 schließen: 'unsafe-eval' aus CSP script-src entfernen — es hielt die zweithäufigste XSS-Payload-Klasse (eval()/new Function für alles, was an der Nonce vorbei schmuggelt) offen. Deliverable: eval-freie Policy überall, dev-HMR bleibt funktional.

Outcome

  • src/lib/csp.ts: buildCspHeader(nonce, { allowDevEval } = { allowDevEval: false }) — Default false, Opt-in fügt 'unsafe-eval' hinzu
  • src/middleware.ts: setzt allowDevEval ausschließlich außerhalb von production (NODE_ENV-Gate; im Edge-Bundle statisch wegoptimiert — verifiziert im kompilierten Chunk: allowDevEval:!1 als Default, kein Runtime-Env-Lookup)
  • next.config.js: statisches Fallback (middleware-unerreichbare Pfade, z. B. /_next/static/*) ebenfalls eval-frei
  • Neue Blackbox-Prüfung 25f „script-src has NO 'unsafe-eval' (eval-class XSS closed)"
  • Unit-Guards: csp-nonce.test.ts (eval-frei by default) + deterministischer vi.stubEnv-Fall in csp-middleware.test.ts („eval gate follows NODE_ENV deterministically")
  • Client-Bundle-Audit: 0× eval()/new Function, 0× String-arg-setTimeout, importScripts nur im Worker-Chunk-Loading (worker-src 'self' blob: — nicht eval-gebannt)

Impact on release goals

  • Security-Härtungs-Kette vollständig: script-src ist jetzt nonce-only + 'self' ('unsafe-inline' → #112, 'unsafe-eval' → dieser PR). Eval-class XSS zu.
  • Blackbox-Suite 113 → 114 Checks (+1 Guard 25f); Unit-Suite 411 → 416 (+5 Guards).

Blackbox

  • python3 scripts/blackbox_test.py --target testing → red-first bewiesen: 113/114, exakt 25f rot (der Pre-Fix-Build läuft dort; grün erst nach Merge+Deploy)
  • Lokaler prod-build (next build + next start): 25f GRÜN, Gesamt 101/102 — die eine Rot ist Test 16 (PDF smart-import, sandbox-bedingt: kein AI_BACKEND lokal; auf testing ✓)
  • Unit: 416/416 grün; tsc clean; next build grün
  • Nonce-Stempfung am Server-HTML per curl verifiziert: alle Inline-Scripts tragen die per-Request-Nonce, 0 un-gestempelte Inline-Scripts

Six-perspectives check

  • Product: keine User-Facing-Regression; Security-Story („freie Bewegung, keine Sorge um Injection") intakt
  • Engineering: minimaler Diff (3 Dateien, +16/−7); Tests rot-vor-grün; kein Env-Lookup im Edge-Runtime-Pfad
  • Psychology: neutral — keine Verhaltens-/Motivationsänderung
  • Biomechanics/Anatomy: n/a (Security-Policy)
  • Feldenkrais: keine Prinzip-Berührung; App-Verhalten unverändert

Risk

Gering. Policy-Builder ohne Verhaltensänderung am App-Code; Nonce-Stempfung live verifiziert. Residual: (a) Playwright-Interaktivitätstest in der Sandbox nicht möglich (Chromium-Download gedrosselt) — stattdessen äquivalenter Nonce-Stempel-Nachweis am Server-HTML; (b) Sandbox-Fallstrick dokumentiert: ein veralteter next-server auf dem Port machte die alte Policy sichtbar. Hinweis aus #112: kein CI-Grün auf PR-Heads möglich (account-weites GitHub-Actions-Billing-Problem, identische Annotation auf dev-HEAD) — lokale Äquivalent-Verifikation mit exakten CI-Befehlen statt dessen.

Checklist

  • Branch ab aktuellstem dev
  • Conventional Commits (test: → fix(security): → docs(development):)
  • Patchlog-Eintrag angehängt
  • Roadmap-Status aktualisiert
  • Keine Secrets / keine Binaries

Created by an AI agent (OpenHands) on behalf of the ATM automation.

## Plan Den dokumentierten Follow-up aus PR #112 schließen: `'unsafe-eval'` aus CSP `script-src` entfernen — es hielt die zweithäufigste XSS-Payload-Klasse (eval()/`new Function` für alles, was an der Nonce vorbei schmuggelt) offen. Deliverable: eval-freie Policy überall, dev-HMR bleibt funktional. ## Outcome - `src/lib/csp.ts`: `buildCspHeader(nonce, { allowDevEval } = { allowDevEval: false })` — **Default false**, Opt-in fügt `'unsafe-eval'` hinzu - `src/middleware.ts`: setzt `allowDevEval` ausschließlich außerhalb von production (NODE_ENV-Gate; im Edge-Bundle statisch wegoptimiert — verifiziert im kompilierten Chunk: `allowDevEval:!1` als Default, kein Runtime-Env-Lookup) - `next.config.js`: statisches Fallback (middleware-unerreichbare Pfade, z. B. `/_next/static/*`) ebenfalls eval-frei - Neue Blackbox-Prüfung **25f** „script-src has NO 'unsafe-eval' (eval-class XSS closed)" - Unit-Guards: `csp-nonce.test.ts` (eval-frei by default) + deterministischer `vi.stubEnv`-Fall in `csp-middleware.test.ts` („eval gate follows NODE_ENV deterministically") - Client-Bundle-Audit: 0× eval()/`new Function`, 0× String-arg-`setTimeout`, `importScripts` nur im Worker-Chunk-Loading (`worker-src 'self' blob:` — nicht eval-gebannt) ## Impact on release goals - Security-Härtungs-Kette vollständig: `script-src` ist jetzt **nonce-only + `'self'`** (`'unsafe-inline'` → #112, `'unsafe-eval'` → dieser PR). Eval-class XSS zu. - Blackbox-Suite 113 → **114 Checks** (+1 Guard 25f); Unit-Suite 411 → **416** (+5 Guards). ## Blackbox - [x] `python3 scripts/blackbox_test.py --target testing` → red-first bewiesen: **113/114, exakt 25f rot** (der Pre-Fix-Build läuft dort; grün erst nach Merge+Deploy) - [x] Lokaler prod-build (`next build` + `next start`): **25f GRÜN**, Gesamt 101/102 — die eine Rot ist Test 16 (PDF smart-import, sandbox-bedingt: kein `AI_BACKEND` lokal; auf testing ✓) - [x] Unit: 416/416 grün; `tsc` clean; `next build` grün - [x] Nonce-Stempfung am Server-HTML per curl verifiziert: alle Inline-Scripts tragen die per-Request-Nonce, **0** un-gestempelte Inline-Scripts ## Six-perspectives check - [x] Product: keine User-Facing-Regression; Security-Story („freie Bewegung, keine Sorge um Injection") intakt - [x] Engineering: minimaler Diff (3 Dateien, +16/−7); Tests rot-vor-grün; kein Env-Lookup im Edge-Runtime-Pfad - [x] Psychology: neutral — keine Verhaltens-/Motivationsänderung - [x] Biomechanics/Anatomy: n/a (Security-Policy) - [x] Feldenkrais: keine Prinzip-Berührung; App-Verhalten unverändert ## Risk Gering. Policy-Builder ohne Verhaltensänderung am App-Code; Nonce-Stempfung live verifiziert. Residual: (a) Playwright-Interaktivitätstest in der Sandbox nicht möglich (Chromium-Download gedrosselt) — stattdessen äquivalenter Nonce-Stempel-Nachweis am Server-HTML; (b) Sandbox-Fallstrick dokumentiert: ein veralteter `next-server` auf dem Port machte die alte Policy sichtbar. Hinweis aus #112: kein CI-Grün auf PR-Heads möglich (account-weites GitHub-Actions-Billing-Problem, identische Annotation auf dev-HEAD) — lokale Äquivalent-Verifikation mit exakten CI-Befehlen statt dessen. ## Checklist - [x] Branch ab aktuellstem `dev` - [x] Conventional Commits (`test:` → `fix(security):` → `docs(development):`) - [x] Patchlog-Eintrag angehängt - [x] Roadmap-Status aktualisiert - [x] Keine Secrets / keine Binaries _Created by an AI agent (OpenHands) on behalf of the ATM automation._
This pull request has changes conflicting with the target branch.
  • .development/patchlog.md
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/csp-unsafe-eval:feat/csp-unsafe-eval
git switch feat/csp-unsafe-eval

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch dev
git merge --no-ff feat/csp-unsafe-eval
git switch feat/csp-unsafe-eval
git rebase dev
git switch dev
git merge --ff-only feat/csp-unsafe-eval
git switch feat/csp-unsafe-eval
git rebase dev
git switch dev
git merge --no-ff feat/csp-unsafe-eval
git switch dev
git merge --squash feat/csp-unsafe-eval
git switch dev
git merge --ff-only feat/csp-unsafe-eval
git switch dev
git merge feat/csp-unsafe-eval
git push origin dev
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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
Kalle/ATM!114
No description provided.