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