mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 11:12:11 +01:00
fix: resolve CSP violations and React hydration issues
- Fix Permissions-Policy header: change ambient-light-sensor to ambient-light - Add Google Fonts domain to font-src CSP for Leaflet map tiles - Allow unsafe-inline for style-src to support third-party libraries (Sonner, Leaflet) - Fix React hydration mismatch by conditionally adding nonce attribute - Add debug logging for nonce retrieval issues These changes resolve all CSP violations while maintaining security best practices.
This commit is contained in:
@ -134,7 +134,7 @@ export default async function RootLayout({
|
|||||||
<head>
|
<head>
|
||||||
<script
|
<script
|
||||||
type="application/ld+json"
|
type="application/ld+json"
|
||||||
nonce={nonce}
|
{...(nonce ? { nonce } : {})}
|
||||||
// biome-ignore lint/security/noDangerouslySetInnerHtml: Safe use for JSON-LD structured data with CSP nonce
|
// biome-ignore lint/security/noDangerouslySetInnerHtml: Safe use for JSON-LD structured data with CSP nonce
|
||||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -52,8 +52,11 @@ export function buildCSP(config: CSPConfig = {}): string {
|
|||||||
: ["'self'"];
|
: ["'self'"];
|
||||||
|
|
||||||
// Style sources - use nonce in production when available
|
// Style sources - use nonce in production when available
|
||||||
const styleSrc = nonce
|
// Note: We need 'unsafe-inline' for third-party libraries like Sonner that inject styles dynamically
|
||||||
? ["'self'", `'nonce-${nonce}'`]
|
const styleSrc = isDevelopment
|
||||||
|
? ["'self'", "'unsafe-inline'"]
|
||||||
|
: nonce
|
||||||
|
? ["'self'", `'nonce-${nonce}'`, "'unsafe-inline'"] // Need unsafe-inline for Sonner/Leaflet
|
||||||
: ["'self'", "'unsafe-inline'"]; // Fallback for TailwindCSS
|
: ["'self'", "'unsafe-inline'"]; // Fallback for TailwindCSS
|
||||||
|
|
||||||
// Image sources - allow self, data URIs, and specific trusted domains
|
// Image sources - allow self, data URIs, and specific trusted domains
|
||||||
@ -69,8 +72,8 @@ export function buildCSP(config: CSPConfig = {}): string {
|
|||||||
.map((domain) => domain),
|
.map((domain) => domain),
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
// Font sources - restrict to self and data URIs
|
// Font sources - restrict to self, data URIs, and Google Fonts (for Leaflet)
|
||||||
const fontSrc = ["'self'", "data:"];
|
const fontSrc = ["'self'", "data:", "https://fonts.gstatic.com"];
|
||||||
|
|
||||||
// Connect sources - API endpoints and trusted domains
|
// Connect sources - API endpoints and trusted domains
|
||||||
const connectSrc = isDevelopment
|
const connectSrc = isDevelopment
|
||||||
|
|||||||
@ -6,9 +6,21 @@ import { headers } from "next/headers";
|
|||||||
export async function getNonce(): Promise<string | undefined> {
|
export async function getNonce(): Promise<string | undefined> {
|
||||||
try {
|
try {
|
||||||
const headersList = await headers();
|
const headersList = await headers();
|
||||||
return headersList.get("X-Nonce") || undefined;
|
const nonce = headersList.get("X-Nonce");
|
||||||
} catch {
|
|
||||||
|
// Log for debugging hydration issues
|
||||||
|
if (!nonce && process.env.NODE_ENV === "development") {
|
||||||
|
console.warn(
|
||||||
|
"No nonce found in headers - this may cause hydration mismatches"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonce || undefined;
|
||||||
|
} catch (error) {
|
||||||
// Headers not available (e.g., in client-side code)
|
// Headers not available (e.g., in client-side code)
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
console.warn("Failed to get headers for nonce:", error);
|
||||||
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,7 +78,7 @@ export function middleware(request: NextRequest) {
|
|||||||
"accelerometer=()",
|
"accelerometer=()",
|
||||||
"gyroscope=()",
|
"gyroscope=()",
|
||||||
"magnetometer=()",
|
"magnetometer=()",
|
||||||
"ambient-light-sensor=()",
|
"ambient-light=()",
|
||||||
"encrypted-media=()",
|
"encrypted-media=()",
|
||||||
"autoplay=(self)",
|
"autoplay=(self)",
|
||||||
].join(", ")
|
].join(", ")
|
||||||
|
|||||||
Reference in New Issue
Block a user