Bug 1685822 [wpt PR 27117] - [Import Maps] Add tests for rejecting multiple import...
[gecko.git] / dom / security / nsCSPService.cpp
blob0dd71222aa770f1138f3943f388919db412d0066
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Logging.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/StaticPrefs_security.h"
10 #include "nsString.h"
11 #include "nsCOMPtr.h"
12 #include "nsIURI.h"
13 #include "nsIContent.h"
14 #include "nsCSPService.h"
15 #include "nsIContentSecurityPolicy.h"
16 #include "nsError.h"
17 #include "nsIAsyncVerifyRedirectCallback.h"
18 #include "nsAsyncRedirectVerifyHelper.h"
19 #include "nsContentUtils.h"
20 #include "nsContentPolicyUtils.h"
21 #include "nsNetUtil.h"
22 #include "nsIProtocolHandler.h"
23 #include "nsQueryObject.h"
24 #include "mozilla/net/DocumentLoadListener.h"
25 #include "mozilla/net/DocumentChannel.h"
27 using namespace mozilla;
29 static LazyLogModule gCspPRLog("CSP");
31 CSPService::CSPService() = default;
33 CSPService::~CSPService() = default;
35 NS_IMPL_ISUPPORTS(CSPService, nsIContentPolicy, nsIChannelEventSink)
37 // Helper function to identify protocols and content types not subject to CSP.
38 bool subjectToCSP(nsIURI* aURI, nsContentPolicyType aContentType) {
39 ExtContentPolicyType contentType =
40 nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
42 // These content types are not subject to CSP content policy checks:
43 // TYPE_CSP_REPORT -- csp can't block csp reports
44 // TYPE_REFRESH -- never passed to ShouldLoad (see nsIContentPolicy.idl)
45 // TYPE_DOCUMENT -- used for frame-ancestors
46 if (contentType == ExtContentPolicy::TYPE_CSP_REPORT ||
47 contentType == ExtContentPolicy::TYPE_REFRESH ||
48 contentType == ExtContentPolicy::TYPE_DOCUMENT) {
49 return false;
52 // The three protocols: data:, blob: and filesystem: share the same
53 // protocol flag (URI_IS_LOCAL_RESOURCE) with other protocols,
54 // but those three protocols get special attention in CSP and
55 // are subject to CSP, hence we have to make sure those
56 // protocols are subject to CSP, see:
57 // http://www.w3.org/TR/CSP2/#source-list-guid-matching
58 if (aURI->SchemeIs("data") || aURI->SchemeIs("blob") ||
59 aURI->SchemeIs("filesystem")) {
60 return true;
63 // Finally we have to allowlist "about:" which does not fall into
64 // the category underneath and also "javascript:" which is not
65 // subject to CSP content loading rules.
66 if (aURI->SchemeIs("about") || aURI->SchemeIs("javascript")) {
67 return false;
70 // Please note that it should be possible for websites to
71 // allowlist their own protocol handlers with respect to CSP,
72 // hence we use protocol flags to accomplish that, but we also
73 // want resource:, chrome: and moz-icon to be subject to CSP
74 // (which also use URI_IS_LOCAL_RESOURCE).
75 // Exception to the rule are images, styles, and localization
76 // DTDs using a scheme of resource: or chrome:
77 bool isImgOrStyleOrDTD = contentType == ExtContentPolicy::TYPE_IMAGE ||
78 contentType == ExtContentPolicy::TYPE_STYLESHEET ||
79 contentType == ExtContentPolicy::TYPE_DTD;
80 if (aURI->SchemeIs("resource")) {
81 nsAutoCString uriSpec;
82 aURI->GetSpec(uriSpec);
83 // Exempt pdf.js from being subject to a page's CSP.
84 if (StringBeginsWith(uriSpec, "resource://pdf.js/"_ns)) {
85 return false;
87 if (!isImgOrStyleOrDTD) {
88 return true;
91 if (aURI->SchemeIs("chrome") && !isImgOrStyleOrDTD) {
92 return true;
94 if (aURI->SchemeIs("moz-icon")) {
95 return true;
97 bool match;
98 nsresult rv = NS_URIChainHasFlags(
99 aURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &match);
100 if (NS_SUCCEEDED(rv) && match) {
101 return false;
103 // all other protocols are subject To CSP.
104 return true;
107 /* static */ nsresult CSPService::ConsultCSP(nsIURI* aContentLocation,
108 nsILoadInfo* aLoadInfo,
109 const nsACString& aMimeTypeGuess,
110 int16_t* aDecision) {
111 if (!aContentLocation) {
112 return NS_ERROR_FAILURE;
115 nsContentPolicyType contentType = aLoadInfo->InternalContentPolicyType();
116 bool parserCreatedScript = aLoadInfo->GetParserCreatedScript();
118 nsCOMPtr<nsICSPEventListener> cspEventListener;
119 nsresult rv =
120 aLoadInfo->GetCspEventListener(getter_AddRefs(cspEventListener));
121 NS_ENSURE_SUCCESS(rv, rv);
123 if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
124 MOZ_LOG(gCspPRLog, LogLevel::Debug,
125 ("CSPService::ShouldLoad called for %s",
126 aContentLocation->GetSpecOrDefault().get()));
129 // default decision, CSP can revise it if there's a policy to enforce
130 *aDecision = nsIContentPolicy::ACCEPT;
132 // No need to continue processing if CSP is disabled or if the protocol
133 // or type is *not* subject to CSP.
134 // Please note, the correct way to opt-out of CSP using a custom
135 // protocolHandler is to set one of the nsIProtocolHandler flags
136 // that are allowlistet in subjectToCSP()
137 if (!StaticPrefs::security_csp_enable() ||
138 !subjectToCSP(aContentLocation, contentType)) {
139 return NS_OK;
142 nsAutoString cspNonce;
143 rv = aLoadInfo->GetCspNonce(cspNonce);
144 NS_ENSURE_SUCCESS(rv, rv);
146 // 1) Apply speculate CSP for preloads
147 bool isPreload = nsContentUtils::IsPreloadType(contentType);
149 if (isPreload) {
150 nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = aLoadInfo->GetPreloadCsp();
151 if (preloadCsp) {
152 // obtain the enforcement decision
153 rv = preloadCsp->ShouldLoad(
154 contentType, cspEventListener, aContentLocation,
155 nullptr, // no redirect, aOriginal URL is null.
156 false, cspNonce, parserCreatedScript, aDecision);
157 NS_ENSURE_SUCCESS(rv, rv);
159 // if the preload policy already denied the load, then there
160 // is no point in checking the real policy
161 if (NS_CP_REJECTED(*aDecision)) {
162 NS_SetRequestBlockingReason(
163 aLoadInfo, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_PRELOAD);
165 return NS_OK;
170 // 2) Apply actual CSP to all loads. Please note that in case
171 // the csp should be overruled (e.g. by an ExpandedPrincipal)
172 // then loadinfo->GetCSP() returns that CSP instead of the
173 // document's CSP.
174 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp();
176 if (csp) {
177 // obtain the enforcement decision
178 rv = csp->ShouldLoad(contentType, cspEventListener, aContentLocation,
179 nullptr, // no redirect, aOriginal URL is null.
180 !isPreload && aLoadInfo->GetSendCSPViolationEvents(),
181 cspNonce, parserCreatedScript, aDecision);
183 if (NS_CP_REJECTED(*aDecision)) {
184 NS_SetRequestBlockingReason(
185 aLoadInfo, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_GENERAL);
188 NS_ENSURE_SUCCESS(rv, rv);
190 return NS_OK;
193 /* nsIContentPolicy implementation */
194 NS_IMETHODIMP
195 CSPService::ShouldLoad(nsIURI* aContentLocation, nsILoadInfo* aLoadInfo,
196 const nsACString& aMimeTypeGuess, int16_t* aDecision) {
197 return ConsultCSP(aContentLocation, aLoadInfo, aMimeTypeGuess, aDecision);
200 NS_IMETHODIMP
201 CSPService::ShouldProcess(nsIURI* aContentLocation, nsILoadInfo* aLoadInfo,
202 const nsACString& aMimeTypeGuess,
203 int16_t* aDecision) {
204 if (!aContentLocation) {
205 return NS_ERROR_FAILURE;
207 nsContentPolicyType contentType = aLoadInfo->InternalContentPolicyType();
209 if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
210 MOZ_LOG(gCspPRLog, LogLevel::Debug,
211 ("CSPService::ShouldProcess called for %s",
212 aContentLocation->GetSpecOrDefault().get()));
215 // ShouldProcess is only relevant to TYPE_OBJECT, so let's convert the
216 // internal contentPolicyType to the mapping external one.
217 // If it is not TYPE_OBJECT, we can return at this point.
218 // Note that we should still pass the internal contentPolicyType
219 // (contentType) to ShouldLoad().
220 ExtContentPolicyType policyType =
221 nsContentUtils::InternalContentPolicyTypeToExternal(contentType);
223 if (policyType != ExtContentPolicy::TYPE_OBJECT) {
224 *aDecision = nsIContentPolicy::ACCEPT;
225 return NS_OK;
228 return ShouldLoad(aContentLocation, aLoadInfo, aMimeTypeGuess, aDecision);
231 /* nsIChannelEventSink implementation */
232 NS_IMETHODIMP
233 CSPService::AsyncOnChannelRedirect(nsIChannel* oldChannel,
234 nsIChannel* newChannel, uint32_t flags,
235 nsIAsyncVerifyRedirectCallback* callback) {
236 net::nsAsyncRedirectAutoCallback autoCallback(callback);
238 if (XRE_IsE10sParentProcess()) {
239 nsCOMPtr<nsIParentChannel> parentChannel;
240 NS_QueryNotificationCallbacks(oldChannel, parentChannel);
241 RefPtr<net::DocumentLoadListener> docListener =
242 do_QueryObject(parentChannel);
243 // Since this is an IPC'd channel we do not have access to the request
244 // context. In turn, we do not have an event target for policy violations.
245 // Enforce the CSP check in the content process where we have that info.
246 // We allow redirect checks to run for document loads via
247 // DocumentLoadListener, since these are fully supported and we don't
248 // expose the redirects to the content process. We can't do this for all
249 // request types yet because we don't serialize nsICSPEventListener.
250 if (parentChannel && !docListener) {
251 return NS_OK;
255 // Don't do these checks if we're switching from DocumentChannel
256 // to a real channel. In that case, we should already have done
257 // the checks in the parent process. AsyncOnChannelRedirect
258 // isn't called in the content process if we switch process,
259 // so checking here would just hide bugs in the process switch
260 // cases.
261 if (RefPtr<net::DocumentChannel> docChannel = do_QueryObject(oldChannel)) {
262 return NS_OK;
265 nsCOMPtr<nsIURI> newUri;
266 nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
267 NS_ENSURE_SUCCESS(rv, rv);
269 nsCOMPtr<nsILoadInfo> loadInfo = oldChannel->LoadInfo();
271 /* Since redirecting channels don't call into nsIContentPolicy, we call our
272 * Content Policy implementation directly when redirects occur using the
273 * information set in the LoadInfo when channels are created.
275 * We check if the CSP permits this host for this type of load, if not,
276 * we cancel the load now.
278 nsCOMPtr<nsIURI> originalUri;
279 rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
280 if (NS_FAILED(rv)) {
281 autoCallback.DontCallback();
282 oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
283 return rv;
286 Maybe<nsresult> cancelCode;
287 rv = ConsultCSPForRedirect(originalUri, newUri, loadInfo, cancelCode);
288 if (cancelCode) {
289 oldChannel->Cancel(*cancelCode);
291 if (NS_FAILED(rv)) {
292 autoCallback.DontCallback();
295 return rv;
298 nsresult CSPService::ConsultCSPForRedirect(nsIURI* aOriginalURI,
299 nsIURI* aNewURI,
300 nsILoadInfo* aLoadInfo,
301 Maybe<nsresult>& aCancelCode) {
302 // Check CSP navigate-to
303 // We need to enforce the CSP of the document that initiated the load,
304 // which is the CSP to inherit.
305 nsCOMPtr<nsIContentSecurityPolicy> cspToInherit =
306 aLoadInfo->GetCspToInherit();
307 if (cspToInherit) {
308 bool allowsNavigateTo = false;
309 nsresult rv = cspToInherit->GetAllowsNavigateTo(
310 aNewURI, aLoadInfo->GetIsFormSubmission(), true, /* aWasRedirected */
311 false, /* aEnforceAllowlist */
312 &allowsNavigateTo);
313 NS_ENSURE_SUCCESS(rv, rv);
315 if (!allowsNavigateTo) {
316 aCancelCode = Some(NS_ERROR_CSP_NAVIGATE_TO_VIOLATION);
317 return NS_OK;
321 // No need to continue processing if CSP is disabled or if the protocol
322 // is *not* subject to CSP.
323 // Please note, the correct way to opt-out of CSP using a custom
324 // protocolHandler is to set one of the nsIProtocolHandler flags
325 // that are allowlistet in subjectToCSP()
326 nsContentPolicyType policyType = aLoadInfo->InternalContentPolicyType();
327 if (!StaticPrefs::security_csp_enable() ||
328 !subjectToCSP(aNewURI, policyType)) {
329 return NS_OK;
332 nsCOMPtr<nsICSPEventListener> cspEventListener;
333 nsresult rv =
334 aLoadInfo->GetCspEventListener(getter_AddRefs(cspEventListener));
335 MOZ_ALWAYS_SUCCEEDS(rv);
337 nsAutoString cspNonce;
338 rv = aLoadInfo->GetCspNonce(cspNonce);
339 MOZ_ALWAYS_SUCCEEDS(rv);
341 bool isPreload = nsContentUtils::IsPreloadType(policyType);
343 /* On redirect, if the content policy is a preload type, rejecting the
344 * preload results in the load silently failing, so we pass true to
345 * the aSendViolationReports parameter. See Bug 1219453.
348 int16_t decision = nsIContentPolicy::ACCEPT;
349 bool parserCreatedScript = aLoadInfo->GetParserCreatedScript();
351 // 1) Apply speculative CSP for preloads
352 if (isPreload) {
353 nsCOMPtr<nsIContentSecurityPolicy> preloadCsp = aLoadInfo->GetPreloadCsp();
354 if (preloadCsp) {
355 // Pass originalURI to indicate the redirect
356 preloadCsp->ShouldLoad(
357 policyType, // load type per nsIContentPolicy (uint32_t)
358 cspEventListener,
359 aNewURI, // nsIURI
360 aOriginalURI, // Original nsIURI
361 true, // aSendViolationReports
362 cspNonce, // nonce
363 parserCreatedScript, &decision);
365 // if the preload policy already denied the load, then there
366 // is no point in checking the real policy
367 if (NS_CP_REJECTED(decision)) {
368 aCancelCode = Some(NS_ERROR_DOM_BAD_URI);
369 return NS_BINDING_FAILED;
374 // 2) Apply actual CSP to all loads
375 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadInfo->GetCsp();
376 if (csp) {
377 // Pass originalURI to indicate the redirect
378 csp->ShouldLoad(policyType, // load type per nsIContentPolicy (uint32_t)
379 cspEventListener,
380 aNewURI, // nsIURI
381 aOriginalURI, // Original nsIURI
382 true, // aSendViolationReports
383 cspNonce, // nonce
384 parserCreatedScript, &decision);
385 if (NS_CP_REJECTED(decision)) {
386 aCancelCode = Some(NS_ERROR_DOM_BAD_URI);
387 return NS_BINDING_FAILED;
391 return NS_OK;