diff --git a/README.md b/README.md index 90e79e64..0feff814 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ Built with Next.js and the JMAP protocol. - **TOTP two-factor authentication** - **Account security panel** - manage passwords and 2FA via Stalwart admin API - **"Remember me"** - AES-256-GCM encrypted httpOnly cookie (opt-in) -- **Security headers** - enforced CSP (script-src 'self', no unsafe-inline), X-Frame-Options, Referrer-Policy; SSRF redirect validation; PDF iframe sandbox; IP spoofing prevention +- **Security headers** - enforced CSP with per-request nonce, X-Frame-Options, Referrer-Policy; SSRF redirect validation; PDF iframe sandbox; IP spoofing prevention - **Plugin hardening** - dangerous-pattern detection, admin approval required, secure HTTP proxy API (no auth-header exposure) - **Newsletter unsubscribe** (RFC 2369) diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 1bedc98c..12b1a4e3 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -18,8 +18,15 @@ export default async function LocaleLayout({ if (!(locales as readonly string[]).includes(locale)) notFound(); + let messages; + try { + messages = (await import(`@/locales/${locale}/common.json`)).default; + } catch { + notFound(); + } + return ( - + diff --git a/app/layout.tsx b/app/layout.tsx index 58c11fe6..c2a54606 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; +import { headers } from "next/headers"; import { getLocale } from "next-intl/server"; import { PWAInstallPrompt } from "@/components/pwa-install-prompt"; import { ServiceWorkerRegistration } from "@/components/service-worker-registration"; @@ -39,6 +40,7 @@ export default async function RootLayout({ children: React.ReactNode; }) { const locale = await getLocale(); + const nonce = (await headers()).get("x-nonce") ?? ""; const parentOrigin = process.env.NEXT_PUBLIC_PARENT_ORIGIN || ""; return ( @@ -55,7 +57,26 @@ export default async function RootLayout({ {parentOrigin && ( )} -