Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
SecureHeaders
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 2
42
0.00% covered (danger)
0.00%
0 / 1
 handle
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
20
 removeUnwantedHeaders
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace App\Http\Middleware;
4
5use Closure;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Route;
8use Illuminate\Support\Facades\View;
9use Illuminate\Support\Facades\Vite;
10use Symfony\Component\HttpFoundation\Response;
11
12class SecureHeaders
13{
14    /**
15     * Enumerate headers which you do not want in your application's responses.
16     * Great starting point would be to go check out @Scott_Helme's:
17     * https://securityheaders.com/
18     *
19     * @var array
20     */
21    private $unwantedHeaderList = [
22        'X-Powered-By',
23        'Server',
24    ];
25
26    /**
27     * Handle an incoming request.
28     *
29     * @param  \Illuminate\Http\Request                                                         $request
30     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
31     * @return \Symfony\Component\HttpFoundation\Response
32     * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
33     */
34    public function handle(Request $request, Closure $next): Response
35    {
36        // phpcs:enable
37        View::share('nonce', $nonce = Vite::useCspNonce());
38        // * For VueJS dev tool to execute
39        $jsDev     = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-{$nonce}' 'strict-dynamic'";
40        $scheme    = config('app.env') === 'local' ? 'http' : 'https';
41        $reportUri = Route::has('cspReportUri') ?
42            \sprintf(' report-uri %s ;', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
43        $this->removeUnwantedHeaders($this->unwantedHeaderList);
44        $response = $next($request);
45        $response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
46        $response->headers->set('X-Content-Type-Options', 'nosniff');
47        $response->headers->set('X-XSS-Protection', '1; mode=block');
48        $response->headers->set('X-Frame-Options', 'SAMEORIGIN');
49        $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
50        $cspPolicies = [
51            "default-src 'none';",
52            "manifest-src 'self';",
53            "connect-src 'self' blob:;",
54            "img-src * data: blob:;",
55            "font-src 'self' data: https://fonts.gstatic.com;",
56            "style-src 'self' 'unsafe-inline' " . \implode(' ', []) . ';',
57            "script-src 'self' {$jsDev} " . \implode(' ', [
58                'https:  blob:',
59            ]) . ';'
60        ];
61
62        $response->headers->set(
63            'Content-Security-Policy',
64            implode(' ', $cspPolicies) . $reportUri
65        );
66        $response->headers->set('Feature-Policy', 'fullscreen \'self\';');
67        return $response;
68    }
69
70    /**
71     * Remove unwanted headers
72     *
73     * @param array $headerList
74     * @return void
75     */
76    private function removeUnwantedHeaders(array $headerList): void
77    {
78        foreach ($headerList as $header) {
79            header_remove($header);
80        }
81    }
82}