# CapaKraken — Umsetzungsplan: Security + Platform Issues Gitea-Repo: `https://gitea.hartmut-noerenberg.com/Hartmut/plANARCHY` Stand: 2026-04-01 | Issues: #19, #20, #21, #22, #23, #24 --- ## Anforderungsanalyse Alle 6 Issues stammen aus einem Security-Audit und einem Plattform-Review. 5 davon sind Security-Findings (OWASP A02/A05/A07/A09), 1 ist ein Plattform-Thema (Docker-Reproduzierbarkeit). Die Security-Issues sind weitgehend unabhängig voneinander; lediglich #23 greift in den tRPC-Request-Pfad ein und sollte zuletzt umgesetzt werden, da es den Haupt-Auth-Pfad berührt. --- ## Betroffene Pakete & Dateien | Issue | Paket/Pfad | Datei | Art | |-------|-----------|-------|-----| | #21 | `apps/web` | `src/app/api/perf/route.ts` | edit | | #19 | `apps/web` | `src/components/security/MfaSetup.tsx` | edit | | #19 | `apps/web` | `package.json` | edit (neue Dep: `qrcode` + `@types/qrcode`) | | #20 | `packages/api` | `src/lib/webhook-dispatcher.ts` | edit | | #20 | `packages/api` | `src/router/webhook-support.ts` | edit | | #20 | `packages/api` | `src/lib/ssrf-guard.ts` | create | | #22 | `apps/web` | `next.config.ts` | edit | | #23 | `apps/web` | `src/app/api/trpc/[trpc]/route.ts` | edit | | #23 | `apps/web` | `src/server/auth.ts` | edit (Doku/Cleanup) | | #24 | root | `Dockerfile.dev`, `Dockerfile.prod`, `docker-compose.yml`, `docker-compose.prod.yml`, `tooling/docker/app-dev-start.sh` | edit | --- ## Task-Liste (in empfohlener Reihenfolge) ### Issue #21 — /api/perf fail-closed + Query-Token entfernen - [ ] **Task 1:** `GET`-Handler in `apps/web/src/app/api/perf/route.ts` ändern: - `if (cronSecret)` → `if (!cronSecret) return 401/403` (fail-closed) - `queryToken`-Zweig vollständig entfernen - Nur noch `Authorization: Bearer ` prüfen - → Datei: `apps/web/src/app/api/perf/route.ts` - [ ] **Task 2:** Unit-Tests für `/api/perf`: - Test: autorisiert per Header → 200 - Test: kein Secret → 401 - Test: Query-Param-Token → 401 (nicht mehr akzeptiert) - Test: fehlende `CRON_SECRET`-Env → fail-closed (kein Metrics-Leak) - → Datei: `apps/web/src/app/api/perf/route.test.ts` (neu) --- ### Issue #19 — MFA QR lokal rendern - [ ] **Task 3:** `qrcode`-Paket und `@types/qrcode` zu `apps/web/package.json` hinzufügen, `pnpm install` ausführen. - [ ] **Task 4:** `MfaSetup.tsx` umschreiben: - `` durch lokale QR-Generierung ersetzen - `qrcode.toDataURL(uri)` im Client-Effekt aufrufen und als `` rendern - Sicherstellen: der `otpauth://`-URI verlässt den Browser nicht mehr - → Datei: `apps/web/src/components/security/MfaSetup.tsx` - [ ] **Task 5:** Test sicherstellen, dass kein Rendering-Request an externe QR-URL geht: - Unit-Test oder Playwright-Test der prüft, dass kein `` mit `qrserver.com` oder `chart.googleapis.com` gerendert wird - → Datei: `apps/web/src/components/security/MfaSetup.test.tsx` (neu oder bestehend ergänzen) --- ### Issue #20 — Webhook SSRF-Schutz - [ ] **Task 6:** `ssrf-guard.ts` erstellen mit einer `assertWebhookUrlAllowed(url: string): void`-Funktion: - Parst die URL, löst Hostname auf (DNS-Check via Node `dns.lookup`) - Blockt: Loopback (`127.0.0.0/8`, `::1`), RFC1918 (`10.x`, `172.16–31.x`, `192.168.x`), Link-Local (`169.254.x`), Cloud-Metadata (`169.254.169.254`) - Blockt: alle Schemes außer `https` (und `http` nur wenn expliziter Dev-Override gesetzt) - Wirft `TRPCError({ code: "BAD_REQUEST" })` mit allgemeiner Fehlermeldung (ohne IP preiszugeben) - → Datei: `packages/api/src/lib/ssrf-guard.ts` - [ ] **Task 7:** `ssrf-guard` in `webhook-support.ts` und `webhook-dispatcher.ts` integrieren: - Vor Speicherung + vor Dispatch `assertWebhookUrlAllowed(url)` aufrufen - → Dateien: `packages/api/src/router/webhook-support.ts`, `packages/api/src/lib/webhook-dispatcher.ts` - [ ] **Task 8:** Unit-Tests für `ssrf-guard.ts`: - Erlaubt: `https://example.com/hook` - Blockt: `http://localhost/…`, `http://127.0.0.1/…`, `http://10.0.0.1/…`, `http://192.168.1.1/…`, `http://169.254.169.254/…`, `ftp://…` - → Datei: `packages/api/src/__tests__/ssrf-guard.test.ts` (neu) --- ### Issue #22 — CSP härten - [ ] **Task 9:** CSP in `apps/web/next.config.ts` überarbeiten: - `unsafe-eval` entfernen oder nur für `NODE_ENV === "development"` erlauben - `unsafe-inline` aus `script-src` entfernen - Nonce-basierte Inline-Scripts prüfen: Next.js 15 unterstützt CSP-Nonces via `nonce`-Prop auf `