environment(); app()->detectEnvironment(fn () => 'production'); config([ 'app.debug' => false, 'app.url' => 'https://test-y0k0.fotospiel.app', 'security_headers.force_hsts' => true, 'cors.allowed_origins' => ['https://test-y0k0.fotospiel.app'], 'cors.paths' => ['*'], ]); Route::middleware('web')->get('/__test/marketing', fn () => response('ok')); Route::middleware('web')->get('/__test/auth', fn () => response('ok')); try { $response = $this->withServerVariables([ 'HTTPS' => 'on', 'HTTP_ORIGIN' => 'https://test-y0k0.fotospiel.app', ])->get('/__test/marketing'); $response->assertOk(); $response->assertHeader('X-Frame-Options', 'SAMEORIGIN'); $response->assertHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); $response->assertHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()'); $response->assertHeader('Content-Security-Policy'); $response->assertHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); $response->assertCookie('XSRF-TOKEN'); $login = $this->withServerVariables([ 'HTTPS' => 'on', 'HTTP_ORIGIN' => 'https://test-y0k0.fotospiel.app', ])->get('/__test/auth'); $login->assertOk(); $login->assertHeader('Content-Security-Policy'); $login->assertHeader('X-Frame-Options', 'SAMEORIGIN'); $login->assertCookie('XSRF-TOKEN'); } finally { app()->detectEnvironment(fn () => $originalEnv); } } public function test_cors_headers_present_for_api_requests_and_error_responses_keep_headers(): void { $originalEnv = app()->environment(); app()->detectEnvironment(fn () => 'production'); $corsConfig = config('cors'); $corsConfig['paths'] = ['*']; $corsConfig['allowed_origins'] = ['https://test-y0k0.fotospiel.app']; config([ 'app.debug' => false, 'app.url' => 'https://test-y0k0.fotospiel.app', 'cors' => $corsConfig, ]); Route::middleware('web')->get('/__test/error', fn () => throw new \RuntimeException('boom')); try { $corsMiddleware = new HandleCors(app(), new CorsService(config('cors'))); $this->assertContains('*', config('cors.paths')); $this->assertContains('https://test-y0k0.fotospiel.app', config('cors.allowed_origins')); $corsRequest = Request::create( uri: '/api/__test/cors', method: 'OPTIONS', server: [ 'HTTP_ORIGIN' => 'https://test-y0k0.fotospiel.app', 'HTTPS' => 'on', 'HTTP_ACCESS_CONTROL_REQUEST_METHOD' => 'GET', ], ); $this->assertSame('https://test-y0k0.fotospiel.app', $corsRequest->headers->get('Origin')); $corsResponse = $corsMiddleware->handle($corsRequest, fn () => response()->noContent()); $this->assertSame('https://test-y0k0.fotospiel.app', $corsResponse->headers->get('Access-Control-Allow-Origin')); $error = $this->withServerVariables([ 'HTTPS' => 'on', ])->get('/__test/error'); $error->assertStatus(500); $error->assertHeader('Content-Security-Policy'); $error->assertHeader('X-Frame-Options', 'SAMEORIGIN'); } finally { app()->detectEnvironment(fn () => $originalEnv); } } public function test_guest_pwa_allows_camera_via_permissions_policy(): void { $originalEnv = app()->environment(); app()->detectEnvironment(fn () => 'production'); config([ 'app.debug' => false, 'app.url' => 'https://test-y0k0.fotospiel.app', 'security_headers.force_hsts' => true, ]); Route::middleware('web')->get('/e/test-token', fn () => response('ok')); try { $response = $this->withServerVariables([ 'HTTPS' => 'on', 'HTTP_ORIGIN' => 'https://test-y0k0.fotospiel.app', ])->get('/e/test-token'); $response->assertOk(); $response->assertHeader('Permissions-Policy', 'camera=(self), microphone=(), geolocation=()'); } finally { app()->detectEnvironment(fn () => $originalEnv); } } }