From: bbudge@chromium.org Date: Mon, 16 Jan 2012 21:18:39 +0000 (+0000) Subject: Changes AssociatedURLLoader to remove non-whitelisted HTTP response headers for CORS... X-Git-Tag: 070512121124~15239 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e8961718701e5e1bad966450acb7c478177acbd4;p=profile%2Fivi%2Fwebkit-efl.git Changes AssociatedURLLoader to remove non-whitelisted HTTP response headers for CORS requests, and Set-Cookie and Set-Cookie2 response headers for all requests. https://bugs.webkit.org/show_bug.cgi?id=76228 Reviewed by Adam Barth. * src/AssociatedURLLoader.cpp: (WebKit::AssociatedURLLoader::ClientAdapter::create): (WebKit::AssociatedURLLoader::ClientAdapter::ClientAdapter): (WebKit::AssociatedURLLoader::ClientAdapter::didReceiveResponse): (WebKit::AssociatedURLLoader::loadAsynchronously): * tests/AssociatedURLLoaderTest.cpp: (WebKit::AssociatedURLLoaderTest::didReceiveResponse): (WebKit::TEST_F): git-svn-id: http://svn.webkit.org/repository/webkit/trunk@105087 268f45cc-cd09-0410-ab3c-d52691b4dbfc --- diff --git a/Source/WebKit/chromium/ChangeLog b/Source/WebKit/chromium/ChangeLog index 050096b..e6cdb3f 100644 --- a/Source/WebKit/chromium/ChangeLog +++ b/Source/WebKit/chromium/ChangeLog @@ -1,3 +1,20 @@ +2012-01-16 Bill Budge + + Changes AssociatedURLLoader to remove non-whitelisted HTTP response headers for CORS requests, + and Set-Cookie and Set-Cookie2 response headers for all requests. + https://bugs.webkit.org/show_bug.cgi?id=76228 + + Reviewed by Adam Barth. + + * src/AssociatedURLLoader.cpp: + (WebKit::AssociatedURLLoader::ClientAdapter::create): + (WebKit::AssociatedURLLoader::ClientAdapter::ClientAdapter): + (WebKit::AssociatedURLLoader::ClientAdapter::didReceiveResponse): + (WebKit::AssociatedURLLoader::loadAsynchronously): + * tests/AssociatedURLLoaderTest.cpp: + (WebKit::AssociatedURLLoaderTest::didReceiveResponse): + (WebKit::TEST_F): + 2012-01-16 xueqing huang Add offline web applications API applicationCache.abort. diff --git a/Source/WebKit/chromium/src/AssociatedURLLoader.cpp b/Source/WebKit/chromium/src/AssociatedURLLoader.cpp index 6adeba7..fdc3cd0 100644 --- a/Source/WebKit/chromium/src/AssociatedURLLoader.cpp +++ b/Source/WebKit/chromium/src/AssociatedURLLoader.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "AssociatedURLLoader.h" +#include "CrossOriginAccessControl.h" #include "DocumentThreadableLoader.h" #include "DocumentThreadableLoaderClient.h" #include "HTTPValidation.h" @@ -45,9 +46,12 @@ #include "XMLHttpRequest.h" #include "platform/WebHTTPHeaderVisitor.h" #include "platform/WebKitPlatformSupport.h" +#include "platform/WebString.h" #include "platform/WebURLError.h" #include "platform/WebURLLoaderClient.h" #include "platform/WebURLRequest.h" +#include +#include using namespace WebCore; using namespace WTF; @@ -56,10 +60,10 @@ namespace WebKit { namespace { -class SafeHTTPHeaderValidator : public WebHTTPHeaderVisitor { - WTF_MAKE_NONCOPYABLE(SafeHTTPHeaderValidator); +class HTTPRequestHeaderValidator : public WebHTTPHeaderVisitor { + WTF_MAKE_NONCOPYABLE(HTTPRequestHeaderValidator); public: - SafeHTTPHeaderValidator() : m_isSafe(true) { } + HTTPRequestHeaderValidator() : m_isSafe(true) { } void visitHeader(const WebString& name, const WebString& value); bool isSafe() const { return m_isSafe; } @@ -68,11 +72,34 @@ private: bool m_isSafe; }; -void SafeHTTPHeaderValidator::visitHeader(const WebString& name, const WebString& value) +void HTTPRequestHeaderValidator::visitHeader(const WebString& name, const WebString& value) { m_isSafe = m_isSafe && isValidHTTPToken(name) && XMLHttpRequest::isAllowedHTTPHeader(name) && isValidHTTPHeaderValue(value); } +class HTTPResponseHeaderValidator : public WebHTTPHeaderVisitor { + WTF_MAKE_NONCOPYABLE(HTTPResponseHeaderValidator); +public: + HTTPResponseHeaderValidator(bool usingAccessControl) : m_usingAccessControl(usingAccessControl) { } + + void visitHeader(const WebString& name, const WebString& value); + const Vector& disallowedHeaders() const { return m_disallowedHeaders; } + +private: + Vector m_disallowedHeaders; + bool m_usingAccessControl; +}; + +void HTTPResponseHeaderValidator::visitHeader(const WebString& name, const WebString& value) +{ + String headerName(name); + // Hide non-whitelisted headers for CORS requests. + // Hide Set-Cookie headers for all requests. + if ((m_usingAccessControl && !isOnAccessControlResponseHeaderWhitelist(headerName)) + || (equalIgnoringCase(headerName, "set-cookie") || equalIgnoringCase(headerName, "set-cookie2"))) + m_disallowedHeaders.append(name); +} + } // This class bridges the interface differences between WebCore and WebKit loader clients. @@ -80,7 +107,7 @@ void SafeHTTPHeaderValidator::visitHeader(const WebString& name, const WebString class AssociatedURLLoader::ClientAdapter : public DocumentThreadableLoaderClient { WTF_MAKE_NONCOPYABLE(ClientAdapter); public: - static PassOwnPtr create(AssociatedURLLoader*, WebURLLoaderClient*, bool /*downloadToFile*/); + static PassOwnPtr create(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&); virtual void didSendData(unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/); virtual void willSendRequest(ResourceRequest& /*newRequest*/, const ResourceResponse& /*redirectResponse*/); @@ -105,30 +132,30 @@ public: void clearClient() { m_client = 0; } private: - ClientAdapter(AssociatedURLLoader*, WebURLLoaderClient*, bool /*downloadToFile*/); + ClientAdapter(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&); void notifyError(Timer*); AssociatedURLLoader* m_loader; WebURLLoaderClient* m_client; + WebURLLoaderOptions m_options; WebURLError m_error; Timer m_errorTimer; - bool m_downloadToFile; bool m_enableErrorNotifications; bool m_didFail; }; -PassOwnPtr AssociatedURLLoader::ClientAdapter::create(AssociatedURLLoader* loader, WebURLLoaderClient* client, bool downloadToFile) +PassOwnPtr AssociatedURLLoader::ClientAdapter::create(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options) { - return adoptPtr(new ClientAdapter(loader, client, downloadToFile)); + return adoptPtr(new ClientAdapter(loader, client, options)); } -AssociatedURLLoader::ClientAdapter::ClientAdapter(AssociatedURLLoader* loader, WebURLLoaderClient* client, bool downloadToFile) +AssociatedURLLoader::ClientAdapter::ClientAdapter(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options) : m_loader(loader) , m_client(client) + , m_options(options) , m_errorTimer(this, &ClientAdapter::notifyError) - , m_downloadToFile(downloadToFile) , m_enableErrorNotifications(false) , m_didFail(false) { @@ -156,8 +183,18 @@ void AssociatedURLLoader::ClientAdapter::didSendData(unsigned long long bytesSen void AssociatedURLLoader::ClientAdapter::didReceiveResponse(unsigned long, const ResourceResponse& response) { - WrappedResourceResponse wrappedResponse(response); - m_client->didReceiveResponse(m_loader, wrappedResponse); + // Try to use the original ResourceResponse if possible. + WebURLResponse validatedResponse = WrappedResourceResponse(response); + HTTPResponseHeaderValidator validator(m_options.crossOriginRequestPolicy == WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl); + validatedResponse.visitHTTPHeaderFields(&validator); + // If there are disallowed headers, copy the response so we can remove them. + const Vector& disallowedHeaders = validator.disallowedHeaders(); + if (!disallowedHeaders.isEmpty()) { + validatedResponse = WebURLResponse(validatedResponse); + for (size_t i = 0; i < disallowedHeaders.size(); ++i) + validatedResponse.clearHTTPHeaderField(disallowedHeaders[i]); + } + m_client->didReceiveResponse(m_loader, validatedResponse); } void AssociatedURLLoader::ClientAdapter::didDownloadData(int dataLength) @@ -263,13 +300,13 @@ void AssociatedURLLoader::loadAsynchronously(const WebURLRequest& request, WebUR allowLoad = isValidHTTPToken(method) && XMLHttpRequest::isAllowedHTTPMethod(method); if (allowLoad) { newRequest.setHTTPMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(method)); - SafeHTTPHeaderValidator validator; + HTTPRequestHeaderValidator validator; newRequest.visitHTTPHeaderFields(&validator); allowLoad = validator.isSafe(); } } - m_clientAdapter = ClientAdapter::create(this, m_client, request.downloadToFile()); + m_clientAdapter = ClientAdapter::create(this, m_client, m_options); if (allowLoad) { ThreadableLoaderOptions options; diff --git a/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp b/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp index 62d12b4..4bb492b 100644 --- a/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp +++ b/Source/WebKit/chromium/tests/AssociatedURLLoaderTest.cpp @@ -137,6 +137,7 @@ public: void didReceiveResponse(WebURLLoader* loader, const WebURLResponse& response) { m_didReceiveResponse = true; + m_actualResponse = WebURLResponse(response); EXPECT_EQ(m_expectedLoader, loader); EXPECT_EQ(m_expectedResponse.url(), response.url()); EXPECT_EQ(m_expectedResponse.httpStatusCode(), response.httpStatusCode()); @@ -226,6 +227,7 @@ protected: WebView* m_webView; WebURLLoader* m_expectedLoader; + WebURLResponse m_actualResponse; WebURLResponse m_expectedResponse; WebURLRequest m_expectedNewRequest; WebURLResponse m_expectedRedirectResponse; @@ -487,4 +489,50 @@ TEST_F(AssociatedURLLoaderTest, UntrustedCheckHeaders) CheckHeaderFails("foo", "bar\x0d\x0ax-csrf-token:\x20test1234"); } +// Test that a CORS load only returns whitelisted headers. +TEST_F(AssociatedURLLoaderTest, CrossOriginHeaderWhitelisting) +{ + // This is cross-origin since the frame was loaded from www.test.com. + GURL url = GURL("http://www.other.com/CrossOriginHeaderWhitelisting.html"); + WebURLRequest request; + request.initialize(); + request.setURL(url); + + m_expectedResponse = WebURLResponse(); + m_expectedResponse.initialize(); + m_expectedResponse.setMIMEType("text/html"); + m_expectedResponse.addHTTPHeaderField("Access-Control-Allow-Origin", "*"); + // These headers are whitelisted and should be in the response. + m_expectedResponse.addHTTPHeaderField("cache-control", "foo"); + m_expectedResponse.addHTTPHeaderField("content-language", "foo"); + m_expectedResponse.addHTTPHeaderField("content-type", "foo"); + m_expectedResponse.addHTTPHeaderField("expires", "foo"); + m_expectedResponse.addHTTPHeaderField("last-modified", "foo"); + m_expectedResponse.addHTTPHeaderField("pragma", "foo"); + // These should never be in the response. + m_expectedResponse.addHTTPHeaderField("Set-Cookie", "foo"); + m_expectedResponse.addHTTPHeaderField("Set-Cookie2", "foo"); + webkit_support::RegisterMockedURL(url, m_expectedResponse, m_frameFilePath); + + WebURLLoaderOptions options; + options.crossOriginRequestPolicy = WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; + m_expectedLoader = createAssociatedURLLoader(options); + EXPECT_TRUE(m_expectedLoader); + m_expectedLoader->loadAsynchronously(request, this); + serveRequests(); + EXPECT_TRUE(m_didReceiveResponse); + EXPECT_TRUE(m_didReceiveData); + EXPECT_TRUE(m_didFinishLoading); + + EXPECT_FALSE(m_actualResponse.httpHeaderField("cache-control").isEmpty()); + EXPECT_FALSE(m_actualResponse.httpHeaderField("content-language").isEmpty()); + EXPECT_FALSE(m_actualResponse.httpHeaderField("content-type").isEmpty()); + EXPECT_FALSE(m_actualResponse.httpHeaderField("expires").isEmpty()); + EXPECT_FALSE(m_actualResponse.httpHeaderField("last-modified").isEmpty()); + EXPECT_FALSE(m_actualResponse.httpHeaderField("pragma").isEmpty()); + + EXPECT_TRUE(m_actualResponse.httpHeaderField("Set-Cookie").isEmpty()); + EXPECT_TRUE(m_actualResponse.httpHeaderField("Set-Cookie2").isEmpty()); +} + }