1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/profiles/profile.h"
6 #include "chrome/browser/ui/browser.h"
7 #include "chrome/browser/ui/tabs/tab_strip_model.h"
8 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
9 #include "chrome/test/base/in_process_browser_test.h"
10 #include "chrome/test/base/ui_test_utils.h"
11 #include "content/public/common/url_constants.h"
12 #include "content/public/test/browser_test_utils.h"
13 #include "content/public/test/test_navigation_observer.h"
14 #include "content/public/test/web_ui_browsertest_util.h"
15 #include "ipc/ipc_security_test_util.h"
16 #include "net/dns/mock_host_resolver.h"
17 #include "net/test/embedded_test_server/embedded_test_server.h"
18 #include "url/url_constants.h"
19 
20 // Tests embedder specific behavior of WebUIs.
21 class ChromeWebUINavigationBrowserTest : public InProcessBrowserTest {
22  public:
ChromeWebUINavigationBrowserTest()23   ChromeWebUINavigationBrowserTest() {
24     content::WebUIControllerFactory::RegisterFactory(&factory_);
25   }
26 
~ChromeWebUINavigationBrowserTest()27   ~ChromeWebUINavigationBrowserTest() override {
28     content::WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
29   }
30 
31  protected:
SetUpOnMainThread()32   void SetUpOnMainThread() override {
33     host_resolver()->AddRule("*", "127.0.0.1");
34     ASSERT_TRUE(embedded_test_server()->Start());
35   }
36 
37  private:
38   content::TestWebUIControllerFactory factory_;
39 };
40 
41 // Verify that a browser check stops websites from embeding chrome:// iframes.
42 // This is a copy of the DisallowEmbeddingChromeSchemeFromWebFrameBrowserCheck
43 // test in content/browser/webui/web_ui_navigation_browsertest.cc. We need a
44 // copy here because the browser side check is done by embedders.
IN_PROC_BROWSER_TEST_F(ChromeWebUINavigationBrowserTest,DisallowEmbeddingChromeSchemeFromWebFrameBrowserCheck)45 IN_PROC_BROWSER_TEST_F(ChromeWebUINavigationBrowserTest,
46                        DisallowEmbeddingChromeSchemeFromWebFrameBrowserCheck) {
47   GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
48   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
49   auto* main_frame = web_contents->GetMainFrame();
50   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
51 
52   // Add iframe but don't navigate it to a chrome:// URL yet.
53   EXPECT_TRUE(content::ExecJs(main_frame,
54                               "var frame = document.createElement('iframe');\n"
55                               "document.body.appendChild(frame);\n",
56                               content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
57                               1 /* world_id */));
58 
59   content::RenderFrameHost* child = content::ChildFrameAt(main_frame, 0);
60   EXPECT_EQ("about:blank", child->GetLastCommittedURL());
61 
62   content::TestNavigationObserver observer(web_contents);
63   GURL webui_url(content::GetWebUIURL("web-ui/title1.html?noxfo=true"));
64   content::PwnMessageHelper::OpenURL(child->GetProcess(), child->GetRoutingID(),
65                                      webui_url);
66   observer.Wait();
67 
68   // Retrieve the RenderFrameHost again since it might have been swapped.
69   child = content::ChildFrameAt(main_frame, 0);
70   EXPECT_EQ(content::kBlockedURL, child->GetLastCommittedURL());
71 }
72 
73 // Verify that a browser check stops websites from embeding chrome-untrusted://
74 // iframes. This is a copy of the
75 // DisallowEmbeddingChromeUntrustedSchemeFromWebFrameBrowserCheck test in
76 // content/browser/webui/web_ui_navigation_browsertest.cc. We need a copy here
77 // because the browser side check is done by embedders.
IN_PROC_BROWSER_TEST_F(ChromeWebUINavigationBrowserTest,DisallowEmbeddingChromeUntrustedSchemeFromWebFrameBrowserCheck)78 IN_PROC_BROWSER_TEST_F(
79     ChromeWebUINavigationBrowserTest,
80     DisallowEmbeddingChromeUntrustedSchemeFromWebFrameBrowserCheck) {
81   GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
82   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
83   auto* main_frame = web_contents->GetMainFrame();
84   EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), main_frame_url));
85 
86   // Add iframe but don't navigate it to a chrome-untrusted:// URL yet.
87   EXPECT_TRUE(content::ExecJs(main_frame,
88                               "var frame = document.createElement('iframe');\n"
89                               "document.body.appendChild(frame);\n",
90                               content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
91                               1 /* world_id */));
92 
93   content::RenderFrameHost* child = content::ChildFrameAt(main_frame, 0);
94   EXPECT_EQ("about:blank", child->GetLastCommittedURL());
95 
96   content::TestNavigationObserver observer(web_contents);
97   content::TestUntrustedDataSourceCSP csp;
98   csp.no_xfo = true;
99   content::AddUntrustedDataSource(browser()->profile(), "test-iframe-host",
100                                   csp);
101 
102   GURL untrusted_url(
103       content::GetChromeUntrustedUIURL("test-iframe-host/title1.html"));
104   content::PwnMessageHelper::OpenURL(child->GetProcess(), child->GetRoutingID(),
105                                      untrusted_url);
106   observer.Wait();
107 
108   // Retrieve the RenderFrameHost again since it might have been swapped.
109   child = content::ChildFrameAt(main_frame, 0);
110   EXPECT_EQ(content::kBlockedURL, child->GetLastCommittedURL());
111 }
112