/* * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "third_party/blink/public/web/web_associated_url_loader.h" #include #include "base/memory/ptr_util.h" #include "base/stl_util.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_string.h" #include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" #include "third_party/blink/public/platform/web_url_request.h" #include "third_party/blink/public/platform/web_url_response.h" #include "third_party/blink/public/web/web_associated_url_loader_client.h" #include "third_party/blink/public/web/web_associated_url_loader_options.h" #include "third_party/blink/public/web/web_frame.h" #include "third_party/blink/public/web/web_view.h" #include "third_party/blink/renderer/core/frame/frame_test_helpers.h" #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" #include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" #include "third_party/blink/renderer/platform/wtf/text/cstring.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" using blink::url_test_helpers::ToKURL; using blink::test::RunPendingTasks; namespace blink { class WebAssociatedURLLoaderTest : public testing::Test, public WebAssociatedURLLoaderClient { public: WebAssociatedURLLoaderTest() : will_follow_redirect_(false), did_send_data_(false), did_receive_response_(false), did_receive_data_(false), did_receive_cached_metadata_(false), did_finish_loading_(false), did_fail_(false) { // Reuse one of the test files from WebFrameTest. frame_file_path_ = test::CoreTestDataPath("iframes_test.html"); } KURL RegisterMockedUrl(const std::string& url_root, const WTF::String& filename) { WebURLResponse response; response.SetMIMEType("text/html"); KURL url = ToKURL(url_root + filename.Utf8().data()); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, response, test::CoreTestDataPath(filename.Utf8().data())); return url; } void SetUp() override { helper_.Initialize(); std::string url_root = "http://www.test.com/"; KURL url = RegisterMockedUrl(url_root, "iframes_test.html"); const char* iframe_support_files[] = { "invisible_iframe.html", "visible_iframe.html", "zero_sized_iframe.html", }; for (size_t i = 0; i < base::size(iframe_support_files); ++i) { RegisterMockedUrl(url_root, iframe_support_files[i]); } frame_test_helpers::LoadFrame(MainFrame(), url.GetString().Utf8().data()); Platform::Current()->GetURLLoaderMockFactory()->UnregisterURL(url); } void TearDown() override { Platform::Current() ->GetURLLoaderMockFactory() ->UnregisterAllURLsAndClearMemoryCache(); } void ServeRequests() { Platform::Current()->GetURLLoaderMockFactory()->ServeAsynchronousRequests(); } std::unique_ptr CreateAssociatedURLLoader( const WebAssociatedURLLoaderOptions options = WebAssociatedURLLoaderOptions()) { return base::WrapUnique(MainFrame()->CreateAssociatedURLLoader(options)); } // WebAssociatedURLLoaderClient implementation. bool WillFollowRedirect(const WebURL& new_url, const WebURLResponse& redirect_response) override { will_follow_redirect_ = true; EXPECT_EQ(expected_new_url_, new_url); EXPECT_EQ(expected_redirect_response_.CurrentRequestUrl(), redirect_response.CurrentRequestUrl()); EXPECT_EQ(expected_redirect_response_.HttpStatusCode(), redirect_response.HttpStatusCode()); EXPECT_EQ(expected_redirect_response_.MimeType(), redirect_response.MimeType()); return true; } void DidSendData(unsigned long long bytes_sent, unsigned long long total_bytes_to_be_sent) override { did_send_data_ = true; } void DidReceiveResponse(const WebURLResponse& response) override { did_receive_response_ = true; actual_response_ = WebURLResponse(response); EXPECT_EQ(expected_response_.CurrentRequestUrl(), response.CurrentRequestUrl()); EXPECT_EQ(expected_response_.HttpStatusCode(), response.HttpStatusCode()); } void DidDownloadData(unsigned long long data_length) override { did_download_data_ = true; } void DidReceiveData(const char* data, int data_length) override { did_receive_data_ = true; EXPECT_TRUE(data); EXPECT_GT(data_length, 0); } void DidReceiveCachedMetadata(const char* data, int data_length) override { did_receive_cached_metadata_ = true; } void DidFinishLoading() override { did_finish_loading_ = true; } void DidFail(const WebURLError& error) override { did_fail_ = true; } void CheckMethodFails(const char* unsafe_method) { WebURLRequest request(ToKURL("http://www.test.com/success.html")); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kSameOrigin); request.SetFetchCredentialsMode( network::mojom::FetchCredentialsMode::kOmit); request.SetHTTPMethod(WebString::FromUTF8(unsafe_method)); WebAssociatedURLLoaderOptions options; options.untrusted_http = true; CheckFails(request, options); } void CheckHeaderFails(const char* header_field) { CheckHeaderFails(header_field, "foo"); } void CheckHeaderFails(const char* header_field, const char* header_value) { WebURLRequest request(ToKURL("http://www.test.com/success.html")); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kSameOrigin); request.SetFetchCredentialsMode( network::mojom::FetchCredentialsMode::kOmit); if (EqualIgnoringASCIICase(WebString::FromUTF8(header_field), "referer")) { request.SetHTTPReferrer(WebString::FromUTF8(header_value), network::mojom::ReferrerPolicy::kDefault); } else { request.SetHTTPHeaderField(WebString::FromUTF8(header_field), WebString::FromUTF8(header_value)); } WebAssociatedURLLoaderOptions options; options.untrusted_http = true; CheckFails(request, options); } void CheckFails( const WebURLRequest& request, WebAssociatedURLLoaderOptions options = WebAssociatedURLLoaderOptions()) { expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); did_fail_ = false; expected_loader_->LoadAsynchronously(request, this); // Failure should not be reported synchronously. EXPECT_FALSE(did_fail_); // Allow the loader to return the error. RunPendingTasks(); EXPECT_TRUE(did_fail_); EXPECT_FALSE(did_receive_response_); } bool CheckAccessControlHeaders(const char* header_name, bool exposed) { std::string id("http://www.other.com/CheckAccessControlExposeHeaders_"); id.append(header_name); if (exposed) id.append("-Exposed"); id.append(".html"); KURL url = ToKURL(id); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors); request.SetFetchCredentialsMode( network::mojom::FetchCredentialsMode::kOmit); WebString header_name_string(WebString::FromUTF8(header_name)); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); expected_response_.AddHTTPHeaderField("Access-Control-Allow-Origin", "*"); if (exposed) { expected_response_.AddHTTPHeaderField("access-control-expose-headers", header_name_string); } expected_response_.AddHTTPHeaderField(header_name_string, "foo"); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); return !actual_response_.HttpHeaderField(header_name_string).IsEmpty(); } WebLocalFrameImpl* MainFrame() const { return helper_.GetWebView()->MainFrameImpl(); } protected: String frame_file_path_; frame_test_helpers::WebViewHelper helper_; std::unique_ptr expected_loader_; WebURLResponse actual_response_; WebURLResponse expected_response_; WebURL expected_new_url_; WebURLResponse expected_redirect_response_; bool will_follow_redirect_; bool did_send_data_; bool did_receive_response_; bool did_download_data_; bool did_receive_data_; bool did_receive_cached_metadata_; bool did_finish_loading_; bool did_fail_; }; // Test a successful same-origin URL load. TEST_F(WebAssociatedURLLoaderTest, SameOriginSuccess) { KURL url = ToKURL("http://www.test.com/SameOriginSuccess.html"); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kSameOrigin); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); expected_loader_ = CreateAssociatedURLLoader(); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); } // Test that the same-origin restriction is the default. TEST_F(WebAssociatedURLLoaderTest, SameOriginRestriction) { // This is cross-origin since the frame was loaded from www.test.com. KURL url = ToKURL("http://www.other.com/SameOriginRestriction.html"); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kSameOrigin); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); CheckFails(request); } // Test a successful cross-origin load. TEST_F(WebAssociatedURLLoaderTest, CrossOriginSuccess) { // This is cross-origin since the frame was loaded from www.test.com. KURL url = ToKURL("http://www.other.com/CrossOriginSuccess"); WebURLRequest request(url); // No-CORS requests (CrossOriginRequestPolicyAllow) aren't allowed for the // default context. So we set the context as Script here. request.SetRequestContext(mojom::RequestContextType::SCRIPT); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); } // Test a successful cross-origin load using CORS. TEST_F(WebAssociatedURLLoaderTest, CrossOriginWithAccessControlSuccess) { // This is cross-origin since the frame was loaded from www.test.com. KURL url = ToKURL("http://www.other.com/CrossOriginWithAccessControlSuccess.html"); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*"); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); } // Test an unsuccessful cross-origin load using CORS. TEST_F(WebAssociatedURLLoaderTest, CrossOriginWithAccessControlFailure) { // This is cross-origin since the frame was loaded from www.test.com. KURL url = ToKURL("http://www.other.com/CrossOriginWithAccessControlFailure.html"); // Send credentials. This will cause the CORS checks to fail, because // credentials can't be sent to a server which returns the header // "access-control-allow-origin" with "*" as its value. WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*"); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); // Failure should not be reported synchronously. EXPECT_FALSE(did_fail_); // The loader needs to receive the response, before doing the CORS check. ServeRequests(); EXPECT_TRUE(did_fail_); EXPECT_FALSE(did_receive_response_); } // Test an unsuccessful cross-origin load using CORS. TEST_F(WebAssociatedURLLoaderTest, CrossOriginWithAccessControlFailureBadStatusCode) { // This is cross-origin since the frame was loaded from www.test.com. KURL url = ToKURL("http://www.other.com/CrossOriginWithAccessControlFailure.html"); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(0); expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*"); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); // Failure should not be reported synchronously. EXPECT_FALSE(did_fail_); // The loader needs to receive the response, before doing the CORS check. ServeRequests(); EXPECT_TRUE(did_fail_); EXPECT_FALSE(did_receive_response_); } // Test a same-origin URL redirect and load. TEST_F(WebAssociatedURLLoaderTest, RedirectSuccess) { KURL url = ToKURL("http://www.test.com/RedirectSuccess.html"); char redirect[] = "http://www.test.com/RedirectSuccess2.html"; // Same-origin KURL redirect_url = ToKURL(redirect); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kSameOrigin); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_redirect_response_ = WebURLResponse(); expected_redirect_response_.SetMIMEType("text/html"); expected_redirect_response_.SetHTTPStatusCode(301); expected_redirect_response_.SetHTTPHeaderField("Location", redirect); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_redirect_response_, frame_file_path_); expected_new_url_ = WebURL(redirect_url); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( redirect_url, expected_response_, frame_file_path_); expected_loader_ = CreateAssociatedURLLoader(); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_TRUE(will_follow_redirect_); EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); } // Test a cross-origin URL redirect without Access Control set. TEST_F(WebAssociatedURLLoaderTest, RedirectCrossOriginFailure) { KURL url = ToKURL("http://www.test.com/RedirectCrossOriginFailure.html"); char redirect[] = "http://www.other.com/RedirectCrossOriginFailure.html"; // Cross-origin KURL redirect_url = ToKURL(redirect); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kSameOrigin); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_redirect_response_ = WebURLResponse(); expected_redirect_response_.SetMIMEType("text/html"); expected_redirect_response_.SetHTTPStatusCode(301); expected_redirect_response_.SetHTTPHeaderField("Location", redirect); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_redirect_response_, frame_file_path_); expected_new_url_ = WebURL(redirect_url); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( redirect_url, expected_response_, frame_file_path_); expected_loader_ = CreateAssociatedURLLoader(); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_FALSE(will_follow_redirect_); EXPECT_FALSE(did_receive_response_); EXPECT_FALSE(did_receive_data_); EXPECT_FALSE(did_finish_loading_); } // Test that a cross origin redirect response without CORS headers fails. TEST_F(WebAssociatedURLLoaderTest, RedirectCrossOriginWithAccessControlFailure) { KURL url = ToKURL( "http://www.test.com/RedirectCrossOriginWithAccessControlFailure.html"); char redirect[] = "http://www.other.com/" "RedirectCrossOriginWithAccessControlFailure.html"; // Cross-origin KURL redirect_url = ToKURL(redirect); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_redirect_response_ = WebURLResponse(); expected_redirect_response_.SetMIMEType("text/html"); expected_redirect_response_.SetHTTPStatusCode(301); expected_redirect_response_.SetHTTPHeaderField("Location", redirect); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_redirect_response_, frame_file_path_); expected_new_url_ = WebURL(redirect_url); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( redirect_url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); // We should get a notification about access control check failure. EXPECT_TRUE(will_follow_redirect_); EXPECT_FALSE(did_receive_response_); EXPECT_FALSE(did_receive_data_); EXPECT_TRUE(did_fail_); } // Test that a cross origin redirect response with CORS headers that allow the // requesting origin succeeds. TEST_F(WebAssociatedURLLoaderTest, RedirectCrossOriginWithAccessControlSuccess) { KURL url = ToKURL( "http://www.test.com/RedirectCrossOriginWithAccessControlSuccess.html"); char redirect[] = "http://www.other.com/" "RedirectCrossOriginWithAccessControlSuccess.html"; // Cross-origin KURL redirect_url = ToKURL(redirect); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); // Add a CORS simple header. request.SetHTTPHeaderField("accept", "application/json"); // Create a redirect response that allows the redirect to pass the access // control checks. expected_redirect_response_ = WebURLResponse(); expected_redirect_response_.SetMIMEType("text/html"); expected_redirect_response_.SetHTTPStatusCode(301); expected_redirect_response_.SetHTTPHeaderField("Location", redirect); expected_redirect_response_.AddHTTPHeaderField("access-control-allow-origin", "*"); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_redirect_response_, frame_file_path_); expected_new_url_ = WebURL(redirect_url); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); expected_response_.AddHTTPHeaderField("access-control-allow-origin", "*"); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( redirect_url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_TRUE(will_follow_redirect_); EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); } // Test that untrusted loads can't use a forbidden method. TEST_F(WebAssociatedURLLoaderTest, UntrustedCheckMethods) { // Check non-token method fails. CheckMethodFails("GET()"); CheckMethodFails("POST\x0d\x0ax-csrf-token:\x20test1234"); // Forbidden methods should fail regardless of casing. CheckMethodFails("CoNneCt"); CheckMethodFails("TrAcK"); CheckMethodFails("TrAcE"); } // This test is flaky on Windows and Android. See . #if defined(OS_WIN) || defined(OS_ANDROID) #define MAYBE_UntrustedCheckHeaders DISABLED_UntrustedCheckHeaders #else #define MAYBE_UntrustedCheckHeaders UntrustedCheckHeaders #endif // Test that untrusted loads can't use a forbidden header field. TEST_F(WebAssociatedURLLoaderTest, MAYBE_UntrustedCheckHeaders) { // Check non-token header fails. CheckHeaderFails("foo()"); // Check forbidden headers fail. CheckHeaderFails("accept-charset"); CheckHeaderFails("accept-encoding"); CheckHeaderFails("connection"); CheckHeaderFails("content-length"); CheckHeaderFails("cookie"); CheckHeaderFails("cookie2"); CheckHeaderFails("date"); CheckHeaderFails("dnt"); CheckHeaderFails("expect"); CheckHeaderFails("host"); CheckHeaderFails("keep-alive"); CheckHeaderFails("origin"); CheckHeaderFails("referer", "http://example.com/"); CheckHeaderFails("te"); CheckHeaderFails("trailer"); CheckHeaderFails("transfer-encoding"); CheckHeaderFails("upgrade"); CheckHeaderFails("user-agent"); CheckHeaderFails("via"); CheckHeaderFails("proxy-"); CheckHeaderFails("proxy-foo"); CheckHeaderFails("sec-"); CheckHeaderFails("sec-foo"); // Check that validation is case-insensitive. CheckHeaderFails("AcCePt-ChArSeT"); CheckHeaderFails("ProXy-FoO"); } // Test that the loader filters response headers according to the CORS standard. TEST_F(WebAssociatedURLLoaderTest, CrossOriginHeaderWhitelisting) { // Test that whitelisted headers are returned without exposing them. EXPECT_TRUE(CheckAccessControlHeaders("cache-control", false)); EXPECT_TRUE(CheckAccessControlHeaders("content-language", false)); EXPECT_TRUE(CheckAccessControlHeaders("content-type", false)); EXPECT_TRUE(CheckAccessControlHeaders("expires", false)); EXPECT_TRUE(CheckAccessControlHeaders("last-modified", false)); EXPECT_TRUE(CheckAccessControlHeaders("pragma", false)); // Test that non-whitelisted headers aren't returned. EXPECT_FALSE(CheckAccessControlHeaders("non-whitelisted", false)); // Test that Set-Cookie headers aren't returned. EXPECT_FALSE(CheckAccessControlHeaders("Set-Cookie", false)); EXPECT_FALSE(CheckAccessControlHeaders("Set-Cookie2", false)); // Test that exposed headers that aren't whitelisted are returned. EXPECT_TRUE(CheckAccessControlHeaders("non-whitelisted", true)); // Test that Set-Cookie headers aren't returned, even if exposed. EXPECT_FALSE(CheckAccessControlHeaders("Set-Cookie", true)); } // Test that the loader can allow non-whitelisted response headers for trusted // CORS loads. TEST_F(WebAssociatedURLLoaderTest, CrossOriginHeaderAllowResponseHeaders) { KURL url = ToKURL("http://www.other.com/CrossOriginHeaderAllowResponseHeaders.html"); WebURLRequest request(url); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kCors); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); WebString header_name_string(WebString::FromUTF8("non-whitelisted")); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/html"); expected_response_.SetHTTPStatusCode(200); expected_response_.AddHTTPHeaderField("Access-Control-Allow-Origin", "*"); expected_response_.AddHTTPHeaderField(header_name_string, "foo"); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; options.expose_all_response_headers = true; // This turns off response whitelisting. expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); EXPECT_FALSE(actual_response_.HttpHeaderField(header_name_string).IsEmpty()); } TEST_F(WebAssociatedURLLoaderTest, AccessCheckForLocalURL) { KURL url = ToKURL("file://test.pdf"); WebURLRequest request(url); request.SetRequestContext(mojom::RequestContextType::PLUGIN); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kNoCors); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/plain"); expected_response_.SetHTTPStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); // The request failes due to a security check. EXPECT_FALSE(did_receive_response_); EXPECT_FALSE(did_receive_data_); EXPECT_FALSE(did_finish_loading_); EXPECT_TRUE(did_fail_); } TEST_F(WebAssociatedURLLoaderTest, BypassAccessCheckForLocalURL) { KURL url = ToKURL("file://test.pdf"); WebURLRequest request(url); request.SetRequestContext(mojom::RequestContextType::PLUGIN); request.SetFetchRequestMode(network::mojom::FetchRequestMode::kNoCors); request.SetFetchCredentialsMode(network::mojom::FetchCredentialsMode::kOmit); expected_response_ = WebURLResponse(); expected_response_.SetMIMEType("text/plain"); expected_response_.SetHTTPStatusCode(200); Platform::Current()->GetURLLoaderMockFactory()->RegisterURL( url, expected_response_, frame_file_path_); WebAssociatedURLLoaderOptions options; options.grant_universal_access = true; expected_loader_ = CreateAssociatedURLLoader(options); EXPECT_TRUE(expected_loader_); expected_loader_->LoadAsynchronously(request, this); ServeRequests(); // The security check is bypassed due to |grant_universal_access|. EXPECT_TRUE(did_receive_response_); EXPECT_TRUE(did_receive_data_); EXPECT_TRUE(did_finish_loading_); EXPECT_FALSE(did_fail_); } #undef MAYBE_UntrustedCheckHeaders } // namespace blink