Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. | 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 33 #include "platform/network/ResourceRequest.h" | 33 #include "platform/network/ResourceRequest.h" |
| 34 #include "platform/network/ResourceResponse.h" | 34 #include "platform/network/ResourceResponse.h" |
| 35 #include "platform/weborigin/SchemeRegistry.h" | 35 #include "platform/weborigin/SchemeRegistry.h" |
| 36 #include "platform/weborigin/SecurityOrigin.h" | 36 #include "platform/weborigin/SecurityOrigin.h" |
| 37 #include "wtf/Threading.h" | 37 #include "wtf/Threading.h" |
| 38 #include "wtf/text/AtomicString.h" | 38 #include "wtf/text/AtomicString.h" |
| 39 #include "wtf/text/StringBuilder.h" | 39 #include "wtf/text/StringBuilder.h" |
| 40 | 40 |
| 41 namespace WebCore { | 41 namespace WebCore { |
| 42 | 42 |
| 43 bool isOnAccessControlSimpleRequestMethodWhitelist(const String& method) | 43 namespace { |
| 44 | |
| 45 struct ForbiddenHeaderNames { | |
| 46 WTF_MAKE_NONCOPYABLE(ForbiddenHeaderNames); WTF_MAKE_FAST_ALLOCATED; | |
| 47 public: | |
| 48 ForbiddenHeaderNames(); | |
| 49 bool has(const String& name) const | |
| 50 { | |
| 51 return m_fixedNames.contains(name) | |
| 52 || name.startsWith(m_proxyHeaderPrefix, false) | |
| 53 || name.startsWith(m_secHeaderPrefix, false); | |
| 54 } | |
| 55 | |
| 56 private: | |
| 57 String m_proxyHeaderPrefix; | |
| 58 String m_secHeaderPrefix; | |
| 59 HashSet<String, CaseFoldingHash> m_fixedNames; | |
| 60 }; | |
| 61 | |
| 62 ForbiddenHeaderNames::ForbiddenHeaderNames() | |
| 63 : m_proxyHeaderPrefix("proxy-") | |
| 64 , m_secHeaderPrefix("sec-") | |
| 44 { | 65 { |
| 45 return method == "GET" || method == "HEAD" || method == "POST"; | 66 m_fixedNames.add("accept-charset"); |
| 67 m_fixedNames.add("accept-encoding"); | |
| 68 m_fixedNames.add("access-control-request-headers"); | |
| 69 m_fixedNames.add("access-control-request-method"); | |
| 70 m_fixedNames.add("connection"); | |
| 71 m_fixedNames.add("content-length"); | |
| 72 m_fixedNames.add("cookie"); | |
| 73 m_fixedNames.add("cookie2"); | |
| 74 m_fixedNames.add("date"); | |
| 75 m_fixedNames.add("dnt"); | |
| 76 m_fixedNames.add("expect"); | |
| 77 m_fixedNames.add("host"); | |
| 78 m_fixedNames.add("keep-alive"); | |
| 79 m_fixedNames.add("origin"); | |
| 80 m_fixedNames.add("referer"); | |
| 81 m_fixedNames.add("te"); | |
| 82 m_fixedNames.add("trailer"); | |
| 83 m_fixedNames.add("transfer-encoding"); | |
| 84 m_fixedNames.add("upgrade"); | |
| 85 m_fixedNames.add("user-agent"); | |
| 86 m_fixedNames.add("via"); | |
| 46 } | 87 } |
| 47 | 88 |
| 48 bool isOnAccessControlSimpleRequestHeaderWhitelist(const AtomicString& name, con st AtomicString& value) | 89 static const ForbiddenHeaderNames* forbiddenHeaderNames = nullptr; |
| 90 | |
| 91 static const ForbiddenHeaderNames* createForbiddenHeaderNames() | |
| 49 { | 92 { |
| 50 if (equalIgnoringCase(name, "accept") | 93 forbiddenHeaderNames = new ForbiddenHeaderNames; |
| 51 || equalIgnoringCase(name, "accept-language") | 94 return forbiddenHeaderNames; |
| 52 || equalIgnoringCase(name, "content-language") | |
| 53 || equalIgnoringCase(name, "origin") | |
| 54 || equalIgnoringCase(name, "referer")) | |
| 55 return true; | |
| 56 | |
| 57 // Preflight is required for MIME types that can not be sent via form submis sion. | |
| 58 if (equalIgnoringCase(name, "content-type")) { | |
| 59 AtomicString mimeType = extractMIMETypeFromMediaType(value); | |
| 60 return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") | |
| 61 || equalIgnoringCase(mimeType, "multipart/form-data") | |
| 62 || equalIgnoringCase(mimeType, "text/plain"); | |
| 63 } | |
| 64 | |
| 65 return false; | |
| 66 } | 95 } |
| 67 | 96 |
| 68 bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& headerMap) | 97 static const ForbiddenHeaderNames* initializeForbiddenHeaderNames() |
|
sof
2014/07/17 08:18:09
Unless there are reasons not to, should we move th
yhirano
2014/08/06 08:48:03
Done.
| |
| 69 { | 98 { |
| 70 if (!isOnAccessControlSimpleRequestMethodWhitelist(method)) | 99 // Uses dummy to avoid warnings about an unused variable. |
|
sof
2014/07/17 08:18:09
I know this is code that you moved into here, but
yhirano
2014/08/06 08:48:03
Done.
| |
| 71 return false; | 100 AtomicallyInitializedStatic(const ForbiddenHeaderNames*, dummy = createForbi ddenHeaderNames()); |
| 101 return dummy; | |
| 102 } | |
| 72 | 103 |
| 73 HTTPHeaderMap::const_iterator end = headerMap.end(); | 104 } // namespace |
| 74 for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { | |
| 75 if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->key, it->value)) | |
| 76 return false; | |
| 77 } | |
| 78 | |
| 79 return true; | |
| 80 } | |
| 81 | 105 |
| 82 static PassOwnPtr<HTTPHeaderSet> createAllowedCrossOriginResponseHeadersSet() | 106 static PassOwnPtr<HTTPHeaderSet> createAllowedCrossOriginResponseHeadersSet() |
| 83 { | 107 { |
| 84 OwnPtr<HTTPHeaderSet> headerSet = adoptPtr(new HashSet<String, CaseFoldingHa sh>); | 108 OwnPtr<HTTPHeaderSet> headerSet = adoptPtr(new HashSet<String, CaseFoldingHa sh>); |
| 85 | 109 |
| 86 headerSet->add("cache-control"); | 110 headerSet->add("cache-control"); |
| 87 headerSet->add("content-language"); | 111 headerSet->add("content-language"); |
| 88 headerSet->add("content-type"); | 112 headerSet->add("content-type"); |
| 89 headerSet->add("expires"); | 113 headerSet->add("expires"); |
| 90 headerSet->add("last-modified"); | 114 headerSet->add("last-modified"); |
| (...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 277 request.clearHTTPOrigin(); | 301 request.clearHTTPOrigin(); |
| 278 request.setHTTPOrigin(securityOrigin->toAtomicString()); | 302 request.setHTTPOrigin(securityOrigin->toAtomicString()); |
| 279 // If the user didn't request credentials in the first place, update our | 303 // If the user didn't request credentials in the first place, update our |
| 280 // state so we neither request them nor expect they must be allowed. | 304 // state so we neither request them nor expect they must be allowed. |
| 281 if (options.credentialsRequested == ClientDidNotRequestCredentials) | 305 if (options.credentialsRequested == ClientDidNotRequestCredentials) |
| 282 options.allowCredentials = DoNotAllowStoredCredentials; | 306 options.allowCredentials = DoNotAllowStoredCredentials; |
| 283 } | 307 } |
| 284 return true; | 308 return true; |
| 285 } | 309 } |
| 286 | 310 |
| 311 bool CrossOriginAccessControl::isSimpleMethod(const String& method) | |
| 312 { | |
| 313 // "A simple method is a method that is `GET`, `HEAD`, or `POST`." | |
|
sof
2014/07/17 08:18:09
Anchor this with a spec href?
yhirano
2014/08/06 08:48:02
Done.
| |
| 314 return method == "GET" || method == "HEAD" || method == "POST"; | |
| 315 } | |
| 316 | |
| 317 bool CrossOriginAccessControl::isSimpleHeader(const AtomicString& name, const At omicString& value) | |
| 318 { | |
| 319 // "A simple header is a header whose name is either one of `Accept`, | |
|
sof
2014/07/17 08:18:09
This is also something with a settled spec referen
yhirano
2014/08/06 08:48:03
Done.
| |
| 320 // `Accept-Language`, and `Content-Language`, or whose name is | |
| 321 // `Content-Type` and value, once parsed, is one of | |
| 322 // `application/x-www-form-urlencoded`, `multipart/form-data`, and | |
| 323 // `text/plain`." | |
| 324 | |
| 325 if (equalIgnoringCase(name, "accept") | |
| 326 || equalIgnoringCase(name, "accept-language") | |
| 327 || equalIgnoringCase(name, "content-language")) | |
| 328 return true; | |
| 329 | |
| 330 // Preflight is required for MIME types that can not be sent via form submis sion. | |
| 331 if (equalIgnoringCase(name, "content-type")) { | |
| 332 AtomicString mimeType = extractMIMETypeFromMediaType(value); | |
| 333 return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") | |
| 334 || equalIgnoringCase(mimeType, "multipart/form-data") | |
| 335 || equalIgnoringCase(mimeType, "text/plain"); | |
| 336 } | |
| 337 | |
| 338 return false; | |
| 339 } | |
| 340 | |
| 341 bool CrossOriginAccessControl::isSimpleRequest(const String& method, const HTTPH eaderMap& headerMap) | |
| 342 { | |
| 343 if (!isSimpleMethod(method)) | |
| 344 return false; | |
| 345 | |
| 346 HTTPHeaderMap::const_iterator end = headerMap.end(); | |
| 347 for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { | |
| 348 if (!isSimpleHeader(it->key, it->value)) | |
| 349 return false; | |
| 350 } | |
| 351 | |
| 352 return true; | |
| 353 } | |
| 354 | |
| 355 bool CrossOriginAccessControl::isForbiddenMethod(const String& method) | |
| 356 { | |
| 357 // "A forbidden method is a method that is a byte case-insensitive match" | |
|
sof
2014/07/17 08:18:09
I think it is a good idea to share definitions lik
yhirano
2014/08/06 08:48:02
I didn't come up with a great name, but moved thes
sof
2014/08/06 19:48:15
Yes, "Utils" classes can grow to become a disparat
| |
| 358 // for one of `CONNECT`, `TRACE`, and `TRACK`." | |
| 359 return equalIgnoringCase(method, "TRACE") | |
| 360 || equalIgnoringCase(method, "TRACK") | |
| 361 || equalIgnoringCase(method, "CONNECT"); | |
| 362 } | |
| 363 | |
| 364 bool CrossOriginAccessControl::isForbiddenHeaderName(const String& name) | |
| 365 { | |
| 366 // "A forbidden header name is a header names that is one of: | |
| 367 // `Accept-Charset`, `Accept-Encoding`, `Access-Control-Request-Headers`, | |
| 368 // `Access-Control-Request-Method`, `Connection`, | |
| 369 // `Content-Length, Cookie`, `Cookie2`, `Date`, `DNT`, `Expect`, `Host`, | |
| 370 // `Keep-Alive`, `Origin`, `Referer`, `TE`, `Trailer`, | |
| 371 // `Transfer-Encoding`, `Upgrade`, `User-Agent`, `Via` | |
| 372 // or starts with `Proxy-` or `Sec-` (including when it is just `Proxy-` or | |
| 373 // `Sec-`)." | |
| 374 | |
| 375 initializeForbiddenHeaderNames(); | |
| 376 return forbiddenHeaderNames->has(name); | |
| 377 } | |
| 378 | |
| 379 bool CrossOriginAccessControl::isForbiddenResponseHeaderName(const String& name) | |
| 380 { | |
| 381 // "A forbidden response header name is a header name that is one of: | |
| 382 // `Set-Cookie`, `Set-Cookie2`" | |
| 383 | |
| 384 return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set -cookie2"); | |
| 385 } | |
| 386 | |
| 387 bool CrossOriginAccessControl::isSimpleOrForbiddenRequest(const String& method, const HTTPHeaderMap& headerMap) | |
| 388 { | |
| 389 if (!isSimpleMethod(method)) | |
| 390 return false; | |
| 391 | |
| 392 HTTPHeaderMap::const_iterator end = headerMap.end(); | |
| 393 for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { | |
| 394 if (!isSimpleHeader(it->key, it->value) && !isForbiddenHeaderName(it->ke y)) | |
| 395 return false; | |
| 396 } | |
| 397 | |
| 398 return true; | |
| 399 } | |
| 400 | |
| 287 } // namespace WebCore | 401 } // namespace WebCore |
| OLD | NEW |