Commit 6cf2a0a0 authored by Yutaka Hirano's avatar Yutaka Hirano Committed by Commit Bot

[OOR-CORS] Support --allow-file-access-from-files

network::ResourceRequest::request_initiator is set based on
blink::ResourceRequest::RequestorOrigin. The former is a url::Origin
the latter is a scoped_refptr<const blink::SecurityOrigin>.

A file: origin is handled in a special manner in blink. Without
--allow-file-access-from-files, it is NOT opaque but it is serialized
as "null". Currently conversion from blink::SecurityOrigin to
url::Origin relies on opacity (i.e., blink::SecurityOrigin::IsOpaque)
but that is not good for CORS.

This CL changes that. With this CL, A SecurityOrigin serializes as
"null" is converted to a unique url::Origin.

Bug: 736308
Cq-Include-Trybots: luci.chromium.try:linux_mojo
Change-Id: I7b5ec4894c52499d85165689fd114e28bbd8bb97
Reviewed-on: https://chromium-review.googlesource.com/1131021Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarTakashi Toyoshima <toyoshim@chromium.org>
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#594625}
parent 813051f3
...@@ -263,17 +263,15 @@ IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTestWithDisableWebSecurity, ...@@ -263,17 +263,15 @@ IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTestWithDisableWebSecurity,
EXPECT_FALSE(is_preflight_requested()); EXPECT_FALSE(is_preflight_requested());
} }
// --allow-file-access-from-files is currently not supported by OOR-CORS.
// We may remove the feature.
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
/* No test prefix */, /* No test prefix */,
CORSFileOriginBrowserTest, CORSFileOriginBrowserTest,
::testing::Values(false)); ::testing::Values(false, true));
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
/* No test prefix */, /* No test prefix */,
CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, CORSFileOriginBrowserTestWithAllowFileAccessFromFiles,
::testing::Values(false)); ::testing::Values(false, true));
INSTANTIATE_TEST_CASE_P( INSTANTIATE_TEST_CASE_P(
/* No test prefix */, /* No test prefix */,
......
...@@ -644,10 +644,17 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request, ...@@ -644,10 +644,17 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request,
resource_request->url = url_; resource_request->url = url_;
resource_request->site_for_cookies = request.SiteForCookies(); resource_request->site_for_cookies = request.SiteForCookies();
resource_request->upgrade_if_insecure = request.UpgradeIfInsecure(); resource_request->upgrade_if_insecure = request.UpgradeIfInsecure();
resource_request->request_initiator = if (!request.RequestorOrigin().IsNull()) {
request.RequestorOrigin().IsNull() if (request.RequestorOrigin().ToString() == "null") {
? base::Optional<url::Origin>() // "file:" origin is treated like an opaque unique origin when
: base::Optional<url::Origin>(request.RequestorOrigin()); // allow-file-access-from-files is not specified. Such origin is not
// opaque (i.e., IsOpaque() returns false) but still serializes to
// "null".
resource_request->request_initiator = url::Origin();
} else {
resource_request->request_initiator = request.RequestorOrigin();
}
}
resource_request->referrer = referrer_url; resource_request->referrer = referrer_url;
resource_request->referrer_policy = resource_request->referrer_policy =
......
...@@ -190,7 +190,6 @@ void CORSURLLoader::OnReceiveResponse( ...@@ -190,7 +190,6 @@ void CORSURLLoader::OnReceiveResponse(
DCHECK(!is_waiting_follow_redirect_call_); DCHECK(!is_waiting_follow_redirect_call_);
if (fetch_cors_flag_) { if (fetch_cors_flag_) {
// TODO(toyoshim): Reflect --allow-file-access-from-files flag.
const auto error_status = CheckAccess( const auto error_status = CheckAccess(
request_.url, response_head.headers->response_code(), request_.url, response_head.headers->response_code(),
GetHeaderString(response_head, header_names::kAccessControlAllowOrigin), GetHeaderString(response_head, header_names::kAccessControlAllowOrigin),
...@@ -226,7 +225,6 @@ void CORSURLLoader::OnReceiveRedirect( ...@@ -226,7 +225,6 @@ void CORSURLLoader::OnReceiveRedirect(
// failure, then return a network error. // failure, then return a network error.
if (fetch_cors_flag_ && if (fetch_cors_flag_ &&
IsCORSEnabledRequestMode(request_.fetch_request_mode)) { IsCORSEnabledRequestMode(request_.fetch_request_mode)) {
// TODO(toyoshim): Reflect --allow-file-access-from-files flag.
const auto error_status = CheckAccess( const auto error_status = CheckAccess(
request_.url, response_head.headers->response_code(), request_.url, response_head.headers->response_code(),
GetHeaderString(response_head, header_names::kAccessControlAllowOrigin), GetHeaderString(response_head, header_names::kAccessControlAllowOrigin),
......
...@@ -120,15 +120,13 @@ std::unique_ptr<PreflightResult> CreatePreflightResult( ...@@ -120,15 +120,13 @@ std::unique_ptr<PreflightResult> CreatePreflightResult(
base::Optional<CORSErrorStatus>* detected_error_status) { base::Optional<CORSErrorStatus>* detected_error_status) {
DCHECK(detected_error_status); DCHECK(detected_error_status);
// TODO(toyoshim): Reflect --allow-file-access-from-files flag.
*detected_error_status = CheckPreflightAccess( *detected_error_status = CheckPreflightAccess(
final_url, head.headers->response_code(), final_url, head.headers->response_code(),
GetHeaderString(head.headers, header_names::kAccessControlAllowOrigin), GetHeaderString(head.headers, header_names::kAccessControlAllowOrigin),
GetHeaderString(head.headers, GetHeaderString(head.headers,
header_names::kAccessControlAllowCredentials), header_names::kAccessControlAllowCredentials),
original_request.fetch_credentials_mode, original_request.fetch_credentials_mode,
tainted ? url::Origin() : *original_request.request_initiator, tainted ? url::Origin() : *original_request.request_initiator);
false /* allow_file_origin */);
if (*detected_error_status) if (*detected_error_status)
return nullptr; return nullptr;
......
...@@ -43,18 +43,6 @@ std::string ExtractMIMETypeFromMediaType(const std::string& media_type) { ...@@ -43,18 +43,6 @@ std::string ExtractMIMETypeFromMediaType(const std::string& media_type) {
return std::string(); return std::string();
} }
// url::Origin::Serialize() serializes all Origins with a 'file' scheme to
// 'file://', but it isn't desirable for CORS check. Returns 'null' instead to
// be aligned with HTTP Origin header calculation in Blink SecurityOrigin.
// |allow_file_origin| is used to realize a behavior change that
// the --allow-file-access-from-files command-line flag needs.
// TODO(mkwst): Generalize and move to url/Origin.
std::string Serialize(const url::Origin& origin, bool allow_file_origin) {
if (!allow_file_origin && origin.scheme() == url::kFileScheme)
return "null";
return origin.Serialize();
}
// Returns true only if |header_value| satisfies ABNF: 1*DIGIT [ "." 1*DIGIT ] // Returns true only if |header_value| satisfies ABNF: 1*DIGIT [ "." 1*DIGIT ]
bool IsSimilarToDoubleABNF(const std::string& header_value) { bool IsSimilarToDoubleABNF(const std::string& header_value) {
if (header_value.empty()) if (header_value.empty())
...@@ -135,8 +123,7 @@ base::Optional<CORSErrorStatus> CheckAccess( ...@@ -135,8 +123,7 @@ base::Optional<CORSErrorStatus> CheckAccess(
const base::Optional<std::string>& allow_origin_header, const base::Optional<std::string>& allow_origin_header,
const base::Optional<std::string>& allow_credentials_header, const base::Optional<std::string>& allow_credentials_header,
mojom::FetchCredentialsMode credentials_mode, mojom::FetchCredentialsMode credentials_mode,
const url::Origin& origin, const url::Origin& origin) {
bool allow_file_origin) {
// TODO(toyoshim): This response status code check should not be needed. We // TODO(toyoshim): This response status code check should not be needed. We
// have another status code check after a CheckAccess() call if it is needed. // have another status code check after a CheckAccess() call if it is needed.
if (!response_status_code) if (!response_status_code)
...@@ -159,7 +146,7 @@ base::Optional<CORSErrorStatus> CheckAccess( ...@@ -159,7 +146,7 @@ base::Optional<CORSErrorStatus> CheckAccess(
return CORSErrorStatus(mojom::CORSError::kWildcardOriginNotAllowed); return CORSErrorStatus(mojom::CORSError::kWildcardOriginNotAllowed);
} else if (!allow_origin_header) { } else if (!allow_origin_header) {
return CORSErrorStatus(mojom::CORSError::kMissingAllowOriginHeader); return CORSErrorStatus(mojom::CORSError::kMissingAllowOriginHeader);
} else if (*allow_origin_header != Serialize(origin, allow_file_origin)) { } else if (*allow_origin_header != origin.Serialize()) {
// We do not use url::Origin::IsSameOriginWith() here for two reasons below. // We do not use url::Origin::IsSameOriginWith() here for two reasons below.
// 1. Allow "null" to match here. The latest spec does not have a clear // 1. Allow "null" to match here. The latest spec does not have a clear
// information about this (https://fetch.spec.whatwg.org/#cors-check), // information about this (https://fetch.spec.whatwg.org/#cors-check),
...@@ -217,12 +204,10 @@ base::Optional<CORSErrorStatus> CheckPreflightAccess( ...@@ -217,12 +204,10 @@ base::Optional<CORSErrorStatus> CheckPreflightAccess(
const base::Optional<std::string>& allow_origin_header, const base::Optional<std::string>& allow_origin_header,
const base::Optional<std::string>& allow_credentials_header, const base::Optional<std::string>& allow_credentials_header,
mojom::FetchCredentialsMode actual_credentials_mode, mojom::FetchCredentialsMode actual_credentials_mode,
const url::Origin& origin, const url::Origin& origin) {
bool allow_file_origin) {
const auto error_status = const auto error_status =
CheckAccess(response_url, response_status_code, allow_origin_header, CheckAccess(response_url, response_status_code, allow_origin_header,
allow_credentials_header, actual_credentials_mode, origin, allow_credentials_header, actual_credentials_mode, origin);
allow_file_origin);
if (!error_status) if (!error_status)
return base::nullopt; return base::nullopt;
......
...@@ -56,8 +56,7 @@ base::Optional<CORSErrorStatus> CheckAccess( ...@@ -56,8 +56,7 @@ base::Optional<CORSErrorStatus> CheckAccess(
const base::Optional<std::string>& allow_origin_header, const base::Optional<std::string>& allow_origin_header,
const base::Optional<std::string>& allow_credentials_header, const base::Optional<std::string>& allow_credentials_header,
mojom::FetchCredentialsMode credentials_mode, mojom::FetchCredentialsMode credentials_mode,
const url::Origin& origin, const url::Origin& origin);
bool allow_file_origin = false);
// Performs a CORS access check on the CORS-preflight response parameters. // Performs a CORS access check on the CORS-preflight response parameters.
// According to the note at https://fetch.spec.whatwg.org/#cors-preflight-fetch // According to the note at https://fetch.spec.whatwg.org/#cors-preflight-fetch
...@@ -70,8 +69,7 @@ base::Optional<CORSErrorStatus> CheckPreflightAccess( ...@@ -70,8 +69,7 @@ base::Optional<CORSErrorStatus> CheckPreflightAccess(
const base::Optional<std::string>& allow_origin_header, const base::Optional<std::string>& allow_origin_header,
const base::Optional<std::string>& allow_credentials_header, const base::Optional<std::string>& allow_credentials_header,
mojom::FetchCredentialsMode actual_credentials_mode, mojom::FetchCredentialsMode actual_credentials_mode,
const url::Origin& origin, const url::Origin& origin);
bool allow_file_origin = false);
// Given a redirected-to URL, checks if the location is allowed // Given a redirected-to URL, checks if the location is allowed
// according to CORS. That is: // according to CORS. That is:
......
...@@ -67,6 +67,14 @@ std::unique_ptr<net::HttpRequestHeaders> CreateNetHttpRequestHeaders( ...@@ -67,6 +67,14 @@ std::unique_ptr<net::HttpRequestHeaders> CreateNetHttpRequestHeaders(
return request_headers; return request_headers;
} }
url::Origin AsUrlOrigin(const SecurityOrigin& origin) {
// "file:" origin is treated like an opaque unique origin when
// allow-file-access-from-files is not specified. Such origin is not
// opaque (i.e., IsOpaque() returns false) but still serializes to
// "null".
return origin.ToString() == "null" ? url::Origin() : origin.ToUrlOrigin();
}
} // namespace } // namespace
namespace CORS { namespace CORS {
...@@ -77,15 +85,12 @@ base::Optional<network::CORSErrorStatus> CheckAccess( ...@@ -77,15 +85,12 @@ base::Optional<network::CORSErrorStatus> CheckAccess(
const HTTPHeaderMap& response_header, const HTTPHeaderMap& response_header,
network::mojom::FetchCredentialsMode credentials_mode, network::mojom::FetchCredentialsMode credentials_mode,
const SecurityOrigin& origin) { const SecurityOrigin& origin) {
std::unique_ptr<SecurityOrigin::PrivilegeData> privilege =
origin.CreatePrivilegeData();
return network::cors::CheckAccess( return network::cors::CheckAccess(
response_url, response_status_code, response_url, response_status_code,
GetHeaderValue(response_header, HTTPNames::Access_Control_Allow_Origin), GetHeaderValue(response_header, HTTPNames::Access_Control_Allow_Origin),
GetHeaderValue(response_header, GetHeaderValue(response_header,
HTTPNames::Access_Control_Allow_Credentials), HTTPNames::Access_Control_Allow_Credentials),
credentials_mode, origin.ToUrlOrigin(), credentials_mode, AsUrlOrigin(origin));
!privilege->block_local_access_from_local_origin_);
} }
base::Optional<network::CORSErrorStatus> CheckPreflightAccess( base::Optional<network::CORSErrorStatus> CheckPreflightAccess(
...@@ -94,15 +99,12 @@ base::Optional<network::CORSErrorStatus> CheckPreflightAccess( ...@@ -94,15 +99,12 @@ base::Optional<network::CORSErrorStatus> CheckPreflightAccess(
const HTTPHeaderMap& response_header, const HTTPHeaderMap& response_header,
network::mojom::FetchCredentialsMode actual_credentials_mode, network::mojom::FetchCredentialsMode actual_credentials_mode,
const SecurityOrigin& origin) { const SecurityOrigin& origin) {
std::unique_ptr<SecurityOrigin::PrivilegeData> privilege =
origin.CreatePrivilegeData();
return network::cors::CheckPreflightAccess( return network::cors::CheckPreflightAccess(
response_url, response_status_code, response_url, response_status_code,
GetHeaderValue(response_header, HTTPNames::Access_Control_Allow_Origin), GetHeaderValue(response_header, HTTPNames::Access_Control_Allow_Origin),
GetHeaderValue(response_header, GetHeaderValue(response_header,
HTTPNames::Access_Control_Allow_Credentials), HTTPNames::Access_Control_Allow_Credentials),
actual_credentials_mode, origin.ToUrlOrigin(), actual_credentials_mode, AsUrlOrigin(origin));
!privilege->block_local_access_from_local_origin_);
} }
base::Optional<network::CORSErrorStatus> CheckRedirectLocation( base::Optional<network::CORSErrorStatus> CheckRedirectLocation(
...@@ -112,7 +114,7 @@ base::Optional<network::CORSErrorStatus> CheckRedirectLocation( ...@@ -112,7 +114,7 @@ base::Optional<network::CORSErrorStatus> CheckRedirectLocation(
CORSFlag cors_flag) { CORSFlag cors_flag) {
base::Optional<url::Origin> origin_to_pass; base::Optional<url::Origin> origin_to_pass;
if (origin) if (origin)
origin_to_pass = origin->ToUrlOrigin(); origin_to_pass = AsUrlOrigin(*origin);
// Blink-side implementations rewrite the origin instead of setting the // Blink-side implementations rewrite the origin instead of setting the
// tainted flag. // tainted flag.
...@@ -198,7 +200,7 @@ network::mojom::FetchResponseType CalculateResponseTainting( ...@@ -198,7 +200,7 @@ network::mojom::FetchResponseType CalculateResponseTainting(
CORSFlag cors_flag) { CORSFlag cors_flag) {
base::Optional<url::Origin> origin_to_pass; base::Optional<url::Origin> origin_to_pass;
if (origin) if (origin)
origin_to_pass = origin->ToUrlOrigin(); origin_to_pass = AsUrlOrigin(*origin);
return network::cors::CalculateResponseTainting( return network::cors::CalculateResponseTainting(
url, request_mode, origin_to_pass, cors_flag == CORSFlag::Set); url, request_mode, origin_to_pass, cors_flag == CORSFlag::Set);
} }
......
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