summaryrefslogtreecommitdiff
path: root/chromium/content/browser/webui
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/content/browser/webui')
-rw-r--r--chromium/content/browser/webui/content_web_ui_controller_factory.cc6
-rw-r--r--chromium/content/browser/webui/shared_resources_data_source.cc69
-rw-r--r--chromium/content/browser/webui/shared_resources_data_source.h35
-rw-r--r--chromium/content/browser/webui/url_data_manager.cc5
-rw-r--r--chromium/content/browser/webui/url_data_manager_backend.cc54
-rw-r--r--chromium/content/browser/webui/web_ui_data_source_impl.cc87
-rw-r--r--chromium/content/browser/webui/web_ui_data_source_impl.h19
-rw-r--r--chromium/content/browser/webui/web_ui_data_source_unittest.cc72
-rw-r--r--chromium/content/browser/webui/web_ui_navigation_browsertest.cc48
-rw-r--r--chromium/content/browser/webui/web_ui_security_browsertest.cc488
-rw-r--r--chromium/content/browser/webui/web_ui_url_loader_factory.cc5
11 files changed, 650 insertions, 238 deletions
diff --git a/chromium/content/browser/webui/content_web_ui_controller_factory.cc b/chromium/content/browser/webui/content_web_ui_controller_factory.cc
index d0a353e6193..6012229fe08 100644
--- a/chromium/content/browser/webui/content_web_ui_controller_factory.cc
+++ b/chromium/content/browser/webui/content_web_ui_controller_factory.cc
@@ -15,6 +15,7 @@
#include "content/browser/process_internals/process_internals_ui.h"
#include "content/browser/service_worker/service_worker_internals_ui.h"
#include "content/browser/tracing/tracing_ui.h"
+#include "content/browser/ukm_internals_ui.h"
#include "content/browser/webrtc/webrtc_internals_ui.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
@@ -42,7 +43,8 @@ WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType(
url.host_piece() == kChromeUIAppCacheInternalsHost ||
url.host_piece() == kChromeUINetworkErrorsListingHost ||
url.host_piece() == kChromeUIProcessInternalsHost ||
- url.host_piece() == kChromeUIConversionInternalsHost) {
+ url.host_piece() == kChromeUIConversionInternalsHost ||
+ url.host_piece() == kChromeUIUkmHost) {
return const_cast<ContentWebUIControllerFactory*>(this);
}
return WebUI::kNoWebUI;
@@ -90,6 +92,8 @@ ContentWebUIControllerFactory::CreateWebUIControllerForURL(WebUI* web_ui,
return std::make_unique<ProcessInternalsUI>(web_ui);
if (url.host_piece() == kChromeUIConversionInternalsHost)
return std::make_unique<ConversionInternalsUI>(web_ui);
+ if (url.host_piece() == kChromeUIUkmHost)
+ return std::make_unique<UkmInternalsUI>(web_ui);
return nullptr;
}
diff --git a/chromium/content/browser/webui/shared_resources_data_source.cc b/chromium/content/browser/webui/shared_resources_data_source.cc
index 91ce33a80d0..402caeee8eb 100644
--- a/chromium/content/browser/webui/shared_resources_data_source.cc
+++ b/chromium/content/browser/webui/shared_resources_data_source.cc
@@ -12,6 +12,7 @@
#include "base/files/file_path.h"
#include "base/memory/ref_counted_memory.h"
#include "base/notreached.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
@@ -28,6 +29,9 @@
#include "content/public/common/url_constants.h"
#include "mojo/public/js/grit/mojo_bindings_resources.h"
#include "mojo/public/js/grit/mojo_bindings_resources_map.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
+#include "skia/grit/skia_resources.h"
+#include "skia/grit/skia_resources_map.h"
#include "ui/base/layout.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/resources/grit/webui_resources.h"
@@ -141,6 +145,17 @@ const std::map<int, std::string> CreateMojoResourceIdToAliasMap() {
};
}
+const std::map<int, std::string> CreateSkiaResourceIdToAliasMap() {
+ return std::map<int, std::string>{
+ {IDR_SKIA_BITMAP_MOJOM_LITE_JS,
+ "mojo/skia/public/mojom/bitmap.mojom-lite.js"},
+ {IDR_SKIA_IMAGE_INFO_MOJOM_LITE_JS,
+ "mojo/skia/public/mojom/image_info.mojom-lite.js"},
+ {IDR_SKIA_SKCOLOR_MOJOM_LITE_JS,
+ "mojo/skia/public/mojom/skcolor.mojom-lite.js"},
+ };
+}
+
#if defined(OS_CHROMEOS)
const std::map<int, std::string> CreateChromeosMojoResourceIdToAliasMap() {
return std::map<int, std::string>{
@@ -186,6 +201,12 @@ const std::map<int, std::string> CreateChromeosMojoResourceIdToAliasMap() {
{IDR_IP_ADDRESS_MOJOM_LITE_JS,
"mojo/services/network/public/mojom/"
"ip_address.mojom-lite.js"},
+ {IDR_NETWORK_HEALTH_MOJOM_HTML,
+ "mojo/chromeos/services/network_health/public/mojom/"
+ "network_health.mojom.html"},
+ {IDR_NETWORK_HEALTH_MOJOM_LITE_JS,
+ "mojo/chromeos/services/network_health/public/mojom/"
+ "network_health.mojom-lite.js"},
};
}
#endif // !defined(OS_CHROMEOS)
@@ -270,6 +291,8 @@ const ResourcesMap* CreateResourcesMap() {
AddAliasedResourcesToMap(CreateMojoResourceIdToAliasMap(),
kMojoBindingsResources, kMojoBindingsResourcesSize,
result);
+ AddAliasedResourcesToMap(CreateSkiaResourceIdToAliasMap(), kSkiaResources,
+ kSkiaResourcesSize, result);
#if defined(OS_CHROMEOS)
AddAliasedResourcesToMap(CreateChromeosMojoResourceIdToAliasMap(),
kChromeosResources, kChromeosResourcesSize, result);
@@ -291,14 +314,39 @@ int GetIdrForPath(const std::string& path) {
} // namespace
-SharedResourcesDataSource::SharedResourcesDataSource() {
+// static
+std::unique_ptr<SharedResourcesDataSource>
+SharedResourcesDataSource::CreateForChromeScheme() {
+ return std::make_unique<SharedResourcesDataSource>(PassKey(),
+ kChromeUIScheme);
}
-SharedResourcesDataSource::~SharedResourcesDataSource() {
+// static
+std::unique_ptr<SharedResourcesDataSource>
+SharedResourcesDataSource::CreateForChromeUntrustedScheme() {
+ return std::make_unique<SharedResourcesDataSource>(PassKey(),
+ kChromeUIUntrustedScheme);
}
+SharedResourcesDataSource::SharedResourcesDataSource(PassKey,
+ const std::string& scheme)
+ : scheme_(scheme) {}
+
+SharedResourcesDataSource::~SharedResourcesDataSource() = default;
+
std::string SharedResourcesDataSource::GetSource() {
- return kChromeUIResourcesHost;
+ // URLDataManagerBackend assumes that chrome:// data sources return just the
+ // hostname for GetSource().
+ if (scheme_ == kChromeUIScheme)
+ return kChromeUIResourcesHost;
+
+ // We only expect chrome-untrusted:// scheme at this point.
+ DCHECK_EQ(kChromeUIUntrustedScheme, scheme_);
+
+ // Other schemes (i.e. chrome-untrusted://) return the scheme and host
+ // together.
+ return base::StrCat(
+ {scheme_, url::kStandardSchemeSeparator, kChromeUIResourcesHost});
}
void SharedResourcesDataSource::StartDataRequest(
@@ -386,12 +434,12 @@ bool SharedResourcesDataSource::ShouldServeMimeTypeAsContentTypeHeader() {
std::string SharedResourcesDataSource::GetAccessControlAllowOriginForOrigin(
const std::string& origin) {
- // For now we give access only for "chrome://*" origins.
+ // For now we give access only for origins with the allowed scheme.
// According to CORS spec, Access-Control-Allow-Origin header doesn't support
// wildcards, so we need to set its value explicitly by passing the |origin|
// back.
- std::string allowed_origin_prefix = kChromeUIScheme;
- allowed_origin_prefix += "://";
+ const std::string allowed_origin_prefix =
+ base::StrCat({scheme_, url::kStandardSchemeSeparator});
if (!base::StartsWith(origin, allowed_origin_prefix,
base::CompareCase::SENSITIVE)) {
return "null";
@@ -399,8 +447,13 @@ std::string SharedResourcesDataSource::GetAccessControlAllowOriginForOrigin(
return origin;
}
-std::string SharedResourcesDataSource::GetContentSecurityPolicyWorkerSrc() {
- return "worker-src blob: 'self';";
+std::string SharedResourcesDataSource::GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName directive) {
+ if (directive == network::mojom::CSPDirectiveName::WorkerSrc) {
+ return "worker-src blob: 'self';";
+ }
+
+ return content::URLDataSource::GetContentSecurityPolicy(directive);
}
#if defined(OS_CHROMEOS)
diff --git a/chromium/content/browser/webui/shared_resources_data_source.h b/chromium/content/browser/webui/shared_resources_data_source.h
index 4500bb40c17..9da35b581bf 100644
--- a/chromium/content/browser/webui/shared_resources_data_source.h
+++ b/chromium/content/browser/webui/shared_resources_data_source.h
@@ -8,16 +8,34 @@
#include <string>
#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/single_thread_task_runner.h"
+#include "base/util/type_safety/pass_key.h"
#include "content/public/browser/url_data_source.h"
namespace content {
-// A DataSource for chrome://resources/ URLs.
+// A DataSource for chrome://resources/ and chrome-untrusted://resources/ URLs.
+// TODO(https://crbug.com/866236): chrome-untrusted://resources/ is not
+// currently fully functional, as some resources have absolute
+// chrome://resources URLs. If you need access to chrome-untrusted://resources/
+// resources that are not currently functional, it is up to you to get them
+// working.
class SharedResourcesDataSource : public URLDataSource {
public:
- SharedResourcesDataSource();
+ using PassKey = util::PassKey<SharedResourcesDataSource>;
+
+ // Creates a SharedResourcesDataSource instance for chrome://resources.
+ static std::unique_ptr<SharedResourcesDataSource> CreateForChromeScheme();
+
+ // Creates a SharedResourcesDataSource instance for
+ // chrome-untrusted://resources.
+ static std::unique_ptr<SharedResourcesDataSource>
+ CreateForChromeUntrustedScheme();
+
+ SharedResourcesDataSource(PassKey, const std::string& scheme);
+ SharedResourcesDataSource(const SharedResourcesDataSource&) = delete;
+ SharedResourcesDataSource& operator=(const SharedResourcesDataSource&) =
+ delete;
+ ~SharedResourcesDataSource() override;
// URLDataSource implementation.
std::string GetSource() override;
@@ -29,7 +47,8 @@ class SharedResourcesDataSource : public URLDataSource {
bool ShouldServeMimeTypeAsContentTypeHeader() override;
std::string GetAccessControlAllowOriginForOrigin(
const std::string& origin) override;
- std::string GetContentSecurityPolicyWorkerSrc() override;
+ std::string GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName directive) override;
#if defined(OS_CHROMEOS)
void DisablePolymer2ForHost(const std::string& host) override;
#endif // defined (OS_CHROMEOS)
@@ -41,9 +60,9 @@ class SharedResourcesDataSource : public URLDataSource {
bool IsPolymer2DisabledForPage(const WebContents::Getter& wc_getter);
#endif // defined (OS_CHROMEOS)
- ~SharedResourcesDataSource() override;
-
- DISALLOW_COPY_AND_ASSIGN(SharedResourcesDataSource);
+ // The URL scheme this data source is accessed from, e.g. "chrome" or
+ // "chrome-untrusted".
+ const std::string scheme_;
};
} // namespace content
diff --git a/chromium/content/browser/webui/url_data_manager.cc b/chromium/content/browser/webui/url_data_manager.cc
index 401b38a0088..ab81bf4e06e 100644
--- a/chromium/content/browser/webui/url_data_manager.cc
+++ b/chromium/content/browser/webui/url_data_manager.cc
@@ -16,7 +16,6 @@
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/synchronization/lock.h"
-#include "base/task/post_task.h"
#include "base/thread_annotations.h"
#include "content/browser/resource_context_impl.h"
#include "content/browser/webui/url_data_manager_backend.h"
@@ -105,8 +104,8 @@ void URLDataManager::DeleteDataSource(const URLDataSourceImpl* data_source) {
}
if (schedule_delete) {
// Schedule a task to delete the DataSource back on the UI thread.
- base::PostTask(FROM_HERE, {BrowserThread::UI},
- base::BindOnce(&URLDataManager::DeleteDataSources));
+ GetUIThreadTaskRunner({})->PostTask(
+ FROM_HERE, base::BindOnce(&URLDataManager::DeleteDataSources));
}
}
diff --git a/chromium/content/browser/webui/url_data_manager_backend.cc b/chromium/content/browser/webui/url_data_manager_backend.cc
index 2e4c8136128..a9d88c5b4cf 100644
--- a/chromium/content/browser/webui/url_data_manager_backend.cc
+++ b/chromium/content/browser/webui/url_data_manager_backend.cc
@@ -43,6 +43,7 @@
#include "net/url_request/url_request_error_job.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_factory.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/base/template_expressions.h"
#include "ui/base/webui/i18n_source_stream.h"
#include "url/url_util.h"
@@ -71,9 +72,17 @@ bool SchemeIsInSchemes(const std::string& scheme,
} // namespace
URLDataManagerBackend::URLDataManagerBackend() : next_request_id_(0) {
- URLDataSource* shared_source = new SharedResourcesDataSource();
- AddDataSource(new URLDataSourceImpl(shared_source->GetSource(),
- base::WrapUnique(shared_source)));
+ // Add a shared data source for chrome://resources. For chrome:// data sources
+ // we use the host name as the source name.
+ AddDataSource(new URLDataSourceImpl(
+ kChromeUIResourcesHost,
+ SharedResourcesDataSource::CreateForChromeScheme()));
+
+ // Add a shared data source for chrome-untrusted://resources. For
+ // chrome-untrusted:// data sources we use the full origin as the source name.
+ AddDataSource(new URLDataSourceImpl(
+ kChromeUIUntrustedResourcesURL,
+ SharedResourcesDataSource::CreateForChromeUntrustedScheme()));
}
URLDataManagerBackend::~URLDataManagerBackend() = default;
@@ -156,19 +165,31 @@ scoped_refptr<net::HttpResponseHeaders> URLDataManagerBackend::GetHeaders(
// response headers.
if (source->ShouldAddContentSecurityPolicy()) {
std::string csp_header;
- csp_header.append(source->GetContentSecurityPolicyChildSrc());
- csp_header.append(source->GetContentSecurityPolicyDefaultSrc());
- csp_header.append(source->GetContentSecurityPolicyImgSrc());
- csp_header.append(source->GetContentSecurityPolicyObjectSrc());
- csp_header.append(source->GetContentSecurityPolicyScriptSrc());
- csp_header.append(source->GetContentSecurityPolicyStyleSrc());
- csp_header.append(source->GetContentSecurityPolicyWorkerSrc());
+
+ const network::mojom::CSPDirectiveName kAllDirectives[] = {
+ network::mojom::CSPDirectiveName::ChildSrc,
+ network::mojom::CSPDirectiveName::DefaultSrc,
+ network::mojom::CSPDirectiveName::ImgSrc,
+ network::mojom::CSPDirectiveName::MediaSrc,
+ network::mojom::CSPDirectiveName::ObjectSrc,
+ network::mojom::CSPDirectiveName::ScriptSrc,
+ network::mojom::CSPDirectiveName::StyleSrc,
+ network::mojom::CSPDirectiveName::WorkerSrc,
+ network::mojom::CSPDirectiveName::ConnectSrc};
+
+ for (auto& directive : kAllDirectives) {
+ csp_header.append(source->GetContentSecurityPolicy(directive));
+ }
+
// TODO(crbug.com/1051745): Both CSP frame ancestors and XFO headers may be
// added to the response but frame ancestors would take precedence. In the
// future, XFO will be removed so when that happens remove the check and
// always add frame ancestors.
- if (source->ShouldDenyXFrameOptions())
- csp_header.append(source->GetContentSecurityPolicyFrameAncestors());
+ if (source->ShouldDenyXFrameOptions()) {
+ csp_header.append(source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::FrameAncestors));
+ }
+
headers->SetHeader(kChromeURLContentSecurityPolicyHeaderName, csp_header);
}
@@ -218,13 +239,12 @@ bool URLDataManagerBackend::CheckURLIsValid(const GURL& url) {
}
bool URLDataManagerBackend::IsValidNetworkErrorCode(int error_code) {
- std::unique_ptr<base::DictionaryValue> error_codes = net::GetNetConstants();
+ base::Value error_codes = net::GetNetConstants();
const base::DictionaryValue* net_error_codes_dict = nullptr;
- for (base::DictionaryValue::Iterator itr(*error_codes); !itr.IsAtEnd();
- itr.Advance()) {
- if (itr.key() == kNetworkErrorKey) {
- itr.value().GetAsDictionary(&net_error_codes_dict);
+ for (const auto& item : error_codes.DictItems()) {
+ if (item.first == kNetworkErrorKey) {
+ item.second.GetAsDictionary(&net_error_codes_dict);
break;
}
}
diff --git a/chromium/content/browser/webui/web_ui_data_source_impl.cc b/chromium/content/browser/webui/web_ui_data_source_impl.cc
index 6df18f9c6e2..3b43e4950ab 100644
--- a/chromium/content/browser/webui/web_ui_data_source_impl.cc
+++ b/chromium/content/browser/webui/web_ui_data_source_impl.cc
@@ -21,6 +21,7 @@
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/url_constants.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "ui/base/template_expressions.h"
#include "ui/base/webui/jstemplate_builder.h"
#include "ui/base/webui/web_ui_util.h"
@@ -80,42 +81,21 @@ class WebUIDataSourceImpl::InternalDataSource : public URLDataSource {
}
bool AllowCaching() override { return false; }
bool ShouldAddContentSecurityPolicy() override { return parent_->add_csp_; }
- std::string GetContentSecurityPolicyChildSrc() override {
- return parent_->child_src_.value_or(
- URLDataSource::GetContentSecurityPolicyChildSrc());
- }
- std::string GetContentSecurityPolicyDefaultSrc() override {
- return parent_->default_src_.value_or(
- URLDataSource::GetContentSecurityPolicyDefaultSrc());
- }
- std::string GetContentSecurityPolicyImgSrc() override {
- return parent_->img_src_.value_or(
- URLDataSource::GetContentSecurityPolicyImgSrc());
- }
- std::string GetContentSecurityPolicyObjectSrc() override {
- return parent_->object_src_.value_or(
- URLDataSource::GetContentSecurityPolicyObjectSrc());
- }
- std::string GetContentSecurityPolicyScriptSrc() override {
- return parent_->script_src_.value_or(
- URLDataSource::GetContentSecurityPolicyScriptSrc());
- }
- std::string GetContentSecurityPolicyStyleSrc() override {
- return parent_->style_src_.value_or(
- URLDataSource::GetContentSecurityPolicyStyleSrc());
- }
- std::string GetContentSecurityPolicyWorkerSrc() override {
- return parent_->worker_src_.value_or(
- URLDataSource::GetContentSecurityPolicyWorkerSrc());
- }
- std::string GetContentSecurityPolicyFrameAncestors() override {
- std::string frame_ancestors;
- if (parent_->frame_ancestors_.size() == 0)
- frame_ancestors += " 'none'";
- for (const GURL& frame_ancestor : parent_->frame_ancestors_) {
- frame_ancestors += " " + frame_ancestor.spec();
+ std::string GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName directive) override {
+ if (parent_->csp_overrides_.contains(directive)) {
+ return parent_->csp_overrides_.at(directive);
+ } else if (directive == network::mojom::CSPDirectiveName::FrameAncestors) {
+ std::string frame_ancestors;
+ if (parent_->frame_ancestors_.size() == 0)
+ frame_ancestors += " 'none'";
+ for (const GURL& frame_ancestor : parent_->frame_ancestors_) {
+ frame_ancestors += " " + frame_ancestor.spec();
+ }
+ return "frame-ancestors" + frame_ancestors + ";";
}
- return "frame-ancestors" + frame_ancestors + ";";
+
+ return URLDataSource::GetContentSecurityPolicy(directive);
}
bool ShouldDenyXFrameOptions() override {
return parent_->deny_xframe_options_;
@@ -214,39 +194,10 @@ void WebUIDataSourceImpl::DisableContentSecurityPolicy() {
add_csp_ = false;
}
-void WebUIDataSourceImpl::OverrideContentSecurityPolicyChildSrc(
- const std::string& data) {
- child_src_ = data;
-}
-
-void WebUIDataSourceImpl::OverrideContentSecurityPolicyDefaultSrc(
- const std::string& data) {
- default_src_ = data;
-}
-
-void WebUIDataSourceImpl::OverrideContentSecurityPolicyImgSrc(
- const std::string& data) {
- img_src_ = data;
-}
-
-void WebUIDataSourceImpl::OverrideContentSecurityPolicyObjectSrc(
- const std::string& data) {
- object_src_ = data;
-}
-
-void WebUIDataSourceImpl::OverrideContentSecurityPolicyScriptSrc(
- const std::string& data) {
- script_src_ = data;
-}
-
-void WebUIDataSourceImpl::OverrideContentSecurityPolicyStyleSrc(
- const std::string& data) {
- style_src_ = data;
-}
-
-void WebUIDataSourceImpl::OverrideContentSecurityPolicyWorkerSrc(
- const std::string& data) {
- worker_src_ = data;
+void WebUIDataSourceImpl::OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName directive,
+ const std::string& value) {
+ csp_overrides_.insert_or_assign(directive, value);
}
void WebUIDataSourceImpl::AddFrameAncestor(const GURL& frame_ancestor) {
diff --git a/chromium/content/browser/webui/web_ui_data_source_impl.h b/chromium/content/browser/webui/web_ui_data_source_impl.h
index 7d8c45420c7..626edd936c2 100644
--- a/chromium/content/browser/webui/web_ui_data_source_impl.h
+++ b/chromium/content/browser/webui/web_ui_data_source_impl.h
@@ -12,6 +12,7 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
+#include "base/containers/flat_map.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/values.h"
@@ -45,14 +46,8 @@ class CONTENT_EXPORT WebUIDataSourceImpl : public URLDataSourceImpl,
handle_request_callback) override;
void DisableReplaceExistingSource() override;
void DisableContentSecurityPolicy() override;
- void OverrideContentSecurityPolicyChildSrc(const std::string& data) override;
- void OverrideContentSecurityPolicyDefaultSrc(
- const std::string& data) override;
- void OverrideContentSecurityPolicyImgSrc(const std::string& data) override;
- void OverrideContentSecurityPolicyObjectSrc(const std::string& data) override;
- void OverrideContentSecurityPolicyScriptSrc(const std::string& data) override;
- void OverrideContentSecurityPolicyStyleSrc(const std::string& data) override;
- void OverrideContentSecurityPolicyWorkerSrc(const std::string& data) override;
+ void OverrideContentSecurityPolicy(network::mojom::CSPDirectiveName directive,
+ const std::string& value) override;
void DisableDenyXFrameOptions() override;
void EnableReplaceI18nInJS() override;
std::string GetSource() override;
@@ -116,13 +111,7 @@ class CONTENT_EXPORT WebUIDataSourceImpl : public URLDataSourceImpl,
bool add_csp_ = true;
- base::Optional<std::string> child_src_;
- base::Optional<std::string> default_src_;
- base::Optional<std::string> img_src_;
- base::Optional<std::string> object_src_;
- base::Optional<std::string> script_src_;
- base::Optional<std::string> style_src_;
- base::Optional<std::string> worker_src_;
+ base::flat_map<network::mojom::CSPDirectiveName, std::string> csp_overrides_;
bool deny_xframe_options_ = true;
bool add_load_time_data_defaults_ = true;
bool replace_existing_source_ = true;
diff --git a/chromium/content/browser/webui/web_ui_data_source_unittest.cc b/chromium/content/browser/webui/web_ui_data_source_unittest.cc
index 36d70553e12..7ec74093d48 100644
--- a/chromium/content/browser/webui/web_ui_data_source_unittest.cc
+++ b/chromium/content/browser/webui/web_ui_data_source_unittest.cc
@@ -9,6 +9,7 @@
#include "content/browser/webui/web_ui_data_source_impl.h"
#include "content/public/test/browser_task_environment.h"
#include "content/test/test_content_client.h"
+#include "services/network/public/mojom/content_security_policy.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
@@ -274,41 +275,76 @@ TEST_F(WebUIDataSourceTest, SetCspValues) {
// Default values.
EXPECT_EQ("child-src 'none';",
- url_data_source->GetContentSecurityPolicyChildSrc());
- EXPECT_EQ("", url_data_source->GetContentSecurityPolicyDefaultSrc());
- EXPECT_EQ("", url_data_source->GetContentSecurityPolicyImgSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ChildSrc));
+ EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::DefaultSrc));
+ EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ImgSrc));
+ EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::MediaSrc));
EXPECT_EQ("object-src 'none';",
- url_data_source->GetContentSecurityPolicyObjectSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ObjectSrc));
EXPECT_EQ("script-src chrome://resources 'self';",
- url_data_source->GetContentSecurityPolicyScriptSrc());
- EXPECT_EQ("", url_data_source->GetContentSecurityPolicyStyleSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ScriptSrc));
+ EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::StyleSrc));
+ EXPECT_EQ("", url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ConnectSrc));
// Override each directive and test it updates the underlying URLDataSource.
- source()->OverrideContentSecurityPolicyChildSrc("child-src 'self';");
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ChildSrc, "child-src 'self';");
EXPECT_EQ("child-src 'self';",
- url_data_source->GetContentSecurityPolicyChildSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ChildSrc));
- source()->OverrideContentSecurityPolicyDefaultSrc("default-src 'self';");
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::DefaultSrc, "default-src 'self';");
EXPECT_EQ("default-src 'self';",
- url_data_source->GetContentSecurityPolicyDefaultSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::DefaultSrc));
- source()->OverrideContentSecurityPolicyImgSrc("img-src 'self' blob:;");
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ImgSrc, "img-src 'self' blob:;");
EXPECT_EQ("img-src 'self' blob:;",
- url_data_source->GetContentSecurityPolicyImgSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ImgSrc));
- source()->OverrideContentSecurityPolicyObjectSrc("object-src 'self' data:;");
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::MediaSrc, "media-src 'self' blob:;");
+ EXPECT_EQ("media-src 'self' blob:;",
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::MediaSrc));
+
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ObjectSrc, "object-src 'self' data:;");
EXPECT_EQ("object-src 'self' data:;",
- url_data_source->GetContentSecurityPolicyObjectSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ObjectSrc));
- source()->OverrideContentSecurityPolicyScriptSrc(
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ScriptSrc,
"script-src chrome://resources 'self' 'unsafe-inline';");
EXPECT_EQ("script-src chrome://resources 'self' 'unsafe-inline';",
- url_data_source->GetContentSecurityPolicyScriptSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ScriptSrc));
- source()->OverrideContentSecurityPolicyStyleSrc(
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::StyleSrc,
"style-src 'self' 'unsafe-inline';");
EXPECT_EQ("style-src 'self' 'unsafe-inline';",
- url_data_source->GetContentSecurityPolicyStyleSrc());
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::StyleSrc));
+
+ source()->OverrideContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ConnectSrc,
+ "connect-src 'self' 'unsafe-inline';");
+ EXPECT_EQ("connect-src 'self' 'unsafe-inline';",
+ url_data_source->GetContentSecurityPolicy(
+ network::mojom::CSPDirectiveName::ConnectSrc));
}
} // namespace content
diff --git a/chromium/content/browser/webui/web_ui_navigation_browsertest.cc b/chromium/content/browser/webui/web_ui_navigation_browsertest.cc
index 6d0542244bf..c77fb54c3fd 100644
--- a/chromium/content/browser/webui/web_ui_navigation_browsertest.cc
+++ b/chromium/content/browser/webui/web_ui_navigation_browsertest.cc
@@ -7,6 +7,7 @@
#include "build/build_config.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/bindings_policy.h"
@@ -35,6 +36,15 @@ const char kAddIframeScript[] =
"frame.src = $1;\n"
"document.body.appendChild(frame);\n";
+content::mojom::OpenURLParamsPtr CreateOpenURLParams(const GURL& url) {
+ auto params = content::mojom::OpenURLParams::New();
+ params->url = url;
+ params->disposition = WindowOpenDisposition::CURRENT_TAB;
+ params->should_replace_current_entry = false;
+ params->user_gesture = true;
+ return params;
+}
+
} // namespace
class WebUINavigationBrowserTest : public ContentBrowserTest {
@@ -241,7 +251,7 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
WebFrameInChromeUntrustedSchemeAllowedByCSP) {
// Add a DataSource with no iframe restrictions.
TestUntrustedDataSourceCSP csp;
- csp.child_src = "";
+ csp.child_src = "child-src * data:;";
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-host", csp);
GURL main_frame_url(GetChromeUntrustedUIURL("test-host/title1.html"));
@@ -378,14 +388,12 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
}
// Verify that a browser check stops websites from embeding chrome:// iframes.
-// This tests the FrameHostMsg_OpenURL path.
+// This tests the OpenURL Mojo method.
IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
DisallowEmbeddingChromeSchemeFromWebFrameBrowserCheck) {
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"));
-
// Add iframe but don't navigate it to a chrome:// URL yet.
EXPECT_TRUE(ExecJs(shell(),
"var frame = document.createElement('iframe');\n"
@@ -403,8 +411,8 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
// This bypasses the renderer-side check that would have stopped the
// navigation.
TestNavigationObserver observer(shell()->web_contents());
- content::PwnMessageHelper::OpenURL(child->GetProcess(), child->GetRoutingID(),
- webui_url);
+ static_cast<content::RenderFrameHostImpl*>(child)->OpenURL(
+ CreateOpenURLParams(GetWebUIURL("web-ui/title1.html?noxfo=true")));
observer.Wait();
child = root->child_at(0)->current_frame_host();
@@ -412,7 +420,7 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
}
// Verify that a browser check stops websites from embeding chrome-untrusted://
-// iframes. This tests the FrameHostMsg_OpenURL path.
+// iframes. This tests the OpenURL Mojo method path.
IN_PROC_BROWSER_TEST_F(
WebUINavigationBrowserTest,
DisallowEmbeddingChromeUntrustedSchemeFromWebFrameBrowserCheck) {
@@ -424,8 +432,6 @@ IN_PROC_BROWSER_TEST_F(
AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
"test-iframe-host", csp);
- GURL untrusted_url(GetChromeUntrustedUIURL("test-iframe-host/title1.html"));
-
// Add iframe but don't navigate it to a chrome-untrusted:// URL yet.
EXPECT_TRUE(ExecJs(shell(),
"var frame = document.createElement('iframe');\n"
@@ -439,11 +445,12 @@ IN_PROC_BROWSER_TEST_F(
RenderFrameHost* child = root->child_at(0)->current_frame_host();
EXPECT_EQ("about:blank", child->GetLastCommittedURL());
- // Simulate an IPC message to navigate the subframe to a
- // chrome-untrusted:// URL.
+ // Simulate a Mojo message to navigate the subframe to a chrome-untrusted://
+ // URL.
TestNavigationObserver observer(shell()->web_contents());
- content::PwnMessageHelper::OpenURL(child->GetProcess(), child->GetRoutingID(),
- untrusted_url);
+ static_cast<content::RenderFrameHostImpl*>(child)->OpenURL(
+ CreateOpenURLParams(
+ GetChromeUntrustedUIURL("test-iframe-host/title1.html")));
observer.Wait();
child = root->child_at(0)->current_frame_host();
@@ -642,16 +649,17 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest,
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
- RenderFrameHost* webui_rfh = root->current_frame_host();
- scoped_refptr<SiteInstance> webui_site_instance =
+ 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->lock_url().is_empty());
EXPECT_EQ(ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
root->current_frame_host()->GetProcess()->GetID()),
- webui_site_instance->GetSiteURL());
+ webui_site_instance->lock_url());
TestUntrustedDataSourceCSP csp;
std::vector<std::string> frame_ancestors({"chrome://web-ui"});
@@ -836,8 +844,8 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, WebUIMainFrameToWebAllowed) {
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
- RenderFrameHost* webui_rfh = root->current_frame_host();
- scoped_refptr<SiteInstance> webui_site_instance =
+ RenderFrameHostImpl* webui_rfh = root->current_frame_host();
+ scoped_refptr<SiteInstanceImpl> webui_site_instance =
webui_rfh->GetSiteInstance();
EXPECT_EQ(chrome_url, webui_rfh->GetLastCommittedURL());
@@ -845,7 +853,7 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, WebUIMainFrameToWebAllowed) {
webui_rfh->GetProcess()->GetID()));
EXPECT_EQ(ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
root->current_frame_host()->GetProcess()->GetID()),
- webui_site_instance->GetSiteURL());
+ webui_site_instance->lock_url());
GURL web_url(embedded_test_server()->GetURL("/title2.html"));
std::string script =
@@ -864,7 +872,7 @@ IN_PROC_BROWSER_TEST_F(WebUINavigationBrowserTest, WebUIMainFrameToWebAllowed) {
root->current_frame_host()->GetProcess()->GetID()));
EXPECT_NE(ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
root->current_frame_host()->GetProcess()->GetID()),
- webui_site_instance->GetSiteURL());
+ webui_site_instance->lock_url());
}
#if !defined(OS_ANDROID)
diff --git a/chromium/content/browser/webui/web_ui_security_browsertest.cc b/chromium/content/browser/webui/web_ui_security_browsertest.cc
index 8e53304c2ab..2fe4c5914c2 100644
--- a/chromium/content/browser/webui/web_ui_security_browsertest.cc
+++ b/chromium/content/browser/webui/web_ui_security_browsertest.cc
@@ -7,20 +7,25 @@
#include "base/hash/hash.h"
#include "base/memory/ref_counted_memory.h"
#include "base/path_service.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
+#include "build/build_config.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/common/content_navigation_policy.h"
#include "content/public/browser/site_isolation_policy.h"
+#include "content/public/browser/url_data_source.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/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"
@@ -36,6 +41,25 @@
namespace content {
+namespace {
+
+// Loads a given module script. The promise resolves to true if the script loads
+// successfully, and false otherwise.
+const char kAddScriptModuleScript[] =
+ "new Promise((resolve, reject) => {\n"
+ " const script = document.createElement('script');\n"
+ " script.src = $1;\n"
+ " script.type = 'module';\n"
+ " script.onload = () => resolve(true);\n"
+ " script.onerror = () => resolve(false);\n"
+ " document.body.appendChild(script);\n"
+ "});\n";
+
+// Path to an existing chrome-untrusted://resources script.
+const char kSharedResourcesModuleJsPath[] = "resources/js/assert.m.js";
+
+} // namespace
+
class WebUISecurityTest : public ContentBrowserTest {
public:
WebUISecurityTest() { WebUIControllerFactory::RegisterFactory(&factory_); }
@@ -295,7 +319,11 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIReuseInSubframe) {
}
EXPECT_EQ(initial_site_instance,
child->current_frame_host()->GetSiteInstance());
- EXPECT_EQ(initial_web_ui, child->current_frame_host()->web_ui());
+ if (CreateNewHostForSameSiteSubframe()) {
+ EXPECT_NE(initial_web_ui, child->current_frame_host()->web_ui());
+ } else {
+ 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"));
@@ -328,7 +356,11 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIReuseInSubframe) {
}
EXPECT_EQ(second_site_instance,
child->current_frame_host()->GetSiteInstance());
- EXPECT_EQ(second_web_ui, child->current_frame_host()->web_ui());
+ if (CreateNewHostForSameSiteSubframe()) {
+ EXPECT_NE(second_web_ui, child->current_frame_host()->web_ui());
+ } else {
+ 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
@@ -428,81 +460,6 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIFailedNavigation) {
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) {
@@ -537,6 +494,46 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
}
}
+#if defined(OS_ANDROID)
+// TODO(https://crbug.com/1085196): This sometimes fails on Android bots.
+#define MAYBE_ChromeUntrustedFramesCanUseChromeUntrustedResources \
+ DISABLED_ChromeUntrustedFramesCanUseChromeUntrustedResources
+#else
+#define MAYBE_ChromeUntrustedFramesCanUseChromeUntrustedResources \
+ ChromeUntrustedFramesCanUseChromeUntrustedResources
+#endif // defined(OS_ANDROID)
+IN_PROC_BROWSER_TEST_F(
+ WebUISecurityTest,
+ MAYBE_ChromeUntrustedFramesCanUseChromeUntrustedResources) {
+ // Add a DataSource whose CSP allows chrome-untrusted://resources scripts.
+ TestUntrustedDataSourceCSP csp;
+ csp.script_src = "script-src chrome-untrusted://resources;";
+ AddUntrustedDataSource(shell()->web_contents()->GetBrowserContext(),
+ "test-host", csp);
+ GURL main_frame_url(GetChromeUntrustedUIURL("test-host/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
+
+ // A chrome-untrusted://resources resources should load successfully.
+ GURL script_url = GetChromeUntrustedUIURL(kSharedResourcesModuleJsPath);
+ EXPECT_TRUE(EvalJs(shell(), JsReplace(kAddScriptModuleScript, script_url),
+ EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */)
+ .ExtractBool());
+}
+
+// Verify that websites cannot access chrome-untrusted://resources scripts.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
+ DisallowChromeUntrustedResourcesFromWebFrame) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
+
+ // A chrome-untrusted://resources resources should fail to load.
+ GURL script_url = GetChromeUntrustedUIURL(kSharedResourcesModuleJsPath);
+ EXPECT_FALSE(EvalJs(shell(), JsReplace(kAddScriptModuleScript, script_url),
+ EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */)
+ .ExtractBool());
+}
+
class WebUISecurityTestWithWebUIReportOnlyTrustedTypesEnabled
: public WebUISecurityTest {
public:
@@ -578,4 +575,341 @@ IN_PROC_BROWSER_TEST_F(WebUISecurityTestWithWebUIReportOnlyTrustedTypesEnabled,
}
}
+namespace {
+class UntrustedSourceWithCorsSupport : public URLDataSource {
+ public:
+ static std::unique_ptr<UntrustedSourceWithCorsSupport> CreateForHost(
+ std::string host) {
+ std::string source_name = base::StrCat(
+ {kChromeUIUntrustedScheme, url::kStandardSchemeSeparator, host, "/"});
+ return std::make_unique<UntrustedSourceWithCorsSupport>(source_name);
+ }
+ explicit UntrustedSourceWithCorsSupport(std::string name) : name_(name) {}
+ UntrustedSourceWithCorsSupport& operator=(
+ const UntrustedSourceWithCorsSupport&) = delete;
+ UntrustedSourceWithCorsSupport(const UntrustedSourceWithCorsSupport&) =
+ delete;
+ ~UntrustedSourceWithCorsSupport() override = default;
+
+ // URLDataSource:
+ std::string GetSource() override { return name_; }
+ std::string GetAccessControlAllowOriginForOrigin(
+ const std::string& origin) override {
+ return origin;
+ }
+ std::string GetMimeType(const std::string& path) override {
+ return "text/html";
+ }
+ void StartDataRequest(const GURL& url,
+ const WebContents::Getter& wc_getter,
+ GotDataCallback callback) override {
+ std::string dummy_html = "<html><body>dummy</body></html>";
+ scoped_refptr<base::RefCountedString> response =
+ base::RefCountedString::TakeString(&dummy_html);
+ std::move(callback).Run(response.get());
+ }
+
+ private:
+ std::string name_;
+};
+
+enum FetchMode { SAME_ORIGIN, CORS, NO_CORS };
+
+EvalJsResult PerformFetch(Shell* shell,
+ const GURL& fetch_url,
+ FetchMode fetch_mode = FetchMode::CORS) {
+ std::string fetch_mode_string;
+ switch (fetch_mode) {
+ case SAME_ORIGIN:
+ fetch_mode_string = "same-origin";
+ break;
+ case CORS:
+ fetch_mode_string = "cors";
+ break;
+ case NO_CORS:
+ fetch_mode_string = "no-cors";
+ break;
+ default:
+ NOTREACHED();
+ }
+ const char kFetchRequestScript[] =
+ "fetch($1, {mode: $2}).then("
+ " response => 'success',"
+ " error => error.message"
+ ");";
+
+ return EvalJs(shell,
+ JsReplace(kFetchRequestScript, fetch_url, fetch_mode_string),
+ EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */);
+}
+} // namespace
+
+// Verify fetch request from web pages to chrome-untrusted:// is blocked,
+// because web pages don't have WebUIURLLoaderFactory for chrome-untrusted://
+// scheme.
+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());
+ ASSERT_TRUE(embedded_test_server()->Start());
+
+ const GURL web_url = embedded_test_server()->GetURL("/title2.html");
+ EXPECT_TRUE(NavigateToURL(shell(), web_url));
+
+ {
+ DevToolsInspectorLogWatcher log_watcher(shell()->web_contents());
+ EXPECT_EQ("Failed to fetch",
+ PerformFetch(shell(), untrusted_url, FetchMode::CORS));
+ log_watcher.FlushAndStopWatching();
+
+ EXPECT_EQ(log_watcher.last_message(),
+ "Failed to load resource: net::ERR_UNKNOWN_URL_SCHEME");
+ }
+
+ {
+ DevToolsInspectorLogWatcher log_watcher(shell()->web_contents());
+ EXPECT_EQ("Failed to fetch",
+ PerformFetch(shell(), untrusted_url, FetchMode::NO_CORS));
+ log_watcher.FlushAndStopWatching();
+
+ EXPECT_EQ(log_watcher.last_message(),
+ "Failed to load resource: net::ERR_UNKNOWN_URL_SCHEME");
+ }
+}
+
+// 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());
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url));
+ EXPECT_EQ("success",
+ PerformFetch(shell(), untrusted_url, FetchMode::SAME_ORIGIN));
+}
+
+// Verify cross-origin fetch request from a chrome-untrusted:// page to another
+// chrome-untrusted:// page is blocked by the default "default-src 'self'"
+// Content Security Policy on URLDataSource.
+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());
+ const GURL untrusted_url2 = GURL("chrome-untrusted://test2/title2.html");
+ URLDataSource::Add(
+ shell()->web_contents()->GetBrowserContext(),
+ UntrustedSourceWithCorsSupport::CreateForHost(untrusted_url2.host()));
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url1));
+
+ {
+ WebContentsConsoleObserver console_observer(shell()->web_contents());
+ EXPECT_EQ("Failed to fetch",
+ PerformFetch(shell(), untrusted_url2, FetchMode::CORS));
+ console_observer.Wait();
+ EXPECT_EQ(console_observer.GetMessageAt(0),
+ base::StringPrintf(
+ "Refused to connect to '%s' because it violates the "
+ "following Content Security Policy directive: \"default-src "
+ "'self'\". Note that 'connect-src' was not explicitly set, "
+ "so 'default-src' is used as a fallback.\n",
+ untrusted_url2.spec().c_str()));
+ }
+
+ {
+ WebContentsConsoleObserver console_observer(shell()->web_contents());
+ EXPECT_EQ("Failed to fetch",
+ PerformFetch(shell(), untrusted_url2, FetchMode::NO_CORS));
+ console_observer.Wait();
+ EXPECT_EQ(console_observer.GetMessageAt(0),
+ base::StringPrintf(
+ "Refused to connect to '%s' because it violates the "
+ "following Content Security Policy directive: \"default-src "
+ "'self'\". Note that 'connect-src' was not explicitly set, "
+ "so 'default-src' is used as a fallback.\n",
+ untrusted_url2.spec().c_str()));
+ }
+}
+
+// Verify cross-origin fetch request from a chrome-untrusted:// page to another
+// chrome-untrusted:// page succeeds if Content Security Policy allows it.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
+ CrossOriginFetchRequestToChromeUntrusted) {
+ 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);
+
+ const GURL untrusted_url2 = GURL("chrome-untrusted://test2/title2.html");
+ URLDataSource::Add(
+ shell()->web_contents()->GetBrowserContext(),
+ UntrustedSourceWithCorsSupport::CreateForHost(untrusted_url2.host()));
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url1));
+ EXPECT_EQ("success", PerformFetch(shell(), untrusted_url2, FetchMode::CORS));
+ EXPECT_EQ("success",
+ PerformFetch(shell(), untrusted_url2, FetchMode::NO_CORS));
+}
+
+// Verify fetch request from a chrome-untrusted:// page to a chrome:// page
+// is blocked because chrome-untrusted:// pages don't have WebUIURLLoaderFactory
+// for chrome:// scheme, even if CSP allows this.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
+ DisallowChromeUntrustedFetchRequestToChrome) {
+ 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);
+
+ const GURL chrome_url = GURL("chrome://webui/title2.html");
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url));
+
+ {
+ WebContentsConsoleObserver console_observer(shell()->web_contents());
+ EXPECT_EQ("Failed to fetch",
+ PerformFetch(shell(), chrome_url, FetchMode::CORS));
+ console_observer.Wait();
+ EXPECT_EQ(console_observer.GetMessageAt(0),
+ base::StringPrintf("Fetch API cannot load %s. URL scheme must be "
+ "\"http\" or \"https\" for CORS request.",
+ chrome_url.spec().c_str()));
+ }
+
+ {
+ WebContentsConsoleObserver console_observer(shell()->web_contents());
+ EXPECT_EQ("Failed to fetch",
+ PerformFetch(shell(), chrome_url, FetchMode::NO_CORS));
+ console_observer.Wait();
+ EXPECT_EQ(
+ console_observer.GetMessageAt(0),
+ base::StringPrintf(
+ "Fetch API cannot load %s. URL scheme \"chrome\" is not supported.",
+ chrome_url.spec().c_str()));
+ }
+}
+
+namespace {
+EvalJsResult PerformXHRRequest(Shell* shell, const GURL& xhr_url) {
+ const char kXHRRequestScript[] =
+ "new Promise((resolve) => {"
+ " const xhr = new XMLHttpRequest();"
+ " xhr.open('GET', $1);"
+ " xhr.onload = () => resolve('success');"
+ " xhr.onerror = progress_event => resolve(progress_event.type);"
+ " xhr.send();"
+ "}); ";
+
+ return EvalJs(shell, JsReplace(kXHRRequestScript, xhr_url),
+ EXECUTE_SCRIPT_DEFAULT_OPTIONS, 1 /* world_id */);
+}
+} // namespace
+
+// Verify XHR request from web pages to chrome-untrusted:// is blocked, because
+// web pages don't have WebUIURLLoader required to load chrome-untrusted://
+// resources.
+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());
+ ASSERT_TRUE(embedded_test_server()->Start());
+ const GURL web_url = embedded_test_server()->GetURL("/title2.html");
+
+ EXPECT_TRUE(NavigateToURL(shell(), web_url));
+
+ DevToolsInspectorLogWatcher log_watcher(shell()->web_contents());
+ EXPECT_EQ("error", PerformXHRRequest(shell(), untrusted_url));
+ log_watcher.FlushAndStopWatching();
+
+ EXPECT_EQ(log_watcher.last_message(),
+ "Failed to load resource: net::ERR_UNKNOWN_URL_SCHEME");
+}
+
+// Verify a chrome-untrusted:// document can XHR itself.
+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());
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url));
+ EXPECT_EQ("success", PerformXHRRequest(shell(), untrusted_url));
+}
+
+// Verify cross-origin XHR request from a chrome-untrusted:// page to another
+// chrome-untrusted:// page is blocked by "default-src 'self';" Content Security
+// Policy.
+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());
+ const GURL untrusted_url2 = GURL("chrome-untrusted://test2/");
+ URLDataSource::Add(
+ shell()->web_contents()->GetBrowserContext(),
+ UntrustedSourceWithCorsSupport::CreateForHost(untrusted_url2.host()));
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url1));
+
+ WebContentsConsoleObserver console_observer(shell()->web_contents());
+ EXPECT_EQ("error", PerformXHRRequest(shell(), untrusted_url2));
+ console_observer.Wait();
+ EXPECT_EQ(console_observer.GetMessageAt(0),
+ base::StringPrintf(
+ "Refused to connect to '%s' because it violates the "
+ "following Content Security Policy directive: \"default-src "
+ "'self'\". Note that 'connect-src' was not explicitly set, "
+ "so 'default-src' is used as a fallback.\n",
+ untrusted_url2.spec().c_str()));
+}
+
+// Verify cross-origin XHR request from a chrome-untrusted:// page to another
+// chrome-untrusted:// page is successful, if Content Security Policy allows it,
+// and the requested resource presents an Access-Control-Allow-Origin header.
+IN_PROC_BROWSER_TEST_F(
+ WebUISecurityTest,
+ CrossOriginXHRRequestToChromeUntrustedIfContenSecurityPolicyAllowsIt) {
+ 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);
+ const GURL untrusted_url2 = GURL("chrome-untrusted://test2/");
+ URLDataSource::Add(
+ shell()->web_contents()->GetBrowserContext(),
+ UntrustedSourceWithCorsSupport::CreateForHost(untrusted_url2.host()));
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url1));
+ EXPECT_EQ("success", PerformXHRRequest(shell(), untrusted_url2));
+}
+
+// Verify XHR request from a chrome-untrusted:// page to a chrome:// page is
+// blocked, even if CSP allows this.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest,
+ DisallowChromeUntrustedXHRRequestToChrome) {
+ 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);
+
+ const GURL chrome_url = GURL("chrome://webui/title2.html");
+
+ EXPECT_TRUE(NavigateToURL(shell(), untrusted_url));
+
+ WebContentsConsoleObserver console_observer(shell()->web_contents());
+ EXPECT_EQ("error", PerformXHRRequest(shell(), chrome_url));
+ console_observer.Wait();
+ EXPECT_EQ(console_observer.GetMessageAt(0),
+ base::StringPrintf("Not allowed to load local resource: %s",
+ chrome_url.spec().c_str()));
+}
+
} // namespace content
diff --git a/chromium/content/browser/webui/web_ui_url_loader_factory.cc b/chromium/content/browser/webui/web_ui_url_loader_factory.cc
index 1814672916a..76fead0019e 100644
--- a/chromium/content/browser/webui/web_ui_url_loader_factory.cc
+++ b/chromium/content/browser/webui/web_ui_url_loader_factory.cc
@@ -14,7 +14,6 @@
#include "base/memory/ref_counted_memory.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h"
-#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "content/browser/bad_message.h"
#include "content/browser/blob_storage/blob_internals_url_loader.h"
@@ -272,8 +271,8 @@ class WebUIURLLoaderFactory : public network::mojom::URLLoaderFactory,
}
if (request.url.host_piece() == kChromeUIBlobInternalsHost) {
- base::PostTask(
- FROM_HERE, {BrowserThread::IO},
+ GetIOThreadTaskRunner({})->PostTask(
+ FROM_HERE,
base::BindOnce(&StartBlobInternalsURLLoader, request,
std::move(client),
base::Unretained(ChromeBlobStorageContext::GetFor(