Files
konstitisyon.nu/next.config.mjs
T
cedric 170c3c5e90 security: Content Security Policy et headers HTTP sécurité
- Renomme next.config.js → next.config.mjs (ESM, satisfait unicorn/prefer-module)
- Ajout de headers() avec CSP stricte :
    script/style-src 'unsafe-inline' (requis Next.js + Emotion/MUI)
    connect-src dynamique depuis les env vars Directus (API + WebSocket)
    object-src 'none', frame-ancestors 'none', base-uri 'self'
    img-src 'self' data: blob: (html2canvas / export PDF)
- Ajout X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
  Permissions-Policy sur toutes les routes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 21:55:40 +04:00

74 lines
2.0 KiB
JavaScript

/** @type {import('next').NextConfig} */
// Les URL Directus sont lues à l'exécution — elles s'adaptent à l'environnement
// (dev local ou production) sans rebuild.
const apiUrl = process.env.NEXT_PUBLIC_DIRECTUS_API_URL ?? ''
const wsUrl = process.env.NEXT_PUBLIC_DIRECTUS_API_WS_URL ?? ''
// Tokens CSP — les guillemets simples font partie de la spec CSP, pas de JS
const SELF = '\'self\''
const NONE = '\'none\''
const UNSAFE_INLINE = '\'unsafe-inline\''
// Content Security Policy
// - unsafe-inline sur script-src : requis par Next.js App Router (hydratation inline)
// - unsafe-inline sur style-src : requis par Emotion/MUI (styles injectés dynamiquement)
// - data: blob: sur img-src : requis par html2canvas (export PDF)
// - frame-ancestors 'none' : anti-clickjacking (complète X-Frame-Options)
const cspDirectives = [
`default-src ${SELF}`,
`script-src ${SELF} ${UNSAFE_INLINE}`,
`style-src ${SELF} ${UNSAFE_INLINE}`,
`connect-src ${SELF} ${apiUrl} ${wsUrl}`.trim(),
`img-src ${SELF} data: blob:`,
`font-src ${SELF}`,
`object-src ${NONE}`,
`base-uri ${SELF}`,
`form-action ${SELF}`,
`frame-ancestors ${NONE}`,
]
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: cspDirectives.join('; '),
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()',
},
]
const nextConfig = {
experimental: {
// Optimiser les imports pour réduire la mémoire
optimizePackageImports: ['@mui/material', '@mui/icons-material', '@emotion/react', '@emotion/styled'],
},
// Réduire l'utilisation mémoire
compress: true,
// Désactiver les source maps en dev pour économiser la mémoire
productionBrowserSourceMaps: false,
async headers() {
return [
{
source: '/(.*)',
headers: securityHeaders,
},
]
},
}
export default nextConfig