| 1 | /* |
| 2 | * Copyright (C) 2018-2019 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| 17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 23 | * THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "NetworkLoadChecker.h" |
| 28 | |
| 29 | #include "Download.h" |
| 30 | #include "Logging.h" |
| 31 | #include "NetworkCORSPreflightChecker.h" |
| 32 | #include "NetworkProcess.h" |
| 33 | #include <WebCore/ContentRuleListResults.h> |
| 34 | #include <WebCore/ContentSecurityPolicy.h> |
| 35 | #include <WebCore/CrossOriginAccessControl.h> |
| 36 | #include <WebCore/CrossOriginPreflightResultCache.h> |
| 37 | #include <WebCore/SchemeRegistry.h> |
| 38 | #include <wtf/Scope.h> |
| 39 | |
| 40 | #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(m_sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - NetworkLoadChecker::" fmt, this, ##__VA_ARGS__) |
| 41 | |
| 42 | namespace WebKit { |
| 43 | |
| 44 | using namespace WebCore; |
| 45 | |
| 46 | static inline bool isSameOrigin(const URL& url, const SecurityOrigin* origin) |
| 47 | { |
| 48 | return url.protocolIsData() || url.protocolIsBlob() || !origin || origin->canRequest(url); |
| 49 | } |
| 50 | |
| 51 | NetworkLoadChecker::(NetworkProcess& networkProcess, FetchOptions&& options, PAL::SessionID sessionID, uint64_t pageID, uint64_t frameID, HTTPHeaderMap&& , URL&& url, RefPtr<SecurityOrigin>&& sourceOrigin, PreflightPolicy preflightPolicy, String&& referrer, bool isHTTPSUpgradeEnabled, bool , LoadType requestLoadType) |
| 52 | : m_options(WTFMove(options)) |
| 53 | , m_sessionID(sessionID) |
| 54 | , m_networkProcess(networkProcess) |
| 55 | , m_pageID(pageID) |
| 56 | , m_frameID(frameID) |
| 57 | , m_originalRequestHeaders(WTFMove(originalRequestHeaders)) |
| 58 | , m_url(WTFMove(url)) |
| 59 | , m_origin(WTFMove(sourceOrigin)) |
| 60 | , m_preflightPolicy(preflightPolicy) |
| 61 | , m_referrer(WTFMove(referrer)) |
| 62 | , m_shouldCaptureExtraNetworkLoadMetrics(shouldCaptureExtraNetworkLoadMetrics) |
| 63 | , m_isHTTPSUpgradeEnabled(isHTTPSUpgradeEnabled) |
| 64 | , m_requestLoadType(requestLoadType) |
| 65 | { |
| 66 | m_isSameOriginRequest = isSameOrigin(m_url, m_origin.get()); |
| 67 | switch (options.credentials) { |
| 68 | case FetchOptions::Credentials::Include: |
| 69 | m_storedCredentialsPolicy = StoredCredentialsPolicy::Use; |
| 70 | break; |
| 71 | case FetchOptions::Credentials::SameOrigin: |
| 72 | m_storedCredentialsPolicy = m_isSameOriginRequest ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse; |
| 73 | break; |
| 74 | case FetchOptions::Credentials::Omit: |
| 75 | m_storedCredentialsPolicy = StoredCredentialsPolicy::DoNotUse; |
| 76 | break; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | NetworkLoadChecker::~NetworkLoadChecker() = default; |
| 81 | |
| 82 | void NetworkLoadChecker::check(ResourceRequest&& request, ContentSecurityPolicyClient* client, ValidationHandler&& handler) |
| 83 | { |
| 84 | ASSERT(!isChecking()); |
| 85 | |
| 86 | if (m_shouldCaptureExtraNetworkLoadMetrics) |
| 87 | m_loadInformation.request = request; |
| 88 | |
| 89 | m_firstRequestHeaders = request.httpHeaderFields(); |
| 90 | checkRequest(WTFMove(request), client, WTFMove(handler)); |
| 91 | } |
| 92 | |
| 93 | static inline NetworkLoadChecker::RedirectionRequestOrError redirectionError(const ResourceResponse& redirectResponse, String&& errorMessage) |
| 94 | { |
| 95 | return makeUnexpected(ResourceError { String { }, 0, redirectResponse.url(), WTFMove(errorMessage), ResourceError::Type::AccessControl }); |
| 96 | } |
| 97 | |
| 98 | void NetworkLoadChecker::checkRedirection(ResourceRequest&& request, ResourceRequest&& redirectRequest, ResourceResponse&& redirectResponse, ContentSecurityPolicyClient* client, RedirectionValidationHandler&& handler) |
| 99 | { |
| 100 | ASSERT(!isChecking()); |
| 101 | |
| 102 | auto error = validateResponse(redirectResponse); |
| 103 | if (!error.isNull()) { |
| 104 | handler(redirectionError(redirectResponse, makeString("Cross-origin redirection to " , redirectRequest.url().string(), " denied by Cross-Origin Resource Sharing policy: " , error.localizedDescription()))); |
| 105 | return; |
| 106 | } |
| 107 | |
| 108 | if (m_options.redirect == FetchOptions::Redirect::Error) { |
| 109 | handler(redirectionError(redirectResponse, makeString("Not allowed to follow a redirection while loading " , redirectResponse.url().string()))); |
| 110 | return; |
| 111 | } |
| 112 | if (m_options.redirect == FetchOptions::Redirect::Manual) { |
| 113 | handler(RedirectionTriplet { WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse) }); |
| 114 | return; |
| 115 | } |
| 116 | |
| 117 | // FIXME: We should check that redirections are only HTTP(s) as per fetch spec. |
| 118 | // See https://github.com/whatwg/fetch/issues/393 |
| 119 | |
| 120 | if (++m_redirectCount > 20) { |
| 121 | handler(redirectionError(redirectResponse, "Load cannot follow more than 20 redirections"_s )); |
| 122 | return; |
| 123 | } |
| 124 | |
| 125 | m_previousURL = WTFMove(m_url); |
| 126 | m_url = redirectRequest.url(); |
| 127 | |
| 128 | checkRequest(WTFMove(redirectRequest), client, [handler = WTFMove(handler), request = WTFMove(request), redirectResponse = WTFMove(redirectResponse)](auto&& result) mutable { |
| 129 | WTF::switchOn(result, |
| 130 | [&handler] (ResourceError& error) mutable { |
| 131 | handler(makeUnexpected(WTFMove(error))); |
| 132 | }, |
| 133 | [&handler, &request, &redirectResponse] (RedirectionTriplet& triplet) mutable { |
| 134 | // FIXME: if checkRequest returns a RedirectionTriplet, it means the requested URL has changed and we should update the redirectResponse to match. |
| 135 | handler(RedirectionTriplet { WTFMove(request), WTFMove(triplet.redirectRequest), WTFMove(redirectResponse) }); |
| 136 | }, |
| 137 | [&handler, &request, &redirectResponse] (ResourceRequest& redirectRequest) mutable { |
| 138 | handler(RedirectionTriplet { WTFMove(request), WTFMove(redirectRequest), WTFMove(redirectResponse) }); |
| 139 | } |
| 140 | ); |
| 141 | }); |
| 142 | } |
| 143 | |
| 144 | ResourceError NetworkLoadChecker::validateResponse(ResourceResponse& response) |
| 145 | { |
| 146 | if (m_redirectCount) |
| 147 | response.setRedirected(true); |
| 148 | |
| 149 | if (response.type() == ResourceResponse::Type::Opaqueredirect) { |
| 150 | response.setTainting(ResourceResponse::Tainting::Opaqueredirect); |
| 151 | return { }; |
| 152 | } |
| 153 | |
| 154 | if (m_options.mode == FetchOptions::Mode::Navigate || m_isSameOriginRequest) { |
| 155 | response.setTainting(ResourceResponse::Tainting::Basic); |
| 156 | return { }; |
| 157 | } |
| 158 | |
| 159 | if (m_options.mode == FetchOptions::Mode::NoCors) { |
| 160 | if (auto error = validateCrossOriginResourcePolicy(*m_origin, m_url, response)) |
| 161 | return WTFMove(*error); |
| 162 | |
| 163 | response.setTainting(ResourceResponse::Tainting::Opaque); |
| 164 | return { }; |
| 165 | } |
| 166 | |
| 167 | ASSERT(m_options.mode == FetchOptions::Mode::Cors); |
| 168 | |
| 169 | // If we have a 304, the cached response is in WebProcess so we let WebProcess do the CORS check on the cached response. |
| 170 | if (response.httpStatusCode() == 304) |
| 171 | return { }; |
| 172 | |
| 173 | String errorMessage; |
| 174 | if (!passesAccessControlCheck(response, m_storedCredentialsPolicy, *m_origin, errorMessage)) |
| 175 | return ResourceError { String { }, 0, m_url, WTFMove(errorMessage), ResourceError::Type::AccessControl }; |
| 176 | |
| 177 | response.setTainting(ResourceResponse::Tainting::Cors); |
| 178 | return { }; |
| 179 | } |
| 180 | |
| 181 | auto NetworkLoadChecker::accessControlErrorForValidationHandler(String&& message) -> RequestOrRedirectionTripletOrError |
| 182 | { |
| 183 | return ResourceError { String { }, 0, m_url, WTFMove(message), ResourceError::Type::AccessControl }; |
| 184 | } |
| 185 | |
| 186 | void NetworkLoadChecker::applyHTTPSUpgradeIfNeeded(ResourceRequest&& request, CompletionHandler<void(ResourceRequest&&)>&& handler) const |
| 187 | { |
| 188 | #if PLATFORM(COCOA) |
| 189 | if (!m_isHTTPSUpgradeEnabled || m_requestLoadType != LoadType::MainFrame) { |
| 190 | handler(WTFMove(request)); |
| 191 | return; |
| 192 | } |
| 193 | |
| 194 | auto& url = request.url(); |
| 195 | |
| 196 | // Only upgrade http urls. |
| 197 | if (!url.protocolIs("http" )) { |
| 198 | handler(WTFMove(request)); |
| 199 | return; |
| 200 | } |
| 201 | |
| 202 | auto& httpsUpgradeChecker = m_networkProcess->networkHTTPSUpgradeChecker(); |
| 203 | |
| 204 | // Do not wait for httpsUpgradeChecker to complete its setup. |
| 205 | if (!httpsUpgradeChecker.didSetupCompleteSuccessfully()) { |
| 206 | handler(WTFMove(request)); |
| 207 | return; |
| 208 | } |
| 209 | |
| 210 | httpsUpgradeChecker.query(url.host().toString(), m_sessionID, [request = WTFMove(request), handler = WTFMove(handler)] (bool foundHost) mutable { |
| 211 | if (foundHost) { |
| 212 | auto newURL = request.url(); |
| 213 | newURL.setProtocol("https"_s ); |
| 214 | request.setURL(newURL); |
| 215 | } |
| 216 | |
| 217 | handler(WTFMove(request)); |
| 218 | }); |
| 219 | #else |
| 220 | handler(WTFMove(request)); |
| 221 | #endif |
| 222 | } |
| 223 | |
| 224 | void NetworkLoadChecker::checkRequest(ResourceRequest&& request, ContentSecurityPolicyClient* client, ValidationHandler&& handler) |
| 225 | { |
| 226 | ResourceRequest originalRequest = request; |
| 227 | |
| 228 | applyHTTPSUpgradeIfNeeded(WTFMove(request), [this, weakThis = makeWeakPtr(*this), client, handler = WTFMove(handler), originalRequest = WTFMove(originalRequest)](auto request) mutable { |
| 229 | if (!weakThis) |
| 230 | return handler({ ResourceError { ResourceError::Type::Cancellation }}); |
| 231 | |
| 232 | if (auto* contentSecurityPolicy = this->contentSecurityPolicy()) { |
| 233 | if (this->isRedirected()) { |
| 234 | auto type = m_options.mode == FetchOptions::Mode::Navigate ? ContentSecurityPolicy::InsecureRequestType::Navigation : ContentSecurityPolicy::InsecureRequestType::Load; |
| 235 | contentSecurityPolicy->upgradeInsecureRequestIfNeeded(request, type); |
| 236 | } |
| 237 | if (!this->isAllowedByContentSecurityPolicy(request, client)) { |
| 238 | handler(this->accessControlErrorForValidationHandler("Blocked by Content Security Policy."_s )); |
| 239 | return; |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | #if ENABLE(CONTENT_EXTENSIONS) |
| 244 | this->processContentRuleListsForLoad(WTFMove(request), [this, weakThis = WTFMove(weakThis), handler = WTFMove(handler), originalRequest = WTFMove(originalRequest)](auto result) mutable { |
| 245 | if (!result.has_value()) { |
| 246 | ASSERT(result.error().isCancellation()); |
| 247 | handler(WTFMove(result.error())); |
| 248 | return; |
| 249 | } |
| 250 | if (result.value().results.summary.blockedLoad) { |
| 251 | handler(this->accessControlErrorForValidationHandler("Blocked by content extension"_s )); |
| 252 | return; |
| 253 | } |
| 254 | |
| 255 | if (!weakThis) |
| 256 | return handler({ ResourceError { ResourceError::Type::Cancellation }}); |
| 257 | this->continueCheckingRequestOrDoSyntheticRedirect(WTFMove(originalRequest), WTFMove(result.value().request), WTFMove(handler)); |
| 258 | }); |
| 259 | #else |
| 260 | this->continueCheckingRequestOrDoSyntheticRedirect(WTFMove(originalRequest), WTFMove(request), WTFMove(handler)); |
| 261 | #endif |
| 262 | }); |
| 263 | } |
| 264 | |
| 265 | void NetworkLoadChecker::continueCheckingRequestOrDoSyntheticRedirect(ResourceRequest&& originalRequest, ResourceRequest&& currentRequest, ValidationHandler&& handler) |
| 266 | { |
| 267 | // If main frame load and request has been modified, trigger a synthetic redirect. |
| 268 | if (m_requestLoadType == LoadType::MainFrame && currentRequest.url() != originalRequest.url()) { |
| 269 | ResourceResponse redirectResponse = ResourceResponse::syntheticRedirectResponse(originalRequest.url(), currentRequest.url()); |
| 270 | handler(RedirectionTriplet { WTFMove(originalRequest), WTFMove(currentRequest), WTFMove(redirectResponse) }); |
| 271 | return; |
| 272 | } |
| 273 | this->continueCheckingRequest(WTFMove(currentRequest), WTFMove(handler)); |
| 274 | } |
| 275 | |
| 276 | bool NetworkLoadChecker::isAllowedByContentSecurityPolicy(const ResourceRequest& request, WebCore::ContentSecurityPolicyClient* client) |
| 277 | { |
| 278 | auto* contentSecurityPolicy = this->contentSecurityPolicy(); |
| 279 | contentSecurityPolicy->setClient(client); |
| 280 | auto clearContentSecurityPolicyClient = makeScopeExit([&] { |
| 281 | contentSecurityPolicy->setClient(nullptr); |
| 282 | }); |
| 283 | |
| 284 | auto redirectResponseReceived = isRedirected() ? ContentSecurityPolicy::RedirectResponseReceived::Yes : ContentSecurityPolicy::RedirectResponseReceived::No; |
| 285 | switch (m_options.destination) { |
| 286 | case FetchOptions::Destination::Worker: |
| 287 | case FetchOptions::Destination::Serviceworker: |
| 288 | case FetchOptions::Destination::Sharedworker: |
| 289 | return contentSecurityPolicy->allowChildContextFromSource(request.url(), redirectResponseReceived); |
| 290 | case FetchOptions::Destination::Script: |
| 291 | if (request.requester() == ResourceRequest::Requester::ImportScripts && !contentSecurityPolicy->allowScriptFromSource(request.url(), redirectResponseReceived)) |
| 292 | return false; |
| 293 | // FIXME: Check CSP for non-importScripts() initiated loads. |
| 294 | return true; |
| 295 | case FetchOptions::Destination::EmptyString: |
| 296 | return contentSecurityPolicy->allowConnectToSource(request.url(), redirectResponseReceived); |
| 297 | case FetchOptions::Destination::Audio: |
| 298 | case FetchOptions::Destination::Document: |
| 299 | case FetchOptions::Destination::Embed: |
| 300 | case FetchOptions::Destination::Font: |
| 301 | case FetchOptions::Destination::Image: |
| 302 | case FetchOptions::Destination::Manifest: |
| 303 | case FetchOptions::Destination::Object: |
| 304 | case FetchOptions::Destination::Report: |
| 305 | case FetchOptions::Destination::Style: |
| 306 | case FetchOptions::Destination::Track: |
| 307 | case FetchOptions::Destination::Video: |
| 308 | case FetchOptions::Destination::Xslt: |
| 309 | // FIXME: Check CSP for these destinations. |
| 310 | return true; |
| 311 | } |
| 312 | ASSERT_NOT_REACHED(); |
| 313 | return true; |
| 314 | } |
| 315 | |
| 316 | void NetworkLoadChecker::continueCheckingRequest(ResourceRequest&& request, ValidationHandler&& handler) |
| 317 | { |
| 318 | if (m_options.credentials == FetchOptions::Credentials::SameOrigin) |
| 319 | m_storedCredentialsPolicy = m_isSameOriginRequest && m_origin->canRequest(request.url()) ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse; |
| 320 | |
| 321 | m_isSameOriginRequest = m_isSameOriginRequest && isSameOrigin(request.url(), m_origin.get()); |
| 322 | |
| 323 | if (doesNotNeedCORSCheck(request.url())) { |
| 324 | handler(WTFMove(request)); |
| 325 | return; |
| 326 | } |
| 327 | |
| 328 | if (m_options.mode == FetchOptions::Mode::SameOrigin) { |
| 329 | String message = makeString("Unsafe attempt to load URL " , request.url().stringCenterEllipsizedToLength(), " from origin " , m_origin->toString(), ". Domains, protocols and ports must match.\n" ); |
| 330 | handler(accessControlErrorForValidationHandler(WTFMove(message))); |
| 331 | return; |
| 332 | } |
| 333 | |
| 334 | if (isRedirected()) { |
| 335 | RELEASE_LOG_IF_ALLOWED("checkRequest - Redirect requires CORS checks" ); |
| 336 | checkCORSRedirectedRequest(WTFMove(request), WTFMove(handler)); |
| 337 | return; |
| 338 | } |
| 339 | |
| 340 | checkCORSRequest(WTFMove(request), WTFMove(handler)); |
| 341 | } |
| 342 | |
| 343 | void NetworkLoadChecker::checkCORSRequest(ResourceRequest&& request, ValidationHandler&& handler) |
| 344 | { |
| 345 | ASSERT(m_options.mode == FetchOptions::Mode::Cors); |
| 346 | |
| 347 | // Except in case where preflight is needed, loading should be able to continue on its own. |
| 348 | switch (m_preflightPolicy) { |
| 349 | case PreflightPolicy::Force: |
| 350 | checkCORSRequestWithPreflight(WTFMove(request), WTFMove(handler)); |
| 351 | break; |
| 352 | case PreflightPolicy::Consider: |
| 353 | if (!m_isSimpleRequest || !isSimpleCrossOriginAccessRequest(request.httpMethod(), m_originalRequestHeaders)) { |
| 354 | checkCORSRequestWithPreflight(WTFMove(request), WTFMove(handler)); |
| 355 | return; |
| 356 | } |
| 357 | FALLTHROUGH; |
| 358 | case PreflightPolicy::Prevent: |
| 359 | updateRequestForAccessControl(request, *m_origin, m_storedCredentialsPolicy); |
| 360 | handler(WTFMove(request)); |
| 361 | break; |
| 362 | } |
| 363 | } |
| 364 | |
| 365 | void NetworkLoadChecker::checkCORSRedirectedRequest(ResourceRequest&& request, ValidationHandler&& handler) |
| 366 | { |
| 367 | ASSERT(m_options.mode == FetchOptions::Mode::Cors); |
| 368 | ASSERT(isRedirected()); |
| 369 | |
| 370 | // Force any subsequent request to use these checks. |
| 371 | m_isSameOriginRequest = false; |
| 372 | |
| 373 | if (!m_origin->canRequest(m_previousURL) && !protocolHostAndPortAreEqual(m_previousURL, request.url())) { |
| 374 | // Use a unique origin for subsequent loads if needed. |
| 375 | // https://fetch.spec.whatwg.org/#concept-http-redirect-fetch (Step 10). |
| 376 | if (!m_origin || !m_origin->isUnique()) |
| 377 | m_origin = SecurityOrigin::createUnique(); |
| 378 | } |
| 379 | |
| 380 | // FIXME: We should set the request referrer according the referrer policy. |
| 381 | |
| 382 | // Let's fetch the request with the original headers (equivalent to request cloning specified by fetch algorithm). |
| 383 | if (!request.httpHeaderFields().contains(HTTPHeaderName::Authorization)) |
| 384 | m_firstRequestHeaders.remove(HTTPHeaderName::Authorization); |
| 385 | request.setHTTPHeaderFields(m_firstRequestHeaders); |
| 386 | |
| 387 | checkCORSRequest(WTFMove(request), WTFMove(handler)); |
| 388 | } |
| 389 | |
| 390 | void NetworkLoadChecker::checkCORSRequestWithPreflight(ResourceRequest&& request, ValidationHandler&& handler) |
| 391 | { |
| 392 | ASSERT(m_options.mode == FetchOptions::Mode::Cors); |
| 393 | |
| 394 | m_isSimpleRequest = false; |
| 395 | // FIXME: We should probably partition preflight result cache by session ID. |
| 396 | if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(m_origin->toString(), request.url(), m_storedCredentialsPolicy, request.httpMethod(), m_originalRequestHeaders)) { |
| 397 | RELEASE_LOG_IF_ALLOWED("checkCORSRequestWithPreflight - preflight can be skipped thanks to cached result" ); |
| 398 | updateRequestForAccessControl(request, *m_origin, m_storedCredentialsPolicy); |
| 399 | handler(WTFMove(request)); |
| 400 | return; |
| 401 | } |
| 402 | |
| 403 | auto requestForPreflight = request; |
| 404 | // We need to set header fields to m_originalRequestHeaders to correctly compute Access-Control-Request-Headers header value. |
| 405 | requestForPreflight.setHTTPHeaderFields(m_originalRequestHeaders); |
| 406 | NetworkCORSPreflightChecker::Parameters parameters = { |
| 407 | WTFMove(requestForPreflight), |
| 408 | *m_origin, |
| 409 | request.httpReferrer(), |
| 410 | request.httpUserAgent(), |
| 411 | m_sessionID, |
| 412 | m_pageID, |
| 413 | m_frameID, |
| 414 | m_storedCredentialsPolicy |
| 415 | }; |
| 416 | m_corsPreflightChecker = std::make_unique<NetworkCORSPreflightChecker>(m_networkProcess.get(), WTFMove(parameters), m_shouldCaptureExtraNetworkLoadMetrics, [this, request = WTFMove(request), handler = WTFMove(handler), isRedirected = isRedirected()](auto&& error) mutable { |
| 417 | RELEASE_LOG_IF_ALLOWED("checkCORSRequestWithPreflight - makeCrossOriginAccessRequestWithPreflight preflight complete, success: %d forRedirect? %d" , error.isNull(), isRedirected); |
| 418 | |
| 419 | if (!error.isNull()) { |
| 420 | handler(WTFMove(error)); |
| 421 | return; |
| 422 | } |
| 423 | |
| 424 | if (m_shouldCaptureExtraNetworkLoadMetrics) |
| 425 | m_loadInformation.transactions.append(m_corsPreflightChecker->takeInformation()); |
| 426 | |
| 427 | auto corsPreflightChecker = WTFMove(m_corsPreflightChecker); |
| 428 | updateRequestForAccessControl(request, *m_origin, m_storedCredentialsPolicy); |
| 429 | handler(WTFMove(request)); |
| 430 | }); |
| 431 | m_corsPreflightChecker->startPreflight(); |
| 432 | } |
| 433 | |
| 434 | bool NetworkLoadChecker::doesNotNeedCORSCheck(const URL& url) const |
| 435 | { |
| 436 | if (m_options.mode == FetchOptions::Mode::NoCors || m_options.mode == FetchOptions::Mode::Navigate) |
| 437 | return true; |
| 438 | |
| 439 | if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(url.protocol().toStringWithoutCopying())) |
| 440 | return true; |
| 441 | |
| 442 | return m_isSameOriginRequest; |
| 443 | } |
| 444 | |
| 445 | ContentSecurityPolicy* NetworkLoadChecker::contentSecurityPolicy() |
| 446 | { |
| 447 | if (!m_contentSecurityPolicy && m_cspResponseHeaders) { |
| 448 | // FIXME: Pass the URL of the protected resource instead of its origin. |
| 449 | m_contentSecurityPolicy = std::make_unique<ContentSecurityPolicy>(URL { URL { }, m_origin->toString() }); |
| 450 | m_contentSecurityPolicy->didReceiveHeaders(*m_cspResponseHeaders, String { m_referrer }, ContentSecurityPolicy::ReportParsingErrors::No); |
| 451 | } |
| 452 | return m_contentSecurityPolicy.get(); |
| 453 | } |
| 454 | |
| 455 | #if ENABLE(CONTENT_EXTENSIONS) |
| 456 | void NetworkLoadChecker::processContentRuleListsForLoad(ResourceRequest&& request, ContentExtensionCallback&& callback) |
| 457 | { |
| 458 | // FIXME: Enable content blockers for navigation loads. |
| 459 | if (!m_checkContentExtensions || !m_userContentControllerIdentifier || m_options.mode == FetchOptions::Mode::Navigate) { |
| 460 | ContentRuleListResults results; |
| 461 | callback(ContentExtensionResult { WTFMove(request), results }); |
| 462 | return; |
| 463 | } |
| 464 | |
| 465 | m_networkProcess->networkContentRuleListManager().contentExtensionsBackend(*m_userContentControllerIdentifier, [this, weakThis = makeWeakPtr(this), request = WTFMove(request), callback = WTFMove(callback)](auto& backend) mutable { |
| 466 | if (!weakThis) { |
| 467 | callback(makeUnexpected(ResourceError { ResourceError::Type::Cancellation })); |
| 468 | return; |
| 469 | } |
| 470 | |
| 471 | auto results = backend.processContentRuleListsForPingLoad(request.url(), m_mainDocumentURL); |
| 472 | WebCore::ContentExtensions::applyResultsToRequest(ContentRuleListResults { results }, nullptr, request); |
| 473 | callback(ContentExtensionResult { WTFMove(request), results }); |
| 474 | }); |
| 475 | } |
| 476 | #endif // ENABLE(CONTENT_EXTENSIONS) |
| 477 | |
| 478 | void NetworkLoadChecker::storeRedirectionIfNeeded(const ResourceRequest& request, const ResourceResponse& response) |
| 479 | { |
| 480 | if (!m_shouldCaptureExtraNetworkLoadMetrics) |
| 481 | return; |
| 482 | m_loadInformation.transactions.append(NetworkTransactionInformation { NetworkTransactionInformation::Type::Redirection, ResourceRequest { request }, ResourceResponse { response }, { } }); |
| 483 | } |
| 484 | |
| 485 | } // namespace WebKit |
| 486 | |
| 487 | #undef RELEASE_LOG_IF_ALLOWED |
| 488 | |