Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(546)

Unified Diff: third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.cpp

Issue 2584423002: Loading: move core/fetch to platform/loader/fetch (Closed)
Patch Set: another try Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.cpp
diff --git a/third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.cpp b/third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.cpp
deleted file mode 100644
index a135fbdddbbdf8483244dbfe844034debe1c46c1..0000000000000000000000000000000000000000
--- a/third_party/WebKit/Source/core/fetch/CrossOriginAccessControl.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc. All Rights Reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "core/fetch/CrossOriginAccessControl.h"
-
-#include "core/fetch/FetchUtils.h"
-#include "core/fetch/Resource.h"
-#include "core/fetch/ResourceLoaderOptions.h"
-#include "platform/network/HTTPParsers.h"
-#include "platform/network/ResourceRequest.h"
-#include "platform/network/ResourceResponse.h"
-#include "platform/weborigin/SchemeRegistry.h"
-#include "platform/weborigin/SecurityOrigin.h"
-#include "wtf/PtrUtil.h"
-#include "wtf/Threading.h"
-#include "wtf/text/AtomicString.h"
-#include "wtf/text/StringBuilder.h"
-#include <algorithm>
-#include <memory>
-
-namespace blink {
-
-bool isOnAccessControlResponseHeaderWhitelist(const String& name) {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- HTTPHeaderSet, allowedCrossOriginResponseHeaders,
- (new HTTPHeaderSet({
- "cache-control", "content-language", "content-type", "expires",
- "last-modified", "pragma",
- })));
- return allowedCrossOriginResponseHeaders.contains(name);
-}
-
-// Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch-0
-static AtomicString createAccessControlRequestHeadersHeader(
- const HTTPHeaderMap& headers) {
- Vector<String> filteredHeaders;
- for (const auto& header : headers) {
- if (FetchUtils::isSimpleHeader(header.key, header.value)) {
- // Exclude simple headers.
- continue;
- }
- if (equalIgnoringCase(header.key, "referer")) {
- // When the request is from a Worker, referrer header was added by
- // WorkerThreadableLoader. But it should not be added to
- // Access-Control-Request-Headers header.
- continue;
- }
- filteredHeaders.push_back(header.key.lower());
- }
- if (!filteredHeaders.size())
- return nullAtom;
-
- // Sort header names lexicographically.
- std::sort(filteredHeaders.begin(), filteredHeaders.end(),
- WTF::codePointCompareLessThan);
- StringBuilder headerBuffer;
- for (const String& header : filteredHeaders) {
- if (!headerBuffer.isEmpty())
- headerBuffer.append(",");
- headerBuffer.append(header);
- }
-
- return AtomicString(headerBuffer.toString());
-}
-
-ResourceRequest createAccessControlPreflightRequest(
- const ResourceRequest& request) {
- const KURL& requestURL = request.url();
-
- DCHECK(requestURL.user().isEmpty());
- DCHECK(requestURL.pass().isEmpty());
-
- ResourceRequest preflightRequest(requestURL);
- preflightRequest.setAllowStoredCredentials(false);
- preflightRequest.setHTTPMethod(HTTPNames::OPTIONS);
- preflightRequest.setHTTPHeaderField(HTTPNames::Access_Control_Request_Method,
- AtomicString(request.httpMethod()));
- preflightRequest.setPriority(request.priority());
- preflightRequest.setRequestContext(request.requestContext());
- preflightRequest.setSkipServiceWorker(WebURLRequest::SkipServiceWorker::All);
-
- if (request.isExternalRequest()) {
- preflightRequest.setHTTPHeaderField(
- HTTPNames::Access_Control_Request_External, "true");
- }
-
- AtomicString requestHeaders =
- createAccessControlRequestHeadersHeader(request.httpHeaderFields());
- if (requestHeaders != nullAtom) {
- preflightRequest.setHTTPHeaderField(
- HTTPNames::Access_Control_Request_Headers, requestHeaders);
- }
-
- return preflightRequest;
-}
-
-static bool isOriginSeparator(UChar ch) {
- return isASCIISpace(ch) || ch == ',';
-}
-
-static bool isInterestingStatusCode(int statusCode) {
- // Predicate that gates what status codes should be included in console error
- // messages for responses containing no access control headers.
- return statusCode >= 400;
-}
-
-static void appendOriginDeniedMessage(StringBuilder& builder,
- const SecurityOrigin* securityOrigin) {
- builder.append(" Origin '");
- builder.append(securityOrigin->toString());
- builder.append("' is therefore not allowed access.");
-}
-
-static void appendNoCORSInformationalMessage(
- StringBuilder& builder,
- WebURLRequest::RequestContext context) {
- if (context != WebURLRequest::RequestContextFetch)
- return;
- builder.append(
- " Have the server send the header with a valid value, or, if an "
- "opaque response serves your needs, set the request's mode to "
- "'no-cors' to fetch the resource with CORS disabled.");
-}
-
-CrossOriginAccessControl::AccessStatus CrossOriginAccessControl::checkAccess(
- const ResourceResponse& response,
- StoredCredentials includeCredentials,
- const SecurityOrigin* securityOrigin) {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- AtomicString, allowOriginHeaderName,
- (new AtomicString("access-control-allow-origin")));
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- AtomicString, allowCredentialsHeaderName,
- (new AtomicString("access-control-allow-credentials")));
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- AtomicString, allowSuboriginHeaderName,
- (new AtomicString("access-control-allow-suborigin")));
-
- int statusCode = response.httpStatusCode();
- if (!statusCode)
- return kInvalidResponse;
-
- const AtomicString& allowOriginHeaderValue =
- response.httpHeaderField(allowOriginHeaderName);
-
- // Check Suborigins, unless the Access-Control-Allow-Origin is '*', which
- // implies that all Suborigins are okay as well.
- if (securityOrigin->hasSuborigin() && allowOriginHeaderValue != starAtom) {
- const AtomicString& allowSuboriginHeaderValue =
- response.httpHeaderField(allowSuboriginHeaderName);
- AtomicString atomicSuboriginName(securityOrigin->suborigin()->name());
- if (allowSuboriginHeaderValue != starAtom &&
- allowSuboriginHeaderValue != atomicSuboriginName) {
- return kSubOriginMismatch;
- }
- }
-
- if (allowOriginHeaderValue == starAtom) {
- // A wildcard Access-Control-Allow-Origin can not be used if credentials are
- // to be sent, even with Access-Control-Allow-Credentials set to true.
- if (includeCredentials == DoNotAllowStoredCredentials)
- return kAccessAllowed;
- if (response.isHTTP()) {
- return kWildcardOriginNotAllowed;
- }
- } else if (allowOriginHeaderValue != securityOrigin->toAtomicString()) {
- if (allowOriginHeaderValue.isNull())
- return kMissingAllowOriginHeader;
- if (allowOriginHeaderValue.getString().find(isOriginSeparator, 0) !=
- kNotFound) {
- return kMultipleAllowOriginValues;
- }
- KURL headerOrigin(KURL(), allowOriginHeaderValue);
- if (!headerOrigin.isValid())
- return kInvalidAllowOriginValue;
-
- return kAllowOriginMismatch;
- }
-
- if (includeCredentials == AllowStoredCredentials) {
- const AtomicString& allowCredentialsHeaderValue =
- response.httpHeaderField(allowCredentialsHeaderName);
- if (allowCredentialsHeaderValue != "true") {
- return kDisallowCredentialsNotSetToTrue;
- }
- }
- return kAccessAllowed;
-}
-
-void CrossOriginAccessControl::accessControlErrorString(
- StringBuilder& builder,
- CrossOriginAccessControl::AccessStatus status,
- const ResourceResponse& response,
- const SecurityOrigin* securityOrigin,
- WebURLRequest::RequestContext context) {
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- AtomicString, allowOriginHeaderName,
- (new AtomicString("access-control-allow-origin")));
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- AtomicString, allowCredentialsHeaderName,
- (new AtomicString("access-control-allow-credentials")));
- DEFINE_THREAD_SAFE_STATIC_LOCAL(
- AtomicString, allowSuboriginHeaderName,
- (new AtomicString("access-control-allow-suborigin")));
-
- switch (status) {
- case kInvalidResponse: {
- builder.append("Invalid response.");
- appendOriginDeniedMessage(builder, securityOrigin);
- return;
- }
- case kSubOriginMismatch: {
- const AtomicString& allowSuboriginHeaderValue =
- response.httpHeaderField(allowSuboriginHeaderName);
- builder.append(
- "The 'Access-Control-Allow-Suborigin' header has a value '");
- builder.append(allowSuboriginHeaderValue);
- builder.append("' that is not equal to the supplied suborigin.");
- appendOriginDeniedMessage(builder, securityOrigin);
- return;
- }
- case kWildcardOriginNotAllowed: {
- builder.append(
- "The value of the 'Access-Control-Allow-Origin' header in the "
- "response must not be the wildcard '*' when the request's "
- "credentials mode is 'include'.");
- appendOriginDeniedMessage(builder, securityOrigin);
- if (context == WebURLRequest::RequestContextXMLHttpRequest) {
- builder.append(
- " The credentials mode of requests initiated by the "
- "XMLHttpRequest is controlled by the withCredentials attribute.");
- }
- return;
- }
- case kMissingAllowOriginHeader: {
- builder.append(
- "No 'Access-Control-Allow-Origin' header is present on the requested "
- "resource.");
- appendOriginDeniedMessage(builder, securityOrigin);
- int statusCode = response.httpStatusCode();
- if (isInterestingStatusCode(statusCode)) {
- builder.append(" The response had HTTP status code ");
- builder.append(String::number(statusCode));
- builder.append('.');
- }
- if (context == WebURLRequest::RequestContextFetch) {
- builder.append(
- " If an opaque response serves your needs, set the request's mode "
- "to 'no-cors' to fetch the resource with CORS disabled.");
- }
- return;
- }
- case kMultipleAllowOriginValues: {
- const AtomicString& allowOriginHeaderValue =
- response.httpHeaderField(allowOriginHeaderName);
- builder.append(
- "The 'Access-Control-Allow-Origin' header contains multiple values "
- "'");
- builder.append(allowOriginHeaderValue);
- builder.append("', but only one is allowed.");
- appendOriginDeniedMessage(builder, securityOrigin);
- appendNoCORSInformationalMessage(builder, context);
- return;
- }
- case kInvalidAllowOriginValue: {
- const AtomicString& allowOriginHeaderValue =
- response.httpHeaderField(allowOriginHeaderName);
- builder.append(
- "The 'Access-Control-Allow-Origin' header contains the invalid "
- "value '");
- builder.append(allowOriginHeaderValue);
- builder.append("'.");
- appendOriginDeniedMessage(builder, securityOrigin);
- appendNoCORSInformationalMessage(builder, context);
- return;
- }
- case kAllowOriginMismatch: {
- const AtomicString& allowOriginHeaderValue =
- response.httpHeaderField(allowOriginHeaderName);
- builder.append("The 'Access-Control-Allow-Origin' header has a value '");
- builder.append(allowOriginHeaderValue);
- builder.append("' that is not equal to the supplied origin.");
- appendOriginDeniedMessage(builder, securityOrigin);
- appendNoCORSInformationalMessage(builder, context);
- return;
- }
- case kDisallowCredentialsNotSetToTrue: {
- const AtomicString& allowCredentialsHeaderValue =
- response.httpHeaderField(allowCredentialsHeaderName);
- builder.append(
- "The value of the 'Access-Control-Allow-Credentials' header in "
- "the response is '");
- builder.append(allowCredentialsHeaderValue);
- builder.append(
- "' which must "
- "be 'true' when the request's credentials mode is 'include'.");
- appendOriginDeniedMessage(builder, securityOrigin);
- if (context == WebURLRequest::RequestContextXMLHttpRequest) {
- builder.append(
- " The credentials mode of requests initiated by the "
- "XMLHttpRequest is controlled by the withCredentials attribute.");
- }
- return;
- }
- default:
- NOTREACHED();
- }
-}
-
-CrossOriginAccessControl::PreflightStatus
-CrossOriginAccessControl::checkPreflight(const ResourceResponse& response) {
- // CORS preflight with 3XX is considered network error in
- // Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch
- // CORS Spec: http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
- // https://crbug.com/452394
- int statusCode = response.httpStatusCode();
- if (!FetchUtils::isOkStatus(statusCode))
- return kPreflightInvalidStatus;
-
- return kPreflightSuccess;
-}
-
-CrossOriginAccessControl::PreflightStatus
-CrossOriginAccessControl::checkExternalPreflight(
- const ResourceResponse& response) {
- AtomicString result =
- response.httpHeaderField(HTTPNames::Access_Control_Allow_External);
- if (result.isNull())
- return kPreflightMissingAllowExternal;
- if (!equalIgnoringCase(result, "true"))
- return kPreflightInvalidAllowExternal;
- return kPreflightSuccess;
-}
-
-void CrossOriginAccessControl::preflightErrorString(
- StringBuilder& builder,
- CrossOriginAccessControl::PreflightStatus status,
- const ResourceResponse& response) {
- switch (status) {
- case kPreflightInvalidStatus: {
- int statusCode = response.httpStatusCode();
- builder.append("Response for preflight has invalid HTTP status code ");
- builder.append(String::number(statusCode));
- return;
- }
- case kPreflightMissingAllowExternal: {
- builder.append(
- "No 'Access-Control-Allow-External' header was present in ");
- builder.append(
- "the preflight response for this external request (This is");
- builder.append(" an experimental header which is defined in ");
- builder.append("'https://wicg.github.io/cors-rfc1918/').");
- return;
- }
- case kPreflightInvalidAllowExternal: {
- String result =
- response.httpHeaderField(HTTPNames::Access_Control_Allow_External);
- builder.append("The 'Access-Control-Allow-External' header in the ");
- builder.append(
- "preflight response for this external request had a value");
- builder.append(" of '");
- builder.append(result);
- builder.append("', not 'true' (This is an experimental header which is");
- builder.append(" defined in 'https://wicg.github.io/cors-rfc1918/').");
- return;
- }
- default:
- NOTREACHED();
- }
-}
-
-void parseAccessControlExposeHeadersAllowList(const String& headerValue,
- HTTPHeaderSet& headerSet) {
- Vector<String> headers;
- headerValue.split(',', false, headers);
- for (unsigned headerCount = 0; headerCount < headers.size(); headerCount++) {
- String strippedHeader = headers[headerCount].stripWhiteSpace();
- if (!strippedHeader.isEmpty())
- headerSet.add(strippedHeader);
- }
-}
-
-void extractCorsExposedHeaderNamesList(const ResourceResponse& response,
- HTTPHeaderSet& headerSet) {
- // If a response was fetched via a service worker, it will always have
- // corsExposedHeaderNames set, either from the Access-Control-Expose-Headers
- // header, or explicitly via foreign fetch. For requests that didn't come from
- // a service worker, foreign fetch doesn't apply so just parse the CORS
- // header.
- if (response.wasFetchedViaServiceWorker()) {
- for (const auto& header : response.corsExposedHeaderNames())
- headerSet.add(header);
- return;
- }
- parseAccessControlExposeHeadersAllowList(
- response.httpHeaderField(HTTPNames::Access_Control_Expose_Headers),
- headerSet);
-}
-
-CrossOriginAccessControl::RedirectStatus
-CrossOriginAccessControl::checkRedirectLocation(const KURL& requestURL) {
- // Block non HTTP(S) schemes as specified in the step 4 in
- // https://fetch.spec.whatwg.org/#http-redirect-fetch. Chromium also allows
- // the data scheme.
- //
- // TODO(tyoshino): This check should be performed regardless of the CORS flag
- // and request's mode.
- if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(requestURL.protocol()))
- return kRedirectDisallowedScheme;
-
- // Block URLs including credentials as specified in the step 9 in
- // https://fetch.spec.whatwg.org/#http-redirect-fetch.
- //
- // TODO(tyoshino): This check should be performed also when request's
- // origin is not same origin with the redirect destination's origin.
- if (!(requestURL.user().isEmpty() && requestURL.pass().isEmpty()))
- return kRedirectContainsCredentials;
-
- return kRedirectSuccess;
-}
-
-void CrossOriginAccessControl::redirectErrorString(
- StringBuilder& builder,
- CrossOriginAccessControl::RedirectStatus status,
- const KURL& requestURL) {
- switch (status) {
- case kRedirectDisallowedScheme: {
- builder.append("Redirect location '");
- builder.append(requestURL.getString());
- builder.append("' has a disallowed scheme for cross-origin requests.");
- return;
- }
- case kRedirectContainsCredentials: {
- builder.append("Redirect location '");
- builder.append(requestURL.getString());
- builder.append(
- "' contains a username and password, which is disallowed for"
- " cross-origin requests.");
- return;
- }
- default:
- NOTREACHED();
- }
-}
-
-bool CrossOriginAccessControl::handleRedirect(
- PassRefPtr<SecurityOrigin> securityOrigin,
- ResourceRequest& newRequest,
- const ResourceResponse& redirectResponse,
- StoredCredentials withCredentials,
- ResourceLoaderOptions& options,
- String& errorMessage) {
- // http://www.w3.org/TR/cors/#redirect-steps terminology:
- const KURL& lastURL = redirectResponse.url();
- const KURL& newURL = newRequest.url();
-
- RefPtr<SecurityOrigin> currentSecurityOrigin = securityOrigin;
-
- RefPtr<SecurityOrigin> newSecurityOrigin = currentSecurityOrigin;
-
- // TODO(tyoshino): This should be fixed to check not only the last one but
- // all redirect responses.
- if (!currentSecurityOrigin->canRequest(lastURL)) {
- // Follow http://www.w3.org/TR/cors/#redirect-steps
- CrossOriginAccessControl::RedirectStatus redirectStatus =
- CrossOriginAccessControl::checkRedirectLocation(newURL);
- if (redirectStatus != kRedirectSuccess) {
- StringBuilder builder;
- builder.append("Redirect from '");
- builder.append(lastURL.getString());
- builder.append("' has been blocked by CORS policy: ");
- CrossOriginAccessControl::redirectErrorString(builder, redirectStatus,
- newURL);
- errorMessage = builder.toString();
- return false;
- }
-
- // Step 5: perform resource sharing access check.
- CrossOriginAccessControl::AccessStatus corsStatus =
- CrossOriginAccessControl::checkAccess(redirectResponse, withCredentials,
- currentSecurityOrigin.get());
- if (corsStatus != kAccessAllowed) {
- StringBuilder builder;
- builder.append("Redirect from '");
- builder.append(lastURL.getString());
- builder.append("' has been blocked by CORS policy: ");
- CrossOriginAccessControl::accessControlErrorString(
- builder, corsStatus, redirectResponse, currentSecurityOrigin.get(),
- newRequest.requestContext());
- errorMessage = builder.toString();
- return false;
- }
-
- RefPtr<SecurityOrigin> lastOrigin = SecurityOrigin::create(lastURL);
- // Set request's origin to a globally unique identifier as specified in
- // the step 10 in https://fetch.spec.whatwg.org/#http-redirect-fetch.
- if (!lastOrigin->canRequest(newURL)) {
- options.securityOrigin = SecurityOrigin::createUnique();
- newSecurityOrigin = options.securityOrigin;
- }
- }
-
- if (!currentSecurityOrigin->canRequest(newURL)) {
- newRequest.clearHTTPOrigin();
- newRequest.setHTTPOrigin(newSecurityOrigin.get());
-
- // Unset credentials flag if request's credentials mode is "same-origin" as
- // request's response tainting becomes "cors".
- //
- // This is equivalent to the step 2 in
- // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
- if (options.credentialsRequested == ClientDidNotRequestCredentials)
- options.allowCredentials = DoNotAllowStoredCredentials;
- }
- return true;
-}
-
-} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698