applyContentSecurityPolicy($request, $handler); } /** * Apply Content-Security-Policy headers to an error response that bypassed * the normal middleware stack (e.g. responses from ErrorController). */ public function applyToResponse(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { return $this->applyContentSecurityPolicy($request, $response); } private function applyContentSecurityPolicy(ServerRequestInterface $request, ResponseInterface|RequestHandlerInterface $subject): ResponseInterface { $site = $request->getAttribute('site'); $cspConfiguration = $site instanceof Site ? ($site->getConfiguration()['contentSecurityPolicies'] ?? []) : []; $dispositionMap = $this->cspConfigurationFactory->buildDispositionMap($cspConfiguration); $behavior = $this->cspConfigurationFactory->buildBehavior($cspConfiguration); // return early in case CSP shall not be used if ($dispositionMap->keys() === []) { return $subject instanceof RequestHandlerInterface ? $subject->handle($request) : $subject; } $scope = Scope::frontendSite($site); $nonce = $this->requestId->nonce; $policyBag = new PolicyBag($scope, $dispositionMap, $behavior, $nonce, $this->directiveHashCollection); // make sure, the nonce value is set before processing the remaining components $request = $request ->withAttribute('nonce', $nonce) ->withAttribute('csp.policyBag', $policyBag); $response = $subject instanceof RequestHandlerInterface ? $subject->handle($request) : $subject; if ($response->hasHeader('Content-Security-Policy') || $response->hasHeader('Content-Security-Policy-Report-Only')) { if ($subject instanceof RequestHandlerInterface) { $this->logger->info('Content-Security-Policy not enforced due to existence of custom header', [ 'scope' => (string)$scope, 'uri' => (string)$request->getUri(), ]); } return $response; } $processedEarlier = $policyBag->hasPolicies(); $this->policyProvider->prepare($policyBag, $request, $response); foreach ($dispositionMap as $disposition => $dispositionConfiguration) { $policy = $policyBag->getPolicy($disposition); if ($policy->isEmpty()) { continue; } $response = $response->withHeader( $disposition->getHttpHeaderName(), $policy->compile($policyBag, $this->cache) ); } if (!$processedEarlier && $policyBag->behavior->useNonce === false) { $response = $this->responseService->dropNonceFromHtmlResponse($response, $nonce); } return $response; } }