Commit 019a2364 authored by Giovanni Ortuño Urquidi's avatar Giovanni Ortuño Urquidi Committed by Chromium LUCI CQ

web-ui: Introduce ChromeUntrustedWebUIControllerFactory

Currently WebUI properties, e.g. CSPs, requesting schemes, host, mojo,
etc. are stored in WebUIControllers themselves. The lifetime of a
WebUIController is bound to the frame, which makes it hard to use for
some use cases. A non-dynamically allocated class where clients could
query a WebUIs properties would be easier to use and audit.

Introduces a new WebUIConfig class that stores properties of WebUIs. For
now the two properties are 1. the WebUI's host and 2. if the WebUI
is enabled. In the future this class could include information like
CSPs, if we should enable Mojo, if we should enable chrome.send(), if
we should allow network requests, URLDataSource, etc.

For now all WebUIConfigs are owned by
ChromeUntrustedWebUIControllerFactory but that could be moved somewhere
else so that anyone could retrieve a WebUIConfig and check its
properties.

ChromeUntrustedWebUIControllerFactory holds a map of hosts to
WebUIConfigs and implements WebUIControllerFactory methods by querying
the appropriate config for its properties.

Bug: 1080384

Change-Id: Ieebb0ded877f57d00d6f47aa5b9304cd250b1bf1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2315572
Commit-Queue: Giovanni Ortuño Urquidi <ortuno@chromium.org>
Reviewed-by: default avatardpapad <dpapad@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarJiewei Qian  <qjw@chromium.org>
Reviewed-by: default avatarcalamity <calamity@chromium.org>
Cr-Commit-Position: refs/heads/master@{#836974}
parent 82b6525f
......@@ -95,6 +95,7 @@
#include "chrome/browser/ui/profile_error_dialog.h"
#include "chrome/browser/ui/startup/bad_flags_prompt.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/browser/ui/webui/chrome_untrusted_web_ui_controller_factory.h"
#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/channel_info.h"
......@@ -1431,6 +1432,7 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() {
// called inside PostProfileInit.
content::WebUIControllerFactory::RegisterFactory(
ChromeWebUIControllerFactory::GetInstance());
ChromeUntrustedWebUIControllerFactory::RegisterInstance();
#if defined(OS_ANDROID)
page_info::SetPageInfoClient(new ChromePageInfoClient());
......
......@@ -196,6 +196,8 @@ static_library("ui") {
"webui/autofill_and_password_manager_internals/internals_ui_handler.h",
"webui/autofill_and_password_manager_internals/password_manager_internals_ui.cc",
"webui/autofill_and_password_manager_internals/password_manager_internals_ui.h",
"webui/chrome_untrusted_web_ui_controller_factory.cc",
"webui/chrome_untrusted_web_ui_controller_factory.h",
"webui/chrome_web_ui_controller_factory.cc",
"webui/chrome_web_ui_controller_factory.h",
"webui/components/components_handler.cc",
......
// Copyright 2020 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 "chrome/browser/ui/webui/chrome_untrusted_web_ui_controller_factory.h"
#include "base/no_destructor.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/common/url_constants.h"
#include "ui/webui/webui_config.h"
#include "url/gurl.h"
using WebUIConfigList =
std::vector<std::pair<std::string, std::unique_ptr<ui::WebUIConfig>>>;
namespace {
// Returns a std::vector<> containing all WebUIConfigs. We use a vector instead
// of adding WebUIConfigs directly into the flat_map because individual inserts
// are O(n), giving O(n^2) construction time for the entire map. By contrast,
// constructing from a vector is O(n log n).
WebUIConfigList CreateConfigs() {
WebUIConfigList config_list;
#if defined(OS_CHROMEOS) && !defined(OFFICIAL_BUILD)
auto register_config =
[&config_list](std::unique_ptr<ui::WebUIConfig> config) {
DCHECK_EQ(config->scheme(), content::kChromeUIUntrustedScheme);
const std::string& host = config->host();
config_list.emplace_back(host, std::move(config));
};
// Register WebUIConfigs below.
// TODO(ortuno): Remove when `register_config` is used to register configs.
DCHECK(&register_config);
#endif
return config_list;
}
} // namespace
// static
void ChromeUntrustedWebUIControllerFactory::RegisterInstance() {
static base::NoDestructor<ChromeUntrustedWebUIControllerFactory> instance;
content::WebUIControllerFactory::RegisterFactory(instance.get());
}
ChromeUntrustedWebUIControllerFactory::ChromeUntrustedWebUIControllerFactory()
: configs_(CreateConfigs()) {}
ChromeUntrustedWebUIControllerFactory::
~ChromeUntrustedWebUIControllerFactory() = default;
const ui::UntrustedWebUIControllerFactory::WebUIConfigMap&
ChromeUntrustedWebUIControllerFactory::GetWebUIConfigMap() {
return configs_;
}
// Copyright 2020 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.
#ifndef CHROME_BROWSER_UI_WEBUI_CHROME_UNTRUSTED_WEB_UI_CONTROLLER_FACTORY_H_
#define CHROME_BROWSER_UI_WEBUI_CHROME_UNTRUSTED_WEB_UI_CONTROLLER_FACTORY_H_
#include "ui/webui/untrusted_web_ui_controller_factory.h"
class ChromeUntrustedWebUIControllerFactory
: public ui::UntrustedWebUIControllerFactory {
public:
// Register the singleton instance of this class.
static void RegisterInstance();
ChromeUntrustedWebUIControllerFactory();
ChromeUntrustedWebUIControllerFactory(
const ChromeUntrustedWebUIControllerFactory&) = delete;
ChromeUntrustedWebUIControllerFactory& operator=(
const ChromeUntrustedWebUIControllerFactory&) = delete;
protected:
const WebUIConfigMap& GetWebUIConfigMap() override;
private:
~ChromeUntrustedWebUIControllerFactory() override;
WebUIConfigMap configs_;
};
#endif // CHROME_BROWSER_UI_WEBUI_CHROME_UNTRUSTED_WEB_UI_CONTROLLER_FACTORY_H_
......@@ -16,6 +16,7 @@
#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"
// Tests embedder specific behavior of WebUIs.
......@@ -23,9 +24,12 @@ class ChromeWebUINavigationBrowserTest : public InProcessBrowserTest {
public:
ChromeWebUINavigationBrowserTest() {
content::WebUIControllerFactory::RegisterFactory(&factory_);
content::WebUIControllerFactory::RegisterFactory(&untrusted_factory_);
}
~ChromeWebUINavigationBrowserTest() override {
content::WebUIControllerFactory::UnregisterFactoryForTesting(
&untrusted_factory_);
content::WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
}
......@@ -35,8 +39,13 @@ class ChromeWebUINavigationBrowserTest : public InProcessBrowserTest {
ASSERT_TRUE(embedded_test_server()->Start());
}
ui::TestUntrustedWebUIControllerFactory& untrusted_factory() {
return untrusted_factory_;
}
private:
content::TestWebUIControllerFactory factory_;
ui::TestUntrustedWebUIControllerFactory untrusted_factory_;
};
// Verify that a browser check stops websites from embeding chrome:// iframes.
......@@ -96,8 +105,8 @@ IN_PROC_BROWSER_TEST_F(
content::TestNavigationObserver observer(web_contents);
content::TestUntrustedDataSourceCSP csp;
csp.no_xfo = true;
content::AddUntrustedDataSource(browser()->profile(), "test-iframe-host",
csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-iframe-host", csp));
content::PwnMessageHelper::OpenURL(
child, content::GetChromeUntrustedUIURL("test-iframe-host/title1.html"));
......
......@@ -872,6 +872,7 @@ if (!is_android) {
"//ui/resources",
"//ui/shell_dialogs:test_support",
"//ui/web_dialogs:test_support",
"//ui/webui:test_support",
"//v8",
]
......
......@@ -39,12 +39,30 @@
#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/blink/public/common/input/web_mouse_wheel_event.h"
#include "ui/events/base_event_utils.h"
#include "ui/webui/untrusted_web_ui_browsertest_util.h"
namespace content {
namespace {
using WebUIImplBrowserTest = ContentBrowserTest;
class WebUIImplBrowserTest : public ContentBrowserTest {
public:
WebUIImplBrowserTest() {
WebUIControllerFactory::RegisterFactory(&untrusted_factory_);
}
~WebUIImplBrowserTest() override {
WebUIControllerFactory::UnregisterFactoryForTesting(&untrusted_factory_);
}
protected:
ui::TestUntrustedWebUIControllerFactory& untrusted_factory() {
return untrusted_factory_;
}
private:
ui::TestUntrustedWebUIControllerFactory untrusted_factory_;
};
// TODO(crbug.com/154571): Shared workers are not available on Android.
#if !defined(OS_ANDROID)
......@@ -224,7 +242,8 @@ IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest,
// SiteInstance swap.
IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, ForceSwapOnFromChromeToUntrusted) {
WebContents* web_contents = shell()->web_contents();
AddUntrustedDataSource(web_contents->GetBrowserContext(), "test-host");
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
const GURL web_ui_url(GetWebUIURL(kChromeUIHistogramHost));
EXPECT_TRUE(ContentWebUIControllerFactory::GetInstance()->UseWebUIForURL(
......@@ -256,7 +275,8 @@ IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, ForceSwapOnFromChromeToUntrusted) {
// SiteInstance swap.
IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, ForceSwapOnFromUntrustedToChrome) {
WebContents* web_contents = shell()->web_contents();
AddUntrustedDataSource(web_contents->GetBrowserContext(), "test-host");
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
ASSERT_TRUE(NavigateToURL(web_contents,
GetChromeUntrustedUIURL("test-host/title1.html")));
......@@ -374,10 +394,11 @@ IN_PROC_BROWSER_TEST_F(WebUIRequiringGestureBrowserTest,
// Verify that we can successfully navigate to a chrome-untrusted:// URL.
IN_PROC_BROWSER_TEST_F(WebUIImplBrowserTest, UntrustedSchemeLoads) {
auto* web_contents = shell()->web_contents();
AddUntrustedDataSource(web_contents->GetBrowserContext(), "test-host");
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
const GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title2.html"));
auto* web_contents = shell()->web_contents();
EXPECT_TRUE(NavigateToURL(web_contents, untrusted_url));
EXPECT_EQ(base::ASCIIToUTF16("Title Of Awesomeness"),
web_contents->GetTitle());
......
......@@ -26,6 +26,7 @@
#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 {
......@@ -58,8 +59,10 @@ class WebUINavigationBrowserTest : public ContentBrowserTest {
public:
WebUINavigationBrowserTest() {
WebUIControllerFactory::RegisterFactory(&factory_);
WebUIControllerFactory::RegisterFactory(&untrusted_factory_);
}
~WebUINavigationBrowserTest() override {
WebUIControllerFactory::UnregisterFactoryForTesting(&untrusted_factory_);
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
}
......@@ -69,6 +72,10 @@ class WebUINavigationBrowserTest : public ContentBrowserTest {
ASSERT_TRUE(embedded_test_server()->Start());
}
ui::TestUntrustedWebUIControllerFactory& untrusted_factory() {
return untrusted_factory_;
}
// Verify that a document running in a process that has WebUI bindings,
// regardless of scheme, can navigate an iframe to web content and the
// resulting document is properly site isolated.
......@@ -194,6 +201,7 @@ class WebUINavigationBrowserTest : public ContentBrowserTest {
private:
TestWebUIControllerFactory factory_;
ui::TestUntrustedWebUIControllerFactory untrusted_factory_;
DISALLOW_COPY_AND_ASSIGN(WebUINavigationBrowserTest);
};
......@@ -256,11 +264,12 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
// no web content can be loaded, even if the CSP allows it.
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInChromeUntrustedSchemeAllowedByCSP) {
// Add a DataSource with no iframe restrictions.
// Add an untrusted WebUI with no iframe restrictions.
TestUntrustedDataSourceCSP csp;
csp.child_src = "child-src * data:;";
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host", csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host", csp));
GURL main_frame_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
......@@ -331,9 +340,9 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
// content when the CSP disallows it.
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInChromeUntrustedSchemeDisallowedByCSP) {
// Add a DataSource which disallows iframes by default.
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host");
// Add an untrusted WebUI which disallows iframes by default.
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
GURL main_frame_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
......@@ -371,9 +380,10 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
// Add a DataSource for chrome-untrusted:// that can be iframe'd.
TestUntrustedDataSourceCSP csp;
csp.no_xfo = true;
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-iframe-host", csp);
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-iframe-host",
csp));
GURL untrusted_url(GetChromeUntrustedUIURL("test-iframe-host/title1.html"));
EXPECT_TRUE(ExecJs(shell(), JsReplace(kAddIframeScript, untrusted_url),
EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */));
observer.Wait();
......@@ -436,8 +446,8 @@ IN_PROC_BROWSER_TEST_F(
TestUntrustedDataSourceCSP csp;
csp.no_xfo = true;
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-iframe-host", csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-iframe-host", csp));
// Add iframe but don't navigate it to a chrome-untrusted:// URL yet.
EXPECT_TRUE(ExecJs(shell(),
......@@ -624,8 +634,8 @@ IN_PROC_BROWSER_TEST_F(
// Add a DataSource for chrome-untrusted:// that can be iframe'd.
TestUntrustedDataSourceCSP csp;
csp.no_xfo = true;
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host", csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host", csp));
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
// Navigate an iframe to a chrome-untrusted URL and verify that the navigation
......@@ -675,8 +685,8 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
// Add a DataSource for the chrome-untrusted:// iframe with frame ancestor
// chrome://web-ui.
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host", csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host", csp));
GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
TestNavigationObserver observer(shell()->web_contents());
......@@ -775,8 +785,8 @@ IN_PROC_BROWSER_TEST_F(
TestUntrustedDataSourceCSP csp;
csp.no_xfo = false;
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-iframe-host", csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-iframe-host", csp));
GURL untrusted_url(GetChromeUntrustedUIURL("test-iframe-host/title1.html"));
TestNavigationObserver observer(shell()->web_contents());
......@@ -959,8 +969,8 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
UntrustedWebUIOriginsRequireDedicatedProcess) {
// Add a DataSource which disallows iframes by default.
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host");
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
GURL chrome_untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
GURL expected_site_url(GetChromeUntrustedUIURL("test-host"));
......
......@@ -35,6 +35,7 @@
#include "content/shell/browser/shell.h"
#include "content/test/content_browser_test_utils_internal.h"
#include "net/base/url_util.h"
#include "ui/webui/untrusted_web_ui_browsertest_util.h"
#include "url/gurl.h"
namespace content {
......@@ -60,16 +61,24 @@ const char kSharedResourcesModuleJsPath[] = "resources/js/assert.m.js";
class WebUISecurityTest : public ContentBrowserTest {
public:
WebUISecurityTest() { WebUIControllerFactory::RegisterFactory(&factory_); }
WebUISecurityTest() {
WebUIControllerFactory::RegisterFactory(&factory_);
WebUIControllerFactory::RegisterFactory(&untrusted_factory_);
}
~WebUISecurityTest() override {
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
WebUIControllerFactory::UnregisterFactoryForTesting(&untrusted_factory_);
}
TestWebUIControllerFactory* factory() { return &factory_; }
ui::TestUntrustedWebUIControllerFactory& untrusted_factory() {
return untrusted_factory_;
}
private:
TestWebUIControllerFactory factory_;
ui::TestUntrustedWebUIControllerFactory untrusted_factory_;
DISALLOW_COPY_AND_ASSIGN(WebUISecurityTest);
};
......@@ -77,7 +86,8 @@ class WebUISecurityTest : public ContentBrowserTest {
// 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");
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
const GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(web_contents, untrusted_url));
......@@ -463,8 +473,8 @@ 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");
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host"));
EXPECT_TRUE(NavigateToURL(shell(), web_url));
EXPECT_EQ(web_url, shell()->web_contents()->GetLastCommittedURL());
......@@ -507,8 +517,8 @@ IN_PROC_BROWSER_TEST_F(
TestUntrustedDataSourceCSP csp;
csp.script_src = "script-src chrome-untrusted://resources;";
csp.no_trusted_types = true;
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host", csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>("test-host", csp));
GURL main_frame_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
......@@ -637,8 +647,8 @@ EvalJsResult PerformFetch(Shell* shell,
IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
DisallowWebPageFetchRequestToChromeUntrusted) {
const GURL untrusted_url = GURL("chrome-untrusted://test/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url.host());
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url.host()));
ASSERT_TRUE(embedded_test_server()->Start());
const GURL web_url = embedded_test_server()->GetURL("/title2.html");
......@@ -668,8 +678,8 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
// Verify a chrome-untrusted:// document can fetch itself.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, ChromeUntrustedFetchRequestToSelf) {
const GURL untrusted_url = GURL("chrome-untrusted://test/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url.host());
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url.host()));
EXPECT_TRUE(NavigateToURL(shell(), untrusted_url));
EXPECT_EQ("success",
......@@ -683,8 +693,9 @@ IN_PROC_BROWSER_TEST_F(
WebUISecurityTest,
DisallowCrossOriginFetchRequestToChromeUntrustedByDefault) {
const GURL untrusted_url1 = GURL("chrome-untrusted://test1/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url1.host());
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url1.host()));
const GURL untrusted_url2 = GURL("chrome-untrusted://test2/title2.html");
URLDataSource::Add(
shell()->web_contents()->GetBrowserContext(),
......@@ -728,8 +739,9 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
TestUntrustedDataSourceCSP csp;
csp.default_src = "default-src chrome-untrusted://test2;";
const GURL untrusted_url1 = GURL("chrome-untrusted://test1/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url1.host(), csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url1.host(),
csp));
const GURL untrusted_url2 = GURL("chrome-untrusted://test2/title2.html");
URLDataSource::Add(
......@@ -750,8 +762,9 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
TestUntrustedDataSourceCSP csp;
csp.default_src = "default-src chrome://webui;";
const GURL untrusted_url = GURL("chrome-untrusted://test1/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url.host(), csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url.host(),
csp));
const GURL chrome_url = GURL("chrome://webui/title2.html");
......@@ -803,8 +816,8 @@ EvalJsResult PerformXHRRequest(Shell* shell, const GURL& xhr_url) {
IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
DisallowWebPageXHRRequestToChromeUntrusted) {
const GURL untrusted_url = GURL("chrome-untrusted://test/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url.host());
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url.host()));
ASSERT_TRUE(embedded_test_server()->Start());
const GURL web_url = embedded_test_server()->GetURL("/title2.html");
......@@ -822,8 +835,8 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
AllowChromeUntrustedXHRRequestToSelf) {
const GURL untrusted_url = GURL("chrome-untrusted://test/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url.host());
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url.host()));
EXPECT_TRUE(NavigateToURL(shell(), untrusted_url));
EXPECT_EQ("success", PerformXHRRequest(shell(), untrusted_url));
......@@ -836,8 +849,9 @@ IN_PROC_BROWSER_TEST_F(
WebUISecurityTest,
DisallowCrossOriginXHRRequestToChromeUntrustedByDefault) {
const GURL untrusted_url1 = GURL("chrome-untrusted://test1/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url1.host());
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url1.host()));
const GURL untrusted_url2 = GURL("chrome-untrusted://test2/");
URLDataSource::Add(
shell()->web_contents()->GetBrowserContext(),
......@@ -866,8 +880,10 @@ IN_PROC_BROWSER_TEST_F(
TestUntrustedDataSourceCSP csp;
csp.default_src = "default-src chrome-untrusted://test2;";
const GURL untrusted_url1 = GURL("chrome-untrusted://test1/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url1.host(), csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url1.host(),
csp));
const GURL untrusted_url2 = GURL("chrome-untrusted://test2/");
URLDataSource::Add(
shell()->web_contents()->GetBrowserContext(),
......@@ -884,8 +900,9 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
TestUntrustedDataSourceCSP csp;
csp.default_src = "default-src chrome://webui;";
const GURL untrusted_url = GURL("chrome-untrusted://test1/title1.html");
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
untrusted_url.host(), csp);
untrusted_factory().add_web_ui_config(
std::make_unique<ui::TestUntrustedWebUIConfig>(untrusted_url.host(),
csp));
const GURL chrome_url = GURL("chrome://webui/title2.html");
......
......@@ -1353,6 +1353,7 @@ test("content_browsertests") {
"//ui/resources",
"//ui/shell_dialogs",
"//ui/snapshot",
"//ui/webui:test_support",
]
data = []
......
......@@ -8,10 +8,16 @@ static_library("webui") {
"mojo_bubble_web_ui_controller.h",
"mojo_web_ui_controller.cc",
"mojo_web_ui_controller.h",
"untrusted_web_ui_controller.cc",
"untrusted_web_ui_controller.h",
"untrusted_web_ui_controller_factory.cc",
"untrusted_web_ui_controller_factory.h",
"webui_allowlist.cc",
"webui_allowlist.h",
"webui_allowlist_provider.cc",
"webui_allowlist_provider.h",
"webui_config.cc",
"webui_config.h",
]
deps = [
......@@ -21,3 +27,19 @@ static_library("webui") {
"//services/service_manager/public/cpp",
]
}
source_set("test_support") {
testonly = true
sources = [
"untrusted_web_ui_browsertest_util.cc",
"untrusted_web_ui_browsertest_util.h",
]
deps = [
":webui",
"//base",
"//content/public/browser",
"//content/test:test_support",
]
}
// 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 "ui/webui/untrusted_web_ui_browsertest_util.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/url_constants.h"
namespace ui {
TestUntrustedWebUIControllerFactory::TestUntrustedWebUIControllerFactory() =
default;
TestUntrustedWebUIControllerFactory::~TestUntrustedWebUIControllerFactory() =
default;
const ui::UntrustedWebUIControllerFactory::WebUIConfigMap&
TestUntrustedWebUIControllerFactory::GetWebUIConfigMap() {
return configs_;
}
TestUntrustedWebUIConfig::TestUntrustedWebUIConfig(base::StringPiece host)
: WebUIConfig(content::kChromeUIUntrustedScheme, host) {}
TestUntrustedWebUIConfig::TestUntrustedWebUIConfig(
base::StringPiece host,
const content::TestUntrustedDataSourceCSP& content_security_policy)
: WebUIConfig(content::kChromeUIUntrustedScheme, host),
content_security_policy_(content_security_policy) {}
TestUntrustedWebUIConfig::~TestUntrustedWebUIConfig() = default;
std::unique_ptr<content::WebUIController>
TestUntrustedWebUIConfig::CreateWebUIController(content::WebUI* web_ui) {
return std::make_unique<TestUntrustedWebUIController>(
web_ui, host(), content_security_policy_);
}
TestUntrustedWebUIController::TestUntrustedWebUIController(
content::WebUI* web_ui,
const std::string& host,
const content::TestUntrustedDataSourceCSP& content_security_policy)
: ui::UntrustedWebUIController(web_ui) {
content::AddUntrustedDataSource(web_ui->GetWebContents()->GetBrowserContext(),
host, content_security_policy);
}
TestUntrustedWebUIController::~TestUntrustedWebUIController() = default;
} // namespace ui
// Copyright 2020 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.
#ifndef UI_WEBUI_UNTRUSTED_WEB_UI_BROWSERTEST_UTIL_H_
#define UI_WEBUI_UNTRUSTED_WEB_UI_BROWSERTEST_UTIL_H_
#include "content/public/test/web_ui_browsertest_util.h"
#include "ui/webui/untrusted_web_ui_controller.h"
#include "ui/webui/untrusted_web_ui_controller_factory.h"
#include "ui/webui/webui_config.h"
namespace ui {
class TestUntrustedWebUIControllerFactory
: public ui::UntrustedWebUIControllerFactory {
public:
TestUntrustedWebUIControllerFactory();
~TestUntrustedWebUIControllerFactory() override;
void add_web_ui_config(std::unique_ptr<ui::WebUIConfig> config) {
const std::string host = config->host();
configs_.insert(std::make_pair(host, std::move(config)));
}
protected:
const WebUIConfigMap& GetWebUIConfigMap() override;
private:
WebUIConfigMap configs_;
};
class TestUntrustedWebUIConfig : public ui::WebUIConfig {
public:
explicit TestUntrustedWebUIConfig(base::StringPiece host);
explicit TestUntrustedWebUIConfig(
base::StringPiece host,
const content::TestUntrustedDataSourceCSP& content_security_policy);
~TestUntrustedWebUIConfig() override;
std::unique_ptr<content::WebUIController> CreateWebUIController(
content::WebUI* web_ui) override;
const content::TestUntrustedDataSourceCSP content_security_policy_;
};
class TestUntrustedWebUIController : public ui::UntrustedWebUIController {
public:
explicit TestUntrustedWebUIController(
content::WebUI* web_ui,
const std::string& host,
const content::TestUntrustedDataSourceCSP& content_security_policy);
~TestUntrustedWebUIController() override;
};
} // namespace ui
#endif // UI_WEBUI_UNTRUSTED_WEB_UI_BROWSERTEST_UTIL_H_
// Copyright 2020 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 "ui/webui/untrusted_web_ui_controller.h"
#include "content/public/browser/web_ui.h"
namespace ui {
UntrustedWebUIController::UntrustedWebUIController(content::WebUI* web_ui)
: content::WebUIController(web_ui) {
// UntrustedWebUIController should never enable bindings.
web_ui->SetBindings(0);
}
UntrustedWebUIController::~UntrustedWebUIController() = default;
} // namespace ui
// Copyright 2020 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.
#ifndef UI_WEBUI_UNTRUSTED_WEB_UI_CONTROLLER_H_
#define UI_WEBUI_UNTRUSTED_WEB_UI_CONTROLLER_H_
#include "content/public/browser/web_ui_controller.h"
namespace content {
class WebUI;
}
namespace ui {
// UntrustedWebUIController is intended for WebUI pages that process untrusted
// content. These WebUIController should never request WebUI bindings.
class UntrustedWebUIController : public content::WebUIController {
public:
explicit UntrustedWebUIController(content::WebUI* contents);
~UntrustedWebUIController() override;
UntrustedWebUIController(UntrustedWebUIController&) = delete;
UntrustedWebUIController& operator=(const UntrustedWebUIController&) = delete;
};
} // namespace ui
#endif // UI_WEBUI_UNTRUSTED_WEB_UI_CONTROLLER_H_
// Copyright 2020 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 "ui/webui/untrusted_web_ui_controller_factory.h"
#include "base/no_destructor.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/common/url_constants.h"
#include "ui/webui/webui_config.h"
#include "url/gurl.h"
namespace ui {
UntrustedWebUIControllerFactory::UntrustedWebUIControllerFactory() = default;
UntrustedWebUIControllerFactory::~UntrustedWebUIControllerFactory() = default;
content::WebUI::TypeID UntrustedWebUIControllerFactory::GetWebUIType(
content::BrowserContext* browser_context,
const GURL& url) {
auto* config = GetConfigIfWebUIEnabled(browser_context, url);
if (!config)
return content::WebUI::kNoWebUI;
return reinterpret_cast<content::WebUI::TypeID>(config);
}
bool UntrustedWebUIControllerFactory::UseWebUIForURL(
content::BrowserContext* browser_context,
const GURL& url) {
return GetConfigIfWebUIEnabled(browser_context, url);
}
std::unique_ptr<content::WebUIController>
UntrustedWebUIControllerFactory::CreateWebUIControllerForURL(
content::WebUI* web_ui,
const GURL& url) {
auto* browser_context = web_ui->GetWebContents()->GetBrowserContext();
auto* config = GetConfigIfWebUIEnabled(browser_context, url);
if (!config)
return nullptr;
return config->CreateWebUIController(web_ui);
}
ui::WebUIConfig* UntrustedWebUIControllerFactory::GetConfigIfWebUIEnabled(
content::BrowserContext* browser_context,
const GURL& url) {
// This factory doesn't support non chrome-untrusted:// WebUIs.
if (!url.SchemeIs(content::kChromeUIUntrustedScheme))
return nullptr;
auto it = GetWebUIConfigMap().find(url.host_piece());
if (it == GetWebUIConfigMap().end())
return nullptr;
if (!it->second->IsWebUIEnabled(browser_context))
return nullptr;
return it->second.get();
}
} // namespace ui
// Copyright 2020 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.
#ifndef UI_WEBUI_UNTRUSTED_WEB_UI_CONTROLLER_FACTORY_H_
#define UI_WEBUI_UNTRUSTED_WEB_UI_CONTROLLER_FACTORY_H_
#include "content/public/browser/web_ui_controller_factory.h"
class GURL;
namespace content {
class BrowserContext;
class WebUIController;
} // namespace content
namespace ui {
class WebUIConfig;
// Factory class for WebUIControllers for chrome-untrusted:// URLs.
//
// To add a new WebUIController, subclass ui::WebUIConfig and add it to
// `CreateConfigs()` in the .cc.
class UntrustedWebUIControllerFactory : public content::WebUIControllerFactory {
public:
UntrustedWebUIControllerFactory();
~UntrustedWebUIControllerFactory() override;
UntrustedWebUIControllerFactory(const UntrustedWebUIControllerFactory&) =
delete;
UntrustedWebUIControllerFactory& operator=(
const UntrustedWebUIControllerFactory&) = delete;
content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
const GURL& url) final;
bool UseWebUIForURL(content::BrowserContext* browser_context,
const GURL& url) final;
std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(
content::WebUI* web_ui,
const GURL& url) final;
protected:
// Map of hosts to their corresponding WebUIConfigs.
using WebUIConfigMap =
base::flat_map<std::string, std::unique_ptr<ui::WebUIConfig>>;
virtual const WebUIConfigMap& GetWebUIConfigMap() = 0;
private:
// Returns the WebUIConfig for |url| if it's registered and the WebUI is
// enabled. (WebUIs can be disabled based on the profile or feature flags.)
ui::WebUIConfig* GetConfigIfWebUIEnabled(
content::BrowserContext* browser_context,
const GURL& url);
};
} // namespace ui
#endif // UI_WEBUI_UNTRUSTED_WEB_UI_CONTROLLER_FACTORY_H_
// Copyright 2020 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 "ui/webui/webui_config.h"
namespace ui {
WebUIConfig::WebUIConfig(base::StringPiece scheme, base::StringPiece host)
: scheme_(scheme), host_(host) {}
WebUIConfig::~WebUIConfig() = default;
bool WebUIConfig::IsWebUIEnabled(content::BrowserContext* browser_context) {
return true;
}
} // namespace ui
// Copyright 2020 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.
#ifndef UI_WEBUI_WEBUI_CONFIG_H_
#define UI_WEBUI_WEBUI_CONFIG_H_
#include <string>
#include "base/strings/string_piece.h"
namespace content {
class BrowserContext;
class WebUIController;
class WebUI;
} // namespace content
namespace ui {
// Class that stores properties for a WebUI.
class WebUIConfig {
public:
explicit WebUIConfig(base::StringPiece scheme, base::StringPiece host);
virtual ~WebUIConfig();
WebUIConfig(const WebUIConfig&) = delete;
WebUIConfig& operator=(const WebUIConfig&) = delete;
// Scheme for the WebUI.
const std::string& scheme() const { return scheme_; }
// Host the WebUI serves.
const std::string& host() const { return host_; }
// Returns whether the WebUI is enabled e.g. the necessary feature flags are
// on/off, the WebUI is enabled in incognito, etc. Defaults to true.
virtual bool IsWebUIEnabled(content::BrowserContext* browser_context);
// Returns a WebUIController for the WebUI.
virtual std::unique_ptr<content::WebUIController> CreateWebUIController(
content::WebUI* web_ui) = 0;
private:
const std::string scheme_;
const std::string host_;
};
} // namespace ui
#endif // UI_WEBUI_WEBUI_CONFIG_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment