From 170c3c5e900ac715db4bc870aee2e7fe730bb764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Mon, 13 Apr 2026 21:55:40 +0400 Subject: [PATCH] =?UTF-8?q?security:=20Content=20Security=20Policy=20et=20?= =?UTF-8?q?headers=20HTTP=20s=C3=A9curit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- next.config.js | 13 --------- next.config.mjs | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ tasks/todo.md | 2 +- 3 files changed, 74 insertions(+), 14 deletions(-) delete mode 100644 next.config.js create mode 100644 next.config.mjs diff --git a/next.config.js b/next.config.js deleted file mode 100644 index 1281d30..0000000 --- a/next.config.js +++ /dev/null @@ -1,13 +0,0 @@ -/** @type {import('next').NextConfig} */ -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, -} - -module.exports = nextConfig diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 0000000..e828713 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,73 @@ +/** @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 diff --git a/tasks/todo.md b/tasks/todo.md index 6fd565f..cd3e77c 100644 --- a/tasks/todo.md +++ b/tasks/todo.md @@ -10,7 +10,7 @@ ## Améliorations hautes (P2) -- [ ] **Headers CSP** — ajouter dans `next.config.js` +- [x] **Headers CSP** — `next.config.mjs` (renommé depuis .js) avec CSP + 4 headers sécurité - [ ] **Tests unitaires** — Vitest sur `lib/format.js`, `lib/version-utils.js`, `lib/rate-limit.js` - [ ] **Tests extensions Directus** — mocks VersionsService - [ ] **Refresh token explicite** — callback `jwt` dans NextAuth options