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