• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include <algorithm>
6 #include <set>
7 #include <vector>
8 
9 #include "include/base/cef_callback.h"
10 #include "include/cef_callback.h"
11 #include "include/cef_origin_whitelist.h"
12 #include "include/cef_scheme.h"
13 #include "include/wrapper/cef_closure_task.h"
14 #include "tests/ceftests/routing_test_handler.h"
15 #include "tests/ceftests/test_request.h"
16 #include "tests/ceftests/test_server.h"
17 #include "tests/ceftests/test_util.h"
18 #include "tests/shared/browser/client_app_browser.h"
19 
20 namespace {
21 
22 // Browser-side app delegate.
23 class CorsBrowserTest : public client::ClientAppBrowser::Delegate {
24  public:
CorsBrowserTest()25   CorsBrowserTest() {}
26 
OnContextInitialized(CefRefPtr<client::ClientAppBrowser> app)27   void OnContextInitialized(CefRefPtr<client::ClientAppBrowser> app) override {
28     if (IsChromeRuntimeEnabled()) {
29       // Disable InsecureFormNavigationThrottle which blocks 307 redirect of
30       // POST requests from HTTPS to custom non-standard scheme causing the
31       // CorsTest.RedirectPost307HttpSchemeToCustomNonStandardScheme test to
32       // fail.
33       CefRefPtr<CefValue> value = CefValue::Create();
34       value->SetBool(false);
35       CefString error;
36       bool result = CefRequestContext::GetGlobalContext()->SetPreference(
37           "profile.mixed_forms_warnings", value, error);
38       CHECK(result) << error.ToString();
39     }
40   }
41 
42  private:
43   IMPLEMENT_REFCOUNTING(CorsBrowserTest);
44 };
45 
46 const char kMimeTypeHtml[] = "text/html";
47 const char kMimeTypeText[] = "text/plain";
48 
49 const char kDefaultHtml[] = "<html><body>TEST</body></html>";
50 const char kDefaultText[] = "TEST";
51 const char kDefaultCookie[] = "testCookie=testVal";
52 
53 const char kSuccessMsg[] = "CorsTestHandler.Success";
54 const char kFailureMsg[] = "CorsTestHandler.Failure";
55 
56 // Source that will handle the request.
57 enum class HandlerType {
58   SERVER,
59   HTTP_SCHEME,
60   CUSTOM_STANDARD_SCHEME,
61   CUSTOM_NONSTANDARD_SCHEME,
62   CUSTOM_UNREGISTERED_SCHEME,
63 };
64 
GetOrigin(HandlerType handler)65 std::string GetOrigin(HandlerType handler) {
66   switch (handler) {
67     case HandlerType::SERVER:
68       return test_server::kServerOrigin;
69     case HandlerType::HTTP_SCHEME:
70       // Use HTTPS because requests from HTTP to the loopback address will be
71       // blocked by https://chromestatus.com/feature/5436853517811712.
72       return "https://corstest.com";
73     case HandlerType::CUSTOM_STANDARD_SCHEME:
74       // Standard scheme that's registered as CORS and fetch enabled.
75       // Registered in scheme_handler_unittest.cc.
76       return "customstdfetch://corstest";
77     case HandlerType::CUSTOM_NONSTANDARD_SCHEME:
78       // Non-standard schemes are not CORS or fetch enabled.
79       // Registered in scheme_handler_unittest.cc.
80       return "customnonstd:corstest";
81     case HandlerType::CUSTOM_UNREGISTERED_SCHEME:
82       // A scheme that isn't registered anywhere is treated as a non-standard
83       // scheme.
84       return "customstdunregistered://corstest";
85   }
86   NOTREACHED();
87   return std::string();
88 }
89 
GetScheme(HandlerType handler)90 std::string GetScheme(HandlerType handler) {
91   switch (handler) {
92     case HandlerType::SERVER:
93       return test_server::kServerScheme;
94     case HandlerType::HTTP_SCHEME:
95       return "https";
96     case HandlerType::CUSTOM_STANDARD_SCHEME:
97       return "customstdfetch";
98     case HandlerType::CUSTOM_NONSTANDARD_SCHEME:
99       return "customnonstd";
100     case HandlerType::CUSTOM_UNREGISTERED_SCHEME:
101       return "customstdunregistered";
102   }
103   NOTREACHED();
104   return std::string();
105 }
106 
IsNonStandardType(HandlerType handler)107 bool IsNonStandardType(HandlerType handler) {
108   return handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME ||
109          handler == HandlerType::CUSTOM_UNREGISTERED_SCHEME;
110 }
111 
IsStandardType(HandlerType handler)112 bool IsStandardType(HandlerType handler) {
113   return !IsNonStandardType(handler);
114 }
115 
GetPathURL(HandlerType handler,const std::string & path)116 std::string GetPathURL(HandlerType handler, const std::string& path) {
117   return GetOrigin(handler) + path;
118 }
119 
120 struct Resource {
121   // Uniquely identifies the resource.
122   HandlerType handler = HandlerType::SERVER;
123   std::string path;
124   // If non-empty the method value must match.
125   std::string method;
126 
127   // Response information that will be returned.
128   CefRefPtr<CefResponse> response;
129   std::string response_data;
130 
131   // Expected error code in OnLoadError.
132   cef_errorcode_t expected_error_code = ERR_NONE;
133 
134   // Expected number of responses.
135   int expected_response_ct = 1;
136 
137   // Expected number of OnQuery calls.
138   int expected_success_query_ct = 0;
139   int expected_failure_query_ct = 0;
140 
141   // Actual number of responses.
142   int response_ct = 0;
143 
144   // Actual number of OnQuery calls.
145   int success_query_ct = 0;
146   int failure_query_ct = 0;
147 
Resource__anona54282bb0111::Resource148   Resource() {}
Resource__anona54282bb0111::Resource149   Resource(HandlerType request_handler,
150            const std::string& request_path,
151            const std::string& mime_type = kMimeTypeHtml,
152            const std::string& data = kDefaultHtml,
153            int status = 200) {
154     Init(request_handler, request_path, mime_type, data, status);
155   }
156 
157   // Perform basic initialization.
Init__anona54282bb0111::Resource158   void Init(HandlerType request_handler,
159             const std::string& request_path,
160             const std::string& mime_type = kMimeTypeHtml,
161             const std::string& data = kDefaultHtml,
162             int status = 200) {
163     handler = request_handler;
164     path = request_path;
165     response_data = data;
166     response = CefResponse::Create();
167     response->SetMimeType(mime_type);
168     response->SetStatus(status);
169   }
170 
171   // Validate expected initial state.
Validate__anona54282bb0111::Resource172   void Validate() const {
173     DCHECK(!path.empty());
174     DCHECK(response);
175     DCHECK(!response->GetMimeType().empty());
176     DCHECK_EQ(0, response_ct);
177     DCHECK_GE(expected_response_ct, 0);
178   }
179 
GetPathURL__anona54282bb0111::Resource180   std::string GetPathURL() const { return ::GetPathURL(handler, path); }
181 
182   // Returns true if all expectations have been met.
IsDone__anona54282bb0111::Resource183   bool IsDone() const {
184     return response_ct == expected_response_ct &&
185            success_query_ct == expected_success_query_ct &&
186            failure_query_ct == expected_failure_query_ct;
187   }
188 
AssertDone__anona54282bb0111::Resource189   void AssertDone() const {
190     EXPECT_EQ(expected_response_ct, response_ct) << GetPathURL();
191     EXPECT_EQ(expected_success_query_ct, success_query_ct) << GetPathURL();
192     EXPECT_EQ(expected_failure_query_ct, failure_query_ct) << GetPathURL();
193   }
194 
195   // Optionally override to verify request contents.
VerifyRequest__anona54282bb0111::Resource196   virtual bool VerifyRequest(CefRefPtr<CefRequest> request) const {
197     return true;
198   }
199 };
200 
201 struct TestSetup {
202   // Available resources.
203   typedef std::vector<Resource*> ResourceList;
204   ResourceList resources;
205 
206   // Used for testing received console messages.
207   std::vector<std::string> console_messages;
208 
209   // If true cookies will be cleared after every test run.
210   bool clear_cookies = false;
211 
AddResource__anona54282bb0111::TestSetup212   void AddResource(Resource* resource) {
213     DCHECK(resource);
214     resource->Validate();
215     resources.push_back(resource);
216   }
217 
AddConsoleMessage__anona54282bb0111::TestSetup218   void AddConsoleMessage(const std::string& message) {
219     DCHECK(!message.empty());
220     console_messages.push_back(message);
221   }
222 
GetResource__anona54282bb0111::TestSetup223   Resource* GetResource(const std::string& url,
224                         const std::string& method = std::string()) const {
225     if (resources.empty())
226       return nullptr;
227 
228     std::set<std::string> matching_methods;
229     if (method.empty()) {
230       // Match standard HTTP methods.
231       matching_methods.insert("GET");
232       matching_methods.insert("POST");
233     } else {
234       matching_methods.insert(method);
235     }
236 
237     const std::string& path_url = test_request::GetPathURL(url);
238     ResourceList::const_iterator it = resources.begin();
239     for (; it != resources.end(); ++it) {
240       Resource* resource = *it;
241       if (resource->GetPathURL() == path_url &&
242           (resource->method.empty() ||
243            matching_methods.find(resource->method) != matching_methods.end())) {
244         return resource;
245       }
246     }
247     return nullptr;
248   }
249 
GetResource__anona54282bb0111::TestSetup250   Resource* GetResource(CefRefPtr<CefRequest> request) const {
251     return GetResource(request->GetURL(), request->GetMethod());
252   }
253 
254   // Validate expected initial state.
Validate__anona54282bb0111::TestSetup255   void Validate() const { DCHECK(!resources.empty()); }
256 
GetMainURL__anona54282bb0111::TestSetup257   std::string GetMainURL() const { return resources.front()->GetPathURL(); }
258 
259   // Returns true if the server will be used.
NeedsServer__anona54282bb0111::TestSetup260   bool NeedsServer() const {
261     ResourceList::const_iterator it = resources.begin();
262     for (; it != resources.end(); ++it) {
263       Resource* resource = *it;
264       if (resource->handler == HandlerType::SERVER)
265         return true;
266     }
267     return false;
268   }
269 
270   // Returns true if all expectations have been met.
IsDone__anona54282bb0111::TestSetup271   bool IsDone() const {
272     ResourceList::const_iterator it = resources.begin();
273     for (; it != resources.end(); ++it) {
274       Resource* resource = *it;
275       if (!resource->IsDone())
276         return false;
277     }
278     return true;
279   }
280 
AssertDone__anona54282bb0111::TestSetup281   void AssertDone() const {
282     ResourceList::const_iterator it = resources.begin();
283     for (; it != resources.end(); ++it) {
284       (*it)->AssertDone();
285     }
286   }
287 
288   // Optionally override to verify cleared cookie contents.
VerifyClearedCookies__anona54282bb0111::TestSetup289   virtual bool VerifyClearedCookies(
290       const test_request::CookieVector& cookies) const {
291     return true;
292   }
293 };
294 
295 class TestServerObserver : public test_server::ObserverHelper {
296  public:
TestServerObserver(TestSetup * setup,base::OnceClosure ready_callback,base::OnceClosure done_callback)297   TestServerObserver(TestSetup* setup,
298                      base::OnceClosure ready_callback,
299                      base::OnceClosure done_callback)
300       : setup_(setup),
301         ready_callback_(std::move(ready_callback)),
302         done_callback_(std::move(done_callback)),
303         weak_ptr_factory_(this) {
304     DCHECK(setup);
305     Initialize();
306   }
307 
~TestServerObserver()308   ~TestServerObserver() override { std::move(done_callback_).Run(); }
309 
OnInitialized(const std::string & server_origin)310   void OnInitialized(const std::string& server_origin) override {
311     CEF_REQUIRE_UI_THREAD();
312     std::move(ready_callback_).Run();
313   }
314 
OnHttpRequest(CefRefPtr<CefServer> server,int connection_id,const CefString & client_address,CefRefPtr<CefRequest> request)315   bool OnHttpRequest(CefRefPtr<CefServer> server,
316                      int connection_id,
317                      const CefString& client_address,
318                      CefRefPtr<CefRequest> request) override {
319     CEF_REQUIRE_UI_THREAD();
320     Resource* resource = setup_->GetResource(request);
321     if (!resource) {
322       // Not a request we handle.
323       return false;
324     }
325 
326     resource->response_ct++;
327     EXPECT_TRUE(resource->VerifyRequest(request))
328         << request->GetURL().ToString();
329     test_server::SendResponse(server, connection_id, resource->response,
330                               resource->response_data);
331 
332     // Stop propagating the callback.
333     return true;
334   }
335 
OnShutdown()336   void OnShutdown() override {
337     CEF_REQUIRE_UI_THREAD();
338     delete this;
339   }
340 
341  private:
342   TestSetup* const setup_;
343   base::OnceClosure ready_callback_;
344   base::OnceClosure done_callback_;
345 
346   base::WeakPtrFactory<TestServerObserver> weak_ptr_factory_;
347 
348   DISALLOW_COPY_AND_ASSIGN(TestServerObserver);
349 };
350 
351 class CorsTestHandler : public RoutingTestHandler {
352  public:
CorsTestHandler(TestSetup * setup)353   explicit CorsTestHandler(TestSetup* setup) : setup_(setup) {
354     setup_->Validate();
355   }
356 
RunTest()357   void RunTest() override {
358     StartServer(base::BindOnce(&CorsTestHandler::TriggerCreateBrowser, this));
359 
360     // Time out the test after a reasonable period of time.
361     SetTestTimeout();
362   }
363 
364   // Necessary to make the method public in order to destroy the test from
365   // ClientSchemeHandlerType::ProcessRequest().
DestroyTest()366   void DestroyTest() override {
367     EXPECT_TRUE(shutting_down_);
368 
369     if (setup_->NeedsServer()) {
370       EXPECT_TRUE(got_stopped_server_);
371     } else {
372       EXPECT_FALSE(got_stopped_server_);
373     }
374 
375     if (setup_->clear_cookies) {
376       EXPECT_TRUE(got_cleared_cookies_);
377     } else {
378       EXPECT_FALSE(got_cleared_cookies_);
379     }
380 
381     setup_->AssertDone();
382     EXPECT_TRUE(setup_->console_messages.empty())
383         << "Did not receive expected console message: "
384         << setup_->console_messages.front();
385 
386     RoutingTestHandler::DestroyTest();
387   }
388 
GetResourceHandler(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefRefPtr<CefRequest> request)389   CefRefPtr<CefResourceHandler> GetResourceHandler(
390       CefRefPtr<CefBrowser> browser,
391       CefRefPtr<CefFrame> frame,
392       CefRefPtr<CefRequest> request) override {
393     CEF_REQUIRE_IO_THREAD();
394     const std::string& url = request->GetURL();
395     const std::string& method = request->GetMethod();
396     if (method == "OPTIONS") {
397       // We should never see the CORS preflight request.
398       ADD_FAILURE() << "Unexpected CORS preflight for " << url;
399     }
400 
401     Resource* resource = setup_->GetResource(request);
402     if (resource && resource->handler != HandlerType::SERVER) {
403       resource->response_ct++;
404       EXPECT_TRUE(resource->VerifyRequest(request)) << url;
405       return test_request::CreateResourceHandler(resource->response,
406                                                  resource->response_data);
407     }
408     return RoutingTestHandler::GetResourceHandler(browser, frame, request);
409   }
410 
OnLoadEnd(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int httpStatusCode)411   void OnLoadEnd(CefRefPtr<CefBrowser> browser,
412                  CefRefPtr<CefFrame> frame,
413                  int httpStatusCode) override {
414     const std::string& url = frame->GetURL();
415     Resource* resource = GetResource(url);
416     if (!resource)
417       return;
418 
419     const int expected_status = resource->response->GetStatus();
420     if (url == main_url_ || expected_status != 200) {
421       // Test that the status code is correct.
422       EXPECT_EQ(expected_status, httpStatusCode) << url;
423     }
424 
425     TriggerDestroyTestIfDone();
426   }
427 
OnLoadError(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,ErrorCode errorCode,const CefString & errorText,const CefString & failedUrl)428   void OnLoadError(CefRefPtr<CefBrowser> browser,
429                    CefRefPtr<CefFrame> frame,
430                    ErrorCode errorCode,
431                    const CefString& errorText,
432                    const CefString& failedUrl) override {
433     Resource* resource = GetResource(failedUrl);
434     if (!resource)
435       return;
436 
437     const cef_errorcode_t expected_error = resource->response->GetError();
438 
439     // Tests sometimes also fail with ERR_ABORTED.
440     if (!(expected_error == ERR_NONE && errorCode == ERR_ABORTED)) {
441       EXPECT_EQ(expected_error, errorCode) << failedUrl.ToString();
442     }
443 
444     TriggerDestroyTestIfDone();
445   }
446 
OnQuery(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int64 query_id,const CefString & request,bool persistent,CefRefPtr<Callback> callback)447   bool OnQuery(CefRefPtr<CefBrowser> browser,
448                CefRefPtr<CefFrame> frame,
449                int64 query_id,
450                const CefString& request,
451                bool persistent,
452                CefRefPtr<Callback> callback) override {
453     Resource* resource = GetResource(frame->GetURL());
454     if (!resource)
455       return false;
456 
457     if (request.ToString() == kSuccessMsg ||
458         request.ToString() == kFailureMsg) {
459       callback->Success("");
460       if (request.ToString() == kSuccessMsg)
461         resource->success_query_ct++;
462       else
463         resource->failure_query_ct++;
464       TriggerDestroyTestIfDone();
465       return true;
466     }
467     return false;
468   }
469 
OnConsoleMessage(CefRefPtr<CefBrowser> browser,cef_log_severity_t level,const CefString & message,const CefString & source,int line)470   bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
471                         cef_log_severity_t level,
472                         const CefString& message,
473                         const CefString& source,
474                         int line) override {
475     bool expected = false;
476     if (!setup_->console_messages.empty()) {
477       std::vector<std::string>::iterator it = setup_->console_messages.begin();
478       for (; it != setup_->console_messages.end(); ++it) {
479         const std::string& possible = *it;
480         const std::string& actual = message.ToString();
481         if (actual.find(possible) == 0U) {
482           expected = true;
483           setup_->console_messages.erase(it);
484           break;
485         }
486       }
487     }
488 
489     EXPECT_TRUE(expected) << "Unexpected console message: "
490                           << message.ToString();
491     return false;
492   }
493 
494  protected:
TriggerCreateBrowser()495   void TriggerCreateBrowser() {
496     main_url_ = setup_->GetMainURL();
497     CreateBrowser(main_url_);
498   }
499 
TriggerDestroyTestIfDone()500   void TriggerDestroyTestIfDone() {
501     CefPostTask(TID_UI,
502                 base::BindOnce(&CorsTestHandler::DestroyTestIfDone, this));
503   }
504 
DestroyTestIfDone()505   void DestroyTestIfDone() {
506     CEF_REQUIRE_UI_THREAD();
507     if (shutting_down_)
508       return;
509 
510     if (setup_->IsDone()) {
511       shutting_down_ = true;
512       StopServer();
513     }
514   }
515 
StartServer(base::OnceClosure next_step)516   void StartServer(base::OnceClosure next_step) {
517     if (!CefCurrentlyOn(TID_UI)) {
518       CefPostTask(TID_UI, base::BindOnce(&CorsTestHandler::StartServer, this,
519                                          std::move(next_step)));
520       return;
521     }
522 
523     if (!setup_->NeedsServer()) {
524       std::move(next_step).Run();
525       return;
526     }
527 
528     // Will delete itself after the server stops.
529     server_ = new TestServerObserver(
530         setup_, std::move(next_step),
531         base::BindOnce(&CorsTestHandler::StoppedServer, this));
532   }
533 
StopServer()534   void StopServer() {
535     CEF_REQUIRE_UI_THREAD();
536     if (!server_) {
537       DCHECK(!setup_->NeedsServer());
538       AfterStoppedServer();
539       return;
540     }
541 
542     // Results in a call to StoppedServer().
543     server_->Shutdown();
544   }
545 
StoppedServer()546   void StoppedServer() {
547     CEF_REQUIRE_UI_THREAD();
548     got_stopped_server_.yes();
549     server_ = nullptr;
550     AfterStoppedServer();
551   }
552 
AfterStoppedServer()553   void AfterStoppedServer() {
554     CEF_REQUIRE_UI_THREAD();
555     if (setup_->clear_cookies) {
556       ClearCookies();
557     } else {
558       DestroyTest();
559     }
560   }
561 
ClearCookies()562   void ClearCookies() {
563     CEF_REQUIRE_UI_THREAD();
564     DCHECK(setup_->clear_cookies);
565     test_request::GetAllCookies(
566         CefCookieManager::GetGlobalManager(nullptr), /*delete_cookies=*/true,
567         base::BindOnce(&CorsTestHandler::ClearedCookies, this));
568   }
569 
ClearedCookies(const test_request::CookieVector & cookies)570   void ClearedCookies(const test_request::CookieVector& cookies) {
571     CEF_REQUIRE_UI_THREAD();
572     got_cleared_cookies_.yes();
573     EXPECT_TRUE(setup_->VerifyClearedCookies(cookies));
574     DestroyTest();
575   }
576 
GetResource(const std::string & url) const577   Resource* GetResource(const std::string& url) const {
578     Resource* resource = setup_->GetResource(url);
579     EXPECT_TRUE(resource) << url;
580     return resource;
581   }
582 
583   TestSetup* setup_;
584   std::string main_url_;
585   TestServerObserver* server_ = nullptr;
586   bool shutting_down_ = false;
587 
588   TrackCallback got_stopped_server_;
589   TrackCallback got_cleared_cookies_;
590 
591   IMPLEMENT_REFCOUNTING(CorsTestHandler);
592   DISALLOW_COPY_AND_ASSIGN(CorsTestHandler);
593 };
594 
595 // JS that results in a call to CorsTestHandler::OnQuery.
GetMsgJS(const std::string & msg)596 std::string GetMsgJS(const std::string& msg) {
597   return "window.testQuery({request:'" + msg + "'});";
598 }
599 
GetSuccessMsgJS()600 std::string GetSuccessMsgJS() {
601   return GetMsgJS(kSuccessMsg);
602 }
GetFailureMsgJS()603 std::string GetFailureMsgJS() {
604   return GetMsgJS(kFailureMsg);
605 }
606 
GetDefaultSuccessMsgHtml()607 std::string GetDefaultSuccessMsgHtml() {
608   return "<html><body>TEST<script>" + GetSuccessMsgJS() +
609          "</script></body></html>";
610 }
611 
612 }  // namespace
613 
614 // Verify the test harness for server requests.
TEST(CorsTest,BasicServer)615 TEST(CorsTest, BasicServer) {
616   TestSetup setup;
617   Resource resource(HandlerType::SERVER, "/CorsTest.BasicServer");
618   setup.AddResource(&resource);
619 
620   CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
621   handler->ExecuteTest();
622   ReleaseAndWaitForDestructor(handler);
623 }
624 
625 // Like above, but also send a query JS message.
TEST(CorsTest,BasicServerWithQuery)626 TEST(CorsTest, BasicServerWithQuery) {
627   TestSetup setup;
628   Resource resource(HandlerType::SERVER, "/CorsTest.BasicServerWithQuery",
629                     kMimeTypeHtml, GetDefaultSuccessMsgHtml());
630   resource.expected_success_query_ct = 1;
631   setup.AddResource(&resource);
632 
633   CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
634   handler->ExecuteTest();
635   ReleaseAndWaitForDestructor(handler);
636 }
637 
638 // Verify the test harness for http scheme requests.
TEST(CorsTest,BasicHttpScheme)639 TEST(CorsTest, BasicHttpScheme) {
640   TestSetup setup;
641   Resource resource(HandlerType::HTTP_SCHEME, "/CorsTest.BasicHttpScheme");
642   setup.AddResource(&resource);
643 
644   CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
645   handler->ExecuteTest();
646   ReleaseAndWaitForDestructor(handler);
647 }
648 
649 // Like above, but also send a query JS message.
TEST(CorsTest,BasicHttpSchemeWithQuery)650 TEST(CorsTest, BasicHttpSchemeWithQuery) {
651   TestSetup setup;
652   Resource resource(HandlerType::HTTP_SCHEME,
653                     "/CorsTest.BasicHttpSchemeWithQuery", kMimeTypeHtml,
654                     GetDefaultSuccessMsgHtml());
655   resource.expected_success_query_ct = 1;
656   setup.AddResource(&resource);
657 
658   CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
659   handler->ExecuteTest();
660   ReleaseAndWaitForDestructor(handler);
661 }
662 
663 // Verify the test harness for custom standard scheme requests.
TEST(CorsTest,BasicCustomStandardScheme)664 TEST(CorsTest, BasicCustomStandardScheme) {
665   TestSetup setup;
666   Resource resource(HandlerType::CUSTOM_STANDARD_SCHEME,
667                     "/CorsTest.BasicCustomStandardScheme");
668   setup.AddResource(&resource);
669 
670   CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
671   handler->ExecuteTest();
672   ReleaseAndWaitForDestructor(handler);
673 }
674 
675 // Like above, but also send a query JS message.
TEST(CorsTest,BasicCustomStandardSchemeWithQuery)676 TEST(CorsTest, BasicCustomStandardSchemeWithQuery) {
677   TestSetup setup;
678   Resource resource(HandlerType::CUSTOM_STANDARD_SCHEME,
679                     "/CorsTest.BasicCustomStandardSchemeWithQuery",
680                     kMimeTypeHtml, GetDefaultSuccessMsgHtml());
681   resource.expected_success_query_ct = 1;
682   setup.AddResource(&resource);
683 
684   CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
685   handler->ExecuteTest();
686   ReleaseAndWaitForDestructor(handler);
687 }
688 
689 namespace {
690 
691 struct CookieTestSetup : TestSetup {
CookieTestSetup__anona54282bb0211::CookieTestSetup692   CookieTestSetup() {}
693 
694   bool expect_cookie = false;
695 
VerifyClearedCookies__anona54282bb0211::CookieTestSetup696   bool VerifyClearedCookies(
697       const test_request::CookieVector& cookies) const override {
698     if (!expect_cookie) {
699       EXPECT_TRUE(cookies.empty());
700       return cookies.empty();
701     }
702 
703     EXPECT_EQ(1U, cookies.size());
704     const std::string& cookie = CefString(&cookies[0].name).ToString() + "=" +
705                                 CefString(&cookies[0].value).ToString();
706     EXPECT_STREQ(kDefaultCookie, cookie.c_str());
707     return cookie == kDefaultCookie;
708   }
709 };
710 
711 struct CookieResource : Resource {
CookieResource__anona54282bb0211::CookieResource712   CookieResource() {}
713 
714   bool expect_cookie = false;
715 
InitSetCookie__anona54282bb0211::CookieResource716   void InitSetCookie() {
717     response->SetHeaderByName("Set-Cookie", kDefaultCookie,
718                               /*override=*/true);
719   }
720 
VerifyRequest__anona54282bb0211::CookieResource721   bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
722     const std::string& cookie = request->GetHeaderByName("Cookie");
723     const std::string& expected_cookie =
724         expect_cookie ? kDefaultCookie : std::string();
725     EXPECT_STREQ(expected_cookie.c_str(), cookie.c_str()) << GetPathURL();
726     return expected_cookie == cookie;
727   }
728 };
729 
SetupCookieExpectations(CookieTestSetup * setup,CookieResource * main_resource,CookieResource * sub_resource)730 void SetupCookieExpectations(CookieTestSetup* setup,
731                              CookieResource* main_resource,
732                              CookieResource* sub_resource) {
733   // All schemes except custom non-standard support cookies.
734   const bool supports_cookies = IsStandardType(main_resource->handler);
735 
736   // The main resource may set the cookie (if cookies are supported), but should
737   // not receive one.
738   main_resource->InitSetCookie();
739   main_resource->expect_cookie = false;
740 
741   // A cookie will be set only for schemes that support cookies.
742   setup->expect_cookie = supports_cookies;
743   // Always clear cookies so we can verify that one wasn't set unexpectedly.
744   setup->clear_cookies = true;
745 
746   // Expect the sub-resource to receive the cookie for same-origin requests
747   // only.
748   sub_resource->expect_cookie =
749       supports_cookies && main_resource->handler == sub_resource->handler;
750 }
751 
GetIframeMainHtml(const std::string & iframe_url,const std::string & sandbox_attribs)752 std::string GetIframeMainHtml(const std::string& iframe_url,
753                               const std::string& sandbox_attribs) {
754   return "<html><body>TEST<iframe src=\"" + iframe_url + "\" sandbox=\"" +
755          sandbox_attribs + "\"></iframe></body></html>";
756 }
757 
GetIframeSubHtml()758 std::string GetIframeSubHtml() {
759   // Try to script the parent frame, then send the SuccessMsg.
760   return "<html><body>TEST<script>try { parent.document.body; } catch "
761          "(exception) { console.log(exception.toString()); }" +
762          GetSuccessMsgJS() + "</script></body></html>";
763 }
764 
HasSandboxAttrib(const std::string & sandbox_attribs,const std::string & attrib)765 bool HasSandboxAttrib(const std::string& sandbox_attribs,
766                       const std::string& attrib) {
767   return sandbox_attribs.find(attrib) != std::string::npos;
768 }
769 
SetupIframeRequest(CookieTestSetup * setup,const std::string & test_name,HandlerType main_handler,CookieResource * main_resource,HandlerType iframe_handler,CookieResource * iframe_resource,const std::string & sandbox_attribs)770 void SetupIframeRequest(CookieTestSetup* setup,
771                         const std::string& test_name,
772                         HandlerType main_handler,
773                         CookieResource* main_resource,
774                         HandlerType iframe_handler,
775                         CookieResource* iframe_resource,
776                         const std::string& sandbox_attribs) {
777   const std::string& base_path = "/" + test_name;
778 
779   // Expect a single iframe request.
780   iframe_resource->Init(iframe_handler, base_path + ".iframe.html",
781                         kMimeTypeHtml, GetIframeSubHtml());
782 
783   // Expect a single main frame request.
784   const std::string& iframe_url = iframe_resource->GetPathURL();
785   main_resource->Init(main_handler, base_path, kMimeTypeHtml,
786                       GetIframeMainHtml(iframe_url, sandbox_attribs));
787 
788   SetupCookieExpectations(setup, main_resource, iframe_resource);
789 
790   if (HasSandboxAttrib(sandbox_attribs, "allow-scripts")) {
791     // Expect the iframe to load successfully and send the SuccessMsg.
792     iframe_resource->expected_success_query_ct = 1;
793 
794     const bool has_same_origin =
795         HasSandboxAttrib(sandbox_attribs, "allow-same-origin");
796     if (!has_same_origin ||
797         (has_same_origin &&
798          (IsNonStandardType(main_handler) || main_handler != iframe_handler))) {
799       // Expect parent frame scripting to fail if:
800       // - "allow-same-origin" is not specified;
801       // - the main frame is a non-standard scheme (e.g. CORS disabled);
802       // - the main frame and iframe origins don't match.
803       // The reported origin will be "null" if "allow-same-origin" is not
804       // specified, or if the iframe is hosted on a non-standard scheme.
805       const std::string& origin =
806           !has_same_origin || IsNonStandardType(iframe_handler)
807               ? "null"
808               : GetOrigin(iframe_handler);
809       setup->AddConsoleMessage("SecurityError: Blocked a frame with origin \"" +
810                                origin +
811                                "\" from accessing a cross-origin frame.");
812     }
813   } else {
814     // Expect JavaScript execution to fail.
815     setup->AddConsoleMessage("Blocked script execution in '" + iframe_url +
816                              "' because the document's frame is sandboxed and "
817                              "the 'allow-scripts' permission is not set.");
818   }
819 
820   setup->AddResource(main_resource);
821   setup->AddResource(iframe_resource);
822 }
823 
824 }  // namespace
825 
826 // Test iframe sandbox attributes with different origin combinations.
827 #define CORS_TEST_IFRAME(test_name, handler_main, handler_iframe,     \
828                          sandbox_attribs)                             \
829   TEST(CorsTest, Iframe##test_name) {                                 \
830     CookieTestSetup setup;                                            \
831     CookieResource resource_main, resource_iframe;                    \
832     SetupIframeRequest(&setup, "CorsTest.Iframe" #test_name,          \
833                        HandlerType::handler_main, &resource_main,     \
834                        HandlerType::handler_iframe, &resource_iframe, \
835                        sandbox_attribs);                              \
836     CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
837     handler->ExecuteTest();                                           \
838     ReleaseAndWaitForDestructor(handler);                             \
839   }
840 
841 // Test all origin combinations (same and cross-origin).
842 #define CORS_TEST_IFRAME_ALL(name, sandbox_attribs)                            \
843   CORS_TEST_IFRAME(name##ServerToServer, SERVER, SERVER, sandbox_attribs)      \
844   CORS_TEST_IFRAME(name##ServerToHttpScheme, SERVER, HTTP_SCHEME,              \
845                    sandbox_attribs)                                            \
846   CORS_TEST_IFRAME(name##ServerToCustomStandardScheme, SERVER,                 \
847                    CUSTOM_STANDARD_SCHEME, sandbox_attribs)                    \
848   CORS_TEST_IFRAME(name##ServerToCustomNonStandardScheme, SERVER,              \
849                    CUSTOM_NONSTANDARD_SCHEME, sandbox_attribs)                 \
850   CORS_TEST_IFRAME(name##ServerToCustomUnregisteredScheme, SERVER,             \
851                    CUSTOM_UNREGISTERED_SCHEME, sandbox_attribs)                \
852   CORS_TEST_IFRAME(name##HttpSchemeToServer, HTTP_SCHEME, SERVER,              \
853                    sandbox_attribs)                                            \
854   CORS_TEST_IFRAME(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME,     \
855                    sandbox_attribs)                                            \
856   CORS_TEST_IFRAME(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME,        \
857                    CUSTOM_STANDARD_SCHEME, sandbox_attribs)                    \
858   CORS_TEST_IFRAME(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME,     \
859                    CUSTOM_NONSTANDARD_SCHEME, sandbox_attribs)                 \
860   CORS_TEST_IFRAME(name##HttpSchemeToCustomUnregisteredScheme, HTTP_SCHEME,    \
861                    CUSTOM_UNREGISTERED_SCHEME, sandbox_attribs)                \
862   CORS_TEST_IFRAME(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME, \
863                    SERVER, sandbox_attribs)                                    \
864   CORS_TEST_IFRAME(name##CustomStandardSchemeToHttpScheme,                     \
865                    CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, sandbox_attribs)       \
866   CORS_TEST_IFRAME(name##CustomStandardSchemeToCustomStandardScheme,           \
867                    CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME,             \
868                    sandbox_attribs)                                            \
869   CORS_TEST_IFRAME(name##CustomStandardSchemeToCustomNonStandardScheme,        \
870                    CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,          \
871                    sandbox_attribs)                                            \
872   CORS_TEST_IFRAME(name##CustomStandardSchemeToCustomUnregisteredScheme,       \
873                    CUSTOM_STANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME,         \
874                    sandbox_attribs)                                            \
875   CORS_TEST_IFRAME(name##CustomNonStandardSchemeToServer,                      \
876                    CUSTOM_NONSTANDARD_SCHEME, SERVER, sandbox_attribs)         \
877   CORS_TEST_IFRAME(name##CustomNonStandardSchemeToHttpScheme,                  \
878                    CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, sandbox_attribs)    \
879   CORS_TEST_IFRAME(name##CustomNonStandardSchemeToCustomStandardScheme,        \
880                    CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME,          \
881                    sandbox_attribs)                                            \
882   CORS_TEST_IFRAME(name##CustomNonStandardSchemeToCustomNonStandardScheme,     \
883                    CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,       \
884                    sandbox_attribs)                                            \
885   CORS_TEST_IFRAME(name##CustomNonStandardSchemeToCustomUnregisteredScheme,    \
886                    CUSTOM_NONSTANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME,      \
887                    sandbox_attribs)                                            \
888   CORS_TEST_IFRAME(name##CustomUnregisteredSchemeToServer,                     \
889                    CUSTOM_UNREGISTERED_SCHEME, SERVER, sandbox_attribs)        \
890   CORS_TEST_IFRAME(name##CustomUnregisteredSchemeToHttpScheme,                 \
891                    CUSTOM_UNREGISTERED_SCHEME, HTTP_SCHEME, sandbox_attribs)   \
892   CORS_TEST_IFRAME(name##CustomUnregisteredSchemeToCustomStandardScheme,       \
893                    CUSTOM_UNREGISTERED_SCHEME, CUSTOM_STANDARD_SCHEME,         \
894                    sandbox_attribs)                                            \
895   CORS_TEST_IFRAME(name##CustomUnregisteredSchemeToCustomNonStandardScheme,    \
896                    CUSTOM_UNREGISTERED_SCHEME, CUSTOM_NONSTANDARD_SCHEME,      \
897                    sandbox_attribs)                                            \
898   CORS_TEST_IFRAME(name##CustomUnregisteredSchemeToCustomUnregisteredScheme,   \
899                    CUSTOM_UNREGISTERED_SCHEME, CUSTOM_UNREGISTERED_SCHEME,     \
900                    sandbox_attribs)
901 
902 // Everything is blocked.
903 CORS_TEST_IFRAME_ALL(None, "")
904 
905 // JavaScript execution is allowed.
906 CORS_TEST_IFRAME_ALL(AllowScripts, "allow-scripts")
907 
908 // JavaScript execution is allowed and scripting the parent is allowed for
909 // same-origin only.
910 CORS_TEST_IFRAME_ALL(AllowScriptsAndSameOrigin,
911                      "allow-scripts allow-same-origin")
912 
913 namespace {
914 
915 const char kSubRequestMethod[] = "GET";
916 const char kSubUnsafeHeaderName[] = "x-unsafe-header";
917 const char kSubUnsafeHeaderValue[] = "not-safe";
918 
919 struct SubResource : CookieResource {
SubResource__anona54282bb0311::SubResource920   SubResource() {}
921 
922   std::string main_origin;
923   bool supports_cors = false;
924   bool is_cross_origin = false;
925 
InitCors__anona54282bb0311::SubResource926   void InitCors(HandlerType main_handler, bool add_header) {
927     // Must specify the method to differentiate from the preflight request.
928     method = kSubRequestMethod;
929 
930     // Origin is always "null" for non-standard schemes.
931     main_origin =
932         IsNonStandardType(main_handler) ? "null" : GetOrigin(main_handler);
933 
934     // True if cross-origin requests are allowed. XHR requests to non-standard
935     // schemes are not allowed (due to the "null" origin).
936     supports_cors = IsStandardType(handler);
937     if (!supports_cors) {
938       // Don't expect the xhr request.
939       expected_response_ct = 0;
940     }
941 
942     // True if the request is considered cross-origin. Any requests between
943     // non-standard schemes are considered cross-origin (due to the "null"
944     // origin).
945     is_cross_origin =
946         main_handler != handler ||
947         (IsNonStandardType(main_handler) && handler == main_handler);
948 
949     if (is_cross_origin && add_header) {
950       response->SetHeaderByName("Access-Control-Allow-Origin", main_origin,
951                                 false);
952     }
953   }
954 
VerifyRequest__anona54282bb0311::SubResource955   bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
956     if (!CookieResource::VerifyRequest(request))
957       return false;
958 
959     const std::string& request_method = request->GetMethod();
960     EXPECT_STREQ(method.c_str(), request_method.c_str()) << GetPathURL();
961     if (request_method != method)
962       return false;
963 
964     // Verify that the "Origin" header contains the expected value.
965     const std::string& origin = request->GetHeaderByName("Origin");
966     const std::string& expected_origin =
967         is_cross_origin ? main_origin : std::string();
968     EXPECT_STREQ(expected_origin.c_str(), origin.c_str()) << GetPathURL();
969     if (expected_origin != origin)
970       return false;
971 
972     // Verify that the "X-Unsafe-Header" header contains the expected value.
973     const std::string& unsafe_header =
974         request->GetHeaderByName(kSubUnsafeHeaderName);
975     EXPECT_STREQ(kSubUnsafeHeaderValue, unsafe_header.c_str()) << GetPathURL();
976     return unsafe_header == kSubUnsafeHeaderValue;
977   }
978 };
979 
980 // See https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
981 // for details of CORS preflight behavior.
982 struct PreflightResource : Resource {
983   std::string main_origin;
984 
InitPreflight__anona54282bb0311::PreflightResource985   void InitPreflight(HandlerType main_handler) {
986     // CORS preflight requests originate from PreflightController in the network
987     // process, so we only expect them for server requests.
988     EXPECT_EQ(HandlerType::SERVER, handler);
989 
990     // Origin is always "null" for non-standard schemes.
991     main_origin =
992         IsNonStandardType(main_handler) ? "null" : GetOrigin(main_handler);
993 
994     method = "OPTIONS";
995     response->SetHeaderByName("Access-Control-Allow-Methods",
996                               "GET,HEAD,OPTIONS,POST", false);
997     response->SetHeaderByName("Access-Control-Allow-Headers",
998                               kSubUnsafeHeaderName, false);
999     response->SetHeaderByName("Access-Control-Allow-Origin", main_origin,
1000                               false);
1001   }
1002 
VerifyRequest__anona54282bb0311::PreflightResource1003   bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
1004     const std::string& request_method = request->GetMethod();
1005     EXPECT_STREQ(method.c_str(), request_method.c_str()) << GetPathURL();
1006     if (request_method != method)
1007       return false;
1008 
1009     const std::string& origin = request->GetHeaderByName("Origin");
1010     EXPECT_STREQ(main_origin.c_str(), origin.c_str()) << GetPathURL();
1011     if (main_origin != origin)
1012       return false;
1013 
1014     const std::string& ac_request_method =
1015         request->GetHeaderByName("Access-Control-Request-Method");
1016     EXPECT_STREQ(kSubRequestMethod, ac_request_method.c_str()) << GetPathURL();
1017     if (ac_request_method != kSubRequestMethod)
1018       return false;
1019 
1020     const std::string& ac_request_headers =
1021         request->GetHeaderByName("Access-Control-Request-Headers");
1022     EXPECT_STREQ(kSubUnsafeHeaderName, ac_request_headers.c_str())
1023         << GetPathURL();
1024     if (ac_request_headers != kSubUnsafeHeaderName)
1025       return false;
1026 
1027     return true;
1028   }
1029 };
1030 
1031 enum class ExecMode {
1032   XHR,
1033   FETCH,
1034 };
1035 
GetXhrExecJS(const std::string & sub_url)1036 std::string GetXhrExecJS(const std::string& sub_url) {
1037   // Inclusion of an unsafe header triggers CORS preflight for cross-origin
1038   // requests to the server.
1039   return "xhr = new XMLHttpRequest();\n"
1040          "xhr.open(\"GET\", \"" +
1041          sub_url +
1042          "\", true)\n;"
1043          "xhr.setRequestHeader('" +
1044          kSubUnsafeHeaderName + "', '" + kSubUnsafeHeaderValue +
1045          "');\n"
1046          "xhr.onload = function(e) {\n"
1047          "  if (xhr.readyState === 4) {\n"
1048          "    if (xhr.status === 200) {\n"
1049          "      onResult(xhr.responseText);\n"
1050          "    } else {\n"
1051          "      console.log('XMLHttpRequest failed with status ' + "
1052          "xhr.status);\n"
1053          "      onResult('FAILURE');\n"
1054          "    }\n"
1055          "  }\n"
1056          "};\n"
1057          "xhr.onerror = function(e) {\n"
1058          "  onResult('FAILURE');\n"
1059          "};\n"
1060          "xhr.send();\n";
1061 }
1062 
GetFetchExecJS(const std::string & sub_url)1063 std::string GetFetchExecJS(const std::string& sub_url) {
1064   // Inclusion of an unsafe header triggers CORS preflight for cross-origin
1065   // requests to the server.
1066   return std::string() +
1067          "let h = new Headers();\n"
1068          "h.append('" +
1069          kSubUnsafeHeaderName + "', '" + kSubUnsafeHeaderValue +
1070          "');\n"
1071          "fetch('" +
1072          sub_url +
1073          "', {headers: h})\n"
1074          ".then(function(response) {\n"
1075          "  if (response.status === 200) {\n"
1076          "      response.text().then(function(text) {\n"
1077          "          onResult(text);\n"
1078          "      }).catch(function(e) {\n"
1079          "          onResult('FAILURE')\n;        "
1080          "      })\n;"
1081          "  } else {\n"
1082          "      onResult('FAILURE');\n"
1083          "  }\n"
1084          "}).catch(function(e) {\n"
1085          "  onResult('FAILURE');\n"
1086          "});\n";
1087 }
1088 
GetExecMainHtml(ExecMode mode,const std::string & sub_url)1089 std::string GetExecMainHtml(ExecMode mode, const std::string& sub_url) {
1090   return std::string() +
1091          "<html><head>\n"
1092          "<script language=\"JavaScript\">\n" +
1093          "function onResult(val) {\n"
1094          "  if (val === '" +
1095          kDefaultText + "') {" + GetSuccessMsgJS() + "} else {" +
1096          GetFailureMsgJS() +
1097          "}\n}\n"
1098          "function execRequest() {\n" +
1099          (mode == ExecMode::XHR ? GetXhrExecJS(sub_url)
1100                                 : GetFetchExecJS(sub_url)) +
1101          "}\n</script>\n"
1102          "</head><body onload=\"execRequest();\">"
1103          "Running execRequest..."
1104          "</body></html>";
1105 }
1106 
1107 // XHR and fetch requests behave the same, except for console message contents.
1108 // In addition to basic CORS header behaviors and request blocking, this test
1109 // verifies that CORS preflight requests are sent and received when expected.
1110 // Since preflight behavior is implemented in the network process we expect it
1111 // to already have substantial test coverage in Chromium.
SetupExecRequest(ExecMode mode,CookieTestSetup * setup,const std::string & test_name,HandlerType main_handler,CookieResource * main_resource,HandlerType sub_handler,SubResource * sub_resource,PreflightResource * preflight_resource,bool add_header)1112 void SetupExecRequest(ExecMode mode,
1113                       CookieTestSetup* setup,
1114                       const std::string& test_name,
1115                       HandlerType main_handler,
1116                       CookieResource* main_resource,
1117                       HandlerType sub_handler,
1118                       SubResource* sub_resource,
1119                       PreflightResource* preflight_resource,
1120                       bool add_header) {
1121   const std::string& base_path = "/" + test_name;
1122 
1123   // Expect a single xhr request.
1124   const std::string& sub_path = base_path + ".sub.txt";
1125   sub_resource->Init(sub_handler, sub_path, kMimeTypeText, kDefaultText);
1126   sub_resource->InitCors(main_handler, add_header);
1127 
1128   // Expect a single main frame request.
1129   const std::string& sub_url = sub_resource->GetPathURL();
1130   main_resource->Init(main_handler, base_path, kMimeTypeHtml,
1131                       GetExecMainHtml(mode, sub_url));
1132 
1133   SetupCookieExpectations(setup, main_resource, sub_resource);
1134 
1135   // Cross-origin requests to a server sub-resource will receive a CORS
1136   // preflight request because we add an unsafe header.
1137   const bool expect_cors_preflight =
1138       sub_resource->is_cross_origin && sub_handler == HandlerType::SERVER;
1139 
1140   if (sub_resource->is_cross_origin &&
1141       (!sub_resource->supports_cors || !add_header)) {
1142     // Expect the cross-origin XHR to be blocked.
1143     main_resource->expected_failure_query_ct = 1;
1144 
1145     if (sub_resource->supports_cors && !add_header) {
1146       // The request supports CORS, but we didn't add the
1147       // "Access-Control-Allow-Origin" header.
1148       if (!expect_cors_preflight || preflight_resource != nullptr) {
1149         // This is the error message when not expecting a CORS preflight
1150         // request, or when the preflight request is handled by the server.
1151         // Unhandled preflight requests will output a different error message
1152         // (see below).
1153         if (mode == ExecMode::XHR) {
1154           setup->AddConsoleMessage(
1155               "Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
1156               sub_resource->main_origin +
1157               "' has been blocked by CORS policy: No "
1158               "'Access-Control-Allow-Origin' "
1159               "header is present on the requested resource.");
1160         } else {
1161           setup->AddConsoleMessage(
1162               "Access to fetch at '" + sub_url + "' from origin '" +
1163               sub_resource->main_origin +
1164               "' has been blocked by CORS policy: No "
1165               "'Access-Control-Allow-Origin' header is present on the "
1166               "requested "
1167               "resource. If an opaque response serves your needs, set the "
1168               "request's mode to 'no-cors' to fetch the resource with CORS "
1169               "disabled.");
1170         }
1171       }
1172     } else if (mode == ExecMode::XHR) {
1173       setup->AddConsoleMessage(
1174           "Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
1175           sub_resource->main_origin +
1176           "' has been blocked by CORS policy: Cross origin requests are only "
1177           "supported for protocol schemes:");
1178     } else {
1179       setup->AddConsoleMessage("Fetch API cannot load " + sub_url +
1180                                ". URL scheme \"" + GetScheme(sub_handler) +
1181                                "\" is not supported.");
1182     }
1183   } else {
1184     // Expect the (possibly cross-origin) XHR to be allowed.
1185     main_resource->expected_success_query_ct = 1;
1186   }
1187 
1188   setup->AddResource(main_resource);
1189   setup->AddResource(sub_resource);
1190 
1191   if (expect_cors_preflight) {
1192     // Expect a CORS preflight request.
1193     if (preflight_resource) {
1194       // The server will handle the preflight request. The cross-origin XHR may
1195       // still be blocked if the "Access-Control-Allow-Origin" header is missing
1196       // (see above).
1197       preflight_resource->Init(sub_handler, sub_path, kMimeTypeText,
1198                                std::string());
1199       preflight_resource->InitPreflight(main_handler);
1200       setup->AddResource(preflight_resource);
1201     } else {
1202       // The server will not handle the preflight request. Expect the
1203       // cross-origin XHR to be blocked.
1204       main_resource->expected_failure_query_ct = 1;
1205       main_resource->expected_success_query_ct = 0;
1206       sub_resource->expected_response_ct = 0;
1207 
1208       if (mode == ExecMode::XHR) {
1209         setup->AddConsoleMessage(
1210             "Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
1211             sub_resource->main_origin +
1212             "' has been blocked by CORS policy: Response to preflight request "
1213             "doesn't pass access control check: No "
1214             "'Access-Control-Allow-Origin' header is present on the requested "
1215             "resource.");
1216       } else {
1217         setup->AddConsoleMessage(
1218             "Access to fetch at '" + sub_url + "' from origin '" +
1219             sub_resource->main_origin +
1220             "' has been blocked by CORS policy: Response to preflight request "
1221             "doesn't pass access control check: No "
1222             "'Access-Control-Allow-Origin' header is present on the requested "
1223             "resource. If an opaque response serves your needs, set the "
1224             "request's mode to 'no-cors' to fetch the resource with CORS "
1225             "disabled.");
1226       }
1227     }
1228   }
1229 }
1230 
1231 }  // namespace
1232 
1233 // Test XHR requests with different origin combinations.
1234 #define CORS_TEST_XHR(test_name, handler_main, handler_sub, add_header) \
1235   TEST(CorsTest, Xhr##test_name) {                                      \
1236     CookieTestSetup setup;                                              \
1237     CookieResource resource_main;                                       \
1238     SubResource resource_sub;                                           \
1239     PreflightResource resource_preflight;                               \
1240     SetupExecRequest(ExecMode::XHR, &setup, "CorsTest.Xhr" #test_name,  \
1241                      HandlerType::handler_main, &resource_main,         \
1242                      HandlerType::handler_sub, &resource_sub,           \
1243                      &resource_preflight, add_header);                  \
1244     CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);   \
1245     handler->ExecuteTest();                                             \
1246     ReleaseAndWaitForDestructor(handler);                               \
1247   }
1248 
1249 // Test all origin combinations (same and cross-origin).
1250 #define CORS_TEST_XHR_ALL(name, add_header)                                    \
1251   CORS_TEST_XHR(name##ServerToServer, SERVER, SERVER, add_header)              \
1252   CORS_TEST_XHR(name##ServerToHttpScheme, SERVER, HTTP_SCHEME, add_header)     \
1253   CORS_TEST_XHR(name##ServerToCustomStandardScheme, SERVER,                    \
1254                 CUSTOM_STANDARD_SCHEME, add_header)                            \
1255   CORS_TEST_XHR(name##ServerToCustomNonStandardScheme, SERVER,                 \
1256                 CUSTOM_NONSTANDARD_SCHEME, add_header)                         \
1257   CORS_TEST_XHR(name##ServerToCustomUnregisteredScheme, SERVER,                \
1258                 CUSTOM_UNREGISTERED_SCHEME, add_header)                        \
1259   CORS_TEST_XHR(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, add_header)     \
1260   CORS_TEST_XHR(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME,        \
1261                 add_header)                                                    \
1262   CORS_TEST_XHR(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME,           \
1263                 CUSTOM_STANDARD_SCHEME, add_header)                            \
1264   CORS_TEST_XHR(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME,        \
1265                 CUSTOM_NONSTANDARD_SCHEME, add_header)                         \
1266   CORS_TEST_XHR(name##HttpSchemeToCustomUnregisteredScheme, HTTP_SCHEME,       \
1267                 CUSTOM_UNREGISTERED_SCHEME, add_header)                        \
1268   CORS_TEST_XHR(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME,    \
1269                 SERVER, add_header)                                            \
1270   CORS_TEST_XHR(name##CustomStandardSchemeToHttpScheme,                        \
1271                 CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, add_header)               \
1272   CORS_TEST_XHR(name##CustomStandardSchemeToCustomStandardScheme,              \
1273                 CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header)    \
1274   CORS_TEST_XHR(name##CustomStandardSchemeToCustomNonStandardScheme,           \
1275                 CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, add_header) \
1276   CORS_TEST_XHR(name##CustomStandardSchemeToCustomUnregisteredScheme,          \
1277                 CUSTOM_STANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME,            \
1278                 add_header)                                                    \
1279   CORS_TEST_XHR(name##CustomNonStandardSchemeToServer,                         \
1280                 CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)                 \
1281   CORS_TEST_XHR(name##CustomNonStandardSchemeToHttpScheme,                     \
1282                 CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, add_header)            \
1283   CORS_TEST_XHR(name##CustomNonStandardSchemeToCustomStandardScheme,           \
1284                 CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header) \
1285   CORS_TEST_XHR(name##CustomNonStandardSchemeToCustomNonStandardScheme,        \
1286                 CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,          \
1287                 add_header)                                                    \
1288   CORS_TEST_XHR(name##CustomNonStandardSchemeToCustomUnregisteredScheme,       \
1289                 CUSTOM_NONSTANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME,         \
1290                 add_header)                                                    \
1291   CORS_TEST_XHR(name##CustomUnregisteredSchemeToServer,                        \
1292                 CUSTOM_UNREGISTERED_SCHEME, SERVER, add_header)                \
1293   CORS_TEST_XHR(name##CustomUnregisteredSchemeToHttpScheme,                    \
1294                 CUSTOM_UNREGISTERED_SCHEME, HTTP_SCHEME, add_header)           \
1295   CORS_TEST_XHR(name##CustomUnregisteredSchemeToCustomStandardScheme,          \
1296                 CUSTOM_UNREGISTERED_SCHEME, CUSTOM_STANDARD_SCHEME,            \
1297                 add_header)                                                    \
1298   CORS_TEST_XHR(name##CustomUnregisteredSchemeToCustomNonStandardScheme,       \
1299                 CUSTOM_UNREGISTERED_SCHEME, CUSTOM_NONSTANDARD_SCHEME,         \
1300                 add_header)                                                    \
1301   CORS_TEST_XHR(name##CustomUnregisteredSchemeToCustomUnregisteredScheme,      \
1302                 CUSTOM_UNREGISTERED_SCHEME, CUSTOM_UNREGISTERED_SCHEME,        \
1303                 add_header)
1304 
1305 // XHR requests without the "Access-Control-Allow-Origin" header.
1306 CORS_TEST_XHR_ALL(NoHeader, false)
1307 
1308 // XHR requests with the "Access-Control-Allow-Origin" header.
1309 CORS_TEST_XHR_ALL(WithHeader, true)
1310 
1311 // Like above, but without handling CORS preflight requests.
1312 #define CORS_TEST_XHR_NO_PREFLIGHT(test_name, handler_main, handler_sub, \
1313                                    add_header)                           \
1314   TEST(CorsTest, Xhr##test_name) {                                       \
1315     CookieTestSetup setup;                                               \
1316     CookieResource resource_main;                                        \
1317     SubResource resource_sub;                                            \
1318     SetupExecRequest(ExecMode::XHR, &setup, "CorsTest.Xhr" #test_name,   \
1319                      HandlerType::handler_main, &resource_main,          \
1320                      HandlerType::handler_sub, &resource_sub, nullptr,   \
1321                      add_header);                                        \
1322     CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);    \
1323     handler->ExecuteTest();                                              \
1324     ReleaseAndWaitForDestructor(handler);                                \
1325   }
1326 
1327 #define CORS_TEST_XHR_NO_PREFLIGHT_SERVER(name, add_header)                    \
1328   CORS_TEST_XHR_NO_PREFLIGHT(name##ServerToServer, SERVER, SERVER, add_header) \
1329   CORS_TEST_XHR_NO_PREFLIGHT(name##HttpSchemeToServer, HTTP_SCHEME, SERVER,    \
1330                              add_header)                                       \
1331   CORS_TEST_XHR_NO_PREFLIGHT(name##CustomStandardSchemeToServer,               \
1332                              CUSTOM_STANDARD_SCHEME, SERVER, add_header)       \
1333   CORS_TEST_XHR_NO_PREFLIGHT(name##CustomNonStandardSchemeToServer,            \
1334                              CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)
1335 
1336 // XHR requests without the "Access-Control-Allow-Origin" header.
1337 CORS_TEST_XHR_NO_PREFLIGHT_SERVER(NoHeaderNoPreflight, false)
1338 
1339 // XHR requests with the "Access-Control-Allow-Origin" header.
1340 CORS_TEST_XHR_NO_PREFLIGHT_SERVER(WithHeaderNoPreflight, true)
1341 
1342 // Test fetch requests with different origin combinations.
1343 #define CORS_TEST_FETCH(test_name, handler_main, handler_sub, add_header)  \
1344   TEST(CorsTest, Fetch##test_name) {                                       \
1345     CookieTestSetup setup;                                                 \
1346     CookieResource resource_main;                                          \
1347     SubResource resource_sub;                                              \
1348     PreflightResource resource_preflight;                                  \
1349     SetupExecRequest(ExecMode::FETCH, &setup, "CorsTest.Fetch" #test_name, \
1350                      HandlerType::handler_main, &resource_main,            \
1351                      HandlerType::handler_sub, &resource_sub,              \
1352                      &resource_preflight, add_header);                     \
1353     CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);      \
1354     handler->ExecuteTest();                                                \
1355     ReleaseAndWaitForDestructor(handler);                                  \
1356   }
1357 
1358 // Test all origin combinations (same and cross-origin).
1359 #define CORS_TEST_FETCH_ALL(name, add_header)                                 \
1360   CORS_TEST_FETCH(name##ServerToServer, SERVER, SERVER, add_header)           \
1361   CORS_TEST_FETCH(name##ServerToHttpScheme, SERVER, HTTP_SCHEME, add_header)  \
1362   CORS_TEST_FETCH(name##ServerToCustomStandardScheme, SERVER,                 \
1363                   CUSTOM_STANDARD_SCHEME, add_header)                         \
1364   CORS_TEST_FETCH(name##ServerToCustomNonStandardScheme, SERVER,              \
1365                   CUSTOM_NONSTANDARD_SCHEME, add_header)                      \
1366   CORS_TEST_FETCH(name##ServerToCustomUnregisteredScheme, SERVER,             \
1367                   CUSTOM_UNREGISTERED_SCHEME, add_header)                     \
1368   CORS_TEST_FETCH(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, add_header)  \
1369   CORS_TEST_FETCH(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME,     \
1370                   add_header)                                                 \
1371   CORS_TEST_FETCH(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME,        \
1372                   CUSTOM_STANDARD_SCHEME, add_header)                         \
1373   CORS_TEST_FETCH(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME,     \
1374                   CUSTOM_NONSTANDARD_SCHEME, add_header)                      \
1375   CORS_TEST_FETCH(name##HttpSchemeToCustomUnregisteredScheme, HTTP_SCHEME,    \
1376                   CUSTOM_UNREGISTERED_SCHEME, add_header)                     \
1377   CORS_TEST_FETCH(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME, \
1378                   SERVER, add_header)                                         \
1379   CORS_TEST_FETCH(name##CustomStandardSchemeToHttpScheme,                     \
1380                   CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, add_header)            \
1381   CORS_TEST_FETCH(name##CustomStandardSchemeToCustomStandardScheme,           \
1382                   CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header) \
1383   CORS_TEST_FETCH(name##CustomStandardSchemeToCustomNonStandardScheme,        \
1384                   CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,          \
1385                   add_header)                                                 \
1386   CORS_TEST_FETCH(name##CustomStandardSchemeToCustomUnregisteredScheme,       \
1387                   CUSTOM_STANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME,         \
1388                   add_header)                                                 \
1389   CORS_TEST_FETCH(name##CustomNonStandardSchemeToServer,                      \
1390                   CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)              \
1391   CORS_TEST_FETCH(name##CustomNonStandardSchemeToHttpScheme,                  \
1392                   CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, add_header)         \
1393   CORS_TEST_FETCH(name##CustomNonStandardSchemeToCustomStandardScheme,        \
1394                   CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME,          \
1395                   add_header)                                                 \
1396   CORS_TEST_FETCH(name##CustomNonStandardSchemeToCustomNonStandardScheme,     \
1397                   CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME,       \
1398                   add_header)                                                 \
1399   CORS_TEST_FETCH(name##CustomNonStandardSchemeToCustomUnregisteredScheme,    \
1400                   CUSTOM_NONSTANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME,      \
1401                   add_header)                                                 \
1402   CORS_TEST_FETCH(name##CustomUnregisteredSchemeToServer,                     \
1403                   CUSTOM_UNREGISTERED_SCHEME, SERVER, add_header)             \
1404   CORS_TEST_FETCH(name##CustomUnregisteredSchemeToHttpScheme,                 \
1405                   CUSTOM_UNREGISTERED_SCHEME, HTTP_SCHEME, add_header)        \
1406   CORS_TEST_FETCH(name##CustomUnregisteredSchemeToCustomStandardScheme,       \
1407                   CUSTOM_UNREGISTERED_SCHEME, CUSTOM_STANDARD_SCHEME,         \
1408                   add_header)                                                 \
1409   CORS_TEST_FETCH(name##CustomUnregisteredSchemeToCustomNonStandardScheme,    \
1410                   CUSTOM_UNREGISTERED_SCHEME, CUSTOM_NONSTANDARD_SCHEME,      \
1411                   add_header)                                                 \
1412   CORS_TEST_FETCH(name##CustomUnregisteredSchemeToCustomUnregisteredScheme,   \
1413                   CUSTOM_UNREGISTERED_SCHEME, CUSTOM_UNREGISTERED_SCHEME,     \
1414                   add_header)
1415 
1416 // Fetch requests without the "Access-Control-Allow-Origin" header.
1417 CORS_TEST_FETCH_ALL(NoHeader, false)
1418 
1419 // Fetch requests with the "Access-Control-Allow-Origin" header.
1420 CORS_TEST_FETCH_ALL(WithHeader, true)
1421 
1422 // Like above, but without handling CORS preflight requests.
1423 #define CORS_TEST_FETCH_NO_PREFLIGHT(test_name, handler_main, handler_sub, \
1424                                      add_header)                           \
1425   TEST(CorsTest, Fetch##test_name) {                                       \
1426     CookieTestSetup setup;                                                 \
1427     CookieResource resource_main;                                          \
1428     SubResource resource_sub;                                              \
1429     SetupExecRequest(ExecMode::FETCH, &setup, "CorsTest.Fetch" #test_name, \
1430                      HandlerType::handler_main, &resource_main,            \
1431                      HandlerType::handler_sub, &resource_sub, nullptr,     \
1432                      add_header);                                          \
1433     CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);      \
1434     handler->ExecuteTest();                                                \
1435     ReleaseAndWaitForDestructor(handler);                                  \
1436   }
1437 
1438 #define CORS_TEST_FETCH_NO_PREFLIGHT_SERVER(name, add_header)                 \
1439   CORS_TEST_FETCH_NO_PREFLIGHT(name##ServerToServer, SERVER, SERVER,          \
1440                                add_header)                                    \
1441   CORS_TEST_FETCH_NO_PREFLIGHT(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, \
1442                                add_header)                                    \
1443   CORS_TEST_FETCH_NO_PREFLIGHT(name##CustomStandardSchemeToServer,            \
1444                                CUSTOM_STANDARD_SCHEME, SERVER, add_header)    \
1445   CORS_TEST_FETCH_NO_PREFLIGHT(name##CustomNonStandardSchemeToServer,         \
1446                                CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)
1447 
1448 // Fetch requests without the "Access-Control-Allow-Origin" header.
1449 CORS_TEST_FETCH_NO_PREFLIGHT_SERVER(NoHeaderNoPreflight, false)
1450 
1451 // Fetch requests with the "Access-Control-Allow-Origin" header.
1452 CORS_TEST_FETCH_NO_PREFLIGHT_SERVER(WithHeaderNoPreflight, true)
1453 
1454 namespace {
1455 
1456 enum class RedirectMode {
1457   MODE_302,
1458   MODE_307,
1459 };
1460 
1461 struct RedirectGetResource : CookieResource {
RedirectGetResource__anona54282bb0411::RedirectGetResource1462   RedirectGetResource() {}
1463 
VerifyRequest__anona54282bb0411::RedirectGetResource1464   bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
1465     if (!CookieResource::VerifyRequest(request))
1466       return false;
1467 
1468     // The "Origin" header should never be present for a redirect.
1469     const std::string& origin = request->GetHeaderByName("Origin");
1470     EXPECT_TRUE(origin.empty()) << GetPathURL();
1471     return origin.empty();
1472   }
1473 };
1474 
SetupRedirectResponse(RedirectMode mode,const std::string & redirect_url,CefRefPtr<CefResponse> response)1475 void SetupRedirectResponse(RedirectMode mode,
1476                            const std::string& redirect_url,
1477                            CefRefPtr<CefResponse> response) {
1478   if (mode == RedirectMode::MODE_302)
1479     response->SetStatus(302);
1480   else if (mode == RedirectMode::MODE_307)
1481     response->SetStatus(307);
1482   else
1483     NOTREACHED();
1484 
1485   response->SetHeaderByName("Location", redirect_url,
1486                             /*override=*/false);
1487 }
1488 
1489 // Test redirect requests.
SetupRedirectGetRequest(RedirectMode mode,CookieTestSetup * setup,const std::string & test_name,HandlerType main_handler,CookieResource * main_resource,HandlerType redirect_handler,RedirectGetResource * redirect_resource)1490 void SetupRedirectGetRequest(RedirectMode mode,
1491                              CookieTestSetup* setup,
1492                              const std::string& test_name,
1493                              HandlerType main_handler,
1494                              CookieResource* main_resource,
1495                              HandlerType redirect_handler,
1496                              RedirectGetResource* redirect_resource) {
1497   const std::string& base_path = "/" + test_name;
1498 
1499   // Expect a single redirect request that sends SuccessMsg.
1500   redirect_resource->Init(redirect_handler, base_path + ".redirect.html",
1501                           kMimeTypeHtml, GetDefaultSuccessMsgHtml());
1502   redirect_resource->expected_success_query_ct = 1;
1503 
1504   // Expect a single main request that results in a redirect.
1505   const std::string& redirect_url = redirect_resource->GetPathURL();
1506   main_resource->Init(main_handler, base_path, kMimeTypeHtml, std::string());
1507   SetupRedirectResponse(mode, redirect_url, main_resource->response);
1508 
1509   SetupCookieExpectations(setup, main_resource, redirect_resource);
1510 
1511   setup->AddResource(main_resource);
1512   setup->AddResource(redirect_resource);
1513 }
1514 
1515 }  // namespace
1516 
1517 // Test redirect GET requests with different origin combinations.
1518 #define CORS_TEST_REDIRECT_GET(test_name, mode, handler_main,          \
1519                                handler_redirect)                       \
1520   TEST(CorsTest, RedirectGet##test_name) {                             \
1521     CookieTestSetup setup;                                             \
1522     CookieResource resource_main;                                      \
1523     RedirectGetResource resource_redirect;                             \
1524     SetupRedirectGetRequest(                                           \
1525         RedirectMode::mode, &setup, "CorsTest.RedirectGet" #test_name, \
1526         HandlerType::handler_main, &resource_main,                     \
1527         HandlerType::handler_redirect, &resource_redirect);            \
1528     CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);  \
1529     handler->ExecuteTest();                                            \
1530     ReleaseAndWaitForDestructor(handler);                              \
1531   }
1532 
1533 // Test all redirect GET combinations (same and cross-origin).
1534 #define CORS_TEST_REDIRECT_GET_ALL(name, mode)                                 \
1535   CORS_TEST_REDIRECT_GET(name##ServerToServer, mode, SERVER, SERVER)           \
1536   CORS_TEST_REDIRECT_GET(name##ServerToHttpScheme, mode, SERVER, HTTP_SCHEME)  \
1537   CORS_TEST_REDIRECT_GET(name##ServerToCustomStandardScheme, mode, SERVER,     \
1538                          CUSTOM_STANDARD_SCHEME)                               \
1539   CORS_TEST_REDIRECT_GET(name##ServerToCustomNonStandardScheme, mode, SERVER,  \
1540                          CUSTOM_NONSTANDARD_SCHEME)                            \
1541   CORS_TEST_REDIRECT_GET(name##ServerToCustomUnregisteredScheme, mode, SERVER, \
1542                          CUSTOM_UNREGISTERED_SCHEME)                           \
1543   CORS_TEST_REDIRECT_GET(name##HttpSchemeToServer, mode, HTTP_SCHEME, SERVER)  \
1544   CORS_TEST_REDIRECT_GET(name##HttpSchemeToHttpScheme, mode, HTTP_SCHEME,      \
1545                          HTTP_SCHEME)                                          \
1546   CORS_TEST_REDIRECT_GET(name##HttpSchemeToCustomStandardScheme, mode,         \
1547                          HTTP_SCHEME, CUSTOM_STANDARD_SCHEME)                  \
1548   CORS_TEST_REDIRECT_GET(name##HttpSchemeToCustomNonStandardScheme, mode,      \
1549                          HTTP_SCHEME, CUSTOM_NONSTANDARD_SCHEME)               \
1550   CORS_TEST_REDIRECT_GET(name##HttpSchemeToCustomUnregisteredScheme, mode,     \
1551                          HTTP_SCHEME, CUSTOM_UNREGISTERED_SCHEME)              \
1552   CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToServer, mode,             \
1553                          CUSTOM_STANDARD_SCHEME, SERVER)                       \
1554   CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToHttpScheme, mode,         \
1555                          CUSTOM_STANDARD_SCHEME, HTTP_SCHEME)                  \
1556   CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToCustomStandardScheme,     \
1557                          mode, CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME) \
1558   CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToCustomNonStandardScheme,  \
1559                          mode, CUSTOM_STANDARD_SCHEME,                         \
1560                          CUSTOM_NONSTANDARD_SCHEME)                            \
1561   CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToCustomUnregisteredScheme, \
1562                          mode, CUSTOM_STANDARD_SCHEME,                         \
1563                          CUSTOM_UNREGISTERED_SCHEME)                           \
1564   CORS_TEST_REDIRECT_GET(name##CustomNonStandardSchemeToServer, mode,          \
1565                          CUSTOM_NONSTANDARD_SCHEME, SERVER)                    \
1566   CORS_TEST_REDIRECT_GET(name##CustomNonStandardSchemeToHttpScheme, mode,      \
1567                          CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME)               \
1568   CORS_TEST_REDIRECT_GET(name##CustomNonStandardSchemeToCustomStandardScheme,  \
1569                          mode, CUSTOM_NONSTANDARD_SCHEME,                      \
1570                          CUSTOM_STANDARD_SCHEME)                               \
1571   CORS_TEST_REDIRECT_GET(                                                      \
1572       name##CustomNonStandardSchemeToCustomNonStandardScheme, mode,            \
1573       CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME)                    \
1574   CORS_TEST_REDIRECT_GET(                                                      \
1575       name##CustomNonStandardSchemeToCustomUnregisteredScheme, mode,           \
1576       CUSTOM_NONSTANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME)                   \
1577   CORS_TEST_REDIRECT_GET(name##CustomUnregisteredSchemeToServer, mode,         \
1578                          CUSTOM_UNREGISTERED_SCHEME, SERVER)                   \
1579   CORS_TEST_REDIRECT_GET(name##CustomUnregisteredSchemeToHttpScheme, mode,     \
1580                          CUSTOM_UNREGISTERED_SCHEME, HTTP_SCHEME)              \
1581   CORS_TEST_REDIRECT_GET(name##CustomUnregisteredSchemeToCustomStandardScheme, \
1582                          mode, CUSTOM_UNREGISTERED_SCHEME,                     \
1583                          CUSTOM_STANDARD_SCHEME)                               \
1584   CORS_TEST_REDIRECT_GET(                                                      \
1585       name##CustomUnregisteredSchemeToCustomNonStandardScheme, mode,           \
1586       CUSTOM_UNREGISTERED_SCHEME, CUSTOM_NONSTANDARD_SCHEME)                   \
1587   CORS_TEST_REDIRECT_GET(                                                      \
1588       name##CustomUnregisteredSchemeToCustomUnregisteredScheme, mode,          \
1589       CUSTOM_UNREGISTERED_SCHEME, CUSTOM_UNREGISTERED_SCHEME)
1590 
1591 // Redirect GET requests.
1592 CORS_TEST_REDIRECT_GET_ALL(302, MODE_302)
1593 CORS_TEST_REDIRECT_GET_ALL(307, MODE_307)
1594 
1595 namespace {
1596 
1597 struct PostResource : CookieResource {
PostResource__anona54282bb0511::PostResource1598   PostResource() {}
1599 
1600   bool expect_downgrade_to_get = false;
1601   bool was_redirected = false;
1602 
1603   std::string main_origin;
1604   bool is_cross_origin;
1605 
InitOrigin__anona54282bb0511::PostResource1606   void InitOrigin(HandlerType main_handler) {
1607     // Origin is always "null" for non-HTTP(S) schemes.
1608     // This should only be "null" for non-standard schemes, but Blink is likely
1609     // using SchemeIsHTTPOrHTTPS() when submitting the form request.
1610     main_origin = IsNonStandardType(main_handler) ||
1611                           main_handler == HandlerType::CUSTOM_STANDARD_SCHEME
1612                       ? "null"
1613                       : GetOrigin(main_handler);
1614 
1615     // True if the request is considered cross-origin. Any requests between
1616     // non-standard schemes are considered cross-origin (due to the "null"
1617     // origin).
1618     is_cross_origin =
1619         main_handler != handler ||
1620         (IsNonStandardType(main_handler) && handler == main_handler);
1621   }
1622 
VerifyRequest__anona54282bb0511::PostResource1623   bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
1624     if (!CookieResource::VerifyRequest(request))
1625       return false;
1626 
1627     // The "Origin" header should be present if the request is POST, and was not
1628     // redirected cross-origin.
1629     std::string expected_origin;
1630     if (!expect_downgrade_to_get) {
1631       if (was_redirected && is_cross_origin) {
1632         // Always "null" for cross-origin redirects.
1633         expected_origin = "null";
1634       } else {
1635         expected_origin = main_origin;
1636       }
1637     }
1638 
1639     const std::string& origin = request->GetHeaderByName("Origin");
1640     EXPECT_STREQ(expected_origin.c_str(), origin.c_str()) << GetPathURL();
1641     if (expected_origin != origin)
1642       return false;
1643 
1644     const std::string& req_method = request->GetMethod();
1645     const bool has_post_data = request->GetPostData() != nullptr;
1646     if (expect_downgrade_to_get) {
1647       EXPECT_FALSE(has_post_data) << GetPathURL();
1648       EXPECT_STREQ("GET", req_method.c_str()) << GetPathURL();
1649       return !has_post_data && req_method == "GET";
1650     } else {
1651       EXPECT_TRUE(has_post_data) << GetPathURL();
1652       EXPECT_STREQ("POST", req_method.c_str()) << GetPathURL();
1653       return has_post_data && req_method == "POST";
1654     }
1655   }
1656 };
1657 
GetPostFormHtml(const std::string & submit_url)1658 std::string GetPostFormHtml(const std::string& submit_url) {
1659   return "<html><body>"
1660          "<form id=\"f\" action=\"" +
1661          submit_url +
1662          "\" method=\"post\">"
1663          "<input type=\"hidden\" name=\"n\" value=\"v\"></form>"
1664          "<script>document.getElementById('f').submit();</script>"
1665          "</body></html>";
1666 }
1667 
1668 // Test redirect requests.
SetupRedirectPostRequest(RedirectMode mode,CookieTestSetup * setup,const std::string & test_name,HandlerType main_handler,CookieResource * main_resource,PostResource * submit_resource,HandlerType redirect_handler,PostResource * redirect_resource)1669 void SetupRedirectPostRequest(RedirectMode mode,
1670                               CookieTestSetup* setup,
1671                               const std::string& test_name,
1672                               HandlerType main_handler,
1673                               CookieResource* main_resource,
1674                               PostResource* submit_resource,
1675                               HandlerType redirect_handler,
1676                               PostResource* redirect_resource) {
1677   const std::string& base_path = "/" + test_name;
1678 
1679   // Expect a single redirect request that sends SuccessMsg.
1680   redirect_resource->Init(redirect_handler, base_path + ".redirect.html",
1681                           kMimeTypeHtml, GetDefaultSuccessMsgHtml());
1682   redirect_resource->InitOrigin(main_handler);
1683   redirect_resource->expected_success_query_ct = 1;
1684 
1685   // 302 redirects will downgrade POST requests to GET.
1686   redirect_resource->expect_downgrade_to_get = mode == RedirectMode::MODE_302;
1687   redirect_resource->was_redirected = true;
1688 
1689   // Expect a single submit request that redirects the response.
1690   const std::string& redirect_url = redirect_resource->GetPathURL();
1691   submit_resource->Init(main_handler, base_path + ".submit.html", kMimeTypeHtml,
1692                         std::string());
1693   submit_resource->InitOrigin(main_handler);
1694   SetupRedirectResponse(mode, redirect_url, submit_resource->response);
1695 
1696   // Expect a single main request that submits the form.
1697   const std::string& submit_url = submit_resource->GetPathURL();
1698   main_resource->Init(main_handler, base_path, kMimeTypeHtml,
1699                       GetPostFormHtml(submit_url));
1700 
1701   SetupCookieExpectations(setup, main_resource, submit_resource);
1702   SetupCookieExpectations(setup, main_resource, redirect_resource);
1703 
1704   setup->AddResource(main_resource);
1705   setup->AddResource(submit_resource);
1706   setup->AddResource(redirect_resource);
1707 }
1708 
1709 }  // namespace
1710 
1711 // Test redirect GET requests with different origin combinations.
1712 #define CORS_TEST_REDIRECT_POST(test_name, mode, handler_main,          \
1713                                 handler_redirect)                       \
1714   TEST(CorsTest, RedirectPost##test_name) {                             \
1715     CookieTestSetup setup;                                              \
1716     CookieResource resource_main;                                       \
1717     PostResource resource_submit, resource_redirect;                    \
1718     SetupRedirectPostRequest(                                           \
1719         RedirectMode::mode, &setup, "CorsTest.RedirectPost" #test_name, \
1720         HandlerType::handler_main, &resource_main, &resource_submit,    \
1721         HandlerType::handler_redirect, &resource_redirect);             \
1722     CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);   \
1723     handler->ExecuteTest();                                             \
1724     ReleaseAndWaitForDestructor(handler);                               \
1725   }
1726 
1727 // Test all redirect GET combinations (same and cross-origin).
1728 #define CORS_TEST_REDIRECT_POST_ALL(name, mode)                                \
1729   CORS_TEST_REDIRECT_POST(name##ServerToServer, mode, SERVER, SERVER)          \
1730   CORS_TEST_REDIRECT_POST(name##ServerToHttpScheme, mode, SERVER, HTTP_SCHEME) \
1731   CORS_TEST_REDIRECT_POST(name##ServerToCustomStandardScheme, mode, SERVER,    \
1732                           CUSTOM_STANDARD_SCHEME)                              \
1733   CORS_TEST_REDIRECT_POST(name##ServerToCustomNonStandardScheme, mode, SERVER, \
1734                           CUSTOM_NONSTANDARD_SCHEME)                           \
1735   CORS_TEST_REDIRECT_POST(name##ServerToCustomUnregisteredScheme, mode,        \
1736                           SERVER, CUSTOM_UNREGISTERED_SCHEME)                  \
1737   CORS_TEST_REDIRECT_POST(name##HttpSchemeToServer, mode, HTTP_SCHEME, SERVER) \
1738   CORS_TEST_REDIRECT_POST(name##HttpSchemeToHttpScheme, mode, HTTP_SCHEME,     \
1739                           HTTP_SCHEME)                                         \
1740   CORS_TEST_REDIRECT_POST(name##HttpSchemeToCustomStandardScheme, mode,        \
1741                           HTTP_SCHEME, CUSTOM_STANDARD_SCHEME)                 \
1742   CORS_TEST_REDIRECT_POST(name##HttpSchemeToCustomNonStandardScheme, mode,     \
1743                           HTTP_SCHEME, CUSTOM_NONSTANDARD_SCHEME)              \
1744   CORS_TEST_REDIRECT_POST(name##HttpSchemeToCustomUnregisteredScheme, mode,    \
1745                           HTTP_SCHEME, CUSTOM_UNREGISTERED_SCHEME)             \
1746   CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToServer, mode,            \
1747                           CUSTOM_STANDARD_SCHEME, SERVER)                      \
1748   CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToHttpScheme, mode,        \
1749                           CUSTOM_STANDARD_SCHEME, HTTP_SCHEME)                 \
1750   CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToCustomStandardScheme,    \
1751                           mode, CUSTOM_STANDARD_SCHEME,                        \
1752                           CUSTOM_STANDARD_SCHEME)                              \
1753   CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToCustomNonStandardScheme, \
1754                           mode, CUSTOM_STANDARD_SCHEME,                        \
1755                           CUSTOM_NONSTANDARD_SCHEME)                           \
1756   CORS_TEST_REDIRECT_POST(                                                     \
1757       name##CustomStandardSchemeToCustomUnregisteredScheme, mode,              \
1758       CUSTOM_STANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME)                      \
1759   CORS_TEST_REDIRECT_POST(name##CustomNonStandardSchemeToServer, mode,         \
1760                           CUSTOM_NONSTANDARD_SCHEME, SERVER)                   \
1761   CORS_TEST_REDIRECT_POST(name##CustomNonStandardSchemeToHttpScheme, mode,     \
1762                           CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME)              \
1763   CORS_TEST_REDIRECT_POST(name##CustomNonStandardSchemeToCustomStandardScheme, \
1764                           mode, CUSTOM_NONSTANDARD_SCHEME,                     \
1765                           CUSTOM_STANDARD_SCHEME)                              \
1766   CORS_TEST_REDIRECT_POST(                                                     \
1767       name##CustomNonStandardSchemeToCustomNonStandardScheme, mode,            \
1768       CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME)                    \
1769   CORS_TEST_REDIRECT_POST(                                                     \
1770       name##CustomNonStandardSchemeToCustomUnregisteredScheme, mode,           \
1771       CUSTOM_NONSTANDARD_SCHEME, CUSTOM_UNREGISTERED_SCHEME)                   \
1772   CORS_TEST_REDIRECT_POST(name##CustomUnregisteredSchemeToServer, mode,        \
1773                           CUSTOM_UNREGISTERED_SCHEME, SERVER)                  \
1774   CORS_TEST_REDIRECT_POST(name##CustomUnregisteredSchemeToHttpScheme, mode,    \
1775                           CUSTOM_UNREGISTERED_SCHEME, HTTP_SCHEME)             \
1776   CORS_TEST_REDIRECT_POST(                                                     \
1777       name##CustomUnregisteredSchemeToCustomStandardScheme, mode,              \
1778       CUSTOM_UNREGISTERED_SCHEME, CUSTOM_STANDARD_SCHEME)                      \
1779   CORS_TEST_REDIRECT_POST(                                                     \
1780       name##CustomUnregisteredSchemeToCustomNonStandardScheme, mode,           \
1781       CUSTOM_UNREGISTERED_SCHEME, CUSTOM_NONSTANDARD_SCHEME)                   \
1782   CORS_TEST_REDIRECT_POST(                                                     \
1783       name##CustomUnregisteredSchemeToCustomUnregisteredScheme, mode,          \
1784       CUSTOM_UNREGISTERED_SCHEME, CUSTOM_UNREGISTERED_SCHEME)
1785 
1786 // Redirect GET requests.
1787 CORS_TEST_REDIRECT_POST_ALL(302, MODE_302)
1788 CORS_TEST_REDIRECT_POST_ALL(307, MODE_307)
1789 
1790 // Entry point for creating plugin browser test objects.
1791 // Called from client_app_delegates.cc.
CreateCorsBrowserTests(client::ClientAppBrowser::DelegateSet & delegates)1792 void CreateCorsBrowserTests(client::ClientAppBrowser::DelegateSet& delegates) {
1793   delegates.insert(new CorsBrowserTest);
1794 }
1795