Fix BLOCKING review findings: /login route + CSP header (WS1)
Behebt zwei BLOCKING-Befunde aus dem Review zu "Projekt-Fundament & Design-System": 1. Route-Namens-Mismatch (Default-deny-Kerngarantie): Login-Seite lag unter (auth)/anmelden, der gesamte downstream Auth-/Gating-Vertrag im Plan erwartet aber /login (NextAuth pages.signIn, requireSession-Redirect, PUBLIC_ALLOWLIST, Middleware-Matcher, auth-gating.spec toHaveURL(/\/login/), Datei-Layout (auth)/login/...). Verzeichnis nach (auth)/login umbenannt; /login als kanonischen Pfad im Guard-Slot-Kommentar von (app)/layout.tsx dokumentiert, damit Workstream 3 dieselbe Route verwendet. 2. Fehlende Content-Security-Policy in SECURITY_HEADERS: Plan Z.1314 fordert CSP mit default-src 'self', img-src 'self' data: blob:, worker-src 'self' blob:, frame-ancestors 'none', form-action 'self'; die security-headers.spec prueft frame-ancestors 'none'. CSP ergaenzt, in Produktion strikt, im Dev-Modus gelockerte script-src/connect-src (unsafe-eval + ws:) fuer Next.js-HMR via NODE_ENV. Verifikation: tsc --noEmit, next lint, next build (Route /login, kein /anmelden) gruen; CSP zur Laufzeit fuer prod/dev geprueft. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,10 @@ import { AppShell } from "@/components/layout/app-shell";
|
|||||||
//
|
//
|
||||||
// import { requireSession } from "@/lib/auth/guards";
|
// import { requireSession } from "@/lib/auth/guards";
|
||||||
// ...
|
// ...
|
||||||
// await requireSession(); // leitet anonyme Aufrufe auf /anmelden um
|
// await requireSession(); // leitet anonyme Aufrufe auf /login um
|
||||||
|
//
|
||||||
|
// Kanonischer Login-Pfad: /login (siehe (auth)/login/page.tsx). Dieselbe Route
|
||||||
|
// nutzen NextAuth pages.signIn, PUBLIC_ALLOWLIST und die Gating-Specs.
|
||||||
//
|
//
|
||||||
// Lese-Seiten dürfen sich NICHT allein auf die Middleware verlassen.
|
// Lese-Seiten dürfen sich NICHT allein auf die Middleware verlassen.
|
||||||
export default async function AppLayout({
|
export default async function AppLayout({
|
||||||
|
|||||||
@@ -1,8 +1,33 @@
|
|||||||
/**
|
/**
|
||||||
* Sicherheits-Header, eingehängt in next.config.ts.
|
* Sicherheits-Header, eingehängt in next.config.ts.
|
||||||
* CSP bewusst konservativ; bei Bedarf von Feature-Workstreams erweitert.
|
*
|
||||||
|
* Content-Security-Policy ist der zentrale Querschnitts-Schutz (Implementierungs-
|
||||||
|
* plan Z.1314): in Produktion strikt mit default-src 'self', frame-ancestors 'none'
|
||||||
|
* und form-action 'self'. Im Dev-Modus benötigt Next.js (HMR/React-Refresh) eine
|
||||||
|
* gelockerte script-src/connect-src-Variante ('unsafe-eval' + ws: für den Dev-Socket).
|
||||||
*/
|
*/
|
||||||
|
const isProd = process.env.NODE_ENV === "production";
|
||||||
|
|
||||||
|
const CSP = [
|
||||||
|
"default-src 'self'",
|
||||||
|
// Dev braucht eval (React Refresh) + inline; Prod bleibt strikt.
|
||||||
|
isProd
|
||||||
|
? "script-src 'self'"
|
||||||
|
: "script-src 'self' 'unsafe-eval' 'unsafe-inline'",
|
||||||
|
"style-src 'self' 'unsafe-inline'",
|
||||||
|
"img-src 'self' data: blob:",
|
||||||
|
"font-src 'self' data:",
|
||||||
|
"worker-src 'self' blob:",
|
||||||
|
// Dev: WebSocket für HMR erlauben.
|
||||||
|
isProd ? "connect-src 'self'" : "connect-src 'self' ws: wss:",
|
||||||
|
"frame-ancestors 'none'",
|
||||||
|
"form-action 'self'",
|
||||||
|
"base-uri 'self'",
|
||||||
|
"object-src 'none'",
|
||||||
|
].join("; ");
|
||||||
|
|
||||||
export const SECURITY_HEADERS: Record<string, string> = {
|
export const SECURITY_HEADERS: Record<string, string> = {
|
||||||
|
"Content-Security-Policy": CSP,
|
||||||
"X-Content-Type-Options": "nosniff",
|
"X-Content-Type-Options": "nosniff",
|
||||||
"X-Frame-Options": "DENY",
|
"X-Frame-Options": "DENY",
|
||||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||||
|
|||||||
Reference in New Issue
Block a user