Bug 1899500 - Implement explicit resource management in Baseline compiler. r=arai
[gecko.git] / dom / security / nsCSPUtils.cpp
blob373adac4ba94551bdcf91fb98ed20c8b7c527082
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 "nsAttrValue.h"
8 #include "nsCharSeparatedTokenizer.h"
9 #include "nsContentUtils.h"
10 #include "nsCSPUtils.h"
11 #include "nsDebug.h"
12 #include "nsCSPParser.h"
13 #include "nsComponentManagerUtils.h"
14 #include "nsIConsoleService.h"
15 #include "nsIContentSecurityPolicy.h"
16 #include "nsIChannel.h"
17 #include "nsICryptoHash.h"
18 #include "nsIScriptError.h"
19 #include "nsIStringBundle.h"
20 #include "nsIURL.h"
21 #include "nsNetUtil.h"
22 #include "nsReadableUtils.h"
23 #include "nsSandboxFlags.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsWhitespaceTokenizer.h"
27 #include "mozilla/Assertions.h"
28 #include "mozilla/Components.h"
29 #include "mozilla/dom/CSPDictionariesBinding.h"
30 #include "mozilla/dom/Document.h"
31 #include "mozilla/dom/SRIMetadata.h"
32 #include "mozilla/StaticPrefs_security.h"
34 using namespace mozilla;
35 using mozilla::dom::SRIMetadata;
37 #define DEFAULT_PORT -1
39 static mozilla::LogModule* GetCspUtilsLog() {
40 static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils");
41 return gCspUtilsPRLog;
44 #define CSPUTILSLOG(args) \
45 MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args)
46 #define CSPUTILSLOGENABLED() \
47 MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug)
49 void CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr) {
50 outDecStr.Truncate();
52 // helper function that should not be visible outside this methods scope
53 struct local {
54 static inline char16_t convertHexDig(char16_t aHexDig) {
55 if (isNumberToken(aHexDig)) {
56 return aHexDig - '0';
58 if (aHexDig >= 'A' && aHexDig <= 'F') {
59 return aHexDig - 'A' + 10;
61 // must be a lower case character
62 // (aHexDig >= 'a' && aHexDig <= 'f')
63 return aHexDig - 'a' + 10;
67 const char16_t *cur, *end, *hexDig1, *hexDig2;
68 cur = aEncStr.BeginReading();
69 end = aEncStr.EndReading();
71 while (cur != end) {
72 // if it's not a percent sign then there is
73 // nothing to do for that character
74 if (*cur != PERCENT_SIGN) {
75 outDecStr.Append(*cur);
76 cur++;
77 continue;
80 // get the two hexDigs following the '%'-sign
81 hexDig1 = cur + 1;
82 hexDig2 = cur + 2;
84 // if there are no hexdigs after the '%' then
85 // there is nothing to do for us.
86 if (hexDig1 == end || hexDig2 == end || !isValidHexDig(*hexDig1) ||
87 !isValidHexDig(*hexDig2)) {
88 outDecStr.Append(PERCENT_SIGN);
89 cur++;
90 continue;
93 // decode "% hexDig1 hexDig2" into a character.
94 char16_t decChar =
95 (local::convertHexDig(*hexDig1) << 4) + local::convertHexDig(*hexDig2);
96 outDecStr.Append(decChar);
98 // increment 'cur' to after the second hexDig
99 cur = ++hexDig2;
103 // The Content Security Policy should be inherited for
104 // local schemes like: "about", "blob", "data", or "filesystem".
105 // see: https://w3c.github.io/webappsec-csp/#initialize-document-csp
106 bool CSP_ShouldResponseInheritCSP(nsIChannel* aChannel) {
107 if (!aChannel) {
108 return false;
111 nsCOMPtr<nsIURI> uri;
112 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
113 NS_ENSURE_SUCCESS(rv, false);
115 bool isAbout = uri->SchemeIs("about");
116 if (isAbout) {
117 nsAutoCString aboutSpec;
118 rv = uri->GetSpec(aboutSpec);
119 NS_ENSURE_SUCCESS(rv, false);
120 // also allow about:blank#foo
121 if (StringBeginsWith(aboutSpec, "about:blank"_ns) ||
122 StringBeginsWith(aboutSpec, "about:srcdoc"_ns)) {
123 return true;
127 return uri->SchemeIs("blob") || uri->SchemeIs("data") ||
128 uri->SchemeIs("filesystem") || uri->SchemeIs("javascript");
131 void CSP_ApplyMetaCSPToDoc(mozilla::dom::Document& aDoc,
132 const nsAString& aPolicyStr) {
133 if (aDoc.IsLoadedAsData()) {
134 return;
137 nsAutoString policyStr(
138 nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
139 aPolicyStr));
141 if (policyStr.IsEmpty()) {
142 return;
145 nsCOMPtr<nsIContentSecurityPolicy> csp = aDoc.GetCsp();
146 if (!csp) {
147 MOZ_ASSERT(false, "how come there is no CSP");
148 return;
151 // Multiple CSPs (delivered through either header of meta tag) need to
152 // be joined together, see:
153 // https://w3c.github.io/webappsec/specs/content-security-policy/#delivery-html-meta-element
154 nsresult rv =
155 csp->AppendPolicy(policyStr,
156 false, // csp via meta tag can not be report only
157 true); // delivered through the meta tag
158 NS_ENSURE_SUCCESS_VOID(rv);
159 if (nsPIDOMWindowInner* inner = aDoc.GetInnerWindow()) {
160 inner->SetCsp(csp);
162 aDoc.ApplySettingsFromCSP(false);
165 void CSP_GetLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
166 nsAString& outResult) {
167 nsCOMPtr<nsIStringBundle> keyStringBundle;
168 nsCOMPtr<nsIStringBundleService> stringBundleService =
169 mozilla::components::StringBundle::Service();
171 NS_ASSERTION(stringBundleService, "String bundle service must be present!");
172 stringBundleService->CreateBundle(
173 "chrome://global/locale/security/csp.properties",
174 getter_AddRefs(keyStringBundle));
176 NS_ASSERTION(keyStringBundle, "Key string bundle must be available!");
178 if (!keyStringBundle) {
179 return;
181 keyStringBundle->FormatStringFromName(aName, aParams, outResult);
184 void CSP_LogStrMessage(const nsAString& aMsg) {
185 nsCOMPtr<nsIConsoleService> console(
186 do_GetService("@mozilla.org/consoleservice;1"));
188 if (!console) {
189 return;
191 nsString msg(aMsg);
192 console->LogStringMessage(msg.get());
195 void CSP_LogMessage(const nsAString& aMessage, const nsACString& aSourceName,
196 const nsAString& aSourceLine, uint32_t aLineNumber,
197 uint32_t aColumnNumber, uint32_t aFlags,
198 const nsACString& aCategory, uint64_t aInnerWindowID,
199 bool aFromPrivateWindow) {
200 nsCOMPtr<nsIConsoleService> console(
201 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
203 nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
205 if (!console || !error) {
206 return;
209 // Prepending CSP to the outgoing console message
210 nsString cspMsg;
211 CSP_GetLocalizedStr("CSPMessagePrefix",
212 AutoTArray<nsString, 1>{nsString(aMessage)}, cspMsg);
214 // Currently 'aSourceLine' is not logged to the console, because similar
215 // information is already included within the source link of the message.
216 // For inline violations however, the line and column number are 0 and
217 // information contained within 'aSourceLine' can be really useful for devs.
218 // E.g. 'aSourceLine' might be: 'onclick attribute on DIV element'.
219 // In such cases we append 'aSourceLine' directly to the error message.
220 if (!aSourceLine.IsEmpty() && aLineNumber == 0) {
221 cspMsg.AppendLiteral(u"\nSource: ");
222 cspMsg.Append(aSourceLine);
225 // Since we are leveraging csp errors as the category names which
226 // we pass to devtools, we should prepend them with "CSP_" to
227 // allow easy distincution in devtools code. e.g.
228 // upgradeInsecureRequest -> CSP_upgradeInsecureRequest
229 nsCString category("CSP_");
230 category.Append(aCategory);
232 nsresult rv;
233 if (aInnerWindowID > 0) {
234 rv =
235 error->InitWithWindowID(cspMsg, aSourceName, aLineNumber, aColumnNumber,
236 aFlags, category, aInnerWindowID);
237 } else {
238 rv = error->Init(cspMsg, aSourceName, aLineNumber, aColumnNumber, aFlags,
239 category, aFromPrivateWindow,
240 true /* from chrome context */);
242 if (NS_FAILED(rv)) {
243 return;
245 console->LogMessage(error);
248 CSPDirective CSP_StringToCSPDirective(const nsAString& aDir) {
249 nsString lowerDir = PromiseFlatString(aDir);
250 ToLowerCase(lowerDir);
252 uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]));
254 for (uint32_t i = 1; i < numDirs; i++) {
255 if (lowerDir.EqualsASCII(CSPStrDirectives[i])) {
256 return static_cast<CSPDirective>(i);
259 return nsIContentSecurityPolicy::NO_DIRECTIVE;
263 * Combines CSP_LogMessage and CSP_GetLocalizedStr into one call.
265 void CSP_LogLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
266 const nsACString& aSourceName,
267 const nsAString& aSourceLine, uint32_t aLineNumber,
268 uint32_t aColumnNumber, uint32_t aFlags,
269 const nsACString& aCategory, uint64_t aInnerWindowID,
270 bool aFromPrivateWindow) {
271 nsAutoString logMsg;
272 CSP_GetLocalizedStr(aName, aParams, logMsg);
273 CSP_LogMessage(logMsg, aSourceName, aSourceLine, aLineNumber, aColumnNumber,
274 aFlags, aCategory, aInnerWindowID, aFromPrivateWindow);
277 /* ===== Helpers ============================ */
278 // This implements
279 // https://w3c.github.io/webappsec-csp/#effective-directive-for-a-request.
280 // However the spec doesn't currently cover all request destinations, which
281 // we roughly represent using nsContentPolicyType.
282 CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) {
283 switch (aType) {
284 case nsIContentPolicy::TYPE_IMAGE:
285 case nsIContentPolicy::TYPE_IMAGESET:
286 case nsIContentPolicy::TYPE_INTERNAL_IMAGE:
287 case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD:
288 case nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON:
289 return nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE;
291 // BLock XSLT as script, see bug 910139
292 case nsIContentPolicy::TYPE_XSLT:
293 case nsIContentPolicy::TYPE_SCRIPT:
294 case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
295 case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
296 case nsIContentPolicy::TYPE_INTERNAL_MODULE:
297 case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
298 case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
299 case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET:
300 case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET:
301 case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT:
302 case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT:
303 // (https://github.com/w3c/webappsec-csp/issues/554)
304 // Some of these types are not explicitly defined in the spec.
306 // Chrome seems to use script-src-elem for worklet!
307 return nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE;
309 case nsIContentPolicy::TYPE_STYLESHEET:
310 case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
311 case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
312 return nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE;
314 case nsIContentPolicy::TYPE_FONT:
315 case nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD:
316 return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE;
318 case nsIContentPolicy::TYPE_MEDIA:
319 case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
320 case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
321 case nsIContentPolicy::TYPE_INTERNAL_TRACK:
322 return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE;
324 case nsIContentPolicy::TYPE_WEB_MANIFEST:
325 return nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE;
327 case nsIContentPolicy::TYPE_INTERNAL_WORKER:
328 case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE:
329 case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
330 case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
331 return nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE;
333 case nsIContentPolicy::TYPE_SUBDOCUMENT:
334 case nsIContentPolicy::TYPE_INTERNAL_FRAME:
335 case nsIContentPolicy::TYPE_INTERNAL_IFRAME:
336 return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
338 case nsIContentPolicy::TYPE_WEBSOCKET:
339 case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
340 case nsIContentPolicy::TYPE_BEACON:
341 case nsIContentPolicy::TYPE_PING:
342 case nsIContentPolicy::TYPE_FETCH:
343 case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC:
344 case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC:
345 case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
346 case nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD:
347 case nsIContentPolicy::TYPE_WEB_IDENTITY:
348 case nsIContentPolicy::TYPE_WEB_TRANSPORT:
349 return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE;
351 case nsIContentPolicy::TYPE_OBJECT:
352 case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
353 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
354 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
355 return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
357 case nsIContentPolicy::TYPE_DTD:
358 case nsIContentPolicy::TYPE_OTHER:
359 case nsIContentPolicy::TYPE_SPECULATIVE:
360 case nsIContentPolicy::TYPE_INTERNAL_DTD:
361 case nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD:
362 return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
364 // CSP does not apply to webrtc connections
365 case nsIContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA:
366 // csp shold not block top level loads, e.g. in case
367 // of a redirect.
368 case nsIContentPolicy::TYPE_DOCUMENT:
369 // CSP can not block csp reports
370 case nsIContentPolicy::TYPE_CSP_REPORT:
371 return nsIContentSecurityPolicy::NO_DIRECTIVE;
373 case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD:
374 case nsIContentPolicy::TYPE_UA_FONT:
375 return nsIContentSecurityPolicy::NO_DIRECTIVE;
377 // Fall through to error for all other directives
378 case nsIContentPolicy::TYPE_INVALID:
379 case nsIContentPolicy::TYPE_END:
380 MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective");
381 // Do not add default: so that compilers can catch the missing case.
383 return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
386 nsCSPHostSrc* CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI) {
387 // Create the host first
388 nsCString host;
389 aSelfURI->GetAsciiHost(host);
390 nsCSPHostSrc* hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host));
391 hostsrc->setGeneratedFromSelfKeyword();
393 // Add the scheme.
394 nsCString scheme;
395 aSelfURI->GetScheme(scheme);
396 hostsrc->setScheme(NS_ConvertUTF8toUTF16(scheme));
398 // An empty host (e.g. for data:) indicates it's effectively a unique origin.
399 // Please note that we still need to set the scheme on hostsrc (see above),
400 // because it's used for reporting.
401 if (host.EqualsLiteral("")) {
402 hostsrc->setIsUniqueOrigin();
403 // no need to query the port in that case.
404 return hostsrc;
407 int32_t port;
408 aSelfURI->GetPort(&port);
409 // Only add port if it's not default port.
410 if (port > 0) {
411 nsAutoString portStr;
412 portStr.AppendInt(port);
413 hostsrc->setPort(portStr);
415 return hostsrc;
418 bool CSP_IsEmptyDirective(const nsAString& aValue, const nsAString& aDir) {
419 return (aDir.Length() == 0 && aValue.Length() == 0);
421 bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir) {
422 return aValue.LowerCaseEqualsASCII(CSP_CSPDirectiveToString(aDir));
425 bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey) {
426 return aValue.LowerCaseEqualsASCII(CSP_EnumToUTF8Keyword(aKey));
429 bool CSP_IsQuotelessKeyword(const nsAString& aKey) {
430 nsString lowerKey;
431 ToLowerCase(aKey, lowerKey);
433 nsAutoString keyword;
434 for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
435 // skipping the leading ' and trimming the trailing '
436 keyword.AssignASCII(gCSPUTF8Keywords[i] + 1);
437 keyword.Trim("'", false, true);
438 if (lowerKey.Equals(keyword)) {
439 return true;
442 return false;
446 * Checks whether the current directive permits a specific
447 * scheme. This function is called from nsCSPSchemeSrc() and
448 * also nsCSPHostSrc.
449 * @param aEnforcementScheme
450 * The scheme that this directive allows
451 * @param aUri
452 * The uri of the subresource load.
453 * @param aReportOnly
454 * Whether the enforced policy is report only or not.
455 * @param aUpgradeInsecure
456 * Whether the policy makes use of the directive
457 * 'upgrade-insecure-requests'.
458 * @param aFromSelfURI
459 * Whether a scheme was generated from the keyword 'self'
460 * which then allows schemeless sources to match ws and wss.
463 bool permitsScheme(const nsAString& aEnforcementScheme, nsIURI* aUri,
464 bool aReportOnly, bool aUpgradeInsecure, bool aFromSelfURI) {
465 nsAutoCString scheme;
466 nsresult rv = aUri->GetScheme(scheme);
467 NS_ENSURE_SUCCESS(rv, false);
469 // no scheme to enforce, let's allow the load (e.g. script-src *)
470 if (aEnforcementScheme.IsEmpty()) {
471 return true;
474 // if the scheme matches, all good - allow the load
475 if (aEnforcementScheme.EqualsASCII(scheme.get())) {
476 return true;
479 // allow scheme-less sources where the protected resource is http
480 // and the load is https, see:
481 // http://www.w3.org/TR/CSP2/#match-source-expression
482 if (aEnforcementScheme.EqualsASCII("http")) {
483 if (scheme.EqualsASCII("https")) {
484 return true;
486 if ((scheme.EqualsASCII("ws") || scheme.EqualsASCII("wss")) &&
487 aFromSelfURI) {
488 return true;
491 if (aEnforcementScheme.EqualsASCII("https")) {
492 if (scheme.EqualsLiteral("wss") && aFromSelfURI) {
493 return true;
496 if (aEnforcementScheme.EqualsASCII("ws") && scheme.EqualsASCII("wss")) {
497 return true;
500 // Allow the load when enforcing upgrade-insecure-requests with the
501 // promise the request gets upgraded from http to https and ws to wss.
502 // See nsHttpChannel::Connect() and also WebSocket.cpp. Please note,
503 // the report only policies should not allow the load and report
504 // the error back to the page.
505 return (
506 (aUpgradeInsecure && !aReportOnly) &&
507 ((scheme.EqualsASCII("http") &&
508 aEnforcementScheme.EqualsASCII("https")) ||
509 (scheme.EqualsASCII("ws") && aEnforcementScheme.EqualsASCII("wss"))));
513 * A helper function for appending a CSP header to an existing CSP
514 * policy.
516 * @param aCsp the CSP policy
517 * @param aHeaderValue the header
518 * @param aReportOnly is this a report-only header?
521 nsresult CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp,
522 const nsAString& aHeaderValue,
523 bool aReportOnly) {
524 NS_ENSURE_ARG(aCsp);
526 // Need to tokenize the header value since multiple headers could be
527 // concatenated into one comma-separated list of policies.
528 // See RFC2616 section 4.2 (last paragraph)
529 nsresult rv = NS_OK;
530 for (const nsAString& policy :
531 nsCharSeparatedTokenizer(aHeaderValue, ',').ToRange()) {
532 rv = aCsp->AppendPolicy(policy, aReportOnly, false);
533 NS_ENSURE_SUCCESS(rv, rv);
535 CSPUTILSLOG(("CSP refined with policy: \"%s\"",
536 NS_ConvertUTF16toUTF8(policy).get()));
539 return NS_OK;
542 /* ===== nsCSPSrc ============================ */
544 nsCSPBaseSrc::nsCSPBaseSrc() {}
546 nsCSPBaseSrc::~nsCSPBaseSrc() = default;
548 // ::permits is only called for external load requests, therefore:
549 // nsCSPKeywordSrc and nsCSPHashSource fall back to this base class
550 // implementation which will never allow the load.
551 bool nsCSPBaseSrc::permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
552 bool aUpgradeInsecure) const {
553 if (CSPUTILSLOGENABLED()) {
554 CSPUTILSLOG(
555 ("nsCSPBaseSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
557 return false;
560 // ::allows is only called for inlined loads, therefore:
561 // nsCSPSchemeSrc, nsCSPHostSrc fall back
562 // to this base class implementation which will never allow the load.
563 bool nsCSPBaseSrc::allows(enum CSPKeyword aKeyword,
564 const nsAString& aHashOrNonce) const {
565 CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
566 aKeyword == CSP_HASH ? "hash" : CSP_EnumToUTF8Keyword(aKeyword),
567 NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
568 return false;
571 /* ====== nsCSPSchemeSrc ===================== */
573 nsCSPSchemeSrc::nsCSPSchemeSrc(const nsAString& aScheme) : mScheme(aScheme) {
574 ToLowerCase(mScheme);
577 nsCSPSchemeSrc::~nsCSPSchemeSrc() = default;
579 bool nsCSPSchemeSrc::permits(nsIURI* aUri, bool aWasRedirected,
580 bool aReportOnly, bool aUpgradeInsecure) const {
581 if (CSPUTILSLOGENABLED()) {
582 CSPUTILSLOG(
583 ("nsCSPSchemeSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
585 MOZ_ASSERT((!mScheme.EqualsASCII("")), "scheme can not be the empty string");
586 return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, false);
589 bool nsCSPSchemeSrc::visit(nsCSPSrcVisitor* aVisitor) const {
590 return aVisitor->visitSchemeSrc(*this);
593 void nsCSPSchemeSrc::toString(nsAString& outStr) const {
594 outStr.Append(mScheme);
595 outStr.AppendLiteral(":");
598 /* ===== nsCSPHostSrc ======================== */
600 nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost)
601 : mHost(aHost),
602 mGeneratedFromSelfKeyword(false),
603 mIsUniqueOrigin(false),
604 mWithinFrameAncstorsDir(false) {
605 ToLowerCase(mHost);
608 nsCSPHostSrc::~nsCSPHostSrc() = default;
611 * Checks whether the current directive permits a specific port.
612 * @param aEnforcementScheme
613 * The scheme that this directive allows
614 * (used to query the default port for that scheme)
615 * @param aEnforcementPort
616 * The port that this directive allows
617 * @param aResourceURI
618 * The uri of the subresource load
620 bool permitsPort(const nsAString& aEnforcementScheme,
621 const nsAString& aEnforcementPort, nsIURI* aResourceURI) {
622 // If enforcement port is the wildcard, don't block the load.
623 if (aEnforcementPort.EqualsASCII("*")) {
624 return true;
627 int32_t resourcePort;
628 nsresult rv = aResourceURI->GetPort(&resourcePort);
629 if (NS_FAILED(rv) && aEnforcementPort.IsEmpty()) {
630 // If we cannot get a Port (e.g. because of an Custom Protocol handler)
631 // We need to check if a default port is associated with the Scheme
632 if (aEnforcementScheme.IsEmpty()) {
633 return false;
635 int defaultPortforScheme =
636 NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get());
638 // If there is no default port associated with the Scheme (
639 // defaultPortforScheme == -1) or it is an externally handled protocol (
640 // defaultPortforScheme == 0 ) and the csp does not enforce a port - we can
641 // allow not having a port
642 return (defaultPortforScheme == -1 || defaultPortforScheme == -0);
644 // Avoid unnecessary string creation/manipulation and don't block the
645 // load if the resource to be loaded uses the default port for that
646 // scheme and there is no port to be enforced.
647 // Note, this optimization relies on scheme checks within permitsScheme().
648 if (resourcePort == DEFAULT_PORT && aEnforcementPort.IsEmpty()) {
649 return true;
652 // By now we know at that either the resourcePort does not use the default
653 // port or there is a port restriction to be enforced. A port value of -1
654 // corresponds to the protocol's default port (eg. -1 implies port 80 for
655 // http URIs), in such a case we have to query the default port of the
656 // resource to be loaded.
657 if (resourcePort == DEFAULT_PORT) {
658 nsAutoCString resourceScheme;
659 rv = aResourceURI->GetScheme(resourceScheme);
660 NS_ENSURE_SUCCESS(rv, false);
661 resourcePort = NS_GetDefaultPort(resourceScheme.get());
664 // If there is a port to be enforced and the ports match, then
665 // don't block the load.
666 nsString resourcePortStr;
667 resourcePortStr.AppendInt(resourcePort);
668 if (aEnforcementPort.Equals(resourcePortStr)) {
669 return true;
672 // If there is no port to be enforced, query the default port for the load.
673 nsString enforcementPort(aEnforcementPort);
674 if (enforcementPort.IsEmpty()) {
675 // For scheme less sources, our parser always generates a scheme
676 // which is the scheme of the protected resource.
677 MOZ_ASSERT(!aEnforcementScheme.IsEmpty(),
678 "need a scheme to query default port");
679 int32_t defaultEnforcementPort =
680 NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get());
681 enforcementPort.Truncate();
682 enforcementPort.AppendInt(defaultEnforcementPort);
685 // If default ports match, don't block the load
686 if (enforcementPort.Equals(resourcePortStr)) {
687 return true;
690 // Additional port matching where the regular URL matching algorithm
691 // treats insecure ports as matching their secure variants.
692 // default port for http is :80
693 // default port for https is :443
694 if (enforcementPort.EqualsLiteral("80") &&
695 resourcePortStr.EqualsLiteral("443")) {
696 return true;
699 // ports do not match, block the load.
700 return false;
703 bool nsCSPHostSrc::permits(nsIURI* aUri, bool aWasRedirected, bool aReportOnly,
704 bool aUpgradeInsecure) const {
705 if (CSPUTILSLOGENABLED()) {
706 CSPUTILSLOG(
707 ("nsCSPHostSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
710 if (mIsUniqueOrigin) {
711 return false;
714 // we are following the enforcement rules from the spec, see:
715 // http://www.w3.org/TR/CSP11/#match-source-expression
717 // 4.3) scheme matching: Check if the scheme matches.
718 if (!permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure,
719 mGeneratedFromSelfKeyword)) {
720 return false;
723 // The host in nsCSpHostSrc should never be empty. In case we are enforcing
724 // just a specific scheme, the parser should generate a nsCSPSchemeSource.
725 NS_ASSERTION((!mHost.IsEmpty()), "host can not be the empty string");
727 // Before we can check if the host matches, we have to
728 // extract the host part from aUri.
729 nsAutoCString uriHost;
730 nsresult rv = aUri->GetAsciiHost(uriHost);
731 NS_ENSURE_SUCCESS(rv, false);
733 nsString decodedUriHost;
734 CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost);
736 // 2) host matching: Enforce a single *
737 if (mHost.EqualsASCII("*")) {
738 // The single ASTERISK character (*) does not match a URI's scheme of a type
739 // designating a globally unique identifier (such as blob:, data:, or
740 // filesystem:) At the moment firefox does not support filesystem; but for
741 // future compatibility we support it in CSP according to the spec,
742 // see: 4.2.2 Matching Source Expressions Note, that allowlisting any of
743 // these schemes would call nsCSPSchemeSrc::permits().
744 if (aUri->SchemeIs("blob") || aUri->SchemeIs("data") ||
745 aUri->SchemeIs("filesystem")) {
746 return false;
749 // If no scheme is present there also wont be a port and folder to check
750 // which means we can return early
751 if (mScheme.IsEmpty()) {
752 return true;
755 // 4.5) host matching: Check if the allowed host starts with a wilcard.
756 else if (mHost.First() == '*') {
757 NS_ASSERTION(
758 mHost[1] == '.',
759 "Second character needs to be '.' whenever host starts with '*'");
761 // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before
762 // checking if the remaining characters match
763 nsString wildCardHost = mHost;
764 wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1);
765 if (!StringEndsWith(decodedUriHost, wildCardHost)) {
766 return false;
769 // 4.6) host matching: Check if hosts match.
770 else if (!mHost.Equals(decodedUriHost)) {
771 return false;
774 // Port matching: Check if the ports match.
775 if (!permitsPort(mScheme, mPort, aUri)) {
776 return false;
779 // 4.9) Path matching: If there is a path, we have to enforce
780 // path-level matching, unless the channel got redirected, see:
781 // http://www.w3.org/TR/CSP11/#source-list-paths-and-redirects
782 if (!aWasRedirected && !mPath.IsEmpty()) {
783 // converting aUri into nsIURL so we can strip query and ref
784 // example.com/test#foo -> example.com/test
785 // example.com/test?val=foo -> example.com/test
786 nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
787 if (!url) {
788 NS_ASSERTION(false, "can't QI into nsIURI");
789 return false;
791 nsAutoCString uriPath;
792 rv = url->GetFilePath(uriPath);
793 NS_ENSURE_SUCCESS(rv, false);
795 if (mWithinFrameAncstorsDir) {
796 // no path matching for frame-ancestors to not leak any path information.
797 return true;
800 nsString decodedUriPath;
801 CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath);
803 // check if the last character of mPath is '/'; if so
804 // we just have to check loading resource is within
805 // the allowed path.
806 if (mPath.Last() == '/') {
807 if (!StringBeginsWith(decodedUriPath, mPath)) {
808 return false;
811 // otherwise mPath refers to a specific file, and we have to
812 // check if the loading resource matches the file.
813 else {
814 if (!mPath.Equals(decodedUriPath)) {
815 return false;
820 // At the end: scheme, host, port and path match -> allow the load.
821 return true;
824 bool nsCSPHostSrc::visit(nsCSPSrcVisitor* aVisitor) const {
825 return aVisitor->visitHostSrc(*this);
828 void nsCSPHostSrc::toString(nsAString& outStr) const {
829 if (mGeneratedFromSelfKeyword) {
830 outStr.AppendLiteral("'self'");
831 return;
834 // If mHost is a single "*", we append the wildcard and return.
835 if (mHost.EqualsASCII("*") && mScheme.IsEmpty() && mPort.IsEmpty()) {
836 outStr.Append(mHost);
837 return;
840 // append scheme
841 outStr.Append(mScheme);
843 // append host
844 outStr.AppendLiteral("://");
845 outStr.Append(mHost);
847 // append port
848 if (!mPort.IsEmpty()) {
849 outStr.AppendLiteral(":");
850 outStr.Append(mPort);
853 // append path
854 outStr.Append(mPath);
857 void nsCSPHostSrc::setScheme(const nsAString& aScheme) {
858 mScheme = aScheme;
859 ToLowerCase(mScheme);
862 void nsCSPHostSrc::setPort(const nsAString& aPort) { mPort = aPort; }
864 void nsCSPHostSrc::appendPath(const nsAString& aPath) { mPath.Append(aPath); }
866 /* ===== nsCSPKeywordSrc ===================== */
868 nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword)
869 : mKeyword(aKeyword) {
870 NS_ASSERTION((aKeyword != CSP_SELF),
871 "'self' should have been replaced in the parser");
874 nsCSPKeywordSrc::~nsCSPKeywordSrc() = default;
876 bool nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword,
877 const nsAString& aHashOrNonce) const {
878 CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s",
879 CSP_EnumToUTF8Keyword(aKeyword),
880 NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
881 return mKeyword == aKeyword;
884 bool nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const {
885 return aVisitor->visitKeywordSrc(*this);
888 void nsCSPKeywordSrc::toString(nsAString& outStr) const {
889 outStr.Append(CSP_EnumToUTF16Keyword(mKeyword));
892 /* ===== nsCSPNonceSrc ==================== */
894 nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce) : mNonce(aNonce) {}
896 nsCSPNonceSrc::~nsCSPNonceSrc() = default;
898 bool nsCSPNonceSrc::allows(enum CSPKeyword aKeyword,
899 const nsAString& aHashOrNonce) const {
900 CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
901 CSP_EnumToUTF8Keyword(aKeyword),
902 NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
904 if (aKeyword != CSP_NONCE) {
905 return false;
907 // nonces can not be invalidated by strict-dynamic
908 return mNonce.Equals(aHashOrNonce);
911 bool nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const {
912 return aVisitor->visitNonceSrc(*this);
915 void nsCSPNonceSrc::toString(nsAString& outStr) const {
916 outStr.Append(CSP_EnumToUTF16Keyword(CSP_NONCE));
917 outStr.Append(mNonce);
918 outStr.AppendLiteral("'");
921 /* ===== nsCSPHashSrc ===================== */
923 nsCSPHashSrc::nsCSPHashSrc(const nsAString& aAlgo, const nsAString& aHash)
924 : mAlgorithm(aAlgo), mHash(aHash) {
925 // Only the algo should be rewritten to lowercase, the hash must remain the
926 // same.
927 ToLowerCase(mAlgorithm);
928 // Normalize the base64url encoding to base64 encoding:
929 char16_t* cur = mHash.BeginWriting();
930 char16_t* end = mHash.EndWriting();
932 for (; cur < end; ++cur) {
933 if (char16_t('-') == *cur) {
934 *cur = char16_t('+');
936 if (char16_t('_') == *cur) {
937 *cur = char16_t('/');
942 nsCSPHashSrc::~nsCSPHashSrc() = default;
944 bool nsCSPHashSrc::allows(enum CSPKeyword aKeyword,
945 const nsAString& aHashOrNonce) const {
946 CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
947 CSP_EnumToUTF8Keyword(aKeyword),
948 NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
950 if (aKeyword != CSP_HASH) {
951 return false;
954 // hashes can not be invalidated by strict-dynamic
956 // Convert aHashOrNonce to UTF-8
957 NS_ConvertUTF16toUTF8 utf8_hash(aHashOrNonce);
959 nsCOMPtr<nsICryptoHash> hasher;
960 nsresult rv = NS_NewCryptoHash(NS_ConvertUTF16toUTF8(mAlgorithm),
961 getter_AddRefs(hasher));
962 NS_ENSURE_SUCCESS(rv, false);
964 rv = hasher->Update((uint8_t*)utf8_hash.get(), utf8_hash.Length());
965 NS_ENSURE_SUCCESS(rv, false);
967 nsAutoCString hash;
968 rv = hasher->Finish(true, hash);
969 NS_ENSURE_SUCCESS(rv, false);
971 return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
974 bool nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const {
975 return aVisitor->visitHashSrc(*this);
978 void nsCSPHashSrc::toString(nsAString& outStr) const {
979 outStr.AppendLiteral("'");
980 outStr.Append(mAlgorithm);
981 outStr.AppendLiteral("-");
982 outStr.Append(mHash);
983 outStr.AppendLiteral("'");
986 /* ===== nsCSPReportURI ===================== */
988 nsCSPReportURI::nsCSPReportURI(nsIURI* aURI) : mReportURI(aURI) {}
990 nsCSPReportURI::~nsCSPReportURI() = default;
992 bool nsCSPReportURI::visit(nsCSPSrcVisitor* aVisitor) const { return false; }
994 void nsCSPReportURI::toString(nsAString& outStr) const {
995 nsAutoCString spec;
996 nsresult rv = mReportURI->GetSpec(spec);
997 if (NS_FAILED(rv)) {
998 return;
1000 outStr.AppendASCII(spec.get());
1003 /* ===== nsCSPReportGroup ===================== */
1005 nsCSPGroup::nsCSPGroup(const nsAString& aGroup) : mGroup(aGroup) {}
1007 nsCSPGroup::~nsCSPGroup() = default;
1009 bool nsCSPGroup::visit(nsCSPSrcVisitor* aVisitor) const { return false; }
1011 void nsCSPGroup::toString(nsAString& aOutStr) const { aOutStr.Append(mGroup); }
1013 /* ===== nsCSPSandboxFlags ===================== */
1015 nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags) : mFlags(aFlags) {
1016 ToLowerCase(mFlags);
1019 nsCSPSandboxFlags::~nsCSPSandboxFlags() = default;
1021 bool nsCSPSandboxFlags::visit(nsCSPSrcVisitor* aVisitor) const { return false; }
1023 void nsCSPSandboxFlags::toString(nsAString& outStr) const {
1024 outStr.Append(mFlags);
1027 /* ===== nsCSPRequireTrustedTypesForDirectiveValue ===================== */
1029 nsCSPRequireTrustedTypesForDirectiveValue::
1030 nsCSPRequireTrustedTypesForDirectiveValue(const nsAString& aValue)
1031 : mValue{aValue} {}
1033 bool nsCSPRequireTrustedTypesForDirectiveValue::visit(
1034 nsCSPSrcVisitor* aVisitor) const {
1035 MOZ_ASSERT_UNREACHABLE(
1036 "This method should only be called for other overloads of this method.");
1037 return false;
1040 void nsCSPRequireTrustedTypesForDirectiveValue::toString(
1041 nsAString& aOutStr) const {
1042 aOutStr.Append(mValue);
1045 /* =============== nsCSPTrustedTypesDirectivePolicyName =============== */
1047 nsCSPTrustedTypesDirectivePolicyName::nsCSPTrustedTypesDirectivePolicyName(
1048 const nsAString& aName)
1049 : mName{aName} {}
1051 bool nsCSPTrustedTypesDirectivePolicyName::visit(
1052 nsCSPSrcVisitor* aVisitor) const {
1053 MOZ_ASSERT_UNREACHABLE(
1054 "Should only be called for other overloads of this method.");
1055 return false;
1058 void nsCSPTrustedTypesDirectivePolicyName::toString(nsAString& aOutStr) const {
1059 aOutStr.Append(mName);
1062 /* ===== nsCSPDirective ====================== */
1064 nsCSPDirective::nsCSPDirective(CSPDirective aDirective) {
1065 mDirective = aDirective;
1068 nsCSPDirective::~nsCSPDirective() {
1069 for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1070 delete mSrcs[i];
1074 // https://w3c.github.io/webappsec-csp/#match-nonce-to-source-list
1075 static bool DoesNonceMatchSourceList(nsILoadInfo* aLoadInfo,
1076 const nsTArray<nsCSPBaseSrc*>& aSrcs) {
1077 // Step 1. Assert: source list is not null. (implicit)
1079 // Note: For code-reuse we do "request’s cryptographic nonce metadata" here
1080 // instead of the caller.
1081 nsAutoString nonce;
1082 MOZ_ALWAYS_SUCCEEDS(aLoadInfo->GetCspNonce(nonce));
1084 // Step 2. If nonce is the empty string, return "Does Not Match".
1085 if (nonce.IsEmpty()) {
1086 return false;
1089 // Step 3. For each expression of source list:
1090 for (nsCSPBaseSrc* src : aSrcs) {
1091 // Step 3.1. If expression matches the nonce-source grammar, and nonce is
1092 // identical to expression’s base64-value part, return "Matches".
1093 if (src->isNonce()) {
1094 nsAutoString srcNonce;
1095 static_cast<nsCSPNonceSrc*>(src)->getNonce(srcNonce);
1096 if (srcNonce == nonce) {
1097 return true;
1102 // Step 4. Return "Does Not Match".
1103 return false;
1106 // https://www.w3.org/TR/SRI/#parse-metadata
1107 // This function is similar to SRICheck::IntegrityMetadata, but also keeps
1108 // SRI metadata with weaker hashes.
1109 // CSP treats "no metadata" and empty results the same way.
1110 static nsTArray<SRIMetadata> ParseSRIMetadata(const nsAString& aMetadata) {
1111 // Step 1. Let result be the empty set.
1112 // Step 2. Let empty be equal to true.
1113 nsTArray<SRIMetadata> result;
1115 NS_ConvertUTF16toUTF8 metadataList(aMetadata);
1116 nsAutoCString token;
1118 // Step 3. For each token returned by splitting metadata on spaces:
1119 nsCWhitespaceTokenizer tokenizer(metadataList);
1120 while (tokenizer.hasMoreTokens()) {
1121 token = tokenizer.nextToken();
1122 // Step 3.1. Set empty to false.
1123 // Step 3.3. Parse token per the grammar in integrity metadata.
1124 SRIMetadata metadata(token);
1125 // Step 3.2. If token is not a valid metadata, skip the remaining steps, and
1126 // proceed to the next token.
1127 if (metadata.IsMalformed()) {
1128 continue;
1131 // Step 3.4. Let algorithm be the alg component of token.
1132 // Step 3.5. If algorithm is a hash function recognized by the user agent,
1133 // add the
1134 // parsed token to result.
1135 if (metadata.IsAlgorithmSupported()) {
1136 result.AppendElement(metadata);
1140 // Step 4. Return no metadata if empty is true, otherwise return result.
1141 return result;
1144 bool nsCSPDirective::permits(CSPDirective aDirective, nsILoadInfo* aLoadInfo,
1145 nsIURI* aUri, bool aWasRedirected,
1146 bool aReportOnly, bool aUpgradeInsecure) const {
1147 MOZ_ASSERT(equals(aDirective) || isDefaultDirective());
1149 if (CSPUTILSLOGENABLED()) {
1150 CSPUTILSLOG(("nsCSPDirective::permits, aUri: %s, aDirective: %s",
1151 aUri->GetSpecOrDefault().get(),
1152 CSP_CSPDirectiveToString(aDirective)));
1155 if (aLoadInfo) {
1156 // https://w3c.github.io/webappsec-csp/#style-src-elem-pre-request
1157 if (aDirective == CSPDirective::STYLE_SRC_ELEM_DIRECTIVE) {
1158 // Step 3. If the result of executing §6.7.2.3 Does nonce match source
1159 // list? on request’s cryptographic nonce metadata and this directive’s
1160 // value is "Matches", return "Allowed".
1161 if (DoesNonceMatchSourceList(aLoadInfo, mSrcs)) {
1162 CSPUTILSLOG((" Allowed by matching nonce (style)"));
1163 return true;
1167 // https://w3c.github.io/webappsec-csp/#script-pre-request
1168 // Step 1. If request’s destination is script-like:
1169 else if (aDirective == CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE ||
1170 aDirective == CSPDirective::WORKER_SRC_DIRECTIVE) {
1171 // Step 1.1. If the result of executing §6.7.2.3 Does nonce match source
1172 // list? on request’s cryptographic nonce metadata and this directive’s
1173 // value is "Matches", return "Allowed".
1174 if (DoesNonceMatchSourceList(aLoadInfo, mSrcs)) {
1175 CSPUTILSLOG((" Allowed by matching nonce (script-like)"));
1176 return true;
1179 // Step 1.2. Let integrity expressions be the set of source expressions in
1180 // directive’s value that match the hash-source grammar.
1181 nsTArray<nsCSPHashSrc*> integrityExpressions;
1182 bool hasStrictDynamicKeyword =
1183 false; // Optimization to reduce number of iterations.
1184 for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1185 if (mSrcs[i]->isHash()) {
1186 integrityExpressions.AppendElement(
1187 static_cast<nsCSPHashSrc*>(mSrcs[i]));
1188 } else if (mSrcs[i]->isKeyword(CSP_STRICT_DYNAMIC)) {
1189 hasStrictDynamicKeyword = true;
1193 // Step 1.3. If integrity expressions is not empty:
1194 if (!integrityExpressions.IsEmpty()) {
1195 // Step 1.3.1. Let integrity sources be the result of executing the
1196 // algorithm defined in [SRI 3.3.3 Parse metadata] on request’s
1197 // integrity metadata.
1198 nsAutoString integrityMetadata;
1199 aLoadInfo->GetIntegrityMetadata(integrityMetadata);
1201 nsTArray<SRIMetadata> integritySources =
1202 ParseSRIMetadata(integrityMetadata);
1203 MOZ_ASSERT(
1204 integritySources.IsEmpty() == integrityMetadata.IsEmpty(),
1205 "The integrity metadata should be only be empty, "
1206 "when the parsed string was completely empty, otherwise it should "
1207 "include at least one valid hash");
1209 // Step 1.3.2. If integrity sources is "no metadata" or an empty set,
1210 // skip the remaining substeps.
1211 if (!integritySources.IsEmpty()) {
1212 // Step 1.3.3. Let bypass due to integrity match be true.
1213 bool bypass = true;
1215 nsAutoCString sourceAlgorithmUTF8;
1216 nsAutoCString sourceHashUTF8;
1217 nsAutoString sourceAlgorithm;
1218 nsAutoString sourceHash;
1219 nsAutoString algorithm;
1220 nsAutoString hash;
1222 // Step 1.3.4. For each source of integrity sources:
1223 for (const SRIMetadata& source : integritySources) {
1224 source.GetAlgorithm(&sourceAlgorithmUTF8);
1225 sourceAlgorithm = NS_ConvertUTF8toUTF16(sourceAlgorithmUTF8);
1226 source.GetHash(0, &sourceHashUTF8);
1227 sourceHash = NS_ConvertUTF8toUTF16(sourceHashUTF8);
1229 // Step 1.3.4.1 If directive’s value does not contain a source
1230 // expression whose hash-algorithm is an ASCII case-insensitive
1231 // match for source’s hash-algorithm, and whose base64-value is
1232 // identical to source’s base64-value, then set bypass due to
1233 // integrity match to false.
1234 bool found = false;
1235 for (const nsCSPHashSrc* hashSrc : integrityExpressions) {
1236 hashSrc->getAlgorithm(algorithm);
1237 hashSrc->getHash(hash);
1239 // The nsCSPHashSrc constructor lowercases algorithm, so this
1240 // is case-insensitive.
1241 if (sourceAlgorithm == algorithm && sourceHash == hash) {
1242 found = true;
1243 break;
1247 if (!found) {
1248 bypass = false;
1249 break;
1253 // Step 1.3.5. If bypass due to integrity match is true, return
1254 // "Allowed".
1255 if (bypass) {
1256 CSPUTILSLOG(
1257 (" Allowed by matching integrity metadata (script-like)"));
1258 return true;
1263 // Step 1.4. If directive’s value contains a source expression that is an
1264 // ASCII case-insensitive match for the "'strict-dynamic'" keyword-source:
1266 // XXX I don't think we should apply strict-dynamic to XSLT.
1267 if (hasStrictDynamicKeyword && aLoadInfo->InternalContentPolicyType() !=
1268 nsIContentPolicy::TYPE_XSLT) {
1269 // Step 1.4.1 If the request’s parser metadata is "parser-inserted",
1270 // return "Blocked". Otherwise, return "Allowed".
1271 if (aLoadInfo->GetParserCreatedScript()) {
1272 CSPUTILSLOG(
1273 (" Blocked by 'strict-dynamic' because parser-inserted"));
1274 return false;
1277 CSPUTILSLOG(
1278 (" Allowed by 'strict-dynamic' because not-parser-inserted"));
1279 return true;
1284 for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1285 if (mSrcs[i]->permits(aUri, aWasRedirected, aReportOnly,
1286 aUpgradeInsecure)) {
1287 return true;
1290 return false;
1293 bool nsCSPDirective::allows(enum CSPKeyword aKeyword,
1294 const nsAString& aHashOrNonce) const {
1295 CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, aHashOrNonce: %s",
1296 CSP_EnumToUTF8Keyword(aKeyword),
1297 NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
1299 for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1300 if (mSrcs[i]->allows(aKeyword, aHashOrNonce)) {
1301 return true;
1304 return false;
1307 // https://w3c.github.io/webappsec-csp/#allow-all-inline
1308 bool nsCSPDirective::allowsAllInlineBehavior(CSPDirective aDir) const {
1309 // Step 1. Let allow all inline be false.
1310 bool allowAll = false;
1312 // Step 2. For each expression of list:
1313 for (nsCSPBaseSrc* src : mSrcs) {
1314 // Step 2.1. If expression matches the nonce-source or hash-source grammar,
1315 // return "Does Not Allow".
1316 if (src->isNonce() || src->isHash()) {
1317 return false;
1320 // Step 2.2. If type is "script", "script attribute" or "navigation" and
1321 // expression matches the keyword-source "'strict-dynamic'", return "Does
1322 // Not Allow".
1323 if ((aDir == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE ||
1324 aDir == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) &&
1325 src->isKeyword(CSP_STRICT_DYNAMIC)) {
1326 return false;
1329 // Step 2.3. If expression is an ASCII case-insensitive match for the
1330 // keyword-source "'unsafe-inline'", set allow all inline to true.
1331 if (src->isKeyword(CSP_UNSAFE_INLINE)) {
1332 allowAll = true;
1336 // Step 3. If allow all inline is true, return "Allows". Otherwise, return
1337 // "Does Not Allow".
1338 return allowAll;
1341 static constexpr auto kWildcard = u"*"_ns;
1343 bool nsCSPDirective::ShouldCreateViolationForNewTrustedTypesPolicy(
1344 const nsAString& aPolicyName,
1345 const nsTArray<nsString>& aCreatedPolicyNames) const {
1346 MOZ_ASSERT(mDirective == nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE);
1348 if (mDirective == nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE) {
1349 if (allows(CSP_NONE, EmptyString())) {
1350 // Step 2.4: if directive’s value only contains a tt-keyword which is a
1351 // match for a value 'none', set createViolation to true.
1352 // `nsCSPParser` ignores the 'none' keyword if other keywords or policy
1353 // names are present. Hence no additional checks required here.
1354 return true;
1357 if (aCreatedPolicyNames.Contains(aPolicyName) &&
1358 !allows(CSP_ALLOW_DUPLICATES, EmptyString())) {
1359 // Step 2.5: if createdPolicyNames contains policyName and directive’s
1360 // value does not contain a tt-keyword which is a match for a value
1361 // 'allow-duplicates', set createViolation to true.
1362 return true;
1365 if (!ContainsTrustedTypesDirectivePolicyName(aPolicyName) &&
1366 !ContainsTrustedTypesDirectivePolicyName(kWildcard)) {
1367 // Step 2.6: if directive’s value does not contain a tt-policy-name, which
1368 // value is policyName, and directive’s value does not contain a
1369 // tt-wildcard, set createViolation to true.
1370 return true;
1374 return false;
1377 void nsCSPDirective::toString(nsAString& outStr) const {
1378 // Append directive name
1379 outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
1381 MOZ_ASSERT(!mSrcs.IsEmpty());
1383 outStr.AppendLiteral(" ");
1385 // Append srcs
1386 StringJoinAppend(outStr, u" "_ns, mSrcs,
1387 [](nsAString& dest, nsCSPBaseSrc* cspBaseSrc) {
1388 cspBaseSrc->toString(dest);
1392 void nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const {
1393 mozilla::dom::Sequence<nsString> srcs;
1394 nsString src;
1395 if (NS_WARN_IF(!srcs.SetCapacity(mSrcs.Length(), mozilla::fallible))) {
1396 MOZ_ASSERT(false,
1397 "Not enough memory for 'sources' sequence in "
1398 "nsCSPDirective::toDomCSPStruct().");
1399 return;
1401 for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1402 src.Truncate();
1403 mSrcs[i]->toString(src);
1404 if (!srcs.AppendElement(src, mozilla::fallible)) {
1405 MOZ_ASSERT(false,
1406 "Failed to append to 'sources' sequence in "
1407 "nsCSPDirective::toDomCSPStruct().");
1411 switch (mDirective) {
1412 case nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE:
1413 outCSP.mDefault_src.Construct();
1414 outCSP.mDefault_src.Value() = std::move(srcs);
1415 return;
1417 case nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE:
1418 outCSP.mScript_src.Construct();
1419 outCSP.mScript_src.Value() = std::move(srcs);
1420 return;
1422 case nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE:
1423 outCSP.mObject_src.Construct();
1424 outCSP.mObject_src.Value() = std::move(srcs);
1425 return;
1427 case nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE:
1428 outCSP.mStyle_src.Construct();
1429 outCSP.mStyle_src.Value() = std::move(srcs);
1430 return;
1432 case nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE:
1433 outCSP.mImg_src.Construct();
1434 outCSP.mImg_src.Value() = std::move(srcs);
1435 return;
1437 case nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE:
1438 outCSP.mMedia_src.Construct();
1439 outCSP.mMedia_src.Value() = std::move(srcs);
1440 return;
1442 case nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE:
1443 outCSP.mFrame_src.Construct();
1444 outCSP.mFrame_src.Value() = std::move(srcs);
1445 return;
1447 case nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE:
1448 outCSP.mFont_src.Construct();
1449 outCSP.mFont_src.Value() = std::move(srcs);
1450 return;
1452 case nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE:
1453 outCSP.mConnect_src.Construct();
1454 outCSP.mConnect_src.Value() = std::move(srcs);
1455 return;
1457 case nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE:
1458 outCSP.mReport_uri.Construct();
1459 outCSP.mReport_uri.Value() = std::move(srcs);
1460 return;
1462 case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE:
1463 outCSP.mFrame_ancestors.Construct();
1464 outCSP.mFrame_ancestors.Value() = std::move(srcs);
1465 return;
1467 case nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE:
1468 outCSP.mManifest_src.Construct();
1469 outCSP.mManifest_src.Value() = std::move(srcs);
1470 return;
1471 // not supporting REFLECTED_XSS_DIRECTIVE
1473 case nsIContentSecurityPolicy::BASE_URI_DIRECTIVE:
1474 outCSP.mBase_uri.Construct();
1475 outCSP.mBase_uri.Value() = std::move(srcs);
1476 return;
1478 case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE:
1479 outCSP.mForm_action.Construct();
1480 outCSP.mForm_action.Value() = std::move(srcs);
1481 return;
1483 case nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT:
1484 outCSP.mBlock_all_mixed_content.Construct();
1485 // does not have any srcs
1486 return;
1488 case nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE:
1489 outCSP.mUpgrade_insecure_requests.Construct();
1490 // does not have any srcs
1491 return;
1493 case nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE:
1494 outCSP.mChild_src.Construct();
1495 outCSP.mChild_src.Value() = std::move(srcs);
1496 return;
1498 case nsIContentSecurityPolicy::SANDBOX_DIRECTIVE:
1499 outCSP.mSandbox.Construct();
1500 outCSP.mSandbox.Value() = std::move(srcs);
1501 return;
1503 case nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE:
1504 outCSP.mWorker_src.Construct();
1505 outCSP.mWorker_src.Value() = std::move(srcs);
1506 return;
1508 case nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE:
1509 outCSP.mScript_src_elem.Construct();
1510 outCSP.mScript_src_elem.Value() = std::move(srcs);
1511 return;
1513 case nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE:
1514 outCSP.mScript_src_attr.Construct();
1515 outCSP.mScript_src_attr.Value() = std::move(srcs);
1516 return;
1518 case nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE:
1519 outCSP.mRequire_trusted_types_for.Construct();
1521 // Here, the srcs represent the sink group
1522 // (https://w3c.github.io/trusted-types/dist/spec/#integration-with-content-security-policy).
1523 outCSP.mRequire_trusted_types_for.Value() = std::move(srcs);
1524 return;
1526 case nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE:
1527 outCSP.mTrusted_types.Construct();
1528 // Here, "srcs" represents tt-expressions
1529 // (https://w3c.github.io/trusted-types/dist/spec/#trusted-types-csp-directive).
1530 outCSP.mTrusted_types.Value() = std::move(srcs);
1531 return;
1533 case nsIContentSecurityPolicy::REPORT_TO_DIRECTIVE:
1534 outCSP.mReport_to.Construct();
1535 outCSP.mReport_to.Value() = std::move(srcs);
1536 return;
1538 default:
1539 NS_ASSERTION(false, "cannot find directive to convert CSP to JSON");
1543 bool nsCSPDirective::isDefaultDirective() const {
1544 return mDirective == nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
1547 void nsCSPDirective::getReportURIs(nsTArray<nsString>& outReportURIs) const {
1548 NS_ASSERTION((mDirective == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE),
1549 "not a report-uri directive");
1551 // append uris
1552 nsString tmpReportURI;
1553 for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1554 tmpReportURI.Truncate();
1555 mSrcs[i]->toString(tmpReportURI);
1556 outReportURIs.AppendElement(tmpReportURI);
1560 void nsCSPDirective::getReportGroup(nsAString& outReportGroup) const {
1561 NS_ASSERTION((mDirective == nsIContentSecurityPolicy::REPORT_TO_DIRECTIVE),
1562 "not a report-to directive");
1564 MOZ_ASSERT(mSrcs.Length() <= 1);
1565 mSrcs[0]->toString(outReportGroup);
1568 bool nsCSPDirective::visitSrcs(nsCSPSrcVisitor* aVisitor) const {
1569 for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1570 if (!mSrcs[i]->visit(aVisitor)) {
1571 return false;
1574 return true;
1577 bool nsCSPDirective::equals(CSPDirective aDirective) const {
1578 return (mDirective == aDirective);
1581 void nsCSPDirective::getDirName(nsAString& outStr) const {
1582 outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
1585 bool nsCSPDirective::hasReportSampleKeyword() const {
1586 for (nsCSPBaseSrc* src : mSrcs) {
1587 if (src->isReportSample()) {
1588 return true;
1592 return false;
1595 bool nsCSPDirective::ContainsTrustedTypesDirectivePolicyName(
1596 const nsAString& aPolicyName) const {
1597 MOZ_ASSERT(mDirective == nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE);
1599 if (mDirective == nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE) {
1600 for (const auto* src : mSrcs) {
1601 if (src->isTrustedTypesDirectivePolicyName()) {
1602 const auto& name =
1603 static_cast<const nsCSPTrustedTypesDirectivePolicyName*>(src)
1604 ->GetName();
1605 if (name.Equals(aPolicyName)) {
1606 return true;
1612 return false;
1615 /* =============== nsCSPChildSrcDirective ============= */
1617 nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
1618 : nsCSPDirective(aDirective),
1619 mRestrictFrames(false),
1620 mRestrictWorkers(false) {}
1622 nsCSPChildSrcDirective::~nsCSPChildSrcDirective() = default;
1624 bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const {
1625 if (aDirective == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) {
1626 return mRestrictFrames;
1628 if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
1629 return mRestrictWorkers;
1631 return (mDirective == aDirective);
1634 /* =============== nsCSPScriptSrcDirective ============= */
1636 nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective)
1637 : nsCSPDirective(aDirective) {}
1639 nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() = default;
1641 bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const {
1642 if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
1643 return mRestrictWorkers;
1645 if (aDirective == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) {
1646 return mRestrictScriptElem;
1648 if (aDirective == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) {
1649 return mRestrictScriptAttr;
1651 return mDirective == aDirective;
1654 /* =============== nsCSPStyleSrcDirective ============= */
1656 nsCSPStyleSrcDirective::nsCSPStyleSrcDirective(CSPDirective aDirective)
1657 : nsCSPDirective(aDirective) {}
1659 nsCSPStyleSrcDirective::~nsCSPStyleSrcDirective() = default;
1661 bool nsCSPStyleSrcDirective::equals(CSPDirective aDirective) const {
1662 if (aDirective == nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) {
1663 return mRestrictStyleElem;
1665 if (aDirective == nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE) {
1666 return mRestrictStyleAttr;
1668 return mDirective == aDirective;
1671 /* =============== nsBlockAllMixedContentDirective ============= */
1673 nsBlockAllMixedContentDirective::nsBlockAllMixedContentDirective(
1674 CSPDirective aDirective)
1675 : nsCSPDirective(aDirective) {}
1677 nsBlockAllMixedContentDirective::~nsBlockAllMixedContentDirective() = default;
1679 void nsBlockAllMixedContentDirective::toString(nsAString& outStr) const {
1680 outStr.AppendASCII(CSP_CSPDirectiveToString(
1681 nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT));
1684 void nsBlockAllMixedContentDirective::getDirName(nsAString& outStr) const {
1685 outStr.AppendASCII(CSP_CSPDirectiveToString(
1686 nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT));
1689 /* =============== nsUpgradeInsecureDirective ============= */
1691 nsUpgradeInsecureDirective::nsUpgradeInsecureDirective(CSPDirective aDirective)
1692 : nsCSPDirective(aDirective) {}
1694 nsUpgradeInsecureDirective::~nsUpgradeInsecureDirective() = default;
1696 void nsUpgradeInsecureDirective::toString(nsAString& outStr) const {
1697 outStr.AppendASCII(CSP_CSPDirectiveToString(
1698 nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
1701 void nsUpgradeInsecureDirective::getDirName(nsAString& outStr) const {
1702 outStr.AppendASCII(CSP_CSPDirectiveToString(
1703 nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
1706 /* ===== nsCSPPolicy ========================= */
1708 nsCSPPolicy::nsCSPPolicy()
1709 : mUpgradeInsecDir(nullptr),
1710 mReportOnly(false),
1711 mDeliveredViaMetaTag(false) {
1712 CSPUTILSLOG(("nsCSPPolicy::nsCSPPolicy"));
1715 nsCSPPolicy::~nsCSPPolicy() {
1716 CSPUTILSLOG(("nsCSPPolicy::~nsCSPPolicy"));
1718 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1719 delete mDirectives[i];
1723 bool nsCSPPolicy::permits(CSPDirective aDir, nsILoadInfo* aLoadInfo,
1724 nsIURI* aUri, bool aWasRedirected, bool aSpecific,
1725 nsAString& outViolatedDirective,
1726 nsAString& outViolatedDirectiveString) const {
1727 if (CSPUTILSLOGENABLED()) {
1728 CSPUTILSLOG(("nsCSPPolicy::permits, aUri: %s, aDir: %s, aSpecific: %s",
1729 aUri->GetSpecOrDefault().get(), CSP_CSPDirectiveToString(aDir),
1730 aSpecific ? "true" : "false"));
1733 NS_ASSERTION(aUri, "permits needs an uri to perform the check!");
1734 outViolatedDirective.Truncate();
1735 outViolatedDirectiveString.Truncate();
1737 nsCSPDirective* defaultDir = nullptr;
1739 // Try to find a relevant directive
1740 // These directive arrays are short (1-5 elements), not worth using a
1741 // hashtable.
1742 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1743 if (mDirectives[i]->equals(aDir)) {
1744 if (!mDirectives[i]->permits(aDir, aLoadInfo, aUri, aWasRedirected,
1745 mReportOnly, mUpgradeInsecDir)) {
1746 mDirectives[i]->getDirName(outViolatedDirective);
1747 mDirectives[i]->toString(outViolatedDirectiveString);
1748 return false;
1750 return true;
1752 if (mDirectives[i]->isDefaultDirective()) {
1753 defaultDir = mDirectives[i];
1757 // If the above loop runs through, we haven't found a matching directive.
1758 // Avoid relooping, just store the result of default-src while looping.
1759 if (!aSpecific && defaultDir) {
1760 if (!defaultDir->permits(aDir, aLoadInfo, aUri, aWasRedirected, mReportOnly,
1761 mUpgradeInsecDir)) {
1762 defaultDir->getDirName(outViolatedDirective);
1763 defaultDir->toString(outViolatedDirectiveString);
1764 return false;
1766 return true;
1769 // Nothing restricts this, so we're allowing the load
1770 // See bug 764937
1771 return true;
1774 bool nsCSPPolicy::allows(CSPDirective aDirective, enum CSPKeyword aKeyword,
1775 const nsAString& aHashOrNonce) const {
1776 CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s",
1777 CSP_EnumToUTF8Keyword(aKeyword),
1778 NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
1780 if (nsCSPDirective* directive = matchingOrDefaultDirective(aDirective)) {
1781 return directive->allows(aKeyword, aHashOrNonce);
1784 // No matching directive or default directive as fallback found, thus
1785 // allowing the load; see Bug 885433
1786 // a) inline scripts (also unsafe eval) should only be blocked
1787 // if there is a [script-src] or [default-src]
1788 // b) inline styles should only be blocked
1789 // if there is a [style-src] or [default-src]
1790 return true;
1793 nsCSPDirective* nsCSPPolicy::matchingOrDefaultDirective(
1794 CSPDirective aDirective) const {
1795 nsCSPDirective* defaultDir = nullptr;
1797 // Try to find a matching directive
1798 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1799 if (mDirectives[i]->isDefaultDirective()) {
1800 defaultDir = mDirectives[i];
1801 continue;
1803 if (mDirectives[i]->equals(aDirective)) {
1804 return mDirectives[i];
1808 return defaultDir;
1811 void nsCSPPolicy::toString(nsAString& outStr) const {
1812 StringJoinAppend(outStr, u"; "_ns, mDirectives,
1813 [](nsAString& dest, nsCSPDirective* cspDirective) {
1814 cspDirective->toString(dest);
1818 void nsCSPPolicy::toDomCSPStruct(mozilla::dom::CSP& outCSP) const {
1819 outCSP.mReport_only = mReportOnly;
1821 for (uint32_t i = 0; i < mDirectives.Length(); ++i) {
1822 mDirectives[i]->toDomCSPStruct(outCSP);
1826 bool nsCSPPolicy::hasDirective(CSPDirective aDir) const {
1827 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1828 if (mDirectives[i]->equals(aDir)) {
1829 return true;
1832 return false;
1835 bool nsCSPPolicy::allowsAllInlineBehavior(CSPDirective aDir) const {
1836 nsCSPDirective* directive = matchingOrDefaultDirective(aDir);
1837 if (!directive) {
1838 // No matching or default directive found thus allow the all inline
1839 // scripts or styles. (See nsCSPPolicy::allows)
1840 return true;
1843 return directive->allowsAllInlineBehavior(aDir);
1846 bool nsCSPPolicy::ShouldCreateViolationForNewTrustedTypesPolicy(
1847 const nsAString& aPolicyName,
1848 const nsTArray<nsString>& aCreatedPolicyNames) const {
1849 for (const auto* directive : mDirectives) {
1850 if (directive->equals(nsIContentSecurityPolicy::TRUSTED_TYPES_DIRECTIVE)) {
1851 return directive->ShouldCreateViolationForNewTrustedTypesPolicy(
1852 aPolicyName, aCreatedPolicyNames);
1856 return false;
1860 * Use this function only after ::allows() returned 'false' or if ensured by
1861 * other means that the directive is violated. First and foremost it's used to
1862 * get the violated directive before sending reports. The parameter
1863 * aDirectiveName is the equivalent of 'outViolatedDirective' for the
1864 * ::permits() function family.
1866 void nsCSPPolicy::getViolatedDirectiveInformation(
1867 CSPDirective aDirective, nsAString& aDirectiveName,
1868 nsAString& aDirectiveNameAndValue, bool* aReportSample) const {
1869 *aReportSample = false;
1870 nsCSPDirective* directive = matchingOrDefaultDirective(aDirective);
1871 if (!directive) {
1872 MOZ_ASSERT_UNREACHABLE("Can not query violated directive");
1873 aDirectiveName.Truncate();
1874 aDirectiveNameAndValue.Truncate();
1875 return;
1878 directive->getDirName(aDirectiveName);
1879 directive->toString(aDirectiveNameAndValue);
1880 *aReportSample = directive->hasReportSampleKeyword();
1884 * Helper function that returns the underlying bit representation of sandbox
1885 * flags. The function returns SANDBOXED_NONE if there are no sandbox
1886 * directives.
1888 uint32_t nsCSPPolicy::getSandboxFlags() const {
1889 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1890 if (mDirectives[i]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
1891 nsAutoString flags;
1892 mDirectives[i]->toString(flags);
1894 if (flags.IsEmpty()) {
1895 return SANDBOX_ALL_FLAGS;
1898 nsAttrValue attr;
1899 attr.ParseAtomArray(flags);
1901 return nsContentUtils::ParseSandboxAttributeToFlags(&attr);
1905 return SANDBOXED_NONE;
1908 void nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const {
1909 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1910 if (mDirectives[i]->equals(
1911 nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
1912 mDirectives[i]->getReportURIs(outReportURIs);
1913 return;
1918 void nsCSPPolicy::getReportGroup(nsAString& outReportGroup) const {
1919 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1920 if (mDirectives[i]->equals(nsIContentSecurityPolicy::REPORT_TO_DIRECTIVE)) {
1921 mDirectives[i]->getReportGroup(outReportGroup);
1922 return;
1927 bool nsCSPPolicy::visitDirectiveSrcs(CSPDirective aDir,
1928 nsCSPSrcVisitor* aVisitor) const {
1929 for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1930 if (mDirectives[i]->equals(aDir)) {
1931 return mDirectives[i]->visitSrcs(aVisitor);
1934 return false;