Index: content/browser/frame_host/navigation_request.cc |
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc |
index 5bf934e4b5ba66a187672cdcc8ac44cb870f24e4..dee3a58b6062ec0d4149f9987fbe643216bec222 100644 |
--- a/content/browser/frame_host/navigation_request.cc |
+++ b/content/browser/frame_host/navigation_request.cc |
@@ -383,7 +383,25 @@ void NavigationRequest::BeginNavigation() { |
DCHECK(state_ == NOT_STARTED || state_ == WAITING_FOR_RENDERER_RESPONSE); |
TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this, |
"BeginNavigation"); |
+ |
state_ = STARTED; |
+ |
+ // Check Content Security Policy before the NavigationThrottles run. This |
+ // gives CSP a chance to modify requests that NavigationThrottles would |
+ // otherwise block. Similarly, the NavigationHandle is created afterwards, so |
+ // that it gets the request URL after potentially being modified by CSP. |
+ if (CheckContentSecurityPolicyFrameSrc(false /* is redirect */) == |
+ CONTENT_SECURITY_POLICY_CHECK_FAILED) { |
+ // Create a navigation handle so that the correct error code can be set on |
+ // it by OnRequestFailed(). |
+ CreateNavigationHandle(); |
+ OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT); |
+ |
+ // DO NOT ADD CODE after this. The previous call to OnRequestFailed has |
+ // destroyed the NavigationRequest. |
+ return; |
+ } |
+ |
CreateNavigationHandle(); |
RenderFrameDevToolsAgentHost::OnBeforeNavigation(navigation_handle_.get()); |
@@ -508,6 +526,18 @@ void NavigationRequest::OnRequestRedirected( |
common_params_.referrer = |
Referrer::SanitizeForRequest(common_params_.url, common_params_.referrer); |
+ // Check Content Security Policy before the NavigationThrottles run. This |
+ // gives CSP a chance to modify requests that NavigationThrottles would |
+ // otherwise block. |
+ if (CheckContentSecurityPolicyFrameSrc(true /* is redirect */) == |
+ CONTENT_SECURITY_POLICY_CHECK_FAILED) { |
+ OnRequestFailed(false, net::ERR_BLOCKED_BY_CLIENT); |
+ |
+ // DO NOT ADD CODE after this. The previous call to OnRequestFailed has |
+ // destroyed the NavigationRequest. |
+ return; |
+ } |
+ |
// For non browser initiated navigations we need to check if the source has |
// access to the URL. We always allow browser initiated requests. |
// TODO(clamy): Kill the renderer if FilterURL fails? |
@@ -894,4 +924,32 @@ void NavigationRequest::CommitNavigation() { |
frame_tree_node_->ResetNavigationRequest(true, true); |
} |
+NavigationRequest::ContentSecurityPolicyCheckResult |
+NavigationRequest::CheckContentSecurityPolicyFrameSrc(bool is_redirect) { |
+ if (common_params_.url.SchemeIs(url::kAboutScheme)) |
+ return CONTENT_SECURITY_POLICY_CHECK_PASSED; |
+ |
+ if (common_params_.should_check_main_world_csp == |
+ CSPDisposition::DO_NOT_CHECK) { |
+ return CONTENT_SECURITY_POLICY_CHECK_PASSED; |
+ } |
+ |
+ // The CSP frame-src directive only applies to subframes. |
+ if (frame_tree_node()->IsMainFrame()) |
+ return CONTENT_SECURITY_POLICY_CHECK_PASSED; |
+ |
+ FrameTreeNode* parent_ftn = frame_tree_node()->parent(); |
+ DCHECK(parent_ftn); |
+ RenderFrameHostImpl* parent = parent_ftn->current_frame_host(); |
+ DCHECK(parent); |
+ |
+ if (parent->IsAllowedByCsp( |
+ CSPDirective::FrameSrc, common_params_.url, is_redirect, |
+ common_params_.source_location.value_or(SourceLocation()))) { |
+ return CONTENT_SECURITY_POLICY_CHECK_PASSED; |
+ } |
+ |
+ return CONTENT_SECURITY_POLICY_CHECK_FAILED; |
+} |
+ |
} // namespace content |