diff --git a/include/capi/cef_scheme_capi.h b/include/capi/cef_scheme_capi.h index 202430c1d..69c884e76 100644 --- a/include/capi/cef_scheme_capi.h +++ b/include/capi/cef_scheme_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=f83c72ec43f10f19eba82d9d3c284cc436cdbd23$ +// $hash=ba3b03834e2db16056cd2be805ec9f116a92e14e$ // #ifndef CEF_INCLUDE_CAPI_CEF_SCHEME_CAPI_H_ @@ -117,6 +117,9 @@ typedef struct _cef_scheme_registrar_t { // Policy (CSP) checks. This value should be false (0) in most cases where // |is_standard| is true (1). // + // If |is_fetch_enabled| is true (1) the scheme can perform Fetch API + // requests. + // // This function may be called on any thread. It should only be called once // per unique |scheme_name| value. If |scheme_name| is already registered or // if an error occurs this function will return false (0). @@ -128,7 +131,8 @@ typedef struct _cef_scheme_registrar_t { int is_display_isolated, int is_secure, int is_cors_enabled, - int is_csp_bypassing); + int is_csp_bypassing, + int is_fetch_enabled); } cef_scheme_registrar_t; /// diff --git a/include/cef_scheme.h b/include/cef_scheme.h index d1b960825..fa83ec47f 100644 --- a/include/cef_scheme.h +++ b/include/cef_scheme.h @@ -137,6 +137,8 @@ class CefSchemeRegistrar : public CefBaseScoped { // (CSP) checks. This value should be false in most cases where |is_standard| // is true. // + // If |is_fetch_enabled| is true the scheme can perform Fetch API requests. + // // This function may be called on any thread. It should only be called once // per unique |scheme_name| value. If |scheme_name| is already registered or // if an error occurs this method will return false. @@ -148,7 +150,8 @@ class CefSchemeRegistrar : public CefBaseScoped { bool is_display_isolated, bool is_secure, bool is_cors_enabled, - bool is_csp_bypassing) = 0; + bool is_csp_bypassing, + bool is_fetch_enabled) = 0; }; /// diff --git a/libcef/common/content_client.h b/libcef/common/content_client.h index 2aefb4a96..56c3161c4 100644 --- a/libcef/common/content_client.h +++ b/libcef/common/content_client.h @@ -81,6 +81,9 @@ class CefContentClient : public content::ContentClient, // A scheme that can bypass Content-Security-Policy (CSP) checks. This value // should be false in most cases where |is_standard| is true. bool is_csp_bypassing; + + // A scheme that can perform fetch request. + bool is_fetch_enabled; }; typedef std::list SchemeInfoList; diff --git a/libcef/common/scheme_registrar_impl.cc b/libcef/common/scheme_registrar_impl.cc index ef7c9d18f..a6fda49b9 100644 --- a/libcef/common/scheme_registrar_impl.cc +++ b/libcef/common/scheme_registrar_impl.cc @@ -31,7 +31,8 @@ bool CefSchemeRegistrarImpl::AddCustomScheme(const CefString& scheme_name, bool is_display_isolated, bool is_secure, bool is_cors_enabled, - bool is_csp_bypassing) { + bool is_csp_bypassing, + bool is_fetch_enabled) { const std::string& scheme = base::ToLowerASCII(scheme_name.ToString()); if (scheme::IsInternalHandledScheme(scheme) || registered_schemes_.find(scheme) != registered_schemes_.end()) { @@ -54,8 +55,8 @@ bool CefSchemeRegistrarImpl::AddCustomScheme(const CefString& scheme_name, schemes_.csp_bypassing_schemes.push_back(scheme); CefContentClient::SchemeInfo scheme_info = { - scheme, is_standard, is_local, is_display_isolated, - is_secure, is_cors_enabled, is_csp_bypassing}; + scheme, is_standard, is_local, is_display_isolated, + is_secure, is_cors_enabled, is_csp_bypassing, is_fetch_enabled}; CefContentClient::Get()->AddCustomScheme(scheme_info); return true; diff --git a/libcef/common/scheme_registrar_impl.h b/libcef/common/scheme_registrar_impl.h index a78d4fb49..66e295de5 100644 --- a/libcef/common/scheme_registrar_impl.h +++ b/libcef/common/scheme_registrar_impl.h @@ -24,7 +24,8 @@ class CefSchemeRegistrarImpl : public CefSchemeRegistrar { bool is_display_isolated, bool is_secure, bool is_cors_enabled, - bool is_csp_bypassing) override; + bool is_csp_bypassing, + bool is_fetch_enabled) override; void GetSchemes(content::ContentClient::Schemes* schemes); diff --git a/libcef/renderer/blink_glue.cc b/libcef/renderer/blink_glue.cc index e1a550751..e0a6ea3fa 100644 --- a/libcef/renderer/blink_glue.cc +++ b/libcef/renderer/blink_glue.cc @@ -216,6 +216,10 @@ void RegisterURLSchemeAsSecure(const blink::WebString& scheme) { blink::SchemeRegistry::RegisterURLSchemeAsSecure(scheme); } +void RegisterURLSchemeAsSupportingFetchAPI(const blink::WebString& scheme) { + blink::SchemeRegistry::RegisterURLSchemeAsSupportingFetchAPI(scheme); +} + struct CefScriptForbiddenScope::Impl { blink::ScriptForbiddenScope scope_; }; diff --git a/libcef/renderer/blink_glue.h b/libcef/renderer/blink_glue.h index f67a4f7f2..80516f11c 100644 --- a/libcef/renderer/blink_glue.h +++ b/libcef/renderer/blink_glue.h @@ -69,6 +69,9 @@ BLINK_EXPORT bool IsScriptForbidden(); BLINK_EXPORT void RegisterURLSchemeAsLocal(const blink::WebString& scheme); BLINK_EXPORT void RegisterURLSchemeAsSecure(const blink::WebString& scheme); +BLINK_EXPORT void RegisterURLSchemeAsSupportingFetchAPI( + const blink::WebString& scheme); + // Wrapper for blink::ScriptForbiddenScope. class BLINK_EXPORT CefScriptForbiddenScope final { public: diff --git a/libcef/renderer/content_renderer_client.cc b/libcef/renderer/content_renderer_client.cc index df2579d15..f0f818a97 100644 --- a/libcef/renderer/content_renderer_client.cc +++ b/libcef/renderer/content_renderer_client.cc @@ -269,6 +269,8 @@ void CefContentRendererClient::WebKitInitialized() { blink::WebSecurityPolicy::RegisterURLSchemeAsDisplayIsolated(scheme); if (info.is_secure) blink_glue::RegisterURLSchemeAsSecure(scheme); + if (info.is_fetch_enabled) + blink_glue::RegisterURLSchemeAsSupportingFetchAPI(scheme); } } diff --git a/libcef_dll/cpptoc/scheme_registrar_cpptoc.cc b/libcef_dll/cpptoc/scheme_registrar_cpptoc.cc index 23cd4b2f2..715469d41 100644 --- a/libcef_dll/cpptoc/scheme_registrar_cpptoc.cc +++ b/libcef_dll/cpptoc/scheme_registrar_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=9371355cdf8f8baca16fdb8558074b4198561b0c$ +// $hash=2f79804767b1c08a00f87b918190f33a1734b346$ // #include "libcef_dll/cpptoc/scheme_registrar_cpptoc.h" @@ -26,7 +26,8 @@ scheme_registrar_add_custom_scheme(struct _cef_scheme_registrar_t* self, int is_display_isolated, int is_secure, int is_cors_enabled, - int is_csp_bypassing) { + int is_csp_bypassing, + int is_fetch_enabled) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING DCHECK(self); @@ -42,7 +43,7 @@ scheme_registrar_add_custom_scheme(struct _cef_scheme_registrar_t* self, CefString(scheme_name), is_standard ? true : false, is_local ? true : false, is_display_isolated ? true : false, is_secure ? true : false, is_cors_enabled ? true : false, - is_csp_bypassing ? true : false); + is_csp_bypassing ? true : false, is_fetch_enabled ? true : false); // Return type: bool return _retval; diff --git a/libcef_dll/ctocpp/scheme_registrar_ctocpp.cc b/libcef_dll/ctocpp/scheme_registrar_ctocpp.cc index f013d8fdb..02aaa898f 100644 --- a/libcef_dll/ctocpp/scheme_registrar_ctocpp.cc +++ b/libcef_dll/ctocpp/scheme_registrar_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=e7d830a10e5beeb36640297489468dae63df32e3$ +// $hash=b35a35cf7c729b333843001e424e13d9e91dc41c$ // #include "libcef_dll/ctocpp/scheme_registrar_ctocpp.h" @@ -23,7 +23,8 @@ bool CefSchemeRegistrarCToCpp::AddCustomScheme(const CefString& scheme_name, bool is_display_isolated, bool is_secure, bool is_cors_enabled, - bool is_csp_bypassing) { + bool is_csp_bypassing, + bool is_fetch_enabled) { cef_scheme_registrar_t* _struct = GetStruct(); if (CEF_MEMBER_MISSING(_struct, add_custom_scheme)) return false; @@ -38,7 +39,8 @@ bool CefSchemeRegistrarCToCpp::AddCustomScheme(const CefString& scheme_name, // Execute int _retval = _struct->add_custom_scheme( _struct, scheme_name.GetStruct(), is_standard, is_local, - is_display_isolated, is_secure, is_cors_enabled, is_csp_bypassing); + is_display_isolated, is_secure, is_cors_enabled, is_csp_bypassing, + is_fetch_enabled); // Return type: bool return _retval ? true : false; diff --git a/libcef_dll/ctocpp/scheme_registrar_ctocpp.h b/libcef_dll/ctocpp/scheme_registrar_ctocpp.h index 55d15d41e..255cc4273 100644 --- a/libcef_dll/ctocpp/scheme_registrar_ctocpp.h +++ b/libcef_dll/ctocpp/scheme_registrar_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=5ce8d9f062d527c665f33c4508da01da9dbf277d$ +// $hash=78be224ecf2427abc51e7006d031861baee0435d$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_SCHEME_REGISTRAR_CTOCPP_H_ @@ -40,7 +40,8 @@ class CefSchemeRegistrarCToCpp bool is_display_isolated, bool is_secure, bool is_cors_enabled, - bool is_csp_bypassing) OVERRIDE; + bool is_csp_bypassing, + bool is_fetch_enabled) OVERRIDE; }; #endif // CEF_LIBCEF_DLL_CTOCPP_SCHEME_REGISTRAR_CTOCPP_H_ diff --git a/tests/cefclient/common/scheme_test_common.cc b/tests/cefclient/common/scheme_test_common.cc index 2c0410e65..6d0786bb1 100644 --- a/tests/cefclient/common/scheme_test_common.cc +++ b/tests/cefclient/common/scheme_test_common.cc @@ -11,7 +11,8 @@ namespace scheme_test { void RegisterCustomSchemes(CefRawPtr registrar, std::vector& cookiable_schemes) { - registrar->AddCustomScheme("client", true, false, false, false, true, false); + registrar->AddCustomScheme("client", true, false, false, false, true, false, + false); } } // namespace scheme_test diff --git a/tests/ceftests/cookie_unittest.cc b/tests/ceftests/cookie_unittest.cc index 62622bdb8..f5af401bf 100644 --- a/tests/ceftests/cookie_unittest.cc +++ b/tests/ceftests/cookie_unittest.cc @@ -1333,5 +1333,6 @@ TEST(CookieTest, GetCookieManagerCustom) { void RegisterCookieCustomSchemes(CefRawPtr registrar, std::vector& cookiable_schemes) { // Used by GetCookieManagerCustom test. - registrar->AddCustomScheme("ccustom", true, false, false, false, true, false); + registrar->AddCustomScheme("ccustom", true, false, false, false, true, false, + false); } diff --git a/tests/ceftests/request_unittest.cc b/tests/ceftests/request_unittest.cc index d2288a4ef..56cafc7af 100644 --- a/tests/ceftests/request_unittest.cc +++ b/tests/ceftests/request_unittest.cc @@ -477,6 +477,8 @@ class TypeTestHandler : public TestHandler { "font/ttf"); AddResource(std::string(kTypeTestOrigin) + "xhr.html", "XHR", "text/html"); + AddResource(std::string(kTypeTestOrigin) + "fetch.html", + "Fetch", "text/html"); CreateBrowser(std::string(kTypeTestOrigin) + "main.html"); diff --git a/tests/ceftests/scheme_handler_unittest.cc b/tests/ceftests/scheme_handler_unittest.cc index 7d4c560c1..148723c1d 100644 --- a/tests/ceftests/scheme_handler_unittest.cc +++ b/tests/ceftests/scheme_handler_unittest.cc @@ -449,6 +449,66 @@ void SetUpXHR(const XHRTestSettings& settings) { g_TestResults.exit_url = "http://tests/exit"; } +struct FetchTestSettings { + FetchTestSettings() {} + + std::string url; + std::string sub_url; + std::string sub_allow_origin; + std::string sub_redirect_url; +}; + +void SetUpFetch(const FetchTestSettings& settings) { + g_TestResults.sub_url = settings.sub_url; + g_TestResults.sub_html = "SUCCESS"; + g_TestResults.sub_status_code = 200; + g_TestResults.sub_allow_origin = settings.sub_allow_origin; + g_TestResults.sub_redirect_url = settings.sub_redirect_url; + + std::string request_url; + if (!settings.sub_redirect_url.empty()) + request_url = settings.sub_redirect_url; + else + request_url = settings.sub_url; + + g_TestResults.url = settings.url; + std::stringstream ss; + ss << "" + "" + "" + "Running execFetchHttpRequest..." + ""; + g_TestResults.html = ss.str(); + g_TestResults.status_code = 200; + + g_TestResults.exit_url = "http://tests/exit"; +} // namespace + void SetUpXSS(const std::string& url, const std::string& sub_url, const std::string& domain = std::string()) { @@ -898,6 +958,78 @@ TEST(SchemeHandlerTest, CustomNonStandardXHRSameOriginAsync) { ClearTestSchemes(); } +// Test that a non fetch enabled custom standard scheme can't generate same +// origin Fetch requests. +TEST(SchemeHandlerTest, CustomStandardFetchSameOrigin) { + RegisterTestScheme("customstd", "test"); + + FetchTestSettings settings; + settings.url = "customstd://test/run.html"; + settings.sub_url = "customstd://test/fetch.html"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_FALSE(g_TestResults.got_sub_request); + EXPECT_FALSE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that a fetch enabled custom standard scheme can generate same origin +// Fetch requests. +TEST(SchemeHandlerTest, FetchCustomStandardFetchSameOrigin) { + RegisterTestScheme("customstdfetch", "test"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test/run.html"; + settings.sub_url = "customstdfetch://test/fetch.html"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + +// Test that custom nonstandard schemes are treated as unique origins that +// cannot generate Fetch requests. +TEST(SchemeHandlerTest, CustomNonStandardFetchSameOrigin) { + RegisterTestScheme("customnonstd", std::string()); + + FetchTestSettings settings; + settings.url = "customnonstd:some%20value"; + settings.sub_url = "customnonstd:xhr%20value"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_FALSE(g_TestResults.got_sub_request); + EXPECT_FALSE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + // Test that a custom standard scheme can generate same origin XSS requests. TEST(SchemeHandlerTest, CustomStandardXSSSameOrigin) { RegisterTestScheme("customstd", "test"); @@ -988,6 +1120,31 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginAsync) { ClearTestSchemes(); } +// Test that a custom standard scheme cannot generate cross-domain Fetch +// requests by default. Behavior should be the same as with HTTP. +TEST(SchemeHandlerTest, CustomStandardFetchDifferentOrigin) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "test2"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://test2/fetch.html"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + // Test that a custom standard scheme cannot generate cross-domain XSS requests // by default. TEST(SchemeHandlerTest, CustomStandardXSSDifferentOrigin) { @@ -1060,6 +1217,31 @@ TEST(SchemeHandlerTest, HttpXHRDifferentOriginAsync) { ClearTestSchemes(); } +// Test that an HTTP scheme cannot generate cross-domain Fetch requests by +// default. +TEST(SchemeHandlerTest, HttpFetchDifferentOriginAsync) { + RegisterTestScheme("http", "test1"); + RegisterTestScheme("http", "test2"); + + FetchTestSettings settings; + settings.url = "http://test1/run.html"; + settings.sub_url = "http://test2/fetch.html"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + // Test that an HTTP scheme cannot generate cross-domain XSS requests by // default. TEST(SchemeHandlerTest, HttpXSSDifferentOrigin) { @@ -1136,6 +1318,33 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithHeaderAsync) { ClearTestSchemes(); } +// Test that a custom standard scheme can generate cross-domain Fetch requests +// when setting the Access-Control-Allow-Origin header. Should behave the same +// as HTTP. +TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithHeader) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "test2"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://test2/fetch.html"; + settings.sub_allow_origin = "customstdfetch://test1"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + // Test that a custom standard scheme can generate cross-domain XHR requests // when using the cross-origin whitelist. TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistSync1) { @@ -1327,6 +1536,100 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginWithWhitelistAsync3) { ClearTestSchemes(); } +// Test that a custom standard scheme can generate cross-domain Fetch requests +// when using the cross-origin whitelist. +TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithWhitelist1) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "test2"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://test2/fetch.html"; + SetUpFetch(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry( + "customstdfetch://test1", "customstdfetch", "test2", false)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches any domain. +TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithWhitelist2) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "test2"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://test2/fetch.html"; + SetUpFetch(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry( + "customstdfetch://test1", "customstdfetch", CefString(), true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches sub-domains. +TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginWithWhitelist3) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "a.test2.foo"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://a.test2.foo/fetch.html"; + SetUpFetch(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry( + "customstdfetch://test1", "customstdfetch", "test2.foo", true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + // Test that an HTTP scheme can generate cross-domain XHR requests when setting // the Access-Control-Allow-Origin header. TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeaderSync) { @@ -1380,6 +1683,32 @@ TEST(SchemeHandlerTest, HttpXHRDifferentOriginWithHeaderAsync) { ClearTestSchemes(); } +// Test that an HTTP scheme can generate cross-domain XHR requests when setting +// the Access-Control-Allow-Origin header. +TEST(SchemeHandlerTest, HttpFetchDifferentOriginWithHeader) { + RegisterTestScheme("http", "test1"); + RegisterTestScheme("http", "test2"); + + FetchTestSettings settings; + settings.url = "http://test1/run.html"; + settings.sub_url = "http://test2/fetch.html"; + settings.sub_allow_origin = "http://test1"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + // Test that a custom standard scheme can generate cross-domain XSS requests // when using document.domain. TEST(SchemeHandlerTest, CustomStandardXSSDifferentOriginWithDomain) { @@ -1479,6 +1808,33 @@ TEST(SchemeHandlerTest, CustomStandardXHRDifferentOriginRedirectAsync) { ClearTestSchemes(); } +// Test that a custom standard scheme cannot generate cross-domain Fetch +// requests that perform redirects. +TEST(SchemeHandlerTest, CustomStandardFetchDifferentOriginRedirect) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "test2"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://test2/fetch.html"; + settings.sub_redirect_url = "customstdfetch://test1/fetch.html"; + SetUpFetch(settings); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_FALSE(g_TestResults.got_sub_success); + + ClearTestSchemes(); +} + // Test that a custom standard scheme can generate cross-domain XHR requests // that perform redirects when using the cross-origin whitelist. TEST(SchemeHandlerTest, @@ -1623,6 +1979,112 @@ TEST(SchemeHandlerTest, ClearTestSchemes(); } +// Test that a custom standard scheme can generate cross-domain Fetch requests +// that perform redirects when using the cross-origin whitelist. This is +// because we add an "Access-Control-Allow-Origin" header internally in +// CefResourceDispatcherHostDelegate::OnRequestRedirected() for the redirect +// request. +TEST(SchemeHandlerTest, + CustomStandardFetchDifferentOriginRedirectWithWhitelist1) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "test2"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://test2/fetch.html"; + settings.sub_redirect_url = "customstdfetch://test1/fetch.html"; + SetUpFetch(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry( + "customstdfetch://test1", "customstdfetch", "test2", false)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches any domain. +TEST(SchemeHandlerTest, + CustomStandardFetchDifferentOriginRedirectWithWhitelist2) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "test2"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://test2/fetch.html"; + settings.sub_redirect_url = "customstdfetch://test1/fetch.html"; + SetUpFetch(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry( + "customstdfetch://test1", "customstdfetch", CefString(), true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + +// Same as above but origin whitelist matches sub-domains. +TEST(SchemeHandlerTest, + CustomStandardFetchDifferentOriginRedirectWithWhitelist3) { + RegisterTestScheme("customstdfetch", "test1"); + RegisterTestScheme("customstdfetch", "a.test2.foo"); + + FetchTestSettings settings; + settings.url = "customstdfetch://test1/run.html"; + settings.sub_url = "customstdfetch://a.test2.foo/fetch.html"; + settings.sub_redirect_url = "customstdfetch://test1/fetch.html"; + SetUpFetch(settings); + + EXPECT_TRUE(CefAddCrossOriginWhitelistEntry( + "customstdfetch://test1", "customstdfetch", "test2.foo", true)); + WaitForUIThread(); + + CefRefPtr handler = new TestSchemeHandler(&g_TestResults); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + EXPECT_TRUE(g_TestResults.got_request); + EXPECT_TRUE(g_TestResults.got_read); + EXPECT_TRUE(g_TestResults.got_output); + EXPECT_TRUE(g_TestResults.got_sub_redirect); + EXPECT_TRUE(g_TestResults.got_sub_request); + EXPECT_TRUE(g_TestResults.got_sub_read); + EXPECT_TRUE(g_TestResults.got_sub_success); + + EXPECT_TRUE(CefClearCrossOriginWhitelist()); + WaitForUIThread(); + + ClearTestSchemes(); +} + // Test per-browser setting of Accept-Language. TEST(SchemeHandlerTest, AcceptLanguage) { RegisterTestScheme("customstd", "test"); @@ -1653,8 +2115,12 @@ void RegisterSchemeHandlerCustomSchemes( std::vector& cookiable_schemes) { // Add a custom standard scheme. registrar->AddCustomScheme("customstd", true, false, false, false, true, - false); - // Ad a custom non-standard scheme. + false, false); + registrar->AddCustomScheme("customstdfetch", true, false, false, false, true, + false, true); + // Add a custom non-standard scheme. registrar->AddCustomScheme("customnonstd", false, false, false, false, false, - false); + false, false); + registrar->AddCustomScheme("customnonstdfetch", false, false, false, false, + false, false, true); } diff --git a/tests/ceftests/urlrequest_unittest.cc b/tests/ceftests/urlrequest_unittest.cc index c2a6ff94e..08e0add08 100644 --- a/tests/ceftests/urlrequest_unittest.cc +++ b/tests/ceftests/urlrequest_unittest.cc @@ -2246,7 +2246,8 @@ void RegisterURLRequestCustomSchemes( CefRawPtr registrar, std::vector& cookiable_schemes) { const std::string& scheme = GetRequestScheme(false); - registrar->AddCustomScheme(scheme, true, false, false, false, true, false); + registrar->AddCustomScheme(scheme, true, false, false, false, true, false, + false); cookiable_schemes.push_back(scheme); }