Commit e5d35d10 authored by Yutaka Hirano's avatar Yutaka Hirano Committed by Commit Bot

Introduce COEP reporting for CORP (2/3)

1: https://crrev.com/c/2074177
2: [this]
3: https://crrev.com/c/2076223

This series of CLs implements https://github.com/mikewest/corpp/pull/9.
We introduce network::mojom::CrossOriginEmbedderPolicyReporter and
its implementation content::CrossOriginEmbedderPolicyReporter, implement
the reporting logic in content::CrossOriginEmbedderPolicyReporter and
the CORP check, and plumb the mojo interface.

This CL implements the reporting logic in the CORP check.

Bug: 1052764
Change-Id: Ia3c4d3aec886c76be6dd32083809e9e447ce2a4f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2075002
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747172}
parent 79be40b5
...@@ -140,8 +140,9 @@ bool ResponseBlockedByCrossOriginResourcePolicy( ...@@ -140,8 +140,9 @@ bool ResponseBlockedByCrossOriginResourcePolicy(
corp_header_value = corp_header->second; corp_header_value = corp_header->second;
return CrossOriginResourcePolicy::IsBlockedByHeaderValue( return CrossOriginResourcePolicy::IsBlockedByHeaderValue(
response->url_list.back(), document_origin, corp_header_value, response->url_list.back(), response->url_list.front(),
RequestMode::kNoCors, document_origin, document_coep) document_origin, corp_header_value, RequestMode::kNoCors,
document_origin, document_coep)
.has_value(); .has_value();
} }
......
...@@ -1793,7 +1793,7 @@ void NavigationRequest::OnResponseStarted( ...@@ -1793,7 +1793,7 @@ void NavigationRequest::OnResponseStarted(
// The CORP check for nested navigation. // The CORP check for nested navigation.
if (base::Optional<network::BlockedByResponseReason> blocked_reason = if (base::Optional<network::BlockedByResponseReason> blocked_reason =
network::CrossOriginResourcePolicy::IsNavigationBlocked( network::CrossOriginResourcePolicy::IsNavigationBlocked(
common_params_->url, common_params_->url, redirect_chain_[0],
GetParentFrame()->GetLastCommittedOrigin(), *response_head_, GetParentFrame()->GetLastCommittedOrigin(), *response_head_,
GetParentFrame()->GetLastCommittedOrigin(), GetParentFrame()->GetLastCommittedOrigin(),
cross_origin_embedder_policy)) { cross_origin_embedder_policy)) {
......
...@@ -848,8 +848,9 @@ CrossOriginReadBlocking::ResponseAnalyzer::ShouldBlockBasedOnHeaders( ...@@ -848,8 +848,9 @@ CrossOriginReadBlocking::ResponseAnalyzer::ShouldBlockBasedOnHeaders(
mojom::RequestMode::kNoCors; mojom::RequestMode::kNoCors;
// COEP is not supported when OOR-CORS is disabled. // COEP is not supported when OOR-CORS is disabled.
if (CrossOriginResourcePolicy::IsBlocked( if (CrossOriginResourcePolicy::IsBlocked(
request_url, request_initiator, response, kOverreachingRequestMode, request_url, request_url, request_initiator, response,
request_initiator_site_lock, CrossOriginEmbedderPolicy())) { kOverreachingRequestMode, request_initiator_site_lock,
CrossOriginEmbedderPolicy())) {
// Ignore mime types and/or sniffing and have CORB block all responses with // Ignore mime types and/or sniffing and have CORB block all responses with
// COR*P* header. // COR*P* header.
return kBlock; return kBlock;
......
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
#include "base/feature_list.h" #include "base/feature_list.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/http/http_response_headers.h" #include "net/http/http_response_headers.h"
#include "services/network/public/cpp/cross_origin_embedder_policy.h"
#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/initiator_lock_compatibility.h" #include "services/network/public/cpp/initiator_lock_compatibility.h"
#include "services/network/public/cpp/url_loader_completion_status.h" #include "services/network/public/cpp/url_loader_completion_status.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h" #include "services/network/public/mojom/url_response_head.mojom.h"
#include "url/gurl.h" #include "url/gurl.h"
#include "url/origin.h" #include "url/origin.h"
...@@ -169,6 +171,45 @@ base::Optional<BlockedByResponseReason> IsBlockedInternal( ...@@ -169,6 +171,45 @@ base::Optional<BlockedByResponseReason> IsBlockedInternal(
return BlockedByResponseReason::kCorpNotSameSite; return BlockedByResponseReason::kCorpNotSameSite;
} }
base::Optional<BlockedByResponseReason> IsBlockedInternalWithReporting(
CrossOriginResourcePolicy::ParsedHeader policy,
const GURL& request_url,
const GURL& original_url,
const base::Optional<url::Origin>& request_initiator,
mojom::RequestMode request_mode,
base::Optional<url::Origin> request_initiator_site_lock,
const CrossOriginEmbedderPolicy& embedder_policy,
mojom::CrossOriginEmbedderPolicyReporter* reporter) {
constexpr auto kBlockedDueToCoep = BlockedByResponseReason::
kCorpNotSameOriginAfterDefaultedToSameOriginByCoep;
if (embedder_policy.report_only_value ==
mojom::CrossOriginEmbedderPolicyValue::kRequireCorp &&
reporter) {
const auto result = IsBlockedInternal(
policy, request_url, request_initiator, request_mode,
request_initiator_site_lock, embedder_policy.report_only_value);
if (result == kBlockedDueToCoep ||
(result.has_value() && request_mode == mojom::RequestMode::kNavigate)) {
reporter->QueueCorpViolationReport(original_url, /*report_only=*/true);
}
}
if (request_mode == mojom::RequestMode::kNavigate &&
embedder_policy.value == mojom::CrossOriginEmbedderPolicyValue::kNone) {
return base::nullopt;
}
const auto result =
IsBlockedInternal(policy, request_url, request_initiator, request_mode,
request_initiator_site_lock, embedder_policy.value);
if (reporter &&
(result == kBlockedDueToCoep ||
(result.has_value() && request_mode == mojom::RequestMode::kNavigate))) {
reporter->QueueCorpViolationReport(original_url, /*report_only=*/false);
}
return result;
}
} // namespace } // namespace
// static // static
...@@ -178,11 +219,13 @@ const char CrossOriginResourcePolicy::kHeaderName[] = ...@@ -178,11 +219,13 @@ const char CrossOriginResourcePolicy::kHeaderName[] =
// static // static
base::Optional<BlockedByResponseReason> CrossOriginResourcePolicy::IsBlocked( base::Optional<BlockedByResponseReason> CrossOriginResourcePolicy::IsBlocked(
const GURL& request_url, const GURL& request_url,
const GURL& original_url,
const base::Optional<url::Origin>& request_initiator, const base::Optional<url::Origin>& request_initiator,
const network::mojom::URLResponseHead& response, const network::mojom::URLResponseHead& response,
mojom::RequestMode request_mode, mojom::RequestMode request_mode,
base::Optional<url::Origin> request_initiator_site_lock, base::Optional<url::Origin> request_initiator_site_lock,
const CrossOriginEmbedderPolicy& embedder_policy) { const CrossOriginEmbedderPolicy& embedder_policy,
mojom::CrossOriginEmbedderPolicyReporter* reporter) {
// From https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header: // From https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header:
// > 1. If request’s mode is not "no-cors", then return allowed. // > 1. If request’s mode is not "no-cors", then return allowed.
if (request_mode != mojom::RequestMode::kNoCors) if (request_mode != mojom::RequestMode::kNoCors)
...@@ -198,19 +241,22 @@ base::Optional<BlockedByResponseReason> CrossOriginResourcePolicy::IsBlocked( ...@@ -198,19 +241,22 @@ base::Optional<BlockedByResponseReason> CrossOriginResourcePolicy::IsBlocked(
ParsedHeader policy = ParsedHeader policy =
ParseHeaderByHttpResponseHeaders(response.headers.get()); ParseHeaderByHttpResponseHeaders(response.headers.get());
return IsBlockedInternal(policy, request_url, request_initiator, request_mode, return IsBlockedInternalWithReporting(
request_initiator_site_lock, embedder_policy.value); policy, request_url, original_url, request_initiator, request_mode,
request_initiator_site_lock, embedder_policy, reporter);
} }
// static // static
base::Optional<BlockedByResponseReason> base::Optional<BlockedByResponseReason>
CrossOriginResourcePolicy::IsBlockedByHeaderValue( CrossOriginResourcePolicy::IsBlockedByHeaderValue(
const GURL& request_url, const GURL& request_url,
const GURL& original_url,
const base::Optional<url::Origin>& request_initiator, const base::Optional<url::Origin>& request_initiator,
base::Optional<std::string> corp_header_value, base::Optional<std::string> corp_header_value,
mojom::RequestMode request_mode, mojom::RequestMode request_mode,
base::Optional<url::Origin> request_initiator_site_lock, base::Optional<url::Origin> request_initiator_site_lock,
const CrossOriginEmbedderPolicy& embedder_policy) { const CrossOriginEmbedderPolicy& embedder_policy,
mojom::CrossOriginEmbedderPolicyReporter* reporter) {
// From https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header: // From https://fetch.spec.whatwg.org/#cross-origin-resource-policy-header:
// > 1. If request’s mode is not "no-cors", then return allowed. // > 1. If request’s mode is not "no-cors", then return allowed.
if (request_mode != mojom::RequestMode::kNoCors) if (request_mode != mojom::RequestMode::kNoCors)
...@@ -218,24 +264,28 @@ CrossOriginResourcePolicy::IsBlockedByHeaderValue( ...@@ -218,24 +264,28 @@ CrossOriginResourcePolicy::IsBlockedByHeaderValue(
ParsedHeader policy = ParseHeaderByString(corp_header_value); ParsedHeader policy = ParseHeaderByString(corp_header_value);
return IsBlockedInternal(policy, request_url, request_initiator, request_mode, return IsBlockedInternalWithReporting(
request_initiator_site_lock, embedder_policy.value); policy, request_url, original_url, request_initiator, request_mode,
request_initiator_site_lock, embedder_policy, reporter);
} }
// static // static
base::Optional<BlockedByResponseReason> base::Optional<BlockedByResponseReason>
CrossOriginResourcePolicy::IsNavigationBlocked( CrossOriginResourcePolicy::IsNavigationBlocked(
const GURL& request_url, const GURL& request_url,
const GURL& original_url,
const base::Optional<url::Origin>& request_initiator, const base::Optional<url::Origin>& request_initiator,
const network::mojom::URLResponseHead& response, const network::mojom::URLResponseHead& response,
base::Optional<url::Origin> request_initiator_site_lock, base::Optional<url::Origin> request_initiator_site_lock,
const CrossOriginEmbedderPolicy& embedder_policy) { const CrossOriginEmbedderPolicy& embedder_policy,
mojom::CrossOriginEmbedderPolicyReporter* reporter) {
ParsedHeader policy = ParsedHeader policy =
ParseHeaderByHttpResponseHeaders(response.headers.get()); ParseHeaderByHttpResponseHeaders(response.headers.get());
return IsBlockedInternal(policy, request_url, request_initiator, return IsBlockedInternalWithReporting(
mojom::RequestMode::kNavigate, policy, request_url, original_url, request_initiator,
request_initiator_site_lock, embedder_policy.value); mojom::RequestMode::kNavigate, request_initiator_site_lock,
embedder_policy, reporter);
} }
// static // static
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/component_export.h" #include "base/component_export.h"
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/optional.h" #include "base/optional.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-forward.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h" #include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/public/mojom/network_context.mojom.h" #include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h" #include "services/network/public/mojom/url_response_head.mojom-forward.h"
...@@ -39,30 +40,38 @@ class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginResourcePolicy { ...@@ -39,30 +40,38 @@ class COMPONENT_EXPORT(NETWORK_CPP) CrossOriginResourcePolicy {
// delivered to a cross-origin or cross-site context. // delivered to a cross-origin or cross-site context.
static base::Optional<BlockedByResponseReason> IsBlocked( static base::Optional<BlockedByResponseReason> IsBlocked(
const GURL& request_url, const GURL& request_url,
const GURL& original_url,
const base::Optional<url::Origin>& request_initiator, const base::Optional<url::Origin>& request_initiator,
const network::mojom::URLResponseHead& response, const network::mojom::URLResponseHead& response,
mojom::RequestMode request_mode, mojom::RequestMode request_mode,
base::Optional<url::Origin> request_initiator_site_lock, base::Optional<url::Origin> request_initiator_site_lock,
const CrossOriginEmbedderPolicy& embedder_policy) WARN_UNUSED_RESULT; const CrossOriginEmbedderPolicy& embedder_policy,
mojom::CrossOriginEmbedderPolicyReporter* reporter = nullptr)
WARN_UNUSED_RESULT;
// Same as IsBlocked(), but this method can take a raw value of // Same as IsBlocked(), but this method can take a raw value of
// Cross-Origin-Resource-Policy header instead of using a URLResponseHead. // Cross-Origin-Resource-Policy header instead of using a URLResponseHead.
static base::Optional<BlockedByResponseReason> IsBlockedByHeaderValue( static base::Optional<BlockedByResponseReason> IsBlockedByHeaderValue(
const GURL& request_url, const GURL& request_url,
const GURL& original_url,
const base::Optional<url::Origin>& request_initiator, const base::Optional<url::Origin>& request_initiator,
base::Optional<std::string> corp_header_value, base::Optional<std::string> corp_header_value,
mojom::RequestMode request_mode, mojom::RequestMode request_mode,
base::Optional<url::Origin> request_initiator_site_lock, base::Optional<url::Origin> request_initiator_site_lock,
const CrossOriginEmbedderPolicy& embedder_policy) WARN_UNUSED_RESULT; const CrossOriginEmbedderPolicy& embedder_policy,
mojom::CrossOriginEmbedderPolicyReporter* reporter = nullptr)
WARN_UNUSED_RESULT;
// The CORP check for navigation requests. This is expected to be called // The CORP check for navigation requests. This is expected to be called
// from the navigation algorithm. // from the navigation algorithm.
static base::Optional<BlockedByResponseReason> IsNavigationBlocked( static base::Optional<BlockedByResponseReason> IsNavigationBlocked(
const GURL& request_url, const GURL& request_url,
const GURL& original_url,
const base::Optional<url::Origin>& request_initiator, const base::Optional<url::Origin>& request_initiator,
const network::mojom::URLResponseHead& response, const network::mojom::URLResponseHead& response,
base::Optional<url::Origin> request_initiator_site_lock, base::Optional<url::Origin> request_initiator_site_lock,
const CrossOriginEmbedderPolicy& embedder_policy); const CrossOriginEmbedderPolicy& embedder_policy,
mojom::CrossOriginEmbedderPolicyReporter* reporter = nullptr);
// Parsing of the Cross-Origin-Resource-Policy http response header. // Parsing of the Cross-Origin-Resource-Policy http response header.
enum ParsedHeader { enum ParsedHeader {
......
...@@ -958,8 +958,9 @@ void URLLoader::OnReceivedRedirect(net::URLRequest* url_request, ...@@ -958,8 +958,9 @@ void URLLoader::OnReceivedRedirect(net::URLRequest* url_request,
if (base::Optional<BlockedByResponseReason> blocked_reason = if (base::Optional<BlockedByResponseReason> blocked_reason =
CrossOriginResourcePolicy::IsBlocked( CrossOriginResourcePolicy::IsBlocked(
url_request_->url(), url_request_->initiator(), *response, url_request_->url(), url_request_->original_url(),
request_mode_, factory_params_->request_initiator_site_lock, url_request_->initiator(), *response, request_mode_,
factory_params_->request_initiator_site_lock,
cross_origin_embedder_policy)) { cross_origin_embedder_policy)) {
CompleteBlockedResponse(net::ERR_BLOCKED_BY_RESPONSE, false, CompleteBlockedResponse(net::ERR_BLOCKED_BY_RESPONSE, false,
blocked_reason); blocked_reason);
...@@ -1120,8 +1121,9 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) { ...@@ -1120,8 +1121,9 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
: kEmpty; : kEmpty;
if (base::Optional<BlockedByResponseReason> blocked_reason = if (base::Optional<BlockedByResponseReason> blocked_reason =
CrossOriginResourcePolicy::IsBlocked( CrossOriginResourcePolicy::IsBlocked(
url_request_->url(), url_request_->initiator(), *response_, url_request_->url(), url_request_->original_url(),
request_mode_, factory_params_->request_initiator_site_lock, url_request_->initiator(), *response_, request_mode_,
factory_params_->request_initiator_site_lock,
cross_origin_embedder_policy)) { cross_origin_embedder_policy)) {
CompleteBlockedResponse(net::ERR_BLOCKED_BY_RESPONSE, false, CompleteBlockedResponse(net::ERR_BLOCKED_BY_RESPONSE, false,
blocked_reason); blocked_reason);
......
...@@ -323,8 +323,8 @@ void FetchRespondWithObserver::OnResponseFulfilled( ...@@ -323,8 +323,8 @@ void FetchRespondWithObserver::OnResponseFulfilled(
auto initiator_origin = auto initiator_origin =
url::Origin::Create(GURL(service_worker_global_scope->Url())); url::Origin::Create(GURL(service_worker_global_scope->Url()));
if (network::CrossOriginResourcePolicy::IsBlockedByHeaderValue( if (network::CrossOriginResourcePolicy::IsBlockedByHeaderValue(
request_url_, initiator_origin, corp_header_value, request_mode_, request_url_, request_url_, initiator_origin, corp_header_value,
initiator_origin, requestor_coep_)) { request_mode_, initiator_origin, requestor_coep_)) {
OnResponseRejected(ServiceWorkerResponseError::kDisallowedByCorp); OnResponseRejected(ServiceWorkerResponseError::kDisallowedByCorp);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment