#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/process_lock.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/site_info.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/frame.mojom.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/webui_config_map.h"
#include "content/public/common/bindings_policy.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.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/scoped_web_ui_controller_factory_registration.h"
#include "content/public/test/test_frame_navigation_observer.h"
#include "content/public/test/test_navigation_observer.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 "ipc/ipc_security_test_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/webui/untrusted_web_ui_browsertest_util.h"
#include "url/url_constants.h"
namespace content {
namespace {
const char kAddIframeScript[] =
"var frame = document.createElement('iframe');\n"
"frame.src = $1;\n"
"document.body.appendChild(frame);\n";
blink::mojom::OpenURLParamsPtr CreateOpenURLParams(const GURL& url) {
auto params = blink::mojom::OpenURLParams::New();
params->url = url;
params->disposition = WindowOpenDisposition::CURRENT_TAB;
params->should_replace_current_entry = false;
params->user_gesture = true;
return params;
}
bool DoesURLRequireDedicatedProcess(const IsolationContext& isolation_context,
const GURL& url) {
return SiteInfo::CreateForTesting(isolation_context, url)
.RequiresDedicatedProcess(isolation_context);
}
}
class WebUINavigationBrowserTest : public ContentBrowserTest {
public:
WebUINavigationBrowserTest() = default;
WebUINavigationBrowserTest(const WebUINavigationBrowserTest&) = delete;
WebUINavigationBrowserTest& operator=(const WebUINavigationBrowserTest&) =
delete;
protected:
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
}
void TestWebFrameInProcessWithWebUIBindings(int bindings) {
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), foo_url));
EXPECT_EQ(foo_url, root->current_frame_host()->GetLastCommittedURL());
EXPECT_FALSE(
ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
root->current_frame_host()->GetProcess()->GetID()));
ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings(
root->current_frame_host()->GetProcess()->GetID(), bindings);
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
root->current_frame_host()->GetProcess()->GetID()));
{
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
std::string script = base::StringPrintf(
"var frame = document.createElement('iframe');\n"
"frame.src = '%s';\n"
"document.body.appendChild(frame);\n",
web_url.spec().c_str());
TestNavigationObserver navigation_observer(shell()->web_contents());
EXPECT_TRUE(ExecuteScript(shell(), script));
navigation_observer.Wait();
EXPECT_EQ(1U, root->child_count());
EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
EXPECT_EQ(web_url, navigation_observer.last_navigation_url());
EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
root->child_at(0)->current_frame_host()->GetSiteInstance());
EXPECT_FALSE(
ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
root->child_at(0)->current_frame_host()->GetProcess()->GetID()));
}
}
void TestWebUISubframeNewWindowToWebAllowed(int bindings) {
GURL main_frame_url(
GetWebUIURL("web-ui/page_with_blank_iframe.html?bindings=" +
base::NumberToString(bindings)));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
FrameTreeNode* child = root->child_at(0);
EXPECT_EQ(bindings, root->current_frame_host()->GetEnabledBindings());
EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
child->current_frame_host()->GetSiteInstance());
RenderFrameHost* webui_rfh = root->current_frame_host();
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
webui_rfh->GetProcess()->GetID()));
{
TestFrameNavigationObserver observer(root->child_at(0));
GURL subframe_url(GetWebUIURL("web-ui/title1.html?noxfo=true&bindings=" +
base::NumberToString(bindings)));
NavigateFrameToURL(root->child_at(0), subframe_url);
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(subframe_url, observer.last_committed_url());
}
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
std::string script = JsReplace(
"var a = document.createElement('a');"
"a.href = $1; a.target = '_blank'; a.click()",
web_url.spec().c_str());
ShellAddedObserver new_shell_observer;
EXPECT_TRUE(ExecJs(root->child_at(0)->current_frame_host(), script,
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
Shell* new_shell = new_shell_observer.GetShell();
WebContents* new_web_contents = new_shell->web_contents();
EXPECT_TRUE(WaitForLoadStop(new_web_contents));
EXPECT_EQ(web_url, new_web_contents->GetLastCommittedURL());
FrameTreeNode* new_root = static_cast<WebContentsImpl*>(new_web_contents)
->GetPrimaryFrameTree()
.root();
EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
new_root->current_frame_host()->GetSiteInstance());
EXPECT_NE(root->current_frame_host()->GetProcess(),
new_root->current_frame_host()->GetProcess());
EXPECT_NE(root->current_frame_host()->web_ui(),
new_root->current_frame_host()->web_ui());
EXPECT_NE(root->current_frame_host()->GetEnabledBindings(),
new_root->current_frame_host()->GetEnabledBindings());
EXPECT_FALSE(
root->current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
new_root->current_frame_host()->GetSiteInstance()));
}
void TestEmbeddingIFrameFailed(const GURL& embedder_url,
const GURL& iframe_url) {
ASSERT_TRUE(NavigateToURL(shell()->web_contents(), embedder_url));
TestNavigationObserver observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, iframe_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_FALSE(observer.last_navigation_succeeded()) << embedder_url;
}
private:
TestWebUIControllerFactory factory_;
ScopedWebUIControllerFactoryRegistration factory_registration_{&factory_};
};
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInChromeSchemeIsAllowed) {
GURL main_frame_url(GetWebUIURL("web-ui/title1.html?noxfo=true&childsrc="));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
root->current_frame_host()->GetEnabledBindings());
EXPECT_EQ(0UL, root->child_count());
{
TestNavigationObserver observer(shell()->web_contents());
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, web_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(1UL, root->child_count());
}
{
TestNavigationObserver observer(shell()->web_contents());
GURL data_url("data:text/html,foo");
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, data_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(2UL, root->child_count());
}
{
TestNavigationObserver observer(shell()->web_contents());
GURL about_blank_url("about:blank");
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, about_blank_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(3UL, root->child_count());
}
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInChromeUntrustedSchemeAllowedByCSP) {
TestUntrustedDataSourceHeaders headers;
headers.child_src = "child-src * data:;";
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host", headers));
GURL main_frame_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(0, root->current_frame_host()->GetEnabledBindings());
{
TestNavigationObserver observer(shell()->web_contents());
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, web_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
}
{
TestNavigationObserver observer(shell()->web_contents());
GURL data_url("data:text/html,foo");
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, data_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
}
{
TestNavigationObserver observer(shell()->web_contents());
GURL about_blank_url("about:blank");
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, about_blank_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
}
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInChromeSchemeDisallowedByCSP) {
GURL main_frame_url(
GetWebUIURL("web-ui/title1.html?childsrc=child-src 'none'"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
EXPECT_EQ(main_frame_url, shell()->web_contents()->GetLastCommittedURL());
{
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
TestNavigationObserver navigation_observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, web_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
navigation_observer.Wait();
EXPECT_FALSE(navigation_observer.last_navigation_succeeded());
}
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInChromeUntrustedSchemeDisallowedByCSP) {
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
GURL main_frame_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(0, root->current_frame_host()->GetEnabledBindings());
{
TestNavigationObserver observer(shell()->web_contents());
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, web_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_FALSE(observer.last_navigation_succeeded());
}
{
TestNavigationObserver observer(shell()->web_contents());
GURL data_url("data:text/html,foo");
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, data_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_FALSE(observer.last_navigation_succeeded());
}
{
TestNavigationObserver observer(shell()->web_contents());
TestUntrustedDataSourceHeaders headers;
headers.no_xfo = true;
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-iframe-host",
headers));
GURL untrusted_url(GetChromeUntrustedUIURL("test-iframe-host/title1.html"));
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, untrusted_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_FALSE(observer.last_navigation_succeeded());
}
{
TestNavigationObserver observer(shell()->web_contents());
GURL about_blank_url("about:blank");
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, about_blank_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
}
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
DisallowEmbeddingChromeSchemeFromWebFrameBrowserCheck) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
EXPECT_TRUE(ExecJs(shell(),
"var frame = document.createElement('iframe');\n"
"document.body.appendChild(frame);\n",
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
EXPECT_EQ("about:blank", child->GetLastCommittedURL());
TestNavigationObserver observer(shell()->web_contents());
static_cast<mojom::FrameHost*>(child)->OpenURL(
CreateOpenURLParams(GetWebUIURL("web-ui/title1.html?noxfo=true")));
observer.Wait();
child = root->child_at(0)->current_frame_host();
EXPECT_EQ(kBlockedURL, child->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(
WebUINavigationBrowserTest,
DisallowEmbeddingChromeUntrustedSchemeFromWebFrameBrowserCheck) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
TestUntrustedDataSourceHeaders headers;
headers.no_xfo = true;
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-iframe-host",
headers));
EXPECT_TRUE(ExecJs(shell(),
"var frame = document.createElement('iframe');\n"
"document.body.appendChild(frame);\n",
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
EXPECT_EQ("about:blank", child->GetLastCommittedURL());
TestNavigationObserver observer(shell()->web_contents());
static_cast<mojom::FrameHost*>(child)->OpenURL(CreateOpenURLParams(
GetChromeUntrustedUIURL("test-iframe-host/title1.html")));
observer.Wait();
child = root->child_at(0)->current_frame_host();
EXPECT_EQ(kBlockedURL, child->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
FrameAncestorsDisallowEmbedding) {
auto* web_contents = shell()->web_contents();
GURL iframe_url(GetWebUIURL("web-ui/title1.html"));
{
GURL main_frame_url(GetWebUIURL("web-ui/title1.html?childsrc="));
TestEmbeddingIFrameFailed(main_frame_url, iframe_url);
}
{
GURL main_frame_url(GetWebUIURL("different-web-ui/title1.html?childsrc="));
TestEmbeddingIFrameFailed(main_frame_url, iframe_url);
}
{
GURL main_frame_url(
embedded_test_server()->GetURL("/title1.html?childsrc="));
ASSERT_TRUE(NavigateToURL(web_contents, main_frame_url));
WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern("Not allowed to load local resource: " +
iframe_url.spec());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, iframe_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
ASSERT_TRUE(console_observer.Wait());
FrameTreeNode* root = static_cast<WebContentsImpl*>(web_contents)
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
RenderFrameHost* child = root->child_at(0)->current_frame_host();
EXPECT_EQ(GURL(), child->GetLastCommittedURL());
}
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
FrameAncestorsAllowEmbedding) {
auto* web_contents = shell()->web_contents();
GURL iframe_url(GetWebUIURL("web-ui/title1.html?frameancestors=" +
GetWebUIURLString("web-ui")));
{
GURL main_frame_url(GetWebUIURL("web-ui/title1.html?childsrc="));
ASSERT_TRUE(NavigateToURL(web_contents, main_frame_url));
TestNavigationObserver observer(web_contents);
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, iframe_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
}
{
GURL main_frame_url(GetWebUIURL("different-web-ui/title1.html?childsrc="));
TestEmbeddingIFrameFailed(main_frame_url, iframe_url);
}
{
GURL main_frame_url(
embedded_test_server()->GetURL("/title1.html?childsrc="));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern("Not allowed to load local resource: " +
iframe_url.spec());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, iframe_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
ASSERT_TRUE(console_observer.Wait());
FrameTreeNode* root = static_cast<WebContentsImpl*>(web_contents)
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
RenderFrameHost* child = root->child_at(0)->current_frame_host();
EXPECT_EQ(GURL(), child->GetLastCommittedURL());
}
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
FrameAncestorsAllowEmbeddingFromOtherHosts) {
auto* web_contents = shell()->web_contents();
GURL iframe_url(GetWebUIURL("different-web-ui/title1.html?frameancestors=" +
GetWebUIURLString("web-ui")));
{
GURL main_frame_url(GetWebUIURL("web-ui/title1.html?childsrc="));
ASSERT_TRUE(NavigateToURL(web_contents, main_frame_url));
TestNavigationObserver observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, iframe_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
}
{
GURL main_frame_url(GetWebUIURL("different-web-ui/title1.html?childsrc="));
TestEmbeddingIFrameFailed(main_frame_url, iframe_url);
}
{
GURL main_frame_url(
embedded_test_server()->GetURL("/title1.html?childsrc="));
ASSERT_TRUE(NavigateToURL(web_contents, main_frame_url));
WebContentsConsoleObserver console_observer(web_contents);
console_observer.SetPattern("Not allowed to load local resource: " +
iframe_url.spec());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, iframe_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
ASSERT_TRUE(console_observer.Wait());
FrameTreeNode* root = static_cast<WebContentsImpl*>(web_contents)
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
RenderFrameHost* child = root->child_at(0)->current_frame_host();
EXPECT_EQ(GURL(), child->GetLastCommittedURL());
}
}
IN_PROC_BROWSER_TEST_F(
WebUINavigationBrowserTest,
ChromeUntrustedFrameInChromeSchemeDisallowedInDefaultWebUI) {
GURL main_frame_url(GetWebUIURL("web-ui/title1.html?noxfo=true&childsrc="));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
root->current_frame_host()->GetEnabledBindings());
EXPECT_EQ(0UL, root->child_count());
TestUntrustedDataSourceHeaders headers;
headers.no_xfo = true;
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host", headers));
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
TestNavigationObserver observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, untrusted_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_EQ(1UL, root->child_count());
RenderFrameHost* child = root->child_at(0)->current_frame_host();
EXPECT_EQ(kBlockedURL, child->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
ChromeUntrustedFrameInChromeSchemeAllowed) {
GURL main_frame_url(
GetWebUIURL("web-ui/"
"title1.html?childsrc=&requestableschemes=chrome-untrusted"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
RenderFrameHostImpl* webui_rfh = root->current_frame_host();
scoped_refptr<SiteInstanceImpl> webui_site_instance =
webui_rfh->GetSiteInstance();
EXPECT_EQ(main_frame_url, webui_rfh->GetLastCommittedURL());
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
webui_rfh->GetProcess()->GetID()));
EXPECT_FALSE(
webui_site_instance->GetSiteInfo().process_lock_url().is_empty());
EXPECT_EQ(root->current_frame_host()->GetProcess()->GetProcessLock(),
ProcessLock::FromSiteInfo(webui_site_instance->GetSiteInfo()));
TestUntrustedDataSourceHeaders headers;
std::vector<std::string> frame_ancestors({"chrome://web-ui"});
headers.frame_ancestors =
absl::make_optional<std::vector<std::string>>(std::move(frame_ancestors));
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host", headers));
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
TestNavigationObserver observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, untrusted_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(untrusted_url,
root->child_at(0)->current_frame_host()->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
DisallowEmbeddingChromeSchemeFromWebFrameRendererCheck) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
GURL webui_url(GetWebUIURL("web-ui/title1.html?noxfo=true"));
WebContentsConsoleObserver console_observer(shell()->web_contents());
console_observer.SetPattern("Not allowed to load local resource: " +
webui_url.spec());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, webui_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
ASSERT_TRUE(console_observer.Wait());
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
RenderFrameHost* child = root->child_at(0)->current_frame_host();
EXPECT_EQ(GURL(), child->GetLastCommittedURL());
}
const char kOpenUrlViaClickTargetFunc[] =
"(function(url) {\n"
" var lnk = document.createElement(\"a\");\n"
" lnk.href = url;\n"
" lnk.rel = 'opener';\n"
" lnk.target = \"_blank\";\n"
" document.body.appendChild(lnk);\n"
" lnk.click();\n"
"})";
void OpenUrlViaClickTarget(const ToRenderFrameHost& adapter, const GURL& url) {
EXPECT_TRUE(ExecuteScript(adapter, std::string(kOpenUrlViaClickTargetFunc) +
"(\"" + url.spec() + "\");"));
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
SharedDomainDifferentSiteInstanceNavigation) {
GURL url1("chrome://foo.web-ui/title1.html?bindings=" +
base::NumberToString(BINDINGS_POLICY_WEB_UI));
GURL url2("chrome://bar.web-ui/title1.html?bindings=" +
base::NumberToString(BINDINGS_POLICY_WEB_UI));
EXPECT_TRUE(NavigateToURL(shell(), url1));
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID()));
SiteInstance* site_instance1 = shell()->web_contents()->GetSiteInstance();
int process1_id = site_instance1->GetProcess()->GetID();
EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(shell(), url2));
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID()));
SiteInstance* site_instance2 = shell()->web_contents()->GetSiteInstance();
int process2_id = site_instance2->GetProcess()->GetID();
EXPECT_NE(process1_id, process2_id);
EXPECT_NE(site_instance2, site_instance1);
EXPECT_NE(static_cast<SiteInstanceImpl*>(site_instance2)->group(),
static_cast<SiteInstanceImpl*>(site_instance1)->group());
EXPECT_FALSE(site_instance2->IsRelatedSiteInstance(site_instance1));
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
SharedDomainDifferentSiteInstanceUrlClick) {
GURL url1("chrome://foo.web-ui/title1.html?bindings=" +
base::NumberToString(BINDINGS_POLICY_WEB_UI));
GURL url2("chrome://bar.web-ui/title1.html?bindings=" +
base::NumberToString(BINDINGS_POLICY_WEB_UI));
EXPECT_TRUE(NavigateToURL(shell(), url1));
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID()));
SiteInstance* site_instance1 = shell()->web_contents()->GetSiteInstance();
int process1_id = site_instance1->GetProcess()->GetID();
TestNavigationObserver nav_observer(nullptr);
nav_observer.StartWatchingNewWebContents();
ShellAddedObserver shao;
OpenUrlViaClickTarget(shell(), url2);
nav_observer.Wait();
Shell* new_shell = shao.GetShell();
WebContentsImpl* new_web_contents =
static_cast<WebContentsImpl*>(new_shell->web_contents());
SiteInstance* site_instance2 = new_web_contents->GetSiteInstance();
int process2_id = site_instance2->GetProcess()->GetID();
EXPECT_NE(process1_id, process2_id);
EXPECT_NE(site_instance2, site_instance1);
EXPECT_NE(static_cast<SiteInstanceImpl*>(site_instance2)->group(),
static_cast<SiteInstanceImpl*>(site_instance1)->group());
EXPECT_FALSE(site_instance2->IsRelatedSiteInstance(site_instance1));
RenderFrameProxyHost* initial_rfph =
new_web_contents->GetPrimaryMainFrame()
->browsing_context_state()
->GetRenderFrameProxyHost(
static_cast<SiteInstanceImpl*>(site_instance1)->group());
ASSERT_TRUE(initial_rfph);
EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(new_shell, url1));
EXPECT_NE(new_web_contents->GetSiteInstance(), site_instance1);
EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
new_web_contents->GetPrimaryMainFrame()->GetEnabledBindings());
}
class WebUINavigationDisabledWebSecurityBrowserTest
: public WebUINavigationBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kDisableWebSecurity);
}
};
IN_PROC_BROWSER_TEST_F(WebUINavigationDisabledWebSecurityBrowserTest,
DisallowEmbeddingChromeSchemeFromWebFrameBrowserCheck2) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
GURL webui_url(GetWebUIURL("web-ui/title1.html?noxfo=true"));
TestNavigationObserver observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, webui_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
observer.Wait();
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
EXPECT_EQ(1U, root->child_count());
RenderFrameHost* child = root->child_at(0)->current_frame_host();
EXPECT_EQ(kBlockedURL, child->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(
WebUINavigationDisabledWebSecurityBrowserTest,
DisallowNavigatingToChromeSchemeFromWebFrameBrowserCheck) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
GURL webui_url(GetWebUIURL("web-ui/title1.html"));
TestNavigationObserver observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace("location.href = $1", webui_url)));
observer.Wait();
EXPECT_EQ(kBlockedURL, shell()->web_contents()->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(
WebUINavigationDisabledWebSecurityBrowserTest,
DisallowNavigatingToChromeUntrustedSchemeFromWebFrameBrowserCheck) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
TestUntrustedDataSourceHeaders headers;
headers.no_xfo = false;
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-iframe-host",
headers));
GURL untrusted_url(GetChromeUntrustedUIURL("test-iframe-host/title1.html"));
TestNavigationObserver observer(shell()->web_contents());
EXPECT_TRUE(ExecJs(shell(), JsReplace("location.href = $1", untrusted_url)));
observer.Wait();
EXPECT_EQ(kBlockedURL, shell()->web_contents()->GetLastCommittedURL());
}
IN_PROC_BROWSER_TEST_F(WebUINavigationDisabledWebSecurityBrowserTest,
DisallowWebWindowOpenToChromeURL) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
GURL chrome_url(GetWebUIURL("web-ui/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
ShellAddedObserver new_shell_observer;
const char kWindowOpenScript[] = "var w = window.open($1, '_blank');";
EXPECT_TRUE(ExecJs(shell(), JsReplace(kWindowOpenScript, chrome_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
Shell* popup = new_shell_observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
EXPECT_EQ(kBlockedURL, popup->web_contents()->GetLastCommittedURL());
RenderFrameHost* main_rfh = shell()->web_contents()->GetPrimaryMainFrame();
RenderFrameHost* popup_rfh = popup->web_contents()->GetPrimaryMainFrame();
EXPECT_EQ(main_rfh->GetSiteInstance(), popup_rfh->GetSiteInstance());
EXPECT_TRUE(main_rfh->GetSiteInstance()->IsRelatedSiteInstance(
popup_rfh->GetSiteInstance()));
}
IN_PROC_BROWSER_TEST_F(WebUINavigationDisabledWebSecurityBrowserTest,
DisallowWebWindowOpenToChromeUntrustedURL) {
GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
GURL chrome_url(GetWebUIURL("web-ui/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
ShellAddedObserver new_shell_observer;
const char kWindowOpenScript[] = "var w = window.open($1, '_blank');";
EXPECT_TRUE(ExecJs(shell(), JsReplace(kWindowOpenScript, chrome_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 ));
Shell* popup = new_shell_observer.GetShell();
EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
EXPECT_EQ(kBlockedURL, popup->web_contents()->GetLastCommittedURL());
RenderFrameHost* main_rfh = shell()->web_contents()->GetPrimaryMainFrame();
RenderFrameHost* popup_rfh = popup->web_contents()->GetPrimaryMainFrame();
EXPECT_EQ(main_rfh->GetSiteInstance(), popup_rfh->GetSiteInstance());
EXPECT_TRUE(main_rfh->GetSiteInstance()->IsRelatedSiteInstance(
popup_rfh->GetSiteInstance()));
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, WebUIMainFrameToWebAllowed) {
GURL chrome_url(GetWebUIURL("web-ui/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), chrome_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.root();
RenderFrameHostImpl* webui_rfh = root->current_frame_host();
scoped_refptr<SiteInstanceImpl> webui_site_instance =
webui_rfh->GetSiteInstance();
EXPECT_EQ(chrome_url, webui_rfh->GetLastCommittedURL());
EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
webui_rfh->GetProcess()->GetID()));
EXPECT_EQ(root->current_frame_host()->GetProcess()->GetProcessLock(),
ProcessLock::FromSiteInfo(webui_site_instance->GetSiteInfo()));
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
std::string script =
base::StringPrintf("location.href = '%s';", web_url.spec().c_str());
TestNavigationObserver navigation_observer(shell()->web_contents());
EXPECT_TRUE(ExecuteScript(shell(), script));
navigation_observer.Wait();
EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
EXPECT_EQ(web_url, root->current_frame_host()->GetLastCommittedURL());
EXPECT_NE(webui_site_instance, root->current_frame_host()->GetSiteInstance());
EXPECT_FALSE(webui_site_instance->IsRelatedSiteInstance(
root->current_frame_host()->GetSiteInstance()));
EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
root->current_frame_host()->GetProcess()->GetID()));
EXPECT_NE(root->current_frame_host()->GetProcess()->GetProcessLock(),
ProcessLock::FromSiteInfo(webui_site_instance->GetSiteInfo()));
}
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInWebUIProcessAllowed) {
TestWebFrameInProcessWithWebUIBindings(BINDINGS_POLICY_WEB_UI);
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInMojoWebUIProcessAllowed) {
TestWebFrameInProcessWithWebUIBindings(BINDINGS_POLICY_MOJO_WEB_UI);
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInHybridWebUIProcessAllowed) {
TestWebFrameInProcessWithWebUIBindings(BINDINGS_POLICY_MOJO_WEB_UI |
BINDINGS_POLICY_WEB_UI);
}
#endif
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebUISubframeNewWindowToWebAllowed) {
TestWebUISubframeNewWindowToWebAllowed(BINDINGS_POLICY_WEB_UI);
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
MojoWebUISubframeNewWindowToWebAllowed) {
TestWebUISubframeNewWindowToWebAllowed(BINDINGS_POLICY_MOJO_WEB_UI);
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
HybridWebUISubframeNewWindowToWebAllowed) {
TestWebUISubframeNewWindowToWebAllowed(BINDINGS_POLICY_MOJO_WEB_UI |
BINDINGS_POLICY_WEB_UI);
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebUIOriginsRequireDedicatedProcess) {
WebContents* web_contents = shell()->web_contents();
BrowserContext* browser_context = web_contents->GetBrowserContext();
IsolationContext isolation_context(browser_context);
GURL chrome_url(GetWebUIURL("web-ui/title1.html"));
auto expected_site_info =
SiteInfo::CreateForTesting(isolation_context, chrome_url);
EXPECT_TRUE(DoesURLRequireDedicatedProcess(isolation_context, chrome_url));
EXPECT_TRUE(NavigateToURL(shell(), chrome_url));
auto site_info = static_cast<SiteInstanceImpl*>(
web_contents->GetPrimaryMainFrame()->GetSiteInstance())
->GetSiteInfo();
EXPECT_EQ(expected_site_info, site_info);
const char* kScript = R"(
var blob = new Blob(['foo'], {type : 'text/html'});
var url = URL.createObjectURL(blob);
url;
)";
GURL blob_url(
EvalJs(shell(), kScript, EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 )
.ExtractString());
EXPECT_EQ(url::kBlobScheme, blob_url.scheme());
EXPECT_TRUE(DoesURLRequireDedicatedProcess(isolation_context, blob_url));
EXPECT_EQ(expected_site_info,
SiteInfo::CreateForTesting(isolation_context, blob_url));
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
UntrustedWebUIOriginsRequireDedicatedProcess) {
WebContents* web_contents = shell()->web_contents();
BrowserContext* browser_context = web_contents->GetBrowserContext();
IsolationContext isolation_context(browser_context);
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
GURL chrome_untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
auto expected_site_info = SiteInfo::CreateForTesting(
isolation_context, GetChromeUntrustedUIURL("test-host"));
EXPECT_TRUE(
DoesURLRequireDedicatedProcess(isolation_context, chrome_untrusted_url));
EXPECT_TRUE(NavigateToURL(shell(), chrome_untrusted_url));
auto site_info = static_cast<SiteInstanceImpl*>(
web_contents->GetPrimaryMainFrame()->GetSiteInstance())
->GetSiteInfo();
EXPECT_EQ(expected_site_info, site_info);
const char* kScript = R"(
var blob = new Blob(['foo'], {type : 'text/html'});
var url = URL.createObjectURL(blob);
url;
)";
GURL blob_url(
EvalJs(shell(), kScript, EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 )
.ExtractString());
EXPECT_EQ(url::kBlobScheme, blob_url.scheme());
EXPECT_TRUE(DoesURLRequireDedicatedProcess(IsolationContext(browser_context),
blob_url));
EXPECT_EQ(expected_site_info,
SiteInfo::CreateForTesting(isolation_context, blob_url));
}
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
SessionHistoryToFailedNavigation) {
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()->GetPrimaryMainFrame()->GetEnabledBindings());
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetPrimaryFrameTree()
.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());
GURL success_url(GetWebUIURL("web-ui/title2.html"));
EXPECT_TRUE(NavigateToURL(shell(), success_url));
EXPECT_EQ(success_url, shell()->web_contents()->GetLastCommittedURL());
{
TestFrameNavigationObserver observer(root);
shell()->web_contents()->GetController().GoBack();
observer.Wait();
EXPECT_FALSE(observer.last_navigation_succeeded());
EXPECT_FALSE(root->current_frame_host()->web_ui());
}
{
TestFrameNavigationObserver observer(root);
shell()->web_contents()->GetController().GoForward();
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_TRUE(root->current_frame_host()->web_ui());
EXPECT_EQ(success_url, observer.last_committed_url());
}
}
}