1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/bind.h"
6 #include "base/bind_helpers.h"
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/macros.h"
10 #include "base/path_service.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/synchronization/waitable_event.h"
15 #include "base/test/scoped_command_line.h"
16 #include "build/build_config.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/common/content_paths.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/common/web_preferences.h"
22 #include "content/public/test/browser_test_utils.h"
23 #include "content/public/test/content_browser_test.h"
24 #include "content/public/test/content_browser_test_utils.h"
25 #include "content/shell/browser/shell.h"
26 #include "net/http/http_request_headers.h"
27 #include "net/http/http_status_code.h"
28 #include "net/test/embedded_test_server/embedded_test_server.h"
29 #include "net/test/embedded_test_server/http_request.h"
30 #include "net/test/embedded_test_server/http_response.h"
31 #include "net/test/embedded_test_server/request_handler_util.h"
32 #include "services/network/public/cpp/cors/cors.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "url/gurl.h"
36 #include "url/url_constants.h"
37 
38 namespace content {
39 
40 namespace {
41 
42 using net::test_server::BasicHttpResponse;
43 using net::test_server::HttpRequest;
44 using net::test_server::HttpResponse;
45 
46 // Tests end to end Origin header and CORS check behaviors without
47 // --allow-file-access-from-files flag.
48 class CorsFileOriginBrowserTest : public ContentBrowserTest {
49  public:
CorsFileOriginBrowserTest()50   CorsFileOriginBrowserTest()
51       : pass_string_(base::ASCIIToUTF16("PASS")),
52         fail_string_(base::ASCIIToUTF16("FAIL")) {}
53   ~CorsFileOriginBrowserTest() override = default;
54 
55  protected:
CreateTestDataURL(const std::string & relative_path)56   GURL CreateTestDataURL(const std::string& relative_path) {
57     base::AutoLock lock(lock_);
58     base::FilePath path(test_data_loader_path_);
59     path = path.AppendASCII(relative_path);
60     // This is wrong if developers checkout the source code in a path that
61     // contains non-ASCII characters to run the browser tests.
62     std::string url = "file://" + path.MaybeAsASCII();
63     return GURL(url);
64   }
65 
CreateWatcher()66   std::unique_ptr<TitleWatcher> CreateWatcher() {
67     // Register all possible result strings here.
68     std::unique_ptr<TitleWatcher> watcher =
69         std::make_unique<TitleWatcher>(shell()->web_contents(), pass_string());
70     watcher->AlsoWaitForTitle(fail_string());
71 
72     // Does not appear in the expectations, but the title can be on unexpected
73     // failures.
74     base::string16 wrong_origin_string =
75         base::ASCIIToUTF16("FAIL: response text does not match");
76     watcher->AlsoWaitForTitle(wrong_origin_string);
77     return watcher;
78   }
79 
target_http_url()80   std::string target_http_url() {
81     return base::StringPrintf("http://127.0.0.1:%d/test", port());
82   }
target_file_url() const83   std::string target_file_url() const { return "get.txt"; }
target_self_file_url() const84   std::string target_self_file_url() const {
85     return "cors_file_origin_test.html";
86   }
87 
pass_string() const88   const base::string16& pass_string() const { return pass_string_; }
fail_string() const89   const base::string16& fail_string() const { return fail_string_; }
90 
port()91   uint16_t port() {
92     base::AutoLock lock(lock_);
93     return port_;
94   }
95 
is_preflight_requested()96   bool is_preflight_requested() {
97     base::AutoLock lock(lock_);
98     return is_preflight_requested_;
99   }
100 
101  private:
AllowFileAccessFromFiles()102   bool AllowFileAccessFromFiles() override { return false; }
IsWebSecurityEnabled() const103   virtual bool IsWebSecurityEnabled() const { return true; }
104 
SetUpCommandLine(base::CommandLine * command_line)105   void SetUpCommandLine(base::CommandLine* command_line) override {
106     if (!IsWebSecurityEnabled()) {
107       command_line->AppendSwitch(switches::kDisableWebSecurity);
108     }
109 
110     ContentBrowserTest::SetUpCommandLine(command_line);
111   }
SetUpOnMainThread()112   void SetUpOnMainThread() override {
113     base::AutoLock lock(lock_);
114 
115     // Need to obtain the path on the main thread.
116     ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &test_data_loader_path_));
117     test_data_loader_path_ =
118         test_data_loader_path_.Append(FILE_PATH_LITERAL("loader"));
119 
120     embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
121         &CorsFileOriginBrowserTest::HandleWithAccessControlAllowOrigin,
122         base::Unretained(this)));
123 
124     ASSERT_TRUE(embedded_test_server()->Start());
125 
126     port_ = embedded_test_server()->port();
127   }
128 
HandleWithAccessControlAllowOrigin(const HttpRequest & request)129   std::unique_ptr<HttpResponse> HandleWithAccessControlAllowOrigin(
130       const HttpRequest& request) {
131     std::unique_ptr<BasicHttpResponse> response;
132 
133     if (net::test_server::ShouldHandle(request, "/test")) {
134       // Accept XHR CORS requests.
135       response = std::make_unique<BasicHttpResponse>();
136       response->set_code(net::HTTP_OK);
137       auto query = net::test_server::ParseQuery(request.GetURL());
138       response->AddCustomHeader(
139           network::cors::header_names::kAccessControlAllowOrigin,
140           query["allow"][0]);
141       if (request.method == net::test_server::METHOD_OPTIONS) {
142         // For CORS-preflight request.
143         response->AddCustomHeader(
144             network::cors::header_names::kAccessControlAllowMethods,
145             "GET, OPTIONS");
146         response->AddCustomHeader(
147             network::cors::header_names::kAccessControlAllowHeaders,
148             "X-NotSimple");
149         response->AddCustomHeader(
150             network::cors::header_names::kAccessControlMaxAge, "0");
151         response->AddCustomHeader(net::HttpRequestHeaders::kCacheControl,
152                                   "no-store");
153         base::AutoLock lock(lock_);
154         is_preflight_requested_ = true;
155       }
156 
157       // Return the request origin header as the body so that JavaScript can
158       // check if it sent the expected origin header.
159       auto origin = request.headers.find(net::HttpRequestHeaders::kOrigin);
160       if (origin != request.headers.end())
161         response->set_content(origin->second);
162     }
163     return response;
164   }
165 
166   base::Lock lock_;
167   base::FilePath test_data_loader_path_;
168   uint16_t port_;
169   bool is_preflight_requested_ = false;
170 
171   const base::string16 pass_string_;
172   const base::string16 fail_string_;
173 
174   DISALLOW_COPY_AND_ASSIGN(CorsFileOriginBrowserTest);
175 };
176 
177 // Tests end to end Origin header and CORS check behaviors with
178 // --allow-file-access-from-files flag.
179 class CorsFileOriginBrowserTestWithAllowFileAccessFromFiles
180     : public CorsFileOriginBrowserTest {
181  private:
AllowFileAccessFromFiles()182   bool AllowFileAccessFromFiles() override { return true; }
183 };
184 
185 // Tests end to end Origin header and CORS check behaviors with
186 // --disable-web-security flag.
187 class CorsFileOriginBrowserTestWithDisableWebSecurity
188     : public CorsFileOriginBrowserTest {
189  private:
AllowFileAccessFromFiles()190   bool AllowFileAccessFromFiles() override { return false; }
IsWebSecurityEnabled() const191   bool IsWebSecurityEnabled() const override { return false; }
192 };
193 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,AccessControlAllowOriginIsNull)194 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,
195                        AccessControlAllowOriginIsNull) {
196   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
197   EXPECT_TRUE(NavigateToURL(
198       shell(),
199       CreateTestDataURL(base::StringPrintf(
200           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
201           target_http_url().c_str(), "null", "null"))));
202 
203   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
204   EXPECT_TRUE(is_preflight_requested());
205 }
206 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,AccessControlAllowOriginIsFile)207 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,
208                        AccessControlAllowOriginIsFile) {
209   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
210   EXPECT_TRUE(NavigateToURL(
211       shell(),
212       CreateTestDataURL(base::StringPrintf(
213           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
214           target_http_url().c_str(), "file://", "null"))));
215 
216   EXPECT_EQ(fail_string(), watcher->WaitAndGetTitle());
217   EXPECT_TRUE(is_preflight_requested());
218 }
219 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,AccessToSelfFileUrl)220 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest, AccessToSelfFileUrl) {
221   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
222   EXPECT_TRUE(NavigateToURL(
223       shell(),
224       CreateTestDataURL(base::StringPrintf(
225           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
226           target_self_file_url().c_str(), "unused", "unused"))));
227 
228   EXPECT_EQ(fail_string(), watcher->WaitAndGetTitle());
229 }
230 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,AccessToAnotherFileUrl)231 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest, AccessToAnotherFileUrl) {
232   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
233   EXPECT_TRUE(NavigateToURL(
234       shell(),
235       CreateTestDataURL(base::StringPrintf(
236           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
237           target_file_url().c_str(), "unused", "unused"))));
238 
239   EXPECT_EQ(fail_string(), watcher->WaitAndGetTitle());
240 }
241 
242 // TODO(lukasza, nasko): https://crbug.com/981018: Enable this test on Macs
243 // after understanding what makes it flakily fail on the mac-rel trybot.
244 #if defined(OS_MACOSX)
245 #define MAYBE_UniversalAccessFromFileUrls DISABLED_UniversalAccessFromFileUrls
246 #else
247 #define MAYBE_UniversalAccessFromFileUrls UniversalAccessFromFileUrls
248 #endif
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,MAYBE_UniversalAccessFromFileUrls)249 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,
250                        MAYBE_UniversalAccessFromFileUrls) {
251   const char* kScript = R"(
252     fetch($1)
253       .then(response => response.text())
254       .then(text => "SUCCESS: " + text)
255       .catch(error => "ERROR: " + error)
256   )";
257   std::string script =
258       JsReplace(kScript, embedded_test_server()->GetURL("/title2.html"));
259 
260   // Activate the preference to allow universal access from file URLs.
261   RenderViewHost* rvh = shell()->web_contents()->GetRenderViewHost();
262   WebPreferences prefs = rvh->GetWebkitPreferences();
263   prefs.allow_universal_access_from_file_urls = true;
264   rvh->UpdateWebkitPreferences(prefs);
265 
266   // Navigate to a file: test page.
267   GURL page_url = GetTestUrl(nullptr, "title1.html");
268   EXPECT_EQ(url::kFileScheme, page_url.scheme());
269   EXPECT_TRUE(NavigateToURL(shell(), page_url));
270 
271   // Fetching http resources should be allowed by CORS when
272   // universal access from file URLs is requested.
273   std::string fetch_result = EvalJs(shell(), script).ExtractString();
274   fetch_result = TrimWhitespaceASCII(fetch_result, base::TRIM_ALL).as_string();
275   EXPECT_THAT(fetch_result, ::testing::HasSubstr("SUCCESS:"));
276   EXPECT_THAT(fetch_result, ::testing::HasSubstr("This page has a title"));
277 }
278 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,AccessControlAllowOriginIsNull)279 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,
280                        AccessControlAllowOriginIsNull) {
281   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
282   EXPECT_TRUE(NavigateToURL(
283       shell(),
284       CreateTestDataURL(base::StringPrintf(
285           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
286           target_http_url().c_str(), "null", "file://"))));
287 
288   EXPECT_EQ(fail_string(), watcher->WaitAndGetTitle());
289   EXPECT_TRUE(is_preflight_requested());
290 }
291 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,AccessControlAllowOriginIsFile)292 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,
293                        AccessControlAllowOriginIsFile) {
294   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
295   EXPECT_TRUE(NavigateToURL(
296       shell(),
297       CreateTestDataURL(base::StringPrintf(
298           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
299           target_http_url().c_str(), "file://", "file://"))));
300 
301   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
302   EXPECT_TRUE(is_preflight_requested());
303 }
304 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,AccessToSelfFileUrl)305 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,
306                        AccessToSelfFileUrl) {
307   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
308   EXPECT_TRUE(NavigateToURL(
309       shell(),
310       CreateTestDataURL(base::StringPrintf(
311           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
312           target_self_file_url().c_str(), "unused", "unused"))));
313 
314   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
315 }
316 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,AccessToAnotherFileUrl)317 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,
318                        AccessToAnotherFileUrl) {
319   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
320   EXPECT_TRUE(NavigateToURL(
321       shell(),
322       CreateTestDataURL(base::StringPrintf(
323           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
324           target_file_url().c_str(), "unused", "unused"))));
325 
326   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
327 }
328 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,AccessControlAllowOriginIsNull)329 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,
330                        AccessControlAllowOriginIsNull) {
331   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
332   EXPECT_TRUE(NavigateToURL(
333       shell(),
334       CreateTestDataURL(base::StringPrintf(
335           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
336           target_http_url().c_str(), "unused", ""))));
337 
338   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
339   EXPECT_FALSE(is_preflight_requested());
340 }
341 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,AccessControlAllowOriginIsFile)342 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,
343                        AccessControlAllowOriginIsFile) {
344   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
345   EXPECT_TRUE(NavigateToURL(
346       shell(),
347       CreateTestDataURL(base::StringPrintf(
348           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
349           target_http_url().c_str(), "unused", ""))));
350 
351   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
352   EXPECT_FALSE(is_preflight_requested());
353 }
354 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,AccessToSelfFileUrl)355 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,
356                        AccessToSelfFileUrl) {
357   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
358   EXPECT_TRUE(NavigateToURL(
359       shell(),
360       CreateTestDataURL(base::StringPrintf(
361           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
362           target_self_file_url().c_str(), "unused", "unused"))));
363 
364   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
365 }
366 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,AccessToAnotherFileUrl)367 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,
368                        AccessToAnotherFileUrl) {
369   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
370   EXPECT_TRUE(NavigateToURL(
371       shell(),
372       CreateTestDataURL(base::StringPrintf(
373           "cors_file_origin_test.html?url=%s&allow=%s&response_text=%s",
374           target_file_url().c_str(), "unused", "unused"))));
375 
376   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
377 }
378 
379 // Test if local image files can be protected by canvas tainting.
380 // We can not have following test cases in web_tests because web_tests run with
381 // --run-web-tests flag that internally specifies --allow-file-access-from-files
382 // that changes this specific behavior.
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,NoCorsImagefileTaint)383 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest, NoCorsImagefileTaint) {
384   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
385   EXPECT_TRUE(NavigateToURL(
386       shell(), CreateTestDataURL("image-taint.html?test=no_cors")));
387   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
388 }
389 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest,CorsImagefileTaint)390 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTest, CorsImagefileTaint) {
391   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
392   EXPECT_TRUE(
393       NavigateToURL(shell(), CreateTestDataURL("image-taint.html?test=cors")));
394   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
395 }
396 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,NoCorsImagefileTaint)397 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,
398                        NoCorsImagefileTaint) {
399   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
400   EXPECT_TRUE(NavigateToURL(
401       shell(),
402       CreateTestDataURL("image-taint.html?test=no_cors_with_file_access")));
403   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
404 }
405 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,CorsImagefileTaint)406 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithAllowFileAccessFromFiles,
407                        CorsImagefileTaint) {
408   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
409   EXPECT_TRUE(NavigateToURL(
410       shell(),
411       CreateTestDataURL("image-taint.html?test=cors_with_file_access")));
412   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
413 }
414 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,NoCorsImagefileTaint)415 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,
416                        NoCorsImagefileTaint) {
417   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
418   EXPECT_TRUE(NavigateToURL(
419       shell(), CreateTestDataURL(
420                    "image-taint.html?test=no_cors_with_disable_web_security")));
421   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
422 }
423 
IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,CorsImagefileTaint)424 IN_PROC_BROWSER_TEST_F(CorsFileOriginBrowserTestWithDisableWebSecurity,
425                        CorsImagefileTaint) {
426   std::unique_ptr<TitleWatcher> watcher = CreateWatcher();
427   EXPECT_TRUE(NavigateToURL(
428       shell(), CreateTestDataURL(
429                    "image-taint.html?test=cors_with_disable_web_security")));
430   EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle());
431 }
432 
433 }  // namespace
434 
435 }  // namespace content
436