Chromium Code Reviews| Index: Source/core/fetch/CrossOriginAccessControl.cpp |
| diff --git a/Source/core/fetch/CrossOriginAccessControl.cpp b/Source/core/fetch/CrossOriginAccessControl.cpp |
| index 03e120eb3fbc08395ee2d537a09e2c6efc77e3ad..9b2f366c6946a253cd649adaeae987f0e1bbfde9 100644 |
| --- a/Source/core/fetch/CrossOriginAccessControl.cpp |
| +++ b/Source/core/fetch/CrossOriginAccessControl.cpp |
| @@ -40,45 +40,69 @@ |
| namespace WebCore { |
| -bool isOnAccessControlSimpleRequestMethodWhitelist(const String& method) |
| -{ |
| - return method == "GET" || method == "HEAD" || method == "POST"; |
| -} |
| +namespace { |
| + |
| +struct ForbiddenHeaderNames { |
| + WTF_MAKE_NONCOPYABLE(ForbiddenHeaderNames); WTF_MAKE_FAST_ALLOCATED; |
| +public: |
| + ForbiddenHeaderNames(); |
| + bool has(const String& name) const |
| + { |
| + return m_fixedNames.contains(name) |
| + || name.startsWith(m_proxyHeaderPrefix, false) |
| + || name.startsWith(m_secHeaderPrefix, false); |
| + } |
| + |
| +private: |
| + String m_proxyHeaderPrefix; |
| + String m_secHeaderPrefix; |
| + HashSet<String, CaseFoldingHash> m_fixedNames; |
| +}; |
| -bool isOnAccessControlSimpleRequestHeaderWhitelist(const AtomicString& name, const AtomicString& value) |
| +ForbiddenHeaderNames::ForbiddenHeaderNames() |
| + : m_proxyHeaderPrefix("proxy-") |
| + , m_secHeaderPrefix("sec-") |
| { |
| - if (equalIgnoringCase(name, "accept") |
| - || equalIgnoringCase(name, "accept-language") |
| - || equalIgnoringCase(name, "content-language") |
| - || equalIgnoringCase(name, "origin") |
| - || equalIgnoringCase(name, "referer")) |
| - return true; |
| + m_fixedNames.add("accept-charset"); |
| + m_fixedNames.add("accept-encoding"); |
| + m_fixedNames.add("access-control-request-headers"); |
| + m_fixedNames.add("access-control-request-method"); |
| + m_fixedNames.add("connection"); |
| + m_fixedNames.add("content-length"); |
| + m_fixedNames.add("cookie"); |
| + m_fixedNames.add("cookie2"); |
| + m_fixedNames.add("date"); |
| + m_fixedNames.add("dnt"); |
| + m_fixedNames.add("expect"); |
| + m_fixedNames.add("host"); |
| + m_fixedNames.add("keep-alive"); |
| + m_fixedNames.add("origin"); |
| + m_fixedNames.add("referer"); |
| + m_fixedNames.add("te"); |
| + m_fixedNames.add("trailer"); |
| + m_fixedNames.add("transfer-encoding"); |
| + m_fixedNames.add("upgrade"); |
| + m_fixedNames.add("user-agent"); |
| + m_fixedNames.add("via"); |
| +} |
| - // Preflight is required for MIME types that can not be sent via form submission. |
| - if (equalIgnoringCase(name, "content-type")) { |
| - AtomicString mimeType = extractMIMETypeFromMediaType(value); |
| - return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") |
| - || equalIgnoringCase(mimeType, "multipart/form-data") |
| - || equalIgnoringCase(mimeType, "text/plain"); |
| - } |
| +static const ForbiddenHeaderNames* forbiddenHeaderNames = nullptr; |
| - return false; |
| +static const ForbiddenHeaderNames* createForbiddenHeaderNames() |
| +{ |
| + forbiddenHeaderNames = new ForbiddenHeaderNames; |
| + return forbiddenHeaderNames; |
| } |
| -bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& headerMap) |
| +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.
|
| { |
| - if (!isOnAccessControlSimpleRequestMethodWhitelist(method)) |
| - return false; |
| - |
| - HTTPHeaderMap::const_iterator end = headerMap.end(); |
| - for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { |
| - if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->key, it->value)) |
| - return false; |
| - } |
| - |
| - return true; |
| + // 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.
|
| + AtomicallyInitializedStatic(const ForbiddenHeaderNames*, dummy = createForbiddenHeaderNames()); |
| + return dummy; |
| } |
| +} // namespace |
| + |
| static PassOwnPtr<HTTPHeaderSet> createAllowedCrossOriginResponseHeadersSet() |
| { |
| OwnPtr<HTTPHeaderSet> headerSet = adoptPtr(new HashSet<String, CaseFoldingHash>); |
| @@ -284,4 +308,94 @@ bool CrossOriginAccessControl::handleRedirect(Resource* resource, SecurityOrigin |
| return true; |
| } |
| +bool CrossOriginAccessControl::isSimpleMethod(const String& method) |
| +{ |
| + // "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.
|
| + return method == "GET" || method == "HEAD" || method == "POST"; |
| +} |
| + |
| +bool CrossOriginAccessControl::isSimpleHeader(const AtomicString& name, const AtomicString& value) |
| +{ |
| + // "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.
|
| + // `Accept-Language`, and `Content-Language`, or whose name is |
| + // `Content-Type` and value, once parsed, is one of |
| + // `application/x-www-form-urlencoded`, `multipart/form-data`, and |
| + // `text/plain`." |
| + |
| + if (equalIgnoringCase(name, "accept") |
| + || equalIgnoringCase(name, "accept-language") |
| + || equalIgnoringCase(name, "content-language")) |
| + return true; |
| + |
| + // Preflight is required for MIME types that can not be sent via form submission. |
| + if (equalIgnoringCase(name, "content-type")) { |
| + AtomicString mimeType = extractMIMETypeFromMediaType(value); |
| + return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") |
| + || equalIgnoringCase(mimeType, "multipart/form-data") |
| + || equalIgnoringCase(mimeType, "text/plain"); |
| + } |
| + |
| + return false; |
| +} |
| + |
| +bool CrossOriginAccessControl::isSimpleRequest(const String& method, const HTTPHeaderMap& headerMap) |
| +{ |
| + if (!isSimpleMethod(method)) |
| + return false; |
| + |
| + HTTPHeaderMap::const_iterator end = headerMap.end(); |
| + for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { |
| + if (!isSimpleHeader(it->key, it->value)) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool CrossOriginAccessControl::isForbiddenMethod(const String& method) |
| +{ |
| + // "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
|
| + // for one of `CONNECT`, `TRACE`, and `TRACK`." |
| + return equalIgnoringCase(method, "TRACE") |
| + || equalIgnoringCase(method, "TRACK") |
| + || equalIgnoringCase(method, "CONNECT"); |
| +} |
| + |
| +bool CrossOriginAccessControl::isForbiddenHeaderName(const String& name) |
| +{ |
| + // "A forbidden header name is a header names that is one of: |
| + // `Accept-Charset`, `Accept-Encoding`, `Access-Control-Request-Headers`, |
| + // `Access-Control-Request-Method`, `Connection`, |
| + // `Content-Length, Cookie`, `Cookie2`, `Date`, `DNT`, `Expect`, `Host`, |
| + // `Keep-Alive`, `Origin`, `Referer`, `TE`, `Trailer`, |
| + // `Transfer-Encoding`, `Upgrade`, `User-Agent`, `Via` |
| + // or starts with `Proxy-` or `Sec-` (including when it is just `Proxy-` or |
| + // `Sec-`)." |
| + |
| + initializeForbiddenHeaderNames(); |
| + return forbiddenHeaderNames->has(name); |
| +} |
| + |
| +bool CrossOriginAccessControl::isForbiddenResponseHeaderName(const String& name) |
| +{ |
| + // "A forbidden response header name is a header name that is one of: |
| + // `Set-Cookie`, `Set-Cookie2`" |
| + |
| + return equalIgnoringCase(name, "set-cookie") || equalIgnoringCase(name, "set-cookie2"); |
| +} |
| + |
| +bool CrossOriginAccessControl::isSimpleOrForbiddenRequest(const String& method, const HTTPHeaderMap& headerMap) |
| +{ |
| + if (!isSimpleMethod(method)) |
| + return false; |
| + |
| + HTTPHeaderMap::const_iterator end = headerMap.end(); |
| + for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { |
| + if (!isSimpleHeader(it->key, it->value) && !isForbiddenHeaderName(it->key)) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| } // namespace WebCore |