blob: e521cc21fd4615f5c52b96322429aab18c215417 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/hash/hash.h"
#include "base/memory/ref_counted_memory.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/public/browser/site_isolation_policy.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/web_ui_browsertest_util.h"
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
namespace content {
class WebUISecurityTest : public ContentBrowserTest {
public:
WebUISecurityTest() { WebUIControllerFactory::RegisterFactory(&factory_); }
~WebUISecurityTest() override {
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
}
TestWebUIControllerFactory* factory() { return &factory_; }
private:
TestWebUIControllerFactory factory_;
DISALLOW_COPY_AND_ASSIGN(WebUISecurityTest);
};
// Verify chrome-untrusted:// have no bindings.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, UntrustedNoBindings) {
auto* web_contents = shell()->web_contents();
AddUntrustedDataSource(web_contents->GetBrowserContext(), "test-host");
const GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(web_contents, untrusted_url));
EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
EXPECT_EQ(0, shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
}
// Loads a WebUI which does not have any bindings.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, NoBindings) {
GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=0"));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
EXPECT_EQ(0, shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
}
// Loads a WebUI which has WebUI bindings.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIBindings) {
GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=" +
base::NumberToString(BINDINGS_POLICY_WEB_UI)));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
}
// Loads a WebUI which has Mojo bindings.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, MojoBindings) {
GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=" +
base::NumberToString(BINDINGS_POLICY_MOJO_WEB_UI)));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
EXPECT_EQ(BINDINGS_POLICY_MOJO_WEB_UI,
shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
}
// Loads a WebUI which has both WebUI and Mojo bindings.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIAndMojoBindings) {
GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=" +
base::NumberToString(BINDINGS_POLICY_WEB_UI |
BINDINGS_POLICY_MOJO_WEB_UI)));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
EXPECT_EQ(BINDINGS_POLICY_WEB_UI | BINDINGS_POLICY_MOJO_WEB_UI,
shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
}
// Verify that reloading a WebUI document or navigating between documents on
// the same WebUI will result in using the same SiteInstance and will not
// create a new WebUI instance.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIReuse) {
GURL test_url(GetWebUIURL("web-ui/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
// Capture the SiteInstance and WebUI used in the first navigation to compare
// with the ones used after the reload.
scoped_refptr<SiteInstance> initial_site_instance =
root->current_frame_host()->GetSiteInstance();
WebUI* initial_web_ui = root->current_frame_host()->web_ui();
// Reload the document and check that SiteInstance and WebUI are reused.
TestFrameNavigationObserver observer(root);
shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(test_url, observer.last_committed_url());
EXPECT_EQ(initial_site_instance,
root->current_frame_host()->GetSiteInstance());
EXPECT_EQ(initial_web_ui, root->current_frame_host()->web_ui());
// Navigate to another document on the same WebUI and check that SiteInstance
// and WebUI are reused.
GURL next_url(GetWebUIURL("web-ui/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), next_url));
EXPECT_EQ(initial_site_instance,
root->current_frame_host()->GetSiteInstance());
EXPECT_EQ(initial_web_ui, root->current_frame_host()->web_ui());
}
// Verify that a WebUI can add a subframe for its own WebUI.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUISameSiteSubframe) {
GURL test_url(GetWebUIURL("web-ui/page_with_blank_iframe.html"));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
EXPECT_EQ(1U, root->child_count());
TestFrameNavigationObserver observer(root->child_at(0));
GURL subframe_url(GetWebUIURL("web-ui/title1.html?noxfo=true"));
NavigateFrameToURL(root->child_at(0), subframe_url);
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(subframe_url, observer.last_committed_url());
EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
root->child_at(0)->current_frame_host()->GetSiteInstance());
EXPECT_EQ(
GetWebUIURL("web-ui"),
root->child_at(0)->current_frame_host()->GetSiteInstance()->GetSiteURL());
// The subframe should have its own WebUI object different from the parent
// frame.
EXPECT_NE(nullptr, root->child_at(0)->current_frame_host()->web_ui());
EXPECT_NE(root->current_frame_host()->web_ui(),
root->child_at(0)->current_frame_host()->web_ui());
}
// Verify that a WebUI can add a subframe to another WebUI and they will be
// correctly isolated in separate SiteInstances and processes. The subframe
// also uses WebUI with bindings different than the parent to ensure this is
// successfully handled.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUICrossSiteSubframe) {
GURL main_frame_url(GetWebUIURL("web-ui/page_with_blank_iframe.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
EXPECT_EQ(1U, root->child_count());
FrameTreeNode* child = root->child_at(0);
EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
root->current_frame_host()->GetEnabledBindings());
EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
child->current_frame_host()->GetSiteInstance());
// Navigate the subframe using renderer-initiated navigation.
{
TestFrameNavigationObserver observer(child);
GURL child_frame_url(
GetWebUIURL("web-ui-subframe/title2.html?noxfo=true&bindings=" +
base::NumberToString(BINDINGS_POLICY_MOJO_WEB_UI)));
EXPECT_TRUE(ExecJs(shell(),
JsReplace("document.getElementById($1).src = $2;",
"test_iframe", child_frame_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(child_frame_url, observer.last_committed_url());
EXPECT_EQ(BINDINGS_POLICY_MOJO_WEB_UI,
child->current_frame_host()->GetEnabledBindings());
EXPECT_EQ(url::Origin::Create(child_frame_url),
child->current_frame_host()->GetLastCommittedOrigin());
}
EXPECT_EQ(GetWebUIURL("web-ui-subframe"),
child->current_frame_host()->GetSiteInstance()->GetSiteURL());
EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
child->current_frame_host()->GetSiteInstance());
EXPECT_NE(root->current_frame_host()->GetProcess(),
child->current_frame_host()->GetProcess());
EXPECT_NE(root->current_frame_host()->web_ui(),
child->current_frame_host()->web_ui());
EXPECT_NE(root->current_frame_host()->GetEnabledBindings(),
child->current_frame_host()->GetEnabledBindings());
// Navigate once more using renderer-initiated navigation.
{
TestFrameNavigationObserver observer(child);
GURL child_frame_url(
GetWebUIURL("web-ui-subframe/title3.html?noxfo=true&bindings=" +
base::NumberToString(BINDINGS_POLICY_MOJO_WEB_UI)));
EXPECT_TRUE(ExecJs(shell(),
JsReplace("document.getElementById($1).src = $2;",
"test_iframe", child_frame_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(child_frame_url, observer.last_committed_url());
EXPECT_EQ(BINDINGS_POLICY_MOJO_WEB_UI,
child->current_frame_host()->GetEnabledBindings());
EXPECT_EQ(url::Origin::Create(child_frame_url),
child->current_frame_host()->GetLastCommittedOrigin());
}
// Navigate the subframe using browser-initiated navigation.
{
TestFrameNavigationObserver observer(child);
GURL child_frame_url(
GetWebUIURL("web-ui-subframe/title1.html?noxfo=true&bindings=" +
base::NumberToString(BINDINGS_POLICY_MOJO_WEB_UI)));
NavigateFrameToURL(child, child_frame_url);
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(child_frame_url, observer.last_committed_url());
EXPECT_EQ(BINDINGS_POLICY_MOJO_WEB_UI,
child->current_frame_host()->GetEnabledBindings());
}
}
// Verify that SiteInstance and WebUI reuse happens in subframes as well.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIReuseInSubframe) {
// Disable X-Frame-Options on all WebUIs in this test, since subframe WebUI
// reuse is expected. If the initial creation does not disable XFO, then
// subsequent navigations will fail.
factory()->set_disable_xfo(true);
GURL main_frame_url(GetWebUIURL("web-ui/page_with_iframe.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
EXPECT_EQ(1U, root->child_count());
FrameTreeNode* child = root->child_at(0);
// Capture the SiteInstance and WebUI used in the first navigation to compare
// with the ones used after the reload.
scoped_refptr<SiteInstance> initial_site_instance =
child->current_frame_host()->GetSiteInstance();
WebUI* initial_web_ui = child->current_frame_host()->web_ui();
GlobalFrameRoutingId initial_rfh_id =
child->current_frame_host()->GetGlobalFrameRoutingId();
GURL subframe_same_site_url(GetWebUIURL("web-ui/title2.html"));
{
TestFrameNavigationObserver observer(child);
NavigateFrameToURL(child, subframe_same_site_url);
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(subframe_same_site_url, observer.last_committed_url());
}
EXPECT_EQ(initial_site_instance,
child->current_frame_host()->GetSiteInstance());
EXPECT_EQ(initial_web_ui, child->current_frame_host()->web_ui());
// Navigate the child frame cross-site.
GURL subframe_cross_site_url(GetWebUIURL("web-ui-subframe/title1.html"));
{
TestFrameNavigationObserver observer(child);
NavigateFrameToURL(child, subframe_cross_site_url);
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(subframe_cross_site_url, observer.last_committed_url());
}
EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
child->current_frame_host()->GetSiteInstance());
EXPECT_NE(root->current_frame_host()->web_ui(),
child->current_frame_host()->web_ui());
EXPECT_NE(initial_web_ui, child->current_frame_host()->web_ui());
// Capture the new SiteInstance and WebUI of the subframe and navigate it to
// another document on the same site.
scoped_refptr<SiteInstance> second_site_instance =
child->current_frame_host()->GetSiteInstance();
WebUI* second_web_ui = child->current_frame_host()->web_ui();
GURL subframe_cross_site_url2(GetWebUIURL("web-ui-subframe/title2.html"));
{
TestFrameNavigationObserver observer(child);
NavigateFrameToURL(child, subframe_cross_site_url2);
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(subframe_cross_site_url2, observer.last_committed_url());
}
EXPECT_EQ(second_site_instance,
child->current_frame_host()->GetSiteInstance());
EXPECT_EQ(second_web_ui, child->current_frame_host()->web_ui());
// Navigate back to the first document in the subframe, which should bring
// it back to the initial SiteInstance, but use a different RenderFrameHost
// and by that a different WebUI instance.
{
TestFrameNavigationObserver observer(child);
shell()->web_contents()->GetController().GoToOffset(-2);
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(subframe_same_site_url, observer.last_committed_url());
}
EXPECT_EQ(initial_site_instance,
child->current_frame_host()->GetSiteInstance());
// Use routing id comparison for the RenderFrameHost as the memory allocator
// sometime places the newly created RenderFrameHost for the back navigation
// at the same memory location as the initial one. For this reason too, it
// is not possible to check the web_ui() for inequality, since in some runs
// the memory in which two different WebUI instances of the same type are
// placed is the same.
EXPECT_NE(initial_rfh_id,
child->current_frame_host()->GetGlobalFrameRoutingId());
}
// Verify that if one WebUI does a window.open() to another WebUI, then the two
// are not sharing a BrowsingInstance, are isolated from each other, and both
// processes have bindings granted to them.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WindowOpenWebUI) {
GURL test_url(GetWebUIURL("web-ui/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
EXPECT_EQ(test_url, shell()->web_contents()->GetLastCommittedURL());
EXPECT_TRUE(shell()->web_contents()->GetMainFrame()->GetEnabledBindings() &
BINDINGS_POLICY_WEB_UI);
TestNavigationObserver new_contents_observer(nullptr, 1);
new_contents_observer.StartWatchingNewWebContents();
// Execute the script in isolated world since the default CSP disables eval
// which ExecJs depends on.
GURL new_tab_url(GetWebUIURL("another-web-ui/title2.html"));
EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1);", new_tab_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */));
new_contents_observer.Wait();
EXPECT_TRUE(new_contents_observer.last_navigation_succeeded());
ASSERT_EQ(2u, Shell::windows().size());
Shell* new_shell = Shell::windows()[1];
EXPECT_EQ(new_tab_url, new_shell->web_contents()->GetLastCommittedURL());
EXPECT_TRUE(new_shell->web_contents()->GetMainFrame()->GetEnabledBindings() &
BINDINGS_POLICY_WEB_UI);
// SiteInstances should be different and unrelated due to the
// BrowsingInstance swaps on navigation.
EXPECT_NE(new_shell->web_contents()->GetMainFrame()->GetSiteInstance(),
shell()->web_contents()->GetMainFrame()->GetSiteInstance());
EXPECT_FALSE(
new_shell->web_contents()
->GetMainFrame()
->GetSiteInstance()
->IsRelatedSiteInstance(
shell()->web_contents()->GetMainFrame()->GetSiteInstance()));
EXPECT_NE(shell()->web_contents()->GetWebUI(),
new_shell->web_contents()->GetWebUI());
}
// Test to verify correctness of WebUI and process model in the following
// sequence of navigations:
// * successful navigation to WebUI
// * failed navigation to WebUI
// * failed navigation to http URL
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIFailedNavigation) {
GURL start_url(GetWebUIURL("web-ui/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), start_url));
EXPECT_EQ(start_url, shell()->web_contents()->GetLastCommittedURL());
EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
shell()->web_contents()->GetMainFrame()->GetEnabledBindings());
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
GURL webui_error_url(GetWebUIURL("web-ui/error"));
EXPECT_FALSE(NavigateToURL(shell(), webui_error_url));
EXPECT_FALSE(root->current_frame_host()->web_ui());
EXPECT_EQ(0, root->current_frame_host()->GetEnabledBindings());
if (SiteIsolationPolicy::IsErrorPageIsolationEnabled(true)) {
EXPECT_EQ(root->current_frame_host()->GetSiteInstance()->GetSiteURL(),
GURL(kUnreachableWebDataURL));
}
ASSERT_TRUE(embedded_test_server()->Start());
GURL http_error_url(
embedded_test_server()->GetURL("foo.com", "/nonexistent"));
EXPECT_FALSE(NavigateToURL(shell(), http_error_url));
EXPECT_FALSE(root->current_frame_host()->web_ui());
EXPECT_EQ(0, root->current_frame_host()->GetEnabledBindings());
}
// Verify fetch request to chrome-untrusted:// is blocked.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
DisallowFetchRequestToChromeUntrusted) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host");
EXPECT_TRUE(NavigateToURL(shell(), web_url));
EXPECT_EQ(web_url, shell()->web_contents()->GetLastCommittedURL());
const char kFetchRequestScript[] =
"(async () => {"
" try {"
" let response = await fetch($1); "
" }"
" catch (e) {"
" return e.message;"
" }"
" throw 'Fetch should fail';"
"})();";
{
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/script.js"));
WebContentsConsoleObserver console_observer(shell()->web_contents());
console_observer.SetPattern(
"Fetch API cannot load " + untrusted_url.spec() +
". URL scheme must be \"http\" or \"https\" for CORS request.");
EXPECT_EQ("Failed to fetch",
EvalJs(shell(), JsReplace(kFetchRequestScript, untrusted_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */));
console_observer.Wait();
}
}
// Verify XHR request to chrome-untrusted:// is blocked.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, DisallowXHRRequestToChromeUntrusted) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host");
EXPECT_TRUE(NavigateToURL(shell(), web_url));
EXPECT_EQ(web_url, shell()->web_contents()->GetLastCommittedURL());
const char kXHRRequest[] =
"new Promise((resolve) => {"
" const xhttp = new XMLHttpRequest();"
" xhttp.open('GET', $1, true);"
" xhttp.onload = () => { "
" resolve('Request should have failed');"
" };"
" xhttp.onerror = () => {"
" resolve('Request failed');"
" };"
" xhttp.send();"
"}); ";
{
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/script.js"));
const std::string host = web_url.GetOrigin().spec();
WebContentsConsoleObserver console_observer(shell()->web_contents());
console_observer.SetPattern(
"Access to XMLHttpRequest at '" + untrusted_url.spec() +
"' from origin '" + host.substr(0, host.length() - 1) +
"' has been blocked by CORS policy: Cross origin requests are only "
"supported for protocol schemes: http, data, chrome, https.");
EXPECT_EQ("Request failed",
EvalJs(shell(), JsReplace(kXHRRequest, untrusted_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */));
console_observer.Wait();
}
}
// Verify load script from chrome-untrusted:// is blocked.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
DisallowResourceRequestToChromeUntrusted) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host");
EXPECT_TRUE(NavigateToURL(shell(), web_url));
EXPECT_EQ(web_url, shell()->web_contents()->GetLastCommittedURL());
const char kLoadResourceScript[] =
"new Promise((resolve) => {"
" const script = document.createElement('script');"
" script.onload = () => {"
" resolve('Script load should have failed');"
" };"
" script.onerror = () => {"
" resolve('Load failed');"
" };"
" script.src = $1;"
" document.body.appendChild(script);"
"});";
// There are no error messages in the console which is why we cannot check for
// them.
{
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/script.js"));
EXPECT_EQ("Load failed",
EvalJs(shell(), JsReplace(kLoadResourceScript, untrusted_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */));
}
}
class WebUISecurityTestWithWebUIReportOnlyTrustedTypesEnabled
: public WebUISecurityTest {
public:
WebUISecurityTestWithWebUIReportOnlyTrustedTypesEnabled() {
feature_list_.InitAndEnableFeature(features::kWebUIReportOnlyTrustedTypes);
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Verify Report-Only Trusted Types won't block assignment to a dangerous sink,
// but logs warning
IN_PROC_BROWSER_TEST_F(WebUISecurityTestWithWebUIReportOnlyTrustedTypesEnabled,
DoNotBlockSinkAssignmentOnReportOnlyTrustedTypes) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL test_url(GetWebUIURL("web-ui/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), test_url));
const char kDangerousSinkUse[] =
"(() => {"
" try {"
" document.body.innerHTML = 1;"
" return 'Assignment succeeded';"
" } catch(e) {"
" throw 'Assignment should have succeeded';"
" }"
"})();";
{
WebContentsConsoleObserver console_observer(shell()->web_contents());
console_observer.SetPattern(
"[Report Only] This document requires 'TrustedHTML' assignment.");
EXPECT_EQ("Assignment succeeded",
EvalJs(shell(), kDangerousSinkUse, EXECUTE_SCRIPT_DEFAULT_OPTIONS,
1 /* world_id */));
console_observer.Wait();
}
}
} // namespace content