OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2008 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 COMPUTER, INC. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 * | |
25 */ | |
26 | |
27 #include "core/fetch/CrossOriginAccessControl.h" | |
28 | |
29 #include "core/fetch/FetchUtils.h" | |
30 #include "core/fetch/Resource.h" | |
31 #include "core/fetch/ResourceLoaderOptions.h" | |
32 #include "platform/network/HTTPParsers.h" | |
33 #include "platform/network/ResourceRequest.h" | |
34 #include "platform/network/ResourceResponse.h" | |
35 #include "platform/weborigin/SchemeRegistry.h" | |
36 #include "platform/weborigin/SecurityOrigin.h" | |
37 #include "wtf/PtrUtil.h" | |
38 #include "wtf/Threading.h" | |
39 #include "wtf/text/AtomicString.h" | |
40 #include "wtf/text/StringBuilder.h" | |
41 #include <algorithm> | |
42 #include <memory> | |
43 | |
44 namespace blink { | |
45 | |
46 bool isOnAccessControlResponseHeaderWhitelist(const String& name) { | |
47 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
48 HTTPHeaderSet, allowedCrossOriginResponseHeaders, | |
49 (new HTTPHeaderSet({ | |
50 "cache-control", "content-language", "content-type", "expires", | |
51 "last-modified", "pragma", | |
52 }))); | |
53 return allowedCrossOriginResponseHeaders.contains(name); | |
54 } | |
55 | |
56 // Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch-0 | |
57 static AtomicString createAccessControlRequestHeadersHeader( | |
58 const HTTPHeaderMap& headers) { | |
59 Vector<String> filteredHeaders; | |
60 for (const auto& header : headers) { | |
61 if (FetchUtils::isSimpleHeader(header.key, header.value)) { | |
62 // Exclude simple headers. | |
63 continue; | |
64 } | |
65 if (equalIgnoringCase(header.key, "referer")) { | |
66 // When the request is from a Worker, referrer header was added by | |
67 // WorkerThreadableLoader. But it should not be added to | |
68 // Access-Control-Request-Headers header. | |
69 continue; | |
70 } | |
71 filteredHeaders.push_back(header.key.lower()); | |
72 } | |
73 if (!filteredHeaders.size()) | |
74 return nullAtom; | |
75 | |
76 // Sort header names lexicographically. | |
77 std::sort(filteredHeaders.begin(), filteredHeaders.end(), | |
78 WTF::codePointCompareLessThan); | |
79 StringBuilder headerBuffer; | |
80 for (const String& header : filteredHeaders) { | |
81 if (!headerBuffer.isEmpty()) | |
82 headerBuffer.append(","); | |
83 headerBuffer.append(header); | |
84 } | |
85 | |
86 return AtomicString(headerBuffer.toString()); | |
87 } | |
88 | |
89 ResourceRequest createAccessControlPreflightRequest( | |
90 const ResourceRequest& request) { | |
91 const KURL& requestURL = request.url(); | |
92 | |
93 DCHECK(requestURL.user().isEmpty()); | |
94 DCHECK(requestURL.pass().isEmpty()); | |
95 | |
96 ResourceRequest preflightRequest(requestURL); | |
97 preflightRequest.setAllowStoredCredentials(false); | |
98 preflightRequest.setHTTPMethod(HTTPNames::OPTIONS); | |
99 preflightRequest.setHTTPHeaderField(HTTPNames::Access_Control_Request_Method, | |
100 AtomicString(request.httpMethod())); | |
101 preflightRequest.setPriority(request.priority()); | |
102 preflightRequest.setRequestContext(request.requestContext()); | |
103 preflightRequest.setSkipServiceWorker(WebURLRequest::SkipServiceWorker::All); | |
104 | |
105 if (request.isExternalRequest()) { | |
106 preflightRequest.setHTTPHeaderField( | |
107 HTTPNames::Access_Control_Request_External, "true"); | |
108 } | |
109 | |
110 AtomicString requestHeaders = | |
111 createAccessControlRequestHeadersHeader(request.httpHeaderFields()); | |
112 if (requestHeaders != nullAtom) { | |
113 preflightRequest.setHTTPHeaderField( | |
114 HTTPNames::Access_Control_Request_Headers, requestHeaders); | |
115 } | |
116 | |
117 return preflightRequest; | |
118 } | |
119 | |
120 static bool isOriginSeparator(UChar ch) { | |
121 return isASCIISpace(ch) || ch == ','; | |
122 } | |
123 | |
124 static bool isInterestingStatusCode(int statusCode) { | |
125 // Predicate that gates what status codes should be included in console error | |
126 // messages for responses containing no access control headers. | |
127 return statusCode >= 400; | |
128 } | |
129 | |
130 static void appendOriginDeniedMessage(StringBuilder& builder, | |
131 const SecurityOrigin* securityOrigin) { | |
132 builder.append(" Origin '"); | |
133 builder.append(securityOrigin->toString()); | |
134 builder.append("' is therefore not allowed access."); | |
135 } | |
136 | |
137 static void appendNoCORSInformationalMessage( | |
138 StringBuilder& builder, | |
139 WebURLRequest::RequestContext context) { | |
140 if (context != WebURLRequest::RequestContextFetch) | |
141 return; | |
142 builder.append( | |
143 " Have the server send the header with a valid value, or, if an " | |
144 "opaque response serves your needs, set the request's mode to " | |
145 "'no-cors' to fetch the resource with CORS disabled."); | |
146 } | |
147 | |
148 CrossOriginAccessControl::AccessStatus CrossOriginAccessControl::checkAccess( | |
149 const ResourceResponse& response, | |
150 StoredCredentials includeCredentials, | |
151 const SecurityOrigin* securityOrigin) { | |
152 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
153 AtomicString, allowOriginHeaderName, | |
154 (new AtomicString("access-control-allow-origin"))); | |
155 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
156 AtomicString, allowCredentialsHeaderName, | |
157 (new AtomicString("access-control-allow-credentials"))); | |
158 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
159 AtomicString, allowSuboriginHeaderName, | |
160 (new AtomicString("access-control-allow-suborigin"))); | |
161 | |
162 int statusCode = response.httpStatusCode(); | |
163 if (!statusCode) | |
164 return kInvalidResponse; | |
165 | |
166 const AtomicString& allowOriginHeaderValue = | |
167 response.httpHeaderField(allowOriginHeaderName); | |
168 | |
169 // Check Suborigins, unless the Access-Control-Allow-Origin is '*', which | |
170 // implies that all Suborigins are okay as well. | |
171 if (securityOrigin->hasSuborigin() && allowOriginHeaderValue != starAtom) { | |
172 const AtomicString& allowSuboriginHeaderValue = | |
173 response.httpHeaderField(allowSuboriginHeaderName); | |
174 AtomicString atomicSuboriginName(securityOrigin->suborigin()->name()); | |
175 if (allowSuboriginHeaderValue != starAtom && | |
176 allowSuboriginHeaderValue != atomicSuboriginName) { | |
177 return kSubOriginMismatch; | |
178 } | |
179 } | |
180 | |
181 if (allowOriginHeaderValue == starAtom) { | |
182 // A wildcard Access-Control-Allow-Origin can not be used if credentials are | |
183 // to be sent, even with Access-Control-Allow-Credentials set to true. | |
184 if (includeCredentials == DoNotAllowStoredCredentials) | |
185 return kAccessAllowed; | |
186 if (response.isHTTP()) { | |
187 return kWildcardOriginNotAllowed; | |
188 } | |
189 } else if (allowOriginHeaderValue != securityOrigin->toAtomicString()) { | |
190 if (allowOriginHeaderValue.isNull()) | |
191 return kMissingAllowOriginHeader; | |
192 if (allowOriginHeaderValue.getString().find(isOriginSeparator, 0) != | |
193 kNotFound) { | |
194 return kMultipleAllowOriginValues; | |
195 } | |
196 KURL headerOrigin(KURL(), allowOriginHeaderValue); | |
197 if (!headerOrigin.isValid()) | |
198 return kInvalidAllowOriginValue; | |
199 | |
200 return kAllowOriginMismatch; | |
201 } | |
202 | |
203 if (includeCredentials == AllowStoredCredentials) { | |
204 const AtomicString& allowCredentialsHeaderValue = | |
205 response.httpHeaderField(allowCredentialsHeaderName); | |
206 if (allowCredentialsHeaderValue != "true") { | |
207 return kDisallowCredentialsNotSetToTrue; | |
208 } | |
209 } | |
210 return kAccessAllowed; | |
211 } | |
212 | |
213 void CrossOriginAccessControl::accessControlErrorString( | |
214 StringBuilder& builder, | |
215 CrossOriginAccessControl::AccessStatus status, | |
216 const ResourceResponse& response, | |
217 const SecurityOrigin* securityOrigin, | |
218 WebURLRequest::RequestContext context) { | |
219 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
220 AtomicString, allowOriginHeaderName, | |
221 (new AtomicString("access-control-allow-origin"))); | |
222 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
223 AtomicString, allowCredentialsHeaderName, | |
224 (new AtomicString("access-control-allow-credentials"))); | |
225 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
226 AtomicString, allowSuboriginHeaderName, | |
227 (new AtomicString("access-control-allow-suborigin"))); | |
228 | |
229 switch (status) { | |
230 case kInvalidResponse: { | |
231 builder.append("Invalid response."); | |
232 appendOriginDeniedMessage(builder, securityOrigin); | |
233 return; | |
234 } | |
235 case kSubOriginMismatch: { | |
236 const AtomicString& allowSuboriginHeaderValue = | |
237 response.httpHeaderField(allowSuboriginHeaderName); | |
238 builder.append( | |
239 "The 'Access-Control-Allow-Suborigin' header has a value '"); | |
240 builder.append(allowSuboriginHeaderValue); | |
241 builder.append("' that is not equal to the supplied suborigin."); | |
242 appendOriginDeniedMessage(builder, securityOrigin); | |
243 return; | |
244 } | |
245 case kWildcardOriginNotAllowed: { | |
246 builder.append( | |
247 "The value of the 'Access-Control-Allow-Origin' header in the " | |
248 "response must not be the wildcard '*' when the request's " | |
249 "credentials mode is 'include'."); | |
250 appendOriginDeniedMessage(builder, securityOrigin); | |
251 if (context == WebURLRequest::RequestContextXMLHttpRequest) { | |
252 builder.append( | |
253 " The credentials mode of requests initiated by the " | |
254 "XMLHttpRequest is controlled by the withCredentials attribute."); | |
255 } | |
256 return; | |
257 } | |
258 case kMissingAllowOriginHeader: { | |
259 builder.append( | |
260 "No 'Access-Control-Allow-Origin' header is present on the requested " | |
261 "resource."); | |
262 appendOriginDeniedMessage(builder, securityOrigin); | |
263 int statusCode = response.httpStatusCode(); | |
264 if (isInterestingStatusCode(statusCode)) { | |
265 builder.append(" The response had HTTP status code "); | |
266 builder.append(String::number(statusCode)); | |
267 builder.append('.'); | |
268 } | |
269 if (context == WebURLRequest::RequestContextFetch) { | |
270 builder.append( | |
271 " If an opaque response serves your needs, set the request's mode " | |
272 "to 'no-cors' to fetch the resource with CORS disabled."); | |
273 } | |
274 return; | |
275 } | |
276 case kMultipleAllowOriginValues: { | |
277 const AtomicString& allowOriginHeaderValue = | |
278 response.httpHeaderField(allowOriginHeaderName); | |
279 builder.append( | |
280 "The 'Access-Control-Allow-Origin' header contains multiple values " | |
281 "'"); | |
282 builder.append(allowOriginHeaderValue); | |
283 builder.append("', but only one is allowed."); | |
284 appendOriginDeniedMessage(builder, securityOrigin); | |
285 appendNoCORSInformationalMessage(builder, context); | |
286 return; | |
287 } | |
288 case kInvalidAllowOriginValue: { | |
289 const AtomicString& allowOriginHeaderValue = | |
290 response.httpHeaderField(allowOriginHeaderName); | |
291 builder.append( | |
292 "The 'Access-Control-Allow-Origin' header contains the invalid " | |
293 "value '"); | |
294 builder.append(allowOriginHeaderValue); | |
295 builder.append("'."); | |
296 appendOriginDeniedMessage(builder, securityOrigin); | |
297 appendNoCORSInformationalMessage(builder, context); | |
298 return; | |
299 } | |
300 case kAllowOriginMismatch: { | |
301 const AtomicString& allowOriginHeaderValue = | |
302 response.httpHeaderField(allowOriginHeaderName); | |
303 builder.append("The 'Access-Control-Allow-Origin' header has a value '"); | |
304 builder.append(allowOriginHeaderValue); | |
305 builder.append("' that is not equal to the supplied origin."); | |
306 appendOriginDeniedMessage(builder, securityOrigin); | |
307 appendNoCORSInformationalMessage(builder, context); | |
308 return; | |
309 } | |
310 case kDisallowCredentialsNotSetToTrue: { | |
311 const AtomicString& allowCredentialsHeaderValue = | |
312 response.httpHeaderField(allowCredentialsHeaderName); | |
313 builder.append( | |
314 "The value of the 'Access-Control-Allow-Credentials' header in " | |
315 "the response is '"); | |
316 builder.append(allowCredentialsHeaderValue); | |
317 builder.append( | |
318 "' which must " | |
319 "be 'true' when the request's credentials mode is 'include'."); | |
320 appendOriginDeniedMessage(builder, securityOrigin); | |
321 if (context == WebURLRequest::RequestContextXMLHttpRequest) { | |
322 builder.append( | |
323 " The credentials mode of requests initiated by the " | |
324 "XMLHttpRequest is controlled by the withCredentials attribute."); | |
325 } | |
326 return; | |
327 } | |
328 default: | |
329 NOTREACHED(); | |
330 } | |
331 } | |
332 | |
333 CrossOriginAccessControl::PreflightStatus | |
334 CrossOriginAccessControl::checkPreflight(const ResourceResponse& response) { | |
335 // CORS preflight with 3XX is considered network error in | |
336 // Fetch API Spec: https://fetch.spec.whatwg.org/#cors-preflight-fetch | |
337 // CORS Spec: http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0 | |
338 // https://crbug.com/452394 | |
339 int statusCode = response.httpStatusCode(); | |
340 if (!FetchUtils::isOkStatus(statusCode)) | |
341 return kPreflightInvalidStatus; | |
342 | |
343 return kPreflightSuccess; | |
344 } | |
345 | |
346 CrossOriginAccessControl::PreflightStatus | |
347 CrossOriginAccessControl::checkExternalPreflight( | |
348 const ResourceResponse& response) { | |
349 AtomicString result = | |
350 response.httpHeaderField(HTTPNames::Access_Control_Allow_External); | |
351 if (result.isNull()) | |
352 return kPreflightMissingAllowExternal; | |
353 if (!equalIgnoringCase(result, "true")) | |
354 return kPreflightInvalidAllowExternal; | |
355 return kPreflightSuccess; | |
356 } | |
357 | |
358 void CrossOriginAccessControl::preflightErrorString( | |
359 StringBuilder& builder, | |
360 CrossOriginAccessControl::PreflightStatus status, | |
361 const ResourceResponse& response) { | |
362 switch (status) { | |
363 case kPreflightInvalidStatus: { | |
364 int statusCode = response.httpStatusCode(); | |
365 builder.append("Response for preflight has invalid HTTP status code "); | |
366 builder.append(String::number(statusCode)); | |
367 return; | |
368 } | |
369 case kPreflightMissingAllowExternal: { | |
370 builder.append( | |
371 "No 'Access-Control-Allow-External' header was present in "); | |
372 builder.append( | |
373 "the preflight response for this external request (This is"); | |
374 builder.append(" an experimental header which is defined in "); | |
375 builder.append("'https://wicg.github.io/cors-rfc1918/')."); | |
376 return; | |
377 } | |
378 case kPreflightInvalidAllowExternal: { | |
379 String result = | |
380 response.httpHeaderField(HTTPNames::Access_Control_Allow_External); | |
381 builder.append("The 'Access-Control-Allow-External' header in the "); | |
382 builder.append( | |
383 "preflight response for this external request had a value"); | |
384 builder.append(" of '"); | |
385 builder.append(result); | |
386 builder.append("', not 'true' (This is an experimental header which is"); | |
387 builder.append(" defined in 'https://wicg.github.io/cors-rfc1918/')."); | |
388 return; | |
389 } | |
390 default: | |
391 NOTREACHED(); | |
392 } | |
393 } | |
394 | |
395 void parseAccessControlExposeHeadersAllowList(const String& headerValue, | |
396 HTTPHeaderSet& headerSet) { | |
397 Vector<String> headers; | |
398 headerValue.split(',', false, headers); | |
399 for (unsigned headerCount = 0; headerCount < headers.size(); headerCount++) { | |
400 String strippedHeader = headers[headerCount].stripWhiteSpace(); | |
401 if (!strippedHeader.isEmpty()) | |
402 headerSet.add(strippedHeader); | |
403 } | |
404 } | |
405 | |
406 void extractCorsExposedHeaderNamesList(const ResourceResponse& response, | |
407 HTTPHeaderSet& headerSet) { | |
408 // If a response was fetched via a service worker, it will always have | |
409 // corsExposedHeaderNames set, either from the Access-Control-Expose-Headers | |
410 // header, or explicitly via foreign fetch. For requests that didn't come from | |
411 // a service worker, foreign fetch doesn't apply so just parse the CORS | |
412 // header. | |
413 if (response.wasFetchedViaServiceWorker()) { | |
414 for (const auto& header : response.corsExposedHeaderNames()) | |
415 headerSet.add(header); | |
416 return; | |
417 } | |
418 parseAccessControlExposeHeadersAllowList( | |
419 response.httpHeaderField(HTTPNames::Access_Control_Expose_Headers), | |
420 headerSet); | |
421 } | |
422 | |
423 CrossOriginAccessControl::RedirectStatus | |
424 CrossOriginAccessControl::checkRedirectLocation(const KURL& requestURL) { | |
425 // Block non HTTP(S) schemes as specified in the step 4 in | |
426 // https://fetch.spec.whatwg.org/#http-redirect-fetch. Chromium also allows | |
427 // the data scheme. | |
428 // | |
429 // TODO(tyoshino): This check should be performed regardless of the CORS flag | |
430 // and request's mode. | |
431 if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(requestURL.protocol())) | |
432 return kRedirectDisallowedScheme; | |
433 | |
434 // Block URLs including credentials as specified in the step 9 in | |
435 // https://fetch.spec.whatwg.org/#http-redirect-fetch. | |
436 // | |
437 // TODO(tyoshino): This check should be performed also when request's | |
438 // origin is not same origin with the redirect destination's origin. | |
439 if (!(requestURL.user().isEmpty() && requestURL.pass().isEmpty())) | |
440 return kRedirectContainsCredentials; | |
441 | |
442 return kRedirectSuccess; | |
443 } | |
444 | |
445 void CrossOriginAccessControl::redirectErrorString( | |
446 StringBuilder& builder, | |
447 CrossOriginAccessControl::RedirectStatus status, | |
448 const KURL& requestURL) { | |
449 switch (status) { | |
450 case kRedirectDisallowedScheme: { | |
451 builder.append("Redirect location '"); | |
452 builder.append(requestURL.getString()); | |
453 builder.append("' has a disallowed scheme for cross-origin requests."); | |
454 return; | |
455 } | |
456 case kRedirectContainsCredentials: { | |
457 builder.append("Redirect location '"); | |
458 builder.append(requestURL.getString()); | |
459 builder.append( | |
460 "' contains a username and password, which is disallowed for" | |
461 " cross-origin requests."); | |
462 return; | |
463 } | |
464 default: | |
465 NOTREACHED(); | |
466 } | |
467 } | |
468 | |
469 bool CrossOriginAccessControl::handleRedirect( | |
470 PassRefPtr<SecurityOrigin> securityOrigin, | |
471 ResourceRequest& newRequest, | |
472 const ResourceResponse& redirectResponse, | |
473 StoredCredentials withCredentials, | |
474 ResourceLoaderOptions& options, | |
475 String& errorMessage) { | |
476 // http://www.w3.org/TR/cors/#redirect-steps terminology: | |
477 const KURL& lastURL = redirectResponse.url(); | |
478 const KURL& newURL = newRequest.url(); | |
479 | |
480 RefPtr<SecurityOrigin> currentSecurityOrigin = securityOrigin; | |
481 | |
482 RefPtr<SecurityOrigin> newSecurityOrigin = currentSecurityOrigin; | |
483 | |
484 // TODO(tyoshino): This should be fixed to check not only the last one but | |
485 // all redirect responses. | |
486 if (!currentSecurityOrigin->canRequest(lastURL)) { | |
487 // Follow http://www.w3.org/TR/cors/#redirect-steps | |
488 CrossOriginAccessControl::RedirectStatus redirectStatus = | |
489 CrossOriginAccessControl::checkRedirectLocation(newURL); | |
490 if (redirectStatus != kRedirectSuccess) { | |
491 StringBuilder builder; | |
492 builder.append("Redirect from '"); | |
493 builder.append(lastURL.getString()); | |
494 builder.append("' has been blocked by CORS policy: "); | |
495 CrossOriginAccessControl::redirectErrorString(builder, redirectStatus, | |
496 newURL); | |
497 errorMessage = builder.toString(); | |
498 return false; | |
499 } | |
500 | |
501 // Step 5: perform resource sharing access check. | |
502 CrossOriginAccessControl::AccessStatus corsStatus = | |
503 CrossOriginAccessControl::checkAccess(redirectResponse, withCredentials, | |
504 currentSecurityOrigin.get()); | |
505 if (corsStatus != kAccessAllowed) { | |
506 StringBuilder builder; | |
507 builder.append("Redirect from '"); | |
508 builder.append(lastURL.getString()); | |
509 builder.append("' has been blocked by CORS policy: "); | |
510 CrossOriginAccessControl::accessControlErrorString( | |
511 builder, corsStatus, redirectResponse, currentSecurityOrigin.get(), | |
512 newRequest.requestContext()); | |
513 errorMessage = builder.toString(); | |
514 return false; | |
515 } | |
516 | |
517 RefPtr<SecurityOrigin> lastOrigin = SecurityOrigin::create(lastURL); | |
518 // Set request's origin to a globally unique identifier as specified in | |
519 // the step 10 in https://fetch.spec.whatwg.org/#http-redirect-fetch. | |
520 if (!lastOrigin->canRequest(newURL)) { | |
521 options.securityOrigin = SecurityOrigin::createUnique(); | |
522 newSecurityOrigin = options.securityOrigin; | |
523 } | |
524 } | |
525 | |
526 if (!currentSecurityOrigin->canRequest(newURL)) { | |
527 newRequest.clearHTTPOrigin(); | |
528 newRequest.setHTTPOrigin(newSecurityOrigin.get()); | |
529 | |
530 // Unset credentials flag if request's credentials mode is "same-origin" as | |
531 // request's response tainting becomes "cors". | |
532 // | |
533 // This is equivalent to the step 2 in | |
534 // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch | |
535 if (options.credentialsRequested == ClientDidNotRequestCredentials) | |
536 options.allowCredentials = DoNotAllowStoredCredentials; | |
537 } | |
538 return true; | |
539 } | |
540 | |
541 } // namespace blink | |
OLD | NEW |