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"
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"
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
) {
52 // helper function that should not be visible outside this methods scope
54 static inline char16_t
convertHexDig(char16_t aHexDig
) {
55 if (isNumberToken(aHexDig
)) {
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();
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
);
80 // get the two hexDigs following the '%'-sign
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
);
93 // decode "% hexDig1 hexDig2" into a character.
95 (local::convertHexDig(*hexDig1
) << 4) + local::convertHexDig(*hexDig2
);
96 outDecStr
.Append(decChar
);
98 // increment 'cur' to after the second hexDig
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
) {
111 nsCOMPtr
<nsIURI
> uri
;
112 nsresult rv
= aChannel
->GetURI(getter_AddRefs(uri
));
113 NS_ENSURE_SUCCESS(rv
, false);
115 bool isAbout
= uri
->SchemeIs("about");
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
)) {
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()) {
137 nsAutoString
policyStr(
138 nsContentUtils::TrimWhitespace
<nsContentUtils::IsHTMLWhitespace
>(
141 if (policyStr
.IsEmpty()) {
145 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aDoc
.GetCsp();
147 MOZ_ASSERT(false, "how come there is no CSP");
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
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()) {
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
) {
181 keyStringBundle
->FormatStringFromName(aName
, aParams
, outResult
);
184 void CSP_LogStrMessage(const nsAString
& aMsg
) {
185 nsCOMPtr
<nsIConsoleService
> console(
186 do_GetService("@mozilla.org/consoleservice;1"));
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
) {
209 // Prepending CSP to the outgoing console message
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
);
233 if (aInnerWindowID
> 0) {
235 error
->InitWithWindowID(cspMsg
, aSourceName
, aLineNumber
, aColumnNumber
,
236 aFlags
, category
, aInnerWindowID
);
238 rv
= error
->Init(cspMsg
, aSourceName
, aLineNumber
, aColumnNumber
, aFlags
,
239 category
, aFromPrivateWindow
,
240 true /* from chrome context */);
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
) {
272 CSP_GetLocalizedStr(aName
, aParams
, logMsg
);
273 CSP_LogMessage(logMsg
, aSourceName
, aSourceLine
, aLineNumber
, aColumnNumber
,
274 aFlags
, aCategory
, aInnerWindowID
, aFromPrivateWindow
);
277 /* ===== Helpers ============================ */
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
) {
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
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
389 aSelfURI
->GetAsciiHost(host
);
390 nsCSPHostSrc
* hostsrc
= new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host
));
391 hostsrc
->setGeneratedFromSelfKeyword();
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.
408 aSelfURI
->GetPort(&port
);
409 // Only add port if it's not default port.
411 nsAutoString portStr
;
412 portStr
.AppendInt(port
);
413 hostsrc
->setPort(portStr
);
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
) {
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
)) {
446 * Checks whether the current directive permits a specific
447 * scheme. This function is called from nsCSPSchemeSrc() and
449 * @param aEnforcementScheme
450 * The scheme that this directive allows
452 * The uri of the subresource load.
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()) {
474 // if the scheme matches, all good - allow the load
475 if (aEnforcementScheme
.EqualsASCII(scheme
.get())) {
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")) {
486 if ((scheme
.EqualsASCII("ws") || scheme
.EqualsASCII("wss")) &&
491 if (aEnforcementScheme
.EqualsASCII("https")) {
492 if (scheme
.EqualsLiteral("wss") && aFromSelfURI
) {
496 if (aEnforcementScheme
.EqualsASCII("ws") && scheme
.EqualsASCII("wss")) {
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.
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
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
,
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)
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()));
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()) {
555 ("nsCSPBaseSrc::permits, aUri: %s", aUri
->GetSpecOrDefault().get()));
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()));
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()) {
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
)
602 mGeneratedFromSelfKeyword(false),
603 mIsUniqueOrigin(false),
604 mWithinFrameAncstorsDir(false) {
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("*")) {
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()) {
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()) {
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
)) {
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
)) {
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")) {
699 // ports do not match, block the load.
703 bool nsCSPHostSrc::permits(nsIURI
* aUri
, bool aWasRedirected
, bool aReportOnly
,
704 bool aUpgradeInsecure
) const {
705 if (CSPUTILSLOGENABLED()) {
707 ("nsCSPHostSrc::permits, aUri: %s", aUri
->GetSpecOrDefault().get()));
710 if (mIsUniqueOrigin
) {
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
)) {
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")) {
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()) {
755 // 4.5) host matching: Check if the allowed host starts with a wilcard.
756 else if (mHost
.First() == '*') {
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
)) {
769 // 4.6) host matching: Check if hosts match.
770 else if (!mHost
.Equals(decodedUriHost
)) {
774 // Port matching: Check if the ports match.
775 if (!permitsPort(mScheme
, mPort
, aUri
)) {
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
);
788 NS_ASSERTION(false, "can't QI into nsIURI");
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.
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
806 if (mPath
.Last() == '/') {
807 if (!StringBeginsWith(decodedUriPath
, mPath
)) {
811 // otherwise mPath refers to a specific file, and we have to
812 // check if the loading resource matches the file.
814 if (!mPath
.Equals(decodedUriPath
)) {
820 // At the end: scheme, host, port and path match -> allow the load.
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'");
834 // If mHost is a single "*", we append the wildcard and return.
835 if (mHost
.EqualsASCII("*") && mScheme
.IsEmpty() && mPort
.IsEmpty()) {
836 outStr
.Append(mHost
);
841 outStr
.Append(mScheme
);
844 outStr
.AppendLiteral("://");
845 outStr
.Append(mHost
);
848 if (!mPort
.IsEmpty()) {
849 outStr
.AppendLiteral(":");
850 outStr
.Append(mPort
);
854 outStr
.Append(mPath
);
857 void nsCSPHostSrc::setScheme(const nsAString
& 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
) {
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
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
) {
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);
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 {
996 nsresult rv
= mReportURI
->GetSpec(spec
);
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
)
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.");
1040 void nsCSPRequireTrustedTypesForDirectiveValue::toString(
1041 nsAString
& aOutStr
) const {
1042 aOutStr
.Append(mValue
);
1045 /* =============== nsCSPTrustedTypesDirectivePolicyName =============== */
1047 nsCSPTrustedTypesDirectivePolicyName::nsCSPTrustedTypesDirectivePolicyName(
1048 const nsAString
& aName
)
1051 bool nsCSPTrustedTypesDirectivePolicyName::visit(
1052 nsCSPSrcVisitor
* aVisitor
) const {
1053 MOZ_ASSERT_UNREACHABLE(
1054 "Should only be called for other overloads of this method.");
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
++) {
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.
1082 MOZ_ALWAYS_SUCCEEDS(aLoadInfo
->GetCspNonce(nonce
));
1084 // Step 2. If nonce is the empty string, return "Does Not Match".
1085 if (nonce
.IsEmpty()) {
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
) {
1102 // Step 4. Return "Does Not Match".
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()) {
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,
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.
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
)));
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)"));
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)"));
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
);
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.
1215 nsAutoCString sourceAlgorithmUTF8
;
1216 nsAutoCString sourceHashUTF8
;
1217 nsAutoString sourceAlgorithm
;
1218 nsAutoString sourceHash
;
1219 nsAutoString algorithm
;
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.
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
) {
1253 // Step 1.3.5. If bypass due to integrity match is true, return
1257 (" Allowed by matching integrity metadata (script-like)"));
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()) {
1273 (" Blocked by 'strict-dynamic' because parser-inserted"));
1278 (" Allowed by 'strict-dynamic' because not-parser-inserted"));
1284 for (uint32_t i
= 0; i
< mSrcs
.Length(); i
++) {
1285 if (mSrcs
[i
]->permits(aUri
, aWasRedirected
, aReportOnly
,
1286 aUpgradeInsecure
)) {
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
)) {
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()) {
1320 // Step 2.2. If type is "script", "script attribute" or "navigation" and
1321 // expression matches the keyword-source "'strict-dynamic'", return "Does
1323 if ((aDir
== nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
||
1324 aDir
== nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
) &&
1325 src
->isKeyword(CSP_STRICT_DYNAMIC
)) {
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
)) {
1336 // Step 3. If allow all inline is true, return "Allows". Otherwise, return
1337 // "Does Not Allow".
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.
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.
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.
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(" ");
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
;
1395 if (NS_WARN_IF(!srcs
.SetCapacity(mSrcs
.Length(), mozilla::fallible
))) {
1397 "Not enough memory for 'sources' sequence in "
1398 "nsCSPDirective::toDomCSPStruct().");
1401 for (uint32_t i
= 0; i
< mSrcs
.Length(); i
++) {
1403 mSrcs
[i
]->toString(src
);
1404 if (!srcs
.AppendElement(src
, mozilla::fallible
)) {
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
);
1417 case nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE
:
1418 outCSP
.mScript_src
.Construct();
1419 outCSP
.mScript_src
.Value() = std::move(srcs
);
1422 case nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE
:
1423 outCSP
.mObject_src
.Construct();
1424 outCSP
.mObject_src
.Value() = std::move(srcs
);
1427 case nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE
:
1428 outCSP
.mStyle_src
.Construct();
1429 outCSP
.mStyle_src
.Value() = std::move(srcs
);
1432 case nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE
:
1433 outCSP
.mImg_src
.Construct();
1434 outCSP
.mImg_src
.Value() = std::move(srcs
);
1437 case nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE
:
1438 outCSP
.mMedia_src
.Construct();
1439 outCSP
.mMedia_src
.Value() = std::move(srcs
);
1442 case nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE
:
1443 outCSP
.mFrame_src
.Construct();
1444 outCSP
.mFrame_src
.Value() = std::move(srcs
);
1447 case nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE
:
1448 outCSP
.mFont_src
.Construct();
1449 outCSP
.mFont_src
.Value() = std::move(srcs
);
1452 case nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE
:
1453 outCSP
.mConnect_src
.Construct();
1454 outCSP
.mConnect_src
.Value() = std::move(srcs
);
1457 case nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE
:
1458 outCSP
.mReport_uri
.Construct();
1459 outCSP
.mReport_uri
.Value() = std::move(srcs
);
1462 case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE
:
1463 outCSP
.mFrame_ancestors
.Construct();
1464 outCSP
.mFrame_ancestors
.Value() = std::move(srcs
);
1467 case nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE
:
1468 outCSP
.mManifest_src
.Construct();
1469 outCSP
.mManifest_src
.Value() = std::move(srcs
);
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
);
1478 case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE
:
1479 outCSP
.mForm_action
.Construct();
1480 outCSP
.mForm_action
.Value() = std::move(srcs
);
1483 case nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT
:
1484 outCSP
.mBlock_all_mixed_content
.Construct();
1485 // does not have any srcs
1488 case nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE
:
1489 outCSP
.mUpgrade_insecure_requests
.Construct();
1490 // does not have any srcs
1493 case nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE
:
1494 outCSP
.mChild_src
.Construct();
1495 outCSP
.mChild_src
.Value() = std::move(srcs
);
1498 case nsIContentSecurityPolicy::SANDBOX_DIRECTIVE
:
1499 outCSP
.mSandbox
.Construct();
1500 outCSP
.mSandbox
.Value() = std::move(srcs
);
1503 case nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE
:
1504 outCSP
.mWorker_src
.Construct();
1505 outCSP
.mWorker_src
.Value() = std::move(srcs
);
1508 case nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
:
1509 outCSP
.mScript_src_elem
.Construct();
1510 outCSP
.mScript_src_elem
.Value() = std::move(srcs
);
1513 case nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
:
1514 outCSP
.mScript_src_attr
.Construct();
1515 outCSP
.mScript_src_attr
.Value() = std::move(srcs
);
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
);
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
);
1533 case nsIContentSecurityPolicy::REPORT_TO_DIRECTIVE
:
1534 outCSP
.mReport_to
.Construct();
1535 outCSP
.mReport_to
.Value() = std::move(srcs
);
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");
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
)) {
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()) {
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()) {
1603 static_cast<const nsCSPTrustedTypesDirectivePolicyName
*>(src
)
1605 if (name
.Equals(aPolicyName
)) {
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),
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
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
);
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
);
1769 // Nothing restricts this, so we're allowing the load
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]
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
];
1803 if (mDirectives
[i
]->equals(aDirective
)) {
1804 return mDirectives
[i
];
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
)) {
1835 bool nsCSPPolicy::allowsAllInlineBehavior(CSPDirective aDir
) const {
1836 nsCSPDirective
* directive
= matchingOrDefaultDirective(aDir
);
1838 // No matching or default directive found thus allow the all inline
1839 // scripts or styles. (See nsCSPPolicy::allows)
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
);
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
);
1872 MOZ_ASSERT_UNREACHABLE("Can not query violated directive");
1873 aDirectiveName
.Truncate();
1874 aDirectiveNameAndValue
.Truncate();
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
1888 uint32_t nsCSPPolicy::getSandboxFlags() const {
1889 for (uint32_t i
= 0; i
< mDirectives
.Length(); i
++) {
1890 if (mDirectives
[i
]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE
)) {
1892 mDirectives
[i
]->toString(flags
);
1894 if (flags
.IsEmpty()) {
1895 return SANDBOX_ALL_FLAGS
;
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
);
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
);
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
);