*/ private $unwantedHeaderList = [ 'X-Powered-By', 'Server', ]; /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next * @return \Symfony\Component\HttpFoundation\Response * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint */ public function handle(Request $request, Closure $next): \Symfony\Component\HttpFoundation\Response { // phpcs:enable // * For Vite DEV $host = \parse_url(\config('app.url'), \PHP_URL_HOST); View::share('nonce', $nonce = Vite::useCspNonce()); // * For VueJS dev tool to execute $jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-{$nonce}' 'strict-dynamic'"; $scheme = config('app.env') === 'local' ? 'http' : 'https'; $reportUri = Route::has('cspReportUri') ? \sprintf(' report-uri %s ;', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : ''; $this->removeUnwantedHeaders($this->unwantedHeaderList); $response = $next($request); $response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade'); $response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('X-XSS-Protection', '1; mode=block'); $response->headers->set('X-Frame-Options', 'SAMEORIGIN'); $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;'); $cspPolicies = [ "default-src 'none';", "manifest-src 'self';", // * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html "connect-src 'self' blob: " . implode(' ', [ // * VITE DEV config('app.debug') ? "wss://{$host}:4443" : '', config('app.debug') ? "https://{$host}:4443" : '', 'https://yl.s-web.fr' ]) . ';', "img-src 'self' data: blob: " . implode(' ', [ // * Leaftlet With OpenStreetmaps 'https://tile.openstreetmap.org', // * Youtube thumbnails 'https://img.youtube.com/vi/', // * VITE DEV config('app.debug') ? "https://{$host}:4443" : '', ]) . ';', "object-src 'self' " . implode(' ', []) . ';', "frame-src 'self' " . implode(' ', [ 'https://www.google.com/recaptcha/', 'https://recaptcha.google.com/recaptcha/', 'https://www.youtube.com', 'https://www.youtube-nocookie.com/embed/', ]) . ';', "font-src 'self' data: " . \implode(' ', [ // * VITE DEV config('app.debug') ? "https://{$host}:4443" : '', 'https://fonts.gstatic.com;' ]) . ';', // ! Using CKEditor will must have unsafe-inline style // phpcs:ignore Generic.Files.LineLength.TooLong "style-src 'self' 'unsafe-inline' " . \implode(' ', [ 'https://cdnjs.cloudflare.com', // * File Manager 'https://fonts.googleapis.com', 'https://cdn.jsdelivr.net', 'https://use.fontawesome.com', 'https://fonts.googleapis.com', // * VITE DEV config('app.debug') ? "https://{$host}:4443" : '', ]) . ';', // ! If you dont use ckeditor use this // "style-src 'self' 'nonce-$nonce';", // phpcs:ignore Generic.Files.LineLength.TooLong "script-src 'self' {$jsDev} " . \implode(' ', [ 'https: blob:', 'https://www.googletagmanager.com', 'https://cdnjs.cloudflare.com', "$scheme://www.google-analytics.com", // * VITE DEV config('app.debug') ? "https://{$host}:4443" : '', ]) . ';' ]; $response->headers->set( 'Content-Security-Policy', implode(' ', $cspPolicies) . $reportUri ); $response->headers->set('Feature-Policy', 'fullscreen \'self\';'); return $response; } /** * Remove unwanted headers. * * @param list $headerList * @return void */ private function removeUnwantedHeaders(array $headerList): void { foreach ($headerList as $header) { header_remove($header); } } }