blob: 88d88786cf1c98acb5d1dc5dcf365b6208c14e70 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2012 The Chromium Authors
[email protected]248ce192011-02-10 15:26:342// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]33c1c26a2013-01-24 21:56:265#include "content/browser/webui/url_data_manager_backend.h"
[email protected]248ce192011-02-10 15:26:346
[email protected]19e6c4c2011-10-05 18:03:047#include <set>
thestig922d34c2016-08-23 03:40:528#include <utility>
[email protected]19e6c4c2011-10-05 18:03:049
Lei Zhangde197672021-04-29 08:11:2410#include "base/containers/contains.h"
Avi Drissmanadac21992023-01-11 23:46:3911#include "base/functional/bind.h"
skyostil95082a62015-06-05 19:53:0712#include "base/location.h"
Lei Zhangc77274da2018-08-28 23:14:0013#include "base/memory/ptr_util.h"
[email protected]bebc8e122012-06-12 19:52:3414#include "base/memory/ref_counted.h"
[email protected]3b63f8f42011-03-28 01:54:1515#include "base/memory/ref_counted_memory.h"
Jayson Adams20123ee92021-07-27 01:38:4316#include "base/no_destructor.h"
edwardjung6e6ebd12015-12-03 13:18:4617#include "base/strings/string_number_conversions.h"
[email protected]348fbaac2013-06-11 06:31:5118#include "base/strings/string_util.h"
Patrick Monette643cdf62021-10-15 19:13:4219#include "base/task/single_thread_task_runner.h"
ssid3e765612015-01-28 04:03:4220#include "base/trace_event/trace_event.h"
David Bienvenuead449e2022-04-07 19:30:0221#include "base/values.h"
[email protected]33c1c26a2013-01-24 21:56:2622#include "content/browser/webui/shared_resources_data_source.h"
[email protected]5bf1646f52013-01-28 03:57:0223#include "content/browser/webui/url_data_source_impl.h"
dbeam25af85bd2016-12-09 03:34:3724#include "content/browser/webui/web_ui_data_source_impl.h"
[email protected]4413d872014-02-20 20:26:5525#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2826#include "content/public/browser/browser_task_traits.h"
[email protected]c38831a12011-10-28 12:44:4927#include "content/public/browser/browser_thread.h"
[email protected]672c8c12013-03-07 12:30:0628#include "content/public/browser/content_browser_client.h"
[email protected]e671c272013-04-23 22:45:0329#include "content/public/browser/render_process_host.h"
Hans Wennborg5ffd1392019-10-16 11:00:0230#include "content/public/common/content_client.h"
[email protected]be22ebe2013-01-23 17:03:0831#include "content/public/common/url_constants.h"
[email protected]248ce192011-02-10 15:26:3432#include "net/base/io_buffer.h"
33#include "net/base/net_errors.h"
Hans Wennborg0d7c5ba2021-07-06 17:52:2034#include "net/http/http_request_headers.h"
[email protected]e3421da2013-04-02 07:46:4635#include "net/http/http_status_code.h"
edwardjung6e6ebd12015-12-03 13:18:4636#include "net/log/net_log_util.h"
Becca Hughes88a4afe2020-06-23 01:30:4637#include "services/network/public/mojom/content_security_policy.mojom.h"
dschuyler613a1032016-12-15 19:22:3538#include "ui/base/template_expressions.h"
Reilly Grantf31ae652017-11-16 20:40:0439#include "ui/base/webui/i18n_source_stream.h"
[email protected]707e1c42013-07-09 21:18:5840#include "url/url_util.h"
[email protected]672c8c12013-03-07 12:30:0641
[email protected]5bf1646f52013-01-28 03:57:0242namespace content {
[email protected]631bb742011-11-02 11:29:3943
[email protected]e02a83762011-03-10 19:42:1844namespace {
45
Matt Menke4426a152020-04-17 03:45:4846const char kChromeURLContentSecurityPolicyHeaderName[] =
47 "Content-Security-Policy";
[email protected]a8c119d2012-02-15 23:00:0948
Trent Apteddbdebc12021-04-06 11:27:0049const char kChromeURLCrossOriginOpenerPolicyName[] =
50 "Cross-Origin-Opener-Policy";
51const char kChromeURLCrossOriginEmbedderPolicyName[] =
52 "Cross-Origin-Embedder-Policy";
53const char kChromeURLCrossOriginResourcePolicyName[] =
54 "Cross-Origin-Resource-Policy";
55
Matt Menke4426a152020-04-17 03:45:4856const char kChromeURLXFrameOptionsHeaderName[] = "X-Frame-Options";
57const char kChromeURLXFrameOptionsHeaderValue[] = "DENY";
thestig922d34c2016-08-23 03:40:5258const char kNetworkErrorKey[] = "netError";
John Abd-El-Malek76ad70a2020-05-18 16:08:1259const char kURLDataManagerBackendKeyName[] = "url_data_manager_backend";
[email protected]31dc6352014-03-17 13:41:0460
[email protected]672c8c12013-03-07 12:30:0661bool SchemeIsInSchemes(const std::string& scheme,
62 const std::vector<std::string>& schemes) {
Jan Wilken Dörrie77c581a2019-06-07 16:25:0663 return base::Contains(schemes, scheme);
[email protected]672c8c12013-03-07 12:30:0664}
65
Alex Moshchukb76927762023-05-09 05:46:3166bool g_disallow_webui_scheme_caching_for_testing = false;
67
68std::vector<std::string> GetWebUISchemesSlow() {
69 std::vector<std::string> schemes;
70 schemes.emplace_back(kChromeUIScheme);
71 schemes.emplace_back(kChromeUIUntrustedScheme);
72 GetContentClient()->browser()->GetAdditionalWebUISchemes(&schemes);
73 return schemes;
74}
75
76std::vector<std::string> GetWebUISchemesCached() {
77 // It's OK to cache this in a static because the class implementing
78 // GetAdditionalWebUISchemes() won't change while the application is
79 // running, and because those methods always add the same items.
80 //
81 // However, be careful using this with unit tests which use
82 // GetAdditionalWebUISchemes() to change the list of WebUI schemes, since
83 // this caching may persist across tests. For those, this caching should be
84 // disabled via SetDisallowWebUISchemeCachingForTesting().
85 static base::NoDestructor<std::vector<std::string>> webui_schemes(
86 GetWebUISchemesSlow());
87
88 return *webui_schemes;
89}
90
[email protected]ad423e4d2011-05-16 20:04:1391} // namespace
[email protected]248ce192011-02-10 15:26:3492
Keren Zhu42e04372024-08-27 17:05:5793URLDataManagerBackend::URLDataManagerBackend() {
Lei Zhang4fcf8402023-03-17 22:58:5194 {
95 // Add a shared data source for chrome://resources.
96 auto* source = new WebUIDataSourceImpl(kChromeUIResourcesHost);
97 PopulateSharedResourcesDataSource(source);
98 AddDataSource(source); // Takes ownership.
99 }
Jiewei Qian155bcb02021-09-30 08:23:28100
Lei Zhang4fcf8402023-03-17 22:58:51101 {
102 // Add a shared data source for chrome-untrusted://resources.
103 auto* source = new WebUIDataSourceImpl(kChromeUIUntrustedResourcesURL);
104 PopulateSharedResourcesDataSource(source);
105 AddDataSource(source); // Takes ownership.
106 }
[email protected]248ce192011-02-10 15:26:34107}
108
Chris Hamiltonbed9bc82018-07-11 23:13:35109URLDataManagerBackend::~URLDataManagerBackend() = default;
[email protected]248ce192011-02-10 15:26:34110
John Abd-El-Malek76ad70a2020-05-18 16:08:12111URLDataManagerBackend* URLDataManagerBackend::GetForBrowserContext(
112 BrowserContext* context) {
113 DCHECK_CURRENTLY_ON(BrowserThread::UI);
114 if (!context->GetUserData(kURLDataManagerBackendKeyName)) {
115 context->SetUserData(kURLDataManagerBackendKeyName,
116 std::make_unique<URLDataManagerBackend>());
117 }
118 return static_cast<URLDataManagerBackend*>(
119 context->GetUserData(kURLDataManagerBackendKeyName));
120}
121
122void URLDataManagerBackend::AddDataSource(URLDataSourceImpl* source) {
123 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Chris Hamiltonbed9bc82018-07-11 23:13:35124 if (!source->source()->ShouldReplaceExistingSource()) {
jdoerrie55ec69d2018-10-08 13:34:46125 auto i = data_sources_.find(source->source_name());
Chris Hamiltonbed9bc82018-07-11 23:13:35126 if (i != data_sources_.end())
[email protected]ffe3d6f2011-02-23 16:42:52127 return;
[email protected]ffe3d6f2011-02-23 16:42:52128 }
[email protected]248ce192011-02-10 15:26:34129 data_sources_[source->source_name()] = source;
Chris Hamiltonbed9bc82018-07-11 23:13:35130 source->backend_ = weak_factory_.GetWeakPtr();
[email protected]248ce192011-02-10 15:26:34131}
132
dbeam25af85bd2016-12-09 03:34:37133void URLDataManagerBackend::UpdateWebUIDataSource(
134 const std::string& source_name,
David Bienvenuead449e2022-04-07 19:30:02135 const base::Value::Dict& update) {
jdoerrie55ec69d2018-10-08 13:34:46136 auto it = data_sources_.find(source_name);
dbeam25af85bd2016-12-09 03:34:37137 if (it == data_sources_.end() || !it->second->IsWebUIDataSourceImpl()) {
Peter Boströmfc7ddc182024-10-31 19:37:21138 NOTREACHED();
dbeam25af85bd2016-12-09 03:34:37139 }
140 static_cast<WebUIDataSourceImpl*>(it->second.get())
141 ->AddLocalizedStrings(update);
142}
143
[email protected]73b718f2014-01-27 02:59:46144URLDataSourceImpl* URLDataManagerBackend::GetDataSourceFromURL(
145 const GURL& url) {
Giovanni Ortuño Urquidi0a3622b2020-01-14 01:54:22146 // chrome-untrusted:// sources keys are of the form "chrome-untrusted://host".
147 if (url.scheme() == kChromeUIUntrustedScheme) {
Mike West800532c2021-10-14 09:26:52148 auto i = data_sources_.find(url.DeprecatedGetOriginAsURL().spec());
Giovanni Ortuño Urquidi0a3622b2020-01-14 01:54:22149 if (i == data_sources_.end())
150 return nullptr;
151 return i->second.get();
152 }
153
[email protected]73b718f2014-01-27 02:59:46154 // The input usually looks like: chrome://source_name/extra_bits?foo
155 // so do a lookup using the host of the URL.
jdoerrie55ec69d2018-10-08 13:34:46156 auto i = data_sources_.find(url.host());
[email protected]73b718f2014-01-27 02:59:46157 if (i != data_sources_.end())
158 return i->second.get();
159
160 // No match using the host of the URL, so do a lookup using the scheme for
161 // URLs on the form source_name://extra_bits/foo .
[email protected]9cb6c302014-01-31 22:49:46162 i = data_sources_.find(url.scheme() + "://");
[email protected]73b718f2014-01-27 02:59:46163 if (i != data_sources_.end())
164 return i->second.get();
165
166 // No matches found, so give up.
thestig922d34c2016-08-23 03:40:52167 return nullptr;
[email protected]73b718f2014-01-27 02:59:46168}
169
jam8c4edd02017-05-06 18:50:33170scoped_refptr<net::HttpResponseHeaders> URLDataManagerBackend::GetHeaders(
171 URLDataSourceImpl* source_impl,
Giovanni Ortuño Urquidi66512d52022-07-20 01:22:11172 const GURL& url,
jam8c4edd02017-05-06 18:50:33173 const std::string& origin) {
174 // Set the headers so that requests serviced by ChromeURLDataManager return a
175 // status code of 200. Without this they return a 0, which makes the status
176 // indistiguishable from other error types. Instant relies on getting a 200.
Lei Zhangc77274da2018-08-28 23:14:00177 auto headers =
178 base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
jam8c4edd02017-05-06 18:50:33179 if (!source_impl)
180 return headers;
181
182 URLDataSource* source = source_impl->source();
183 // Determine the least-privileged content security policy header, if any,
184 // that is compatible with a given WebUI URL, and append it to the existing
185 // response headers.
186 if (source->ShouldAddContentSecurityPolicy()) {
Matt Menke4426a152020-04-17 03:45:48187 std::string csp_header;
Becca Hughes88a4afe2020-06-23 01:30:46188
189 const network::mojom::CSPDirectiveName kAllDirectives[] = {
Jun Kokatsu509e38f2021-02-11 05:00:57190 network::mojom::CSPDirectiveName::BaseURI,
Becca Hughes88a4afe2020-06-23 01:30:46191 network::mojom::CSPDirectiveName::ChildSrc,
Oleh Lamzin54b81ab2020-07-20 17:54:28192 network::mojom::CSPDirectiveName::ConnectSrc,
Becca Hughes88a4afe2020-06-23 01:30:46193 network::mojom::CSPDirectiveName::DefaultSrc,
Nan Linefd66c82022-01-24 20:33:15194 network::mojom::CSPDirectiveName::FencedFrameSrc,
Jun Kokatsu509e38f2021-02-11 05:00:57195 network::mojom::CSPDirectiveName::FormAction,
Toby Huang90da0c62022-02-17 00:54:20196 network::mojom::CSPDirectiveName::FontSrc,
Oleh Lamzin171aff9f2020-07-17 08:23:41197 network::mojom::CSPDirectiveName::FrameSrc,
Becca Hughes88a4afe2020-06-23 01:30:46198 network::mojom::CSPDirectiveName::ImgSrc,
199 network::mojom::CSPDirectiveName::MediaSrc,
200 network::mojom::CSPDirectiveName::ObjectSrc,
Jun Kokatsu5cb72d52020-08-21 21:27:19201 network::mojom::CSPDirectiveName::RequireTrustedTypesFor,
Becca Hughes88a4afe2020-06-23 01:30:46202 network::mojom::CSPDirectiveName::ScriptSrc,
203 network::mojom::CSPDirectiveName::StyleSrc,
Jun Kokatsu5cb72d52020-08-21 21:27:19204 network::mojom::CSPDirectiveName::TrustedTypes,
Oleh Lamzin54b81ab2020-07-20 17:54:28205 network::mojom::CSPDirectiveName::WorkerSrc};
Becca Hughes88a4afe2020-06-23 01:30:46206
207 for (auto& directive : kAllDirectives) {
208 csp_header.append(source->GetContentSecurityPolicy(directive));
209 }
210
Alison Gale81f4f2c72024-04-22 19:33:31211 // TODO(crbug.com/40118579): Both CSP frame ancestors and XFO headers may be
jessing8eb67f092020-02-20 01:29:27212 // added to the response but frame ancestors would take precedence. In the
213 // future, XFO will be removed so when that happens remove the check and
214 // always add frame ancestors.
Becca Hughes88a4afe2020-06-23 01:30:46215 if (source->ShouldDenyXFrameOptions()) {
216 csp_header.append(source->GetContentSecurityPolicy(
217 network::mojom::CSPDirectiveName::FrameAncestors));
218 }
219
Matt Menke4426a152020-04-17 03:45:48220 headers->SetHeader(kChromeURLContentSecurityPolicyHeaderName, csp_header);
jam8c4edd02017-05-06 18:50:33221 }
222
Matt Menke4426a152020-04-17 03:45:48223 if (source->ShouldDenyXFrameOptions()) {
224 headers->SetHeader(kChromeURLXFrameOptionsHeaderName,
225 kChromeURLXFrameOptionsHeaderValue);
226 }
jam8c4edd02017-05-06 18:50:33227
228 if (!source->AllowCaching())
Matt Menke4426a152020-04-17 03:45:48229 headers->SetHeader("Cache-Control", "no-cache");
jam8c4edd02017-05-06 18:50:33230
Giovanni Ortuño Urquidi66512d52022-07-20 01:22:11231 std::string mime_type = source->GetMimeType(url);
Matt Menke4426a152020-04-17 03:45:48232 if (source->ShouldServeMimeTypeAsContentTypeHeader() && !mime_type.empty())
233 headers->SetHeader(net::HttpRequestHeaders::kContentType, mime_type);
jam8c4edd02017-05-06 18:50:33234
Trent Apteddbdebc12021-04-06 11:27:00235 const std::string coop_value = source->GetCrossOriginOpenerPolicy();
236 if (!coop_value.empty()) {
237 headers->SetHeader(kChromeURLCrossOriginOpenerPolicyName, coop_value);
238 }
239 const std::string coep_value = source->GetCrossOriginEmbedderPolicy();
240 if (!coep_value.empty()) {
241 headers->SetHeader(kChromeURLCrossOriginEmbedderPolicyName, coep_value);
242 }
243 const std::string corp_value = source->GetCrossOriginResourcePolicy();
244 if (!corp_value.empty()) {
245 headers->SetHeader(kChromeURLCrossOriginResourcePolicyName, corp_value);
246 }
247
jam8c4edd02017-05-06 18:50:33248 if (!origin.empty()) {
249 std::string header = source->GetAccessControlAllowOriginForOrigin(origin);
250 DCHECK(header.empty() || header == origin || header == "*" ||
251 header == "null");
252 if (!header.empty()) {
Matt Menke4426a152020-04-17 03:45:48253 headers->SetHeader("Access-Control-Allow-Origin", header);
254 headers->SetHeader("Vary", "Origin");
jam8c4edd02017-05-06 18:50:33255 }
256 }
257
258 return headers;
259}
260
261bool URLDataManagerBackend::CheckURLIsValid(const GURL& url) {
262 std::vector<std::string> additional_schemes;
Andrey Kosyakov031e9e32017-08-18 21:00:35263 DCHECK(url.SchemeIs(kChromeUIScheme) ||
Giovanni Ortuño Urquidi0a3622b2020-01-14 01:54:22264 url.SchemeIs(kChromeUIUntrustedScheme) ||
jam8c4edd02017-05-06 18:50:33265 (GetContentClient()->browser()->GetAdditionalWebUISchemes(
266 &additional_schemes),
267 SchemeIsInSchemes(url.scheme(), additional_schemes)));
268
269 if (!url.is_valid()) {
Peter Boströmfc7ddc182024-10-31 19:37:21270 NOTREACHED();
jam8c4edd02017-05-06 18:50:33271 }
272
273 return true;
274}
275
jam4374b952017-05-10 21:16:41276bool URLDataManagerBackend::IsValidNetworkErrorCode(int error_code) {
Matt Menkef6d1512a2022-06-02 00:29:45277 base::Value::Dict error_codes = net::GetNetConstants();
278 const base::Value::Dict* net_error_codes_dict =
279 error_codes.FindDict(kNetworkErrorKey);
jam4374b952017-05-10 21:16:41280
281 if (net_error_codes_dict != nullptr) {
Matt Menkef6d1512a2022-06-02 00:29:45282 for (auto it = net_error_codes_dict->begin();
283 it != net_error_codes_dict->end(); ++it) {
284 if (error_code == it->second.GetInt())
jam4374b952017-05-10 21:16:41285 return true;
286 }
287 }
288 return false;
289}
290
jam1a97290b2017-05-09 04:30:50291std::vector<std::string> URLDataManagerBackend::GetWebUISchemes() {
Alex Moshchukb76927762023-05-09 05:46:31292 if (g_disallow_webui_scheme_caching_for_testing) {
293 return GetWebUISchemesSlow();
294 }
Jayson Adams20123ee92021-07-27 01:38:43295
Alex Moshchukb76927762023-05-09 05:46:31296 return GetWebUISchemesCached();
297}
298
299void URLDataManagerBackend::SetDisallowWebUISchemeCachingForTesting(
300 bool disallow_caching) {
301 g_disallow_webui_scheme_caching_for_testing = disallow_caching;
jam1a97290b2017-05-09 04:30:50302}
303
[email protected]5bf1646f52013-01-28 03:57:02304} // namespace content