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/. */
6 
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 "nsIChannel.h"
16 #include "nsICryptoHash.h"
17 #include "nsIScriptError.h"
18 #include "nsIStringBundle.h"
19 #include "nsIURL.h"
20 #include "nsNetUtil.h"
21 #include "nsReadableUtils.h"
22 #include "nsSandboxFlags.h"
23 #include "nsServiceManagerUtils.h"
24 
25 #include "mozilla/Components.h"
26 #include "mozilla/dom/CSPDictionariesBinding.h"
27 #include "mozilla/dom/Document.h"
28 #include "mozilla/StaticPrefs_security.h"
29 
30 #define DEFAULT_PORT -1
31 
GetCspUtilsLog()32 static mozilla::LogModule* GetCspUtilsLog() {
33   static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils");
34   return gCspUtilsPRLog;
35 }
36 
37 #define CSPUTILSLOG(args) \
38   MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args)
39 #define CSPUTILSLOGENABLED() \
40   MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug)
41 
CSP_PercentDecodeStr(const nsAString & aEncStr,nsAString & outDecStr)42 void CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr) {
43   outDecStr.Truncate();
44 
45   // helper function that should not be visible outside this methods scope
46   struct local {
47     static inline char16_t convertHexDig(char16_t aHexDig) {
48       if (isNumberToken(aHexDig)) {
49         return aHexDig - '0';
50       }
51       if (aHexDig >= 'A' && aHexDig <= 'F') {
52         return aHexDig - 'A' + 10;
53       }
54       // must be a lower case character
55       // (aHexDig >= 'a' && aHexDig <= 'f')
56       return aHexDig - 'a' + 10;
57     }
58   };
59 
60   const char16_t *cur, *end, *hexDig1, *hexDig2;
61   cur = aEncStr.BeginReading();
62   end = aEncStr.EndReading();
63 
64   while (cur != end) {
65     // if it's not a percent sign then there is
66     // nothing to do for that character
67     if (*cur != PERCENT_SIGN) {
68       outDecStr.Append(*cur);
69       cur++;
70       continue;
71     }
72 
73     // get the two hexDigs following the '%'-sign
74     hexDig1 = cur + 1;
75     hexDig2 = cur + 2;
76 
77     // if there are no hexdigs after the '%' then
78     // there is nothing to do for us.
79     if (hexDig1 == end || hexDig2 == end || !isValidHexDig(*hexDig1) ||
80         !isValidHexDig(*hexDig2)) {
81       outDecStr.Append(PERCENT_SIGN);
82       cur++;
83       continue;
84     }
85 
86     // decode "% hexDig1 hexDig2" into a character.
87     char16_t decChar =
88         (local::convertHexDig(*hexDig1) << 4) + local::convertHexDig(*hexDig2);
89     outDecStr.Append(decChar);
90 
91     // increment 'cur' to after the second hexDig
92     cur = ++hexDig2;
93   }
94 }
95 
96 // The Content Security Policy should be inherited for
97 // local schemes like: "about", "blob", "data", or "filesystem".
98 // see: https://w3c.github.io/webappsec-csp/#initialize-document-csp
CSP_ShouldResponseInheritCSP(nsIChannel * aChannel)99 bool CSP_ShouldResponseInheritCSP(nsIChannel* aChannel) {
100   if (!aChannel) {
101     return false;
102   }
103 
104   nsCOMPtr<nsIURI> uri;
105   nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
106   NS_ENSURE_SUCCESS(rv, false);
107 
108   bool isAbout = uri->SchemeIs("about");
109   if (isAbout) {
110     nsAutoCString aboutSpec;
111     rv = uri->GetSpec(aboutSpec);
112     NS_ENSURE_SUCCESS(rv, false);
113     // also allow about:blank#foo
114     if (StringBeginsWith(aboutSpec, "about:blank"_ns) ||
115         StringBeginsWith(aboutSpec, "about:srcdoc"_ns)) {
116       return true;
117     }
118   }
119 
120   return uri->SchemeIs("blob") || uri->SchemeIs("data") ||
121          uri->SchemeIs("filesystem") || uri->SchemeIs("javascript");
122 }
123 
CSP_ApplyMetaCSPToDoc(mozilla::dom::Document & aDoc,const nsAString & aPolicyStr)124 void CSP_ApplyMetaCSPToDoc(mozilla::dom::Document& aDoc,
125                            const nsAString& aPolicyStr) {
126   if (!mozilla::StaticPrefs::security_csp_enable() || aDoc.IsLoadedAsData()) {
127     return;
128   }
129 
130   nsAutoString policyStr(
131       nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
132           aPolicyStr));
133 
134   if (policyStr.IsEmpty()) {
135     return;
136   }
137 
138   nsCOMPtr<nsIContentSecurityPolicy> csp = aDoc.GetCsp();
139   if (!csp) {
140     MOZ_ASSERT(false, "how come there is no CSP");
141     return;
142   }
143 
144   // Multiple CSPs (delivered through either header of meta tag) need to
145   // be joined together, see:
146   // https://w3c.github.io/webappsec/specs/content-security-policy/#delivery-html-meta-element
147   nsresult rv =
148       csp->AppendPolicy(policyStr,
149                         false,  // csp via meta tag can not be report only
150                         true);  // delivered through the meta tag
151   NS_ENSURE_SUCCESS_VOID(rv);
152   if (nsPIDOMWindowInner* inner = aDoc.GetInnerWindow()) {
153     inner->SetCsp(csp);
154   }
155   aDoc.ApplySettingsFromCSP(false);
156 }
157 
CSP_GetLocalizedStr(const char * aName,const nsTArray<nsString> & aParams,nsAString & outResult)158 void CSP_GetLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
159                          nsAString& outResult) {
160   nsCOMPtr<nsIStringBundle> keyStringBundle;
161   nsCOMPtr<nsIStringBundleService> stringBundleService =
162       mozilla::components::StringBundle::Service();
163 
164   NS_ASSERTION(stringBundleService, "String bundle service must be present!");
165   stringBundleService->CreateBundle(
166       "chrome://global/locale/security/csp.properties",
167       getter_AddRefs(keyStringBundle));
168 
169   NS_ASSERTION(keyStringBundle, "Key string bundle must be available!");
170 
171   if (!keyStringBundle) {
172     return;
173   }
174   keyStringBundle->FormatStringFromName(aName, aParams, outResult);
175 }
176 
CSP_LogStrMessage(const nsAString & aMsg)177 void CSP_LogStrMessage(const nsAString& aMsg) {
178   nsCOMPtr<nsIConsoleService> console(
179       do_GetService("@mozilla.org/consoleservice;1"));
180 
181   if (!console) {
182     return;
183   }
184   nsString msg(aMsg);
185   console->LogStringMessage(msg.get());
186 }
187 
CSP_LogMessage(const nsAString & aMessage,const nsAString & aSourceName,const nsAString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber,uint32_t aFlags,const nsACString & aCategory,uint64_t aInnerWindowID,bool aFromPrivateWindow)188 void CSP_LogMessage(const nsAString& aMessage, const nsAString& aSourceName,
189                     const nsAString& aSourceLine, uint32_t aLineNumber,
190                     uint32_t aColumnNumber, uint32_t aFlags,
191                     const nsACString& aCategory, uint64_t aInnerWindowID,
192                     bool aFromPrivateWindow) {
193   nsCOMPtr<nsIConsoleService> console(
194       do_GetService(NS_CONSOLESERVICE_CONTRACTID));
195 
196   nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
197 
198   if (!console || !error) {
199     return;
200   }
201 
202   // Prepending CSP to the outgoing console message
203   nsString cspMsg;
204   cspMsg.AppendLiteral(u"Content Security Policy: ");
205   cspMsg.Append(aMessage);
206 
207   // Currently 'aSourceLine' is not logged to the console, because similar
208   // information is already included within the source link of the message.
209   // For inline violations however, the line and column number are 0 and
210   // information contained within 'aSourceLine' can be really useful for devs.
211   // E.g. 'aSourceLine' might be: 'onclick attribute on DIV element'.
212   // In such cases we append 'aSourceLine' directly to the error message.
213   if (!aSourceLine.IsEmpty()) {
214     cspMsg.AppendLiteral(u" Source: ");
215     cspMsg.Append(aSourceLine);
216     cspMsg.AppendLiteral(u".");
217   }
218 
219   // Since we are leveraging csp errors as the category names which
220   // we pass to devtools, we should prepend them with "CSP_" to
221   // allow easy distincution in devtools code. e.g.
222   // upgradeInsecureRequest -> CSP_upgradeInsecureRequest
223   nsCString category("CSP_");
224   category.Append(aCategory);
225 
226   nsresult rv;
227   if (aInnerWindowID > 0) {
228     rv = error->InitWithWindowID(cspMsg, aSourceName, aSourceLine, aLineNumber,
229                                  aColumnNumber, aFlags, category,
230                                  aInnerWindowID);
231   } else {
232     rv = error->Init(cspMsg, aSourceName, aSourceLine, aLineNumber,
233                      aColumnNumber, aFlags, category.get(), aFromPrivateWindow,
234                      true /* from chrome context */);
235   }
236   if (NS_FAILED(rv)) {
237     return;
238   }
239   console->LogMessage(error);
240 }
241 
242 /**
243  * Combines CSP_LogMessage and CSP_GetLocalizedStr into one call.
244  */
CSP_LogLocalizedStr(const char * aName,const nsTArray<nsString> & aParams,const nsAString & aSourceName,const nsAString & aSourceLine,uint32_t aLineNumber,uint32_t aColumnNumber,uint32_t aFlags,const nsACString & aCategory,uint64_t aInnerWindowID,bool aFromPrivateWindow)245 void CSP_LogLocalizedStr(const char* aName, const nsTArray<nsString>& aParams,
246                          const nsAString& aSourceName,
247                          const nsAString& aSourceLine, uint32_t aLineNumber,
248                          uint32_t aColumnNumber, uint32_t aFlags,
249                          const nsACString& aCategory, uint64_t aInnerWindowID,
250                          bool aFromPrivateWindow) {
251   nsAutoString logMsg;
252   CSP_GetLocalizedStr(aName, aParams, logMsg);
253   CSP_LogMessage(logMsg, aSourceName, aSourceLine, aLineNumber, aColumnNumber,
254                  aFlags, aCategory, aInnerWindowID, aFromPrivateWindow);
255 }
256 
257 /* ===== Helpers ============================ */
CSP_ContentTypeToDirective(nsContentPolicyType aType)258 CSPDirective CSP_ContentTypeToDirective(nsContentPolicyType aType) {
259   // We need to know if this is a worker so child-src  can handle that case
260   // correctly.
261   switch (aType) {
262     case nsIContentPolicy::TYPE_IMAGE:
263     case nsIContentPolicy::TYPE_IMAGESET:
264     case nsIContentPolicy::TYPE_INTERNAL_IMAGE:
265     case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD:
266     case nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON:
267       return nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE;
268 
269     // BLock XSLT as script, see bug 910139
270     case nsIContentPolicy::TYPE_XSLT:
271     case nsIContentPolicy::TYPE_SCRIPT:
272     case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
273     case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
274     case nsIContentPolicy::TYPE_INTERNAL_MODULE:
275     case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD:
276     case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
277     case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET:
278     case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET:
279     case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT:
280     case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT:
281       return nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE;
282 
283     case nsIContentPolicy::TYPE_STYLESHEET:
284     case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET:
285     case nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD:
286       return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE;
287 
288     case nsIContentPolicy::TYPE_FONT:
289     case nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD:
290       return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE;
291 
292     case nsIContentPolicy::TYPE_MEDIA:
293     case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
294     case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
295     case nsIContentPolicy::TYPE_INTERNAL_TRACK:
296       return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE;
297 
298     case nsIContentPolicy::TYPE_WEB_MANIFEST:
299       return nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE;
300 
301     case nsIContentPolicy::TYPE_INTERNAL_WORKER:
302     case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
303     case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
304       return nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE;
305 
306     case nsIContentPolicy::TYPE_SUBDOCUMENT:
307     case nsIContentPolicy::TYPE_INTERNAL_FRAME:
308     case nsIContentPolicy::TYPE_INTERNAL_IFRAME:
309       return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
310 
311     case nsIContentPolicy::TYPE_WEBSOCKET:
312     case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
313     case nsIContentPolicy::TYPE_BEACON:
314     case nsIContentPolicy::TYPE_PING:
315     case nsIContentPolicy::TYPE_FETCH:
316     case nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST:
317     case nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE:
318     case nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD:
319       return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE;
320 
321     case nsIContentPolicy::TYPE_OBJECT:
322     case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
323     case nsIContentPolicy::TYPE_INTERNAL_EMBED:
324     case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
325       return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
326 
327     case nsIContentPolicy::TYPE_DTD:
328     case nsIContentPolicy::TYPE_OTHER:
329     case nsIContentPolicy::TYPE_SPECULATIVE:
330     case nsIContentPolicy::TYPE_INTERNAL_DTD:
331     case nsIContentPolicy::TYPE_INTERNAL_FORCE_ALLOWED_DTD:
332       return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
333 
334     // csp shold not block top level loads, e.g. in case
335     // of a redirect.
336     case nsIContentPolicy::TYPE_DOCUMENT:
337     // CSP can not block csp reports
338     case nsIContentPolicy::TYPE_CSP_REPORT:
339       return nsIContentSecurityPolicy::NO_DIRECTIVE;
340 
341     case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD:
342     case nsIContentPolicy::TYPE_UA_FONT:
343       return nsIContentSecurityPolicy::NO_DIRECTIVE;
344 
345     // Fall through to error for all other directives
346     // Note that we should never end up here for navigate-to
347     case nsIContentPolicy::TYPE_INVALID:
348       MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective");
349       // Do not add default: so that compilers can catch the missing case.
350   }
351   return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
352 }
353 
CSP_CreateHostSrcFromSelfURI(nsIURI * aSelfURI)354 nsCSPHostSrc* CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI) {
355   // Create the host first
356   nsCString host;
357   aSelfURI->GetAsciiHost(host);
358   nsCSPHostSrc* hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host));
359   hostsrc->setGeneratedFromSelfKeyword();
360 
361   // Add the scheme.
362   nsCString scheme;
363   aSelfURI->GetScheme(scheme);
364   hostsrc->setScheme(NS_ConvertUTF8toUTF16(scheme));
365 
366   // An empty host (e.g. for data:) indicates it's effectively a unique origin.
367   // Please note that we still need to set the scheme on hostsrc (see above),
368   // because it's used for reporting.
369   if (host.EqualsLiteral("")) {
370     hostsrc->setIsUniqueOrigin();
371     // no need to query the port in that case.
372     return hostsrc;
373   }
374 
375   int32_t port;
376   aSelfURI->GetPort(&port);
377   // Only add port if it's not default port.
378   if (port > 0) {
379     nsAutoString portStr;
380     portStr.AppendInt(port);
381     hostsrc->setPort(portStr);
382   }
383   return hostsrc;
384 }
385 
CSP_IsEmptyDirective(const nsAString & aValue,const nsAString & aDir)386 bool CSP_IsEmptyDirective(const nsAString& aValue, const nsAString& aDir) {
387   return (aDir.Length() == 0 && aValue.Length() == 0);
388 }
CSP_IsDirective(const nsAString & aValue,CSPDirective aDir)389 bool CSP_IsDirective(const nsAString& aValue, CSPDirective aDir) {
390   return aValue.LowerCaseEqualsASCII(CSP_CSPDirectiveToString(aDir));
391 }
392 
CSP_IsKeyword(const nsAString & aValue,enum CSPKeyword aKey)393 bool CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey) {
394   return aValue.LowerCaseEqualsASCII(CSP_EnumToUTF8Keyword(aKey));
395 }
396 
CSP_IsQuotelessKeyword(const nsAString & aKey)397 bool CSP_IsQuotelessKeyword(const nsAString& aKey) {
398   nsString lowerKey;
399   ToLowerCase(aKey, lowerKey);
400 
401   nsAutoString keyword;
402   for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
403     // skipping the leading ' and trimming the trailing '
404     keyword.AssignASCII(gCSPUTF8Keywords[i] + 1);
405     keyword.Trim("'", false, true);
406     if (lowerKey.Equals(keyword)) {
407       return true;
408     }
409   }
410   return false;
411 }
412 
413 /*
414  * Checks whether the current directive permits a specific
415  * scheme. This function is called from nsCSPSchemeSrc() and
416  * also nsCSPHostSrc.
417  * @param aEnforcementScheme
418  *        The scheme that this directive allows
419  * @param aUri
420  *        The uri of the subresource load.
421  * @param aReportOnly
422  *        Whether the enforced policy is report only or not.
423  * @param aUpgradeInsecure
424  *        Whether the policy makes use of the directive
425  *        'upgrade-insecure-requests'.
426  * @param aFromSelfURI
427  *        Whether a scheme was generated from the keyword 'self'
428  *        which then allows schemeless sources to match ws and wss.
429  */
430 
permitsScheme(const nsAString & aEnforcementScheme,nsIURI * aUri,bool aReportOnly,bool aUpgradeInsecure,bool aFromSelfURI)431 bool permitsScheme(const nsAString& aEnforcementScheme, nsIURI* aUri,
432                    bool aReportOnly, bool aUpgradeInsecure, bool aFromSelfURI) {
433   nsAutoCString scheme;
434   nsresult rv = aUri->GetScheme(scheme);
435   NS_ENSURE_SUCCESS(rv, false);
436 
437   // no scheme to enforce, let's allow the load (e.g. script-src *)
438   if (aEnforcementScheme.IsEmpty()) {
439     return true;
440   }
441 
442   // if the scheme matches, all good - allow the load
443   if (aEnforcementScheme.EqualsASCII(scheme.get())) {
444     return true;
445   }
446 
447   // allow scheme-less sources where the protected resource is http
448   // and the load is https, see:
449   // http://www.w3.org/TR/CSP2/#match-source-expression
450   if (aEnforcementScheme.EqualsASCII("http")) {
451     if (scheme.EqualsASCII("https")) {
452       return true;
453     }
454     if ((scheme.EqualsASCII("ws") || scheme.EqualsASCII("wss")) &&
455         aFromSelfURI) {
456       return true;
457     }
458   }
459   if (aEnforcementScheme.EqualsASCII("https")) {
460     if (scheme.EqualsLiteral("wss") && aFromSelfURI) {
461       return true;
462     }
463   }
464   if (aEnforcementScheme.EqualsASCII("ws") && scheme.EqualsASCII("wss")) {
465     return true;
466   }
467 
468   // Allow the load when enforcing upgrade-insecure-requests with the
469   // promise the request gets upgraded from http to https and ws to wss.
470   // See nsHttpChannel::Connect() and also WebSocket.cpp. Please note,
471   // the report only policies should not allow the load and report
472   // the error back to the page.
473   return (
474       (aUpgradeInsecure && !aReportOnly) &&
475       ((scheme.EqualsASCII("http") &&
476         aEnforcementScheme.EqualsASCII("https")) ||
477        (scheme.EqualsASCII("ws") && aEnforcementScheme.EqualsASCII("wss"))));
478 }
479 
480 /*
481  * A helper function for appending a CSP header to an existing CSP
482  * policy.
483  *
484  * @param aCsp           the CSP policy
485  * @param aHeaderValue   the header
486  * @param aReportOnly    is this a report-only header?
487  */
488 
CSP_AppendCSPFromHeader(nsIContentSecurityPolicy * aCsp,const nsAString & aHeaderValue,bool aReportOnly)489 nsresult CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp,
490                                  const nsAString& aHeaderValue,
491                                  bool aReportOnly) {
492   NS_ENSURE_ARG(aCsp);
493 
494   // Need to tokenize the header value since multiple headers could be
495   // concatenated into one comma-separated list of policies.
496   // See RFC2616 section 4.2 (last paragraph)
497   nsresult rv = NS_OK;
498   for (const nsAString& policy :
499        nsCharSeparatedTokenizer(aHeaderValue, ',').ToRange()) {
500     rv = aCsp->AppendPolicy(policy, aReportOnly, false);
501     NS_ENSURE_SUCCESS(rv, rv);
502     {
503       CSPUTILSLOG(("CSP refined with policy: \"%s\"",
504                    NS_ConvertUTF16toUTF8(policy).get()));
505     }
506   }
507   return NS_OK;
508 }
509 
510 /* ===== nsCSPSrc ============================ */
511 
nsCSPBaseSrc()512 nsCSPBaseSrc::nsCSPBaseSrc() : mInvalidated(false) {}
513 
514 nsCSPBaseSrc::~nsCSPBaseSrc() = default;
515 
516 // ::permits is only called for external load requests, therefore:
517 // nsCSPKeywordSrc and nsCSPHashSource fall back to this base class
518 // implementation which will never allow the load.
permits(nsIURI * aUri,const nsAString & aNonce,bool aWasRedirected,bool aReportOnly,bool aUpgradeInsecure,bool aParserCreated) const519 bool nsCSPBaseSrc::permits(nsIURI* aUri, const nsAString& aNonce,
520                            bool aWasRedirected, bool aReportOnly,
521                            bool aUpgradeInsecure, bool aParserCreated) const {
522   if (CSPUTILSLOGENABLED()) {
523     CSPUTILSLOG(
524         ("nsCSPBaseSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
525   }
526   return false;
527 }
528 
529 // ::allows is only called for inlined loads, therefore:
530 // nsCSPSchemeSrc, nsCSPHostSrc fall back
531 // to this base class implementation which will never allow the load.
allows(enum CSPKeyword aKeyword,const nsAString & aHashOrNonce,bool aParserCreated) const532 bool nsCSPBaseSrc::allows(enum CSPKeyword aKeyword,
533                           const nsAString& aHashOrNonce,
534                           bool aParserCreated) const {
535   CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
536                aKeyword == CSP_HASH ? "hash" : CSP_EnumToUTF8Keyword(aKeyword),
537                NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
538   return false;
539 }
540 
541 /* ====== nsCSPSchemeSrc ===================== */
542 
nsCSPSchemeSrc(const nsAString & aScheme)543 nsCSPSchemeSrc::nsCSPSchemeSrc(const nsAString& aScheme) : mScheme(aScheme) {
544   ToLowerCase(mScheme);
545 }
546 
547 nsCSPSchemeSrc::~nsCSPSchemeSrc() = default;
548 
permits(nsIURI * aUri,const nsAString & aNonce,bool aWasRedirected,bool aReportOnly,bool aUpgradeInsecure,bool aParserCreated) const549 bool nsCSPSchemeSrc::permits(nsIURI* aUri, const nsAString& aNonce,
550                              bool aWasRedirected, bool aReportOnly,
551                              bool aUpgradeInsecure, bool aParserCreated) const {
552   if (CSPUTILSLOGENABLED()) {
553     CSPUTILSLOG(
554         ("nsCSPSchemeSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
555   }
556   MOZ_ASSERT((!mScheme.EqualsASCII("")), "scheme can not be the empty string");
557   if (mInvalidated) {
558     return false;
559   }
560   return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, false);
561 }
562 
visit(nsCSPSrcVisitor * aVisitor) const563 bool nsCSPSchemeSrc::visit(nsCSPSrcVisitor* aVisitor) const {
564   return aVisitor->visitSchemeSrc(*this);
565 }
566 
toString(nsAString & outStr) const567 void nsCSPSchemeSrc::toString(nsAString& outStr) const {
568   outStr.Append(mScheme);
569   outStr.AppendLiteral(":");
570 }
571 
572 /* ===== nsCSPHostSrc ======================== */
573 
nsCSPHostSrc(const nsAString & aHost)574 nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost)
575     : mHost(aHost),
576       mGeneratedFromSelfKeyword(false),
577       mIsUniqueOrigin(false),
578       mWithinFrameAncstorsDir(false) {
579   ToLowerCase(mHost);
580 }
581 
582 nsCSPHostSrc::~nsCSPHostSrc() = default;
583 
584 /*
585  * Checks whether the current directive permits a specific port.
586  * @param aEnforcementScheme
587  *        The scheme that this directive allows
588  *        (used to query the default port for that scheme)
589  * @param aEnforcementPort
590  *        The port that this directive allows
591  * @param aResourceURI
592  *        The uri of the subresource load
593  */
permitsPort(const nsAString & aEnforcementScheme,const nsAString & aEnforcementPort,nsIURI * aResourceURI)594 bool permitsPort(const nsAString& aEnforcementScheme,
595                  const nsAString& aEnforcementPort, nsIURI* aResourceURI) {
596   // If enforcement port is the wildcard, don't block the load.
597   if (aEnforcementPort.EqualsASCII("*")) {
598     return true;
599   }
600 
601   int32_t resourcePort;
602   nsresult rv = aResourceURI->GetPort(&resourcePort);
603   if (NS_FAILED(rv) && aEnforcementPort.IsEmpty()) {
604     // If we cannot get a Port (e.g. because of an Custom Protocol handler)
605     // We need to check if a default port is associated with the Scheme
606     if (aEnforcementScheme.IsEmpty()) {
607       return false;
608     }
609     int defaultPortforScheme =
610         NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get());
611 
612     // If there is no default port associated with the Scheme (
613     // defaultPortforScheme == -1) or it is an externally handled protocol (
614     // defaultPortforScheme == 0 ) and the csp does not enforce a port - we can
615     // allow not having a port
616     return (defaultPortforScheme == -1 || defaultPortforScheme == -0);
617   }
618   // Avoid unnecessary string creation/manipulation and don't block the
619   // load if the resource to be loaded uses the default port for that
620   // scheme and there is no port to be enforced.
621   // Note, this optimization relies on scheme checks within permitsScheme().
622   if (resourcePort == DEFAULT_PORT && aEnforcementPort.IsEmpty()) {
623     return true;
624   }
625 
626   // By now we know at that either the resourcePort does not use the default
627   // port or there is a port restriction to be enforced. A port value of -1
628   // corresponds to the protocol's default port (eg. -1 implies port 80 for
629   // http URIs), in such a case we have to query the default port of the
630   // resource to be loaded.
631   if (resourcePort == DEFAULT_PORT) {
632     nsAutoCString resourceScheme;
633     rv = aResourceURI->GetScheme(resourceScheme);
634     NS_ENSURE_SUCCESS(rv, false);
635     resourcePort = NS_GetDefaultPort(resourceScheme.get());
636   }
637 
638   // If there is a port to be enforced and the ports match, then
639   // don't block the load.
640   nsString resourcePortStr;
641   resourcePortStr.AppendInt(resourcePort);
642   if (aEnforcementPort.Equals(resourcePortStr)) {
643     return true;
644   }
645 
646   // If there is no port to be enforced, query the default port for the load.
647   nsString enforcementPort(aEnforcementPort);
648   if (enforcementPort.IsEmpty()) {
649     // For scheme less sources, our parser always generates a scheme
650     // which is the scheme of the protected resource.
651     MOZ_ASSERT(!aEnforcementScheme.IsEmpty(),
652                "need a scheme to query default port");
653     int32_t defaultEnforcementPort =
654         NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get());
655     enforcementPort.Truncate();
656     enforcementPort.AppendInt(defaultEnforcementPort);
657   }
658 
659   // If default ports match, don't block the load
660   if (enforcementPort.Equals(resourcePortStr)) {
661     return true;
662   }
663 
664   // Additional port matching where the regular URL matching algorithm
665   // treats insecure ports as matching their secure variants.
666   // default port for http is  :80
667   // default port for https is :443
668   if (enforcementPort.EqualsLiteral("80") &&
669       resourcePortStr.EqualsLiteral("443")) {
670     return true;
671   }
672 
673   // ports do not match, block the load.
674   return false;
675 }
676 
permits(nsIURI * aUri,const nsAString & aNonce,bool aWasRedirected,bool aReportOnly,bool aUpgradeInsecure,bool aParserCreated) const677 bool nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce,
678                            bool aWasRedirected, bool aReportOnly,
679                            bool aUpgradeInsecure, bool aParserCreated) const {
680   if (CSPUTILSLOGENABLED()) {
681     CSPUTILSLOG(
682         ("nsCSPHostSrc::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
683   }
684 
685   if (mInvalidated || mIsUniqueOrigin) {
686     return false;
687   }
688 
689   // we are following the enforcement rules from the spec, see:
690   // http://www.w3.org/TR/CSP11/#match-source-expression
691 
692   // 4.3) scheme matching: Check if the scheme matches.
693   if (!permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure,
694                      mGeneratedFromSelfKeyword)) {
695     return false;
696   }
697 
698   // The host in nsCSpHostSrc should never be empty. In case we are enforcing
699   // just a specific scheme, the parser should generate a nsCSPSchemeSource.
700   NS_ASSERTION((!mHost.IsEmpty()), "host can not be the empty string");
701 
702   // Before we can check if the host matches, we have to
703   // extract the host part from aUri.
704   nsAutoCString uriHost;
705   nsresult rv = aUri->GetAsciiHost(uriHost);
706   NS_ENSURE_SUCCESS(rv, false);
707 
708   nsString decodedUriHost;
709   CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost);
710 
711   // 2) host matching: Enforce a single *
712   if (mHost.EqualsASCII("*")) {
713     // The single ASTERISK character (*) does not match a URI's scheme of a type
714     // designating a globally unique identifier (such as blob:, data:, or
715     // filesystem:) At the moment firefox does not support filesystem; but for
716     // future compatibility we support it in CSP according to the spec,
717     // see: 4.2.2 Matching Source Expressions Note, that allowlisting any of
718     // these schemes would call nsCSPSchemeSrc::permits().
719     if (aUri->SchemeIs("blob") || aUri->SchemeIs("data") ||
720         aUri->SchemeIs("filesystem")) {
721       return false;
722     }
723 
724     // If no scheme is present there also wont be a port and folder to check
725     // which means we can return early
726     if (mScheme.IsEmpty()) {
727       return true;
728     }
729   }
730   // 4.5) host matching: Check if the allowed host starts with a wilcard.
731   else if (mHost.First() == '*') {
732     NS_ASSERTION(
733         mHost[1] == '.',
734         "Second character needs to be '.' whenever host starts with '*'");
735 
736     // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before
737     // checking if the remaining characters match
738     nsString wildCardHost = mHost;
739     wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1);
740     if (!StringEndsWith(decodedUriHost, wildCardHost)) {
741       return false;
742     }
743   }
744   // 4.6) host matching: Check if hosts match.
745   else if (!mHost.Equals(decodedUriHost)) {
746     return false;
747   }
748 
749   // Port matching: Check if the ports match.
750   if (!permitsPort(mScheme, mPort, aUri)) {
751     return false;
752   }
753 
754   // 4.9) Path matching: If there is a path, we have to enforce
755   // path-level matching, unless the channel got redirected, see:
756   // http://www.w3.org/TR/CSP11/#source-list-paths-and-redirects
757   if (!aWasRedirected && !mPath.IsEmpty()) {
758     // converting aUri into nsIURL so we can strip query and ref
759     // example.com/test#foo     -> example.com/test
760     // example.com/test?val=foo -> example.com/test
761     nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
762     if (!url) {
763       NS_ASSERTION(false, "can't QI into nsIURI");
764       return false;
765     }
766     nsAutoCString uriPath;
767     rv = url->GetFilePath(uriPath);
768     NS_ENSURE_SUCCESS(rv, false);
769 
770     if (mWithinFrameAncstorsDir) {
771       // no path matching for frame-ancestors to not leak any path information.
772       return true;
773     }
774 
775     nsString decodedUriPath;
776     CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath);
777 
778     // check if the last character of mPath is '/'; if so
779     // we just have to check loading resource is within
780     // the allowed path.
781     if (mPath.Last() == '/') {
782       if (!StringBeginsWith(decodedUriPath, mPath)) {
783         return false;
784       }
785     }
786     // otherwise mPath refers to a specific file, and we have to
787     // check if the loading resource matches the file.
788     else {
789       if (!mPath.Equals(decodedUriPath)) {
790         return false;
791       }
792     }
793   }
794 
795   // At the end: scheme, host, port and path match -> allow the load.
796   return true;
797 }
798 
visit(nsCSPSrcVisitor * aVisitor) const799 bool nsCSPHostSrc::visit(nsCSPSrcVisitor* aVisitor) const {
800   return aVisitor->visitHostSrc(*this);
801 }
802 
toString(nsAString & outStr) const803 void nsCSPHostSrc::toString(nsAString& outStr) const {
804   if (mGeneratedFromSelfKeyword) {
805     outStr.AppendLiteral("'self'");
806     return;
807   }
808 
809   // If mHost is a single "*", we append the wildcard and return.
810   if (mHost.EqualsASCII("*") && mScheme.IsEmpty() && mPort.IsEmpty()) {
811     outStr.Append(mHost);
812     return;
813   }
814 
815   // append scheme
816   outStr.Append(mScheme);
817 
818   // append host
819   outStr.AppendLiteral("://");
820   outStr.Append(mHost);
821 
822   // append port
823   if (!mPort.IsEmpty()) {
824     outStr.AppendLiteral(":");
825     outStr.Append(mPort);
826   }
827 
828   // append path
829   outStr.Append(mPath);
830 }
831 
setScheme(const nsAString & aScheme)832 void nsCSPHostSrc::setScheme(const nsAString& aScheme) {
833   mScheme = aScheme;
834   ToLowerCase(mScheme);
835 }
836 
setPort(const nsAString & aPort)837 void nsCSPHostSrc::setPort(const nsAString& aPort) { mPort = aPort; }
838 
appendPath(const nsAString & aPath)839 void nsCSPHostSrc::appendPath(const nsAString& aPath) { mPath.Append(aPath); }
840 
841 /* ===== nsCSPKeywordSrc ===================== */
842 
nsCSPKeywordSrc(enum CSPKeyword aKeyword)843 nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword)
844     : mKeyword(aKeyword) {
845   NS_ASSERTION((aKeyword != CSP_SELF),
846                "'self' should have been replaced in the parser");
847 }
848 
849 nsCSPKeywordSrc::~nsCSPKeywordSrc() = default;
850 
permits(nsIURI * aUri,const nsAString & aNonce,bool aWasRedirected,bool aReportOnly,bool aUpgradeInsecure,bool aParserCreated) const851 bool nsCSPKeywordSrc::permits(nsIURI* aUri, const nsAString& aNonce,
852                               bool aWasRedirected, bool aReportOnly,
853                               bool aUpgradeInsecure,
854                               bool aParserCreated) const {
855   // no need to check for invalidated, this will always return false unless
856   // it is an nsCSPKeywordSrc for 'strict-dynamic', which should allow non
857   // parser created scripts.
858   return ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated);
859 }
860 
allows(enum CSPKeyword aKeyword,const nsAString & aHashOrNonce,bool aParserCreated) const861 bool nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword,
862                              const nsAString& aHashOrNonce,
863                              bool aParserCreated) const {
864   CSPUTILSLOG(
865       ("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: "
866        "%s",
867        CSP_EnumToUTF8Keyword(aKeyword),
868        NS_ConvertUTF16toUTF8(aHashOrNonce).get(),
869        mInvalidated ? "yes" : "false"));
870 
871   if (mInvalidated) {
872     // only 'self', 'report-sample' and 'unsafe-inline' are keywords that can be
873     // ignored. Please note that the parser already translates 'self' into a uri
874     // (see assertion in constructor).
875     MOZ_ASSERT(mKeyword == CSP_UNSAFE_INLINE || mKeyword == CSP_REPORT_SAMPLE,
876                "should only invalidate unsafe-inline");
877     return false;
878   }
879   // either the keyword allows the load or the policy contains 'strict-dynamic',
880   // in which case we have to make sure the script is not parser created before
881   // allowing the load and also eval should be blocked even if 'strict-dynamic'
882   // is present. Should be allowed only if 'unsafe-eval' is present.
883   return ((mKeyword == aKeyword) ||
884           ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated &&
885            aKeyword != CSP_UNSAFE_EVAL));
886 }
887 
visit(nsCSPSrcVisitor * aVisitor) const888 bool nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const {
889   return aVisitor->visitKeywordSrc(*this);
890 }
891 
toString(nsAString & outStr) const892 void nsCSPKeywordSrc::toString(nsAString& outStr) const {
893   outStr.Append(CSP_EnumToUTF16Keyword(mKeyword));
894 }
895 
896 /* ===== nsCSPNonceSrc ==================== */
897 
nsCSPNonceSrc(const nsAString & aNonce)898 nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce) : mNonce(aNonce) {}
899 
900 nsCSPNonceSrc::~nsCSPNonceSrc() = default;
901 
permits(nsIURI * aUri,const nsAString & aNonce,bool aWasRedirected,bool aReportOnly,bool aUpgradeInsecure,bool aParserCreated) const902 bool nsCSPNonceSrc::permits(nsIURI* aUri, const nsAString& aNonce,
903                             bool aWasRedirected, bool aReportOnly,
904                             bool aUpgradeInsecure, bool aParserCreated) const {
905   if (CSPUTILSLOGENABLED()) {
906     CSPUTILSLOG(("nsCSPNonceSrc::permits, aUri: %s, aNonce: %s",
907                  aUri->GetSpecOrDefault().get(),
908                  NS_ConvertUTF16toUTF8(aNonce).get()));
909   }
910 
911   if (aReportOnly && aWasRedirected && aNonce.IsEmpty()) {
912     /* Fix for Bug 1505412
913      *  If we land here, we're currently handling a script-preload which got
914      *  redirected. Preloads do not have any info about the nonce assiociated.
915      *  Because of Report-Only the preload passes the 1st CSP-check so the
916      *  preload does not get retried with a nonce attached.
917      *  Currently we're relying on the script-manager to
918      *  provide a fake loadinfo to check the preloads against csp.
919      *  So during HTTPChannel->OnRedirect we cant check csp for this case.
920      *  But as the script-manager already checked the csp,
921      *  a report would already have been send,
922      *  if the nonce didnt match.
923      *  So we can pass the check here for Report-Only Cases.
924      */
925     MOZ_ASSERT(aParserCreated == false,
926                "Skipping nonce-check is only allowed for Preloads");
927     return true;
928   }
929 
930   // nonces can not be invalidated by strict-dynamic
931   return mNonce.Equals(aNonce);
932 }
933 
allows(enum CSPKeyword aKeyword,const nsAString & aHashOrNonce,bool aParserCreated) const934 bool nsCSPNonceSrc::allows(enum CSPKeyword aKeyword,
935                            const nsAString& aHashOrNonce,
936                            bool aParserCreated) const {
937   CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
938                CSP_EnumToUTF8Keyword(aKeyword),
939                NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
940 
941   if (aKeyword != CSP_NONCE) {
942     return false;
943   }
944   // nonces can not be invalidated by strict-dynamic
945   return mNonce.Equals(aHashOrNonce);
946 }
947 
visit(nsCSPSrcVisitor * aVisitor) const948 bool nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const {
949   return aVisitor->visitNonceSrc(*this);
950 }
951 
toString(nsAString & outStr) const952 void nsCSPNonceSrc::toString(nsAString& outStr) const {
953   outStr.Append(CSP_EnumToUTF16Keyword(CSP_NONCE));
954   outStr.Append(mNonce);
955   outStr.AppendLiteral("'");
956 }
957 
958 /* ===== nsCSPHashSrc ===================== */
959 
nsCSPHashSrc(const nsAString & aAlgo,const nsAString & aHash)960 nsCSPHashSrc::nsCSPHashSrc(const nsAString& aAlgo, const nsAString& aHash)
961     : mAlgorithm(aAlgo), mHash(aHash) {
962   // Only the algo should be rewritten to lowercase, the hash must remain the
963   // same.
964   ToLowerCase(mAlgorithm);
965 }
966 
967 nsCSPHashSrc::~nsCSPHashSrc() = default;
968 
allows(enum CSPKeyword aKeyword,const nsAString & aHashOrNonce,bool aParserCreated) const969 bool nsCSPHashSrc::allows(enum CSPKeyword aKeyword,
970                           const nsAString& aHashOrNonce,
971                           bool aParserCreated) const {
972   CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
973                CSP_EnumToUTF8Keyword(aKeyword),
974                NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
975 
976   if (aKeyword != CSP_HASH) {
977     return false;
978   }
979 
980   // hashes can not be invalidated by strict-dynamic
981 
982   // Convert aHashOrNonce to UTF-8
983   NS_ConvertUTF16toUTF8 utf8_hash(aHashOrNonce);
984 
985   nsresult rv;
986   nsCOMPtr<nsICryptoHash> hasher;
987   hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
988   NS_ENSURE_SUCCESS(rv, false);
989 
990   rv = hasher->InitWithString(NS_ConvertUTF16toUTF8(mAlgorithm));
991   NS_ENSURE_SUCCESS(rv, false);
992 
993   rv = hasher->Update((uint8_t*)utf8_hash.get(), utf8_hash.Length());
994   NS_ENSURE_SUCCESS(rv, false);
995 
996   nsAutoCString hash;
997   rv = hasher->Finish(true, hash);
998   NS_ENSURE_SUCCESS(rv, false);
999 
1000   return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
1001 }
1002 
visit(nsCSPSrcVisitor * aVisitor) const1003 bool nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const {
1004   return aVisitor->visitHashSrc(*this);
1005 }
1006 
toString(nsAString & outStr) const1007 void nsCSPHashSrc::toString(nsAString& outStr) const {
1008   outStr.AppendLiteral("'");
1009   outStr.Append(mAlgorithm);
1010   outStr.AppendLiteral("-");
1011   outStr.Append(mHash);
1012   outStr.AppendLiteral("'");
1013 }
1014 
1015 /* ===== nsCSPReportURI ===================== */
1016 
nsCSPReportURI(nsIURI * aURI)1017 nsCSPReportURI::nsCSPReportURI(nsIURI* aURI) : mReportURI(aURI) {}
1018 
1019 nsCSPReportURI::~nsCSPReportURI() = default;
1020 
visit(nsCSPSrcVisitor * aVisitor) const1021 bool nsCSPReportURI::visit(nsCSPSrcVisitor* aVisitor) const { return false; }
1022 
toString(nsAString & outStr) const1023 void nsCSPReportURI::toString(nsAString& outStr) const {
1024   nsAutoCString spec;
1025   nsresult rv = mReportURI->GetSpec(spec);
1026   if (NS_FAILED(rv)) {
1027     return;
1028   }
1029   outStr.AppendASCII(spec.get());
1030 }
1031 
1032 /* ===== nsCSPSandboxFlags ===================== */
1033 
nsCSPSandboxFlags(const nsAString & aFlags)1034 nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags) : mFlags(aFlags) {
1035   ToLowerCase(mFlags);
1036 }
1037 
1038 nsCSPSandboxFlags::~nsCSPSandboxFlags() = default;
1039 
visit(nsCSPSrcVisitor * aVisitor) const1040 bool nsCSPSandboxFlags::visit(nsCSPSrcVisitor* aVisitor) const { return false; }
1041 
toString(nsAString & outStr) const1042 void nsCSPSandboxFlags::toString(nsAString& outStr) const {
1043   outStr.Append(mFlags);
1044 }
1045 
1046 /* ===== nsCSPDirective ====================== */
1047 
nsCSPDirective(CSPDirective aDirective)1048 nsCSPDirective::nsCSPDirective(CSPDirective aDirective) {
1049   mDirective = aDirective;
1050 }
1051 
~nsCSPDirective()1052 nsCSPDirective::~nsCSPDirective() {
1053   for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1054     delete mSrcs[i];
1055   }
1056 }
1057 
permits(nsIURI * aUri,const nsAString & aNonce,bool aWasRedirected,bool aReportOnly,bool aUpgradeInsecure,bool aParserCreated) const1058 bool nsCSPDirective::permits(nsIURI* aUri, const nsAString& aNonce,
1059                              bool aWasRedirected, bool aReportOnly,
1060                              bool aUpgradeInsecure, bool aParserCreated) const {
1061   if (CSPUTILSLOGENABLED()) {
1062     CSPUTILSLOG(
1063         ("nsCSPDirective::permits, aUri: %s", aUri->GetSpecOrDefault().get()));
1064   }
1065 
1066   for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1067     if (mSrcs[i]->permits(aUri, aNonce, aWasRedirected, aReportOnly,
1068                           aUpgradeInsecure, aParserCreated)) {
1069       return true;
1070     }
1071   }
1072   return false;
1073 }
1074 
allows(enum CSPKeyword aKeyword,const nsAString & aHashOrNonce,bool aParserCreated) const1075 bool nsCSPDirective::allows(enum CSPKeyword aKeyword,
1076                             const nsAString& aHashOrNonce,
1077                             bool aParserCreated) const {
1078   CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, a HashOrNonce: %s",
1079                CSP_EnumToUTF8Keyword(aKeyword),
1080                NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
1081 
1082   for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1083     if (mSrcs[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) {
1084       return true;
1085     }
1086   }
1087   return false;
1088 }
1089 
toString(nsAString & outStr) const1090 void nsCSPDirective::toString(nsAString& outStr) const {
1091   // Append directive name
1092   outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
1093   outStr.AppendLiteral(" ");
1094 
1095   // Append srcs
1096   StringJoinAppend(outStr, u" "_ns, mSrcs,
1097                    [](nsAString& dest, nsCSPBaseSrc* cspBaseSrc) {
1098                      cspBaseSrc->toString(dest);
1099                    });
1100 }
1101 
toDomCSPStruct(mozilla::dom::CSP & outCSP) const1102 void nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const {
1103   mozilla::dom::Sequence<nsString> srcs;
1104   nsString src;
1105   for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1106     src.Truncate();
1107     mSrcs[i]->toString(src);
1108     if (!srcs.AppendElement(src, mozilla::fallible)) {
1109       // XXX(Bug 1632090) Instead of extending the array 1-by-1 (which might
1110       // involve multiple reallocations) and potentially crashing here,
1111       // SetCapacity could be called outside the loop once.
1112       mozalloc_handle_oom(0);
1113     }
1114   }
1115 
1116   switch (mDirective) {
1117     case nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE:
1118       outCSP.mDefault_src.Construct();
1119       outCSP.mDefault_src.Value() = std::move(srcs);
1120       return;
1121 
1122     case nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE:
1123       outCSP.mScript_src.Construct();
1124       outCSP.mScript_src.Value() = std::move(srcs);
1125       return;
1126 
1127     case nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE:
1128       outCSP.mObject_src.Construct();
1129       outCSP.mObject_src.Value() = std::move(srcs);
1130       return;
1131 
1132     case nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE:
1133       outCSP.mStyle_src.Construct();
1134       outCSP.mStyle_src.Value() = std::move(srcs);
1135       return;
1136 
1137     case nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE:
1138       outCSP.mImg_src.Construct();
1139       outCSP.mImg_src.Value() = std::move(srcs);
1140       return;
1141 
1142     case nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE:
1143       outCSP.mMedia_src.Construct();
1144       outCSP.mMedia_src.Value() = std::move(srcs);
1145       return;
1146 
1147     case nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE:
1148       outCSP.mFrame_src.Construct();
1149       outCSP.mFrame_src.Value() = std::move(srcs);
1150       return;
1151 
1152     case nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE:
1153       outCSP.mFont_src.Construct();
1154       outCSP.mFont_src.Value() = std::move(srcs);
1155       return;
1156 
1157     case nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE:
1158       outCSP.mConnect_src.Construct();
1159       outCSP.mConnect_src.Value() = std::move(srcs);
1160       return;
1161 
1162     case nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE:
1163       outCSP.mReport_uri.Construct();
1164       outCSP.mReport_uri.Value() = std::move(srcs);
1165       return;
1166 
1167     case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE:
1168       outCSP.mFrame_ancestors.Construct();
1169       outCSP.mFrame_ancestors.Value() = std::move(srcs);
1170       return;
1171 
1172     case nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE:
1173       outCSP.mManifest_src.Construct();
1174       outCSP.mManifest_src.Value() = std::move(srcs);
1175       return;
1176       // not supporting REFLECTED_XSS_DIRECTIVE
1177 
1178     case nsIContentSecurityPolicy::BASE_URI_DIRECTIVE:
1179       outCSP.mBase_uri.Construct();
1180       outCSP.mBase_uri.Value() = std::move(srcs);
1181       return;
1182 
1183     case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE:
1184       outCSP.mForm_action.Construct();
1185       outCSP.mForm_action.Value() = std::move(srcs);
1186       return;
1187 
1188     case nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT:
1189       outCSP.mBlock_all_mixed_content.Construct();
1190       // does not have any srcs
1191       return;
1192 
1193     case nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE:
1194       outCSP.mUpgrade_insecure_requests.Construct();
1195       // does not have any srcs
1196       return;
1197 
1198     case nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE:
1199       outCSP.mChild_src.Construct();
1200       outCSP.mChild_src.Value() = std::move(srcs);
1201       return;
1202 
1203     case nsIContentSecurityPolicy::SANDBOX_DIRECTIVE:
1204       outCSP.mSandbox.Construct();
1205       outCSP.mSandbox.Value() = std::move(srcs);
1206       return;
1207 
1208     case nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE:
1209       outCSP.mWorker_src.Construct();
1210       outCSP.mWorker_src.Value() = std::move(srcs);
1211       return;
1212 
1213     default:
1214       NS_ASSERTION(false, "cannot find directive to convert CSP to JSON");
1215   }
1216 }
1217 
getReportURIs(nsTArray<nsString> & outReportURIs) const1218 void nsCSPDirective::getReportURIs(nsTArray<nsString>& outReportURIs) const {
1219   NS_ASSERTION((mDirective == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE),
1220                "not a report-uri directive");
1221 
1222   // append uris
1223   nsString tmpReportURI;
1224   for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1225     tmpReportURI.Truncate();
1226     mSrcs[i]->toString(tmpReportURI);
1227     outReportURIs.AppendElement(tmpReportURI);
1228   }
1229 }
1230 
visitSrcs(nsCSPSrcVisitor * aVisitor) const1231 bool nsCSPDirective::visitSrcs(nsCSPSrcVisitor* aVisitor) const {
1232   for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1233     if (!mSrcs[i]->visit(aVisitor)) {
1234       return false;
1235     }
1236   }
1237   return true;
1238 }
1239 
equals(CSPDirective aDirective) const1240 bool nsCSPDirective::equals(CSPDirective aDirective) const {
1241   return (mDirective == aDirective);
1242 }
1243 
getDirName(nsAString & outStr) const1244 void nsCSPDirective::getDirName(nsAString& outStr) const {
1245   outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
1246 }
1247 
hasReportSampleKeyword() const1248 bool nsCSPDirective::hasReportSampleKeyword() const {
1249   for (nsCSPBaseSrc* src : mSrcs) {
1250     if (src->isReportSample()) {
1251       return true;
1252     }
1253   }
1254 
1255   return false;
1256 }
1257 
1258 /* =============== nsCSPChildSrcDirective ============= */
1259 
nsCSPChildSrcDirective(CSPDirective aDirective)1260 nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
1261     : nsCSPDirective(aDirective),
1262       mRestrictFrames(false),
1263       mRestrictWorkers(false) {}
1264 
1265 nsCSPChildSrcDirective::~nsCSPChildSrcDirective() = default;
1266 
equals(CSPDirective aDirective) const1267 bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const {
1268   if (aDirective == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) {
1269     return mRestrictFrames;
1270   }
1271   if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
1272     return mRestrictWorkers;
1273   }
1274   return (mDirective == aDirective);
1275 }
1276 
1277 /* =============== nsCSPScriptSrcDirective ============= */
1278 
nsCSPScriptSrcDirective(CSPDirective aDirective)1279 nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective)
1280     : nsCSPDirective(aDirective), mRestrictWorkers(false) {}
1281 
1282 nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() = default;
1283 
equals(CSPDirective aDirective) const1284 bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const {
1285   if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
1286     return mRestrictWorkers;
1287   }
1288   return (mDirective == aDirective);
1289 }
1290 
1291 /* =============== nsBlockAllMixedContentDirective ============= */
1292 
nsBlockAllMixedContentDirective(CSPDirective aDirective)1293 nsBlockAllMixedContentDirective::nsBlockAllMixedContentDirective(
1294     CSPDirective aDirective)
1295     : nsCSPDirective(aDirective) {}
1296 
1297 nsBlockAllMixedContentDirective::~nsBlockAllMixedContentDirective() = default;
1298 
toString(nsAString & outStr) const1299 void nsBlockAllMixedContentDirective::toString(nsAString& outStr) const {
1300   outStr.AppendASCII(CSP_CSPDirectiveToString(
1301       nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT));
1302 }
1303 
getDirName(nsAString & outStr) const1304 void nsBlockAllMixedContentDirective::getDirName(nsAString& outStr) const {
1305   outStr.AppendASCII(CSP_CSPDirectiveToString(
1306       nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT));
1307 }
1308 
1309 /* =============== nsUpgradeInsecureDirective ============= */
1310 
nsUpgradeInsecureDirective(CSPDirective aDirective)1311 nsUpgradeInsecureDirective::nsUpgradeInsecureDirective(CSPDirective aDirective)
1312     : nsCSPDirective(aDirective) {}
1313 
1314 nsUpgradeInsecureDirective::~nsUpgradeInsecureDirective() = default;
1315 
toString(nsAString & outStr) const1316 void nsUpgradeInsecureDirective::toString(nsAString& outStr) const {
1317   outStr.AppendASCII(CSP_CSPDirectiveToString(
1318       nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
1319 }
1320 
getDirName(nsAString & outStr) const1321 void nsUpgradeInsecureDirective::getDirName(nsAString& outStr) const {
1322   outStr.AppendASCII(CSP_CSPDirectiveToString(
1323       nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
1324 }
1325 
1326 /* ===== nsCSPPolicy ========================= */
1327 
nsCSPPolicy()1328 nsCSPPolicy::nsCSPPolicy()
1329     : mUpgradeInsecDir(nullptr),
1330       mReportOnly(false),
1331       mDeliveredViaMetaTag(false) {
1332   CSPUTILSLOG(("nsCSPPolicy::nsCSPPolicy"));
1333 }
1334 
~nsCSPPolicy()1335 nsCSPPolicy::~nsCSPPolicy() {
1336   CSPUTILSLOG(("nsCSPPolicy::~nsCSPPolicy"));
1337 
1338   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1339     delete mDirectives[i];
1340   }
1341 }
1342 
permits(CSPDirective aDir,nsIURI * aUri,const nsAString & aNonce,bool aWasRedirected,bool aSpecific,bool aParserCreated,nsAString & outViolatedDirective) const1343 bool nsCSPPolicy::permits(CSPDirective aDir, nsIURI* aUri,
1344                           const nsAString& aNonce, bool aWasRedirected,
1345                           bool aSpecific, bool aParserCreated,
1346                           nsAString& outViolatedDirective) const {
1347   if (CSPUTILSLOGENABLED()) {
1348     CSPUTILSLOG(("nsCSPPolicy::permits, aUri: %s, aDir: %d, aSpecific: %s",
1349                  aUri->GetSpecOrDefault().get(), aDir,
1350                  aSpecific ? "true" : "false"));
1351   }
1352 
1353   NS_ASSERTION(aUri, "permits needs an uri to perform the check!");
1354   outViolatedDirective.Truncate();
1355 
1356   nsCSPDirective* defaultDir = nullptr;
1357 
1358   // Try to find a relevant directive
1359   // These directive arrays are short (1-5 elements), not worth using a
1360   // hashtable.
1361   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1362     if (mDirectives[i]->equals(aDir)) {
1363       if (!mDirectives[i]->permits(aUri, aNonce, aWasRedirected, mReportOnly,
1364                                    mUpgradeInsecDir, aParserCreated)) {
1365         mDirectives[i]->getDirName(outViolatedDirective);
1366         return false;
1367       }
1368       return true;
1369     }
1370     if (mDirectives[i]->isDefaultDirective()) {
1371       defaultDir = mDirectives[i];
1372     }
1373   }
1374 
1375   // If the above loop runs through, we haven't found a matching directive.
1376   // Avoid relooping, just store the result of default-src while looping.
1377   if (!aSpecific && defaultDir) {
1378     if (!defaultDir->permits(aUri, aNonce, aWasRedirected, mReportOnly,
1379                              mUpgradeInsecDir, aParserCreated)) {
1380       defaultDir->getDirName(outViolatedDirective);
1381       return false;
1382     }
1383     return true;
1384   }
1385 
1386   // Nothing restricts this, so we're allowing the load
1387   // See bug 764937
1388   return true;
1389 }
1390 
allows(CSPDirective aDirective,enum CSPKeyword aKeyword,const nsAString & aHashOrNonce,bool aParserCreated) const1391 bool nsCSPPolicy::allows(CSPDirective aDirective, enum CSPKeyword aKeyword,
1392                          const nsAString& aHashOrNonce,
1393                          bool aParserCreated) const {
1394   CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s",
1395                CSP_EnumToUTF8Keyword(aKeyword),
1396                NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
1397 
1398   nsCSPDirective* defaultDir = nullptr;
1399 
1400   // Try to find a matching directive
1401   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1402     if (mDirectives[i]->isDefaultDirective()) {
1403       defaultDir = mDirectives[i];
1404       continue;
1405     }
1406     if (mDirectives[i]->equals(aDirective)) {
1407       if (mDirectives[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) {
1408         return true;
1409       }
1410       return false;
1411     }
1412   }
1413 
1414   // {nonce,hash}-source should not consult default-src:
1415   //   * return false if default-src is specified
1416   //   * but allow the load if default-src is *not* specified (Bug 1198422)
1417   if (aKeyword == CSP_NONCE || aKeyword == CSP_HASH) {
1418     if (!defaultDir) {
1419       return true;
1420     }
1421     return false;
1422   }
1423 
1424   // If the above loop runs through, we haven't found a matching directive.
1425   // Avoid relooping, just store the result of default-src while looping.
1426   if (defaultDir) {
1427     return defaultDir->allows(aKeyword, aHashOrNonce, aParserCreated);
1428   }
1429 
1430   // Allowing the load; see Bug 885433
1431   // a) inline scripts (also unsafe eval) should only be blocked
1432   //    if there is a [script-src] or [default-src]
1433   // b) inline styles should only be blocked
1434   //    if there is a [style-src] or [default-src]
1435   return true;
1436 }
1437 
toString(nsAString & outStr) const1438 void nsCSPPolicy::toString(nsAString& outStr) const {
1439   StringJoinAppend(outStr, u"; "_ns, mDirectives,
1440                    [](nsAString& dest, nsCSPDirective* cspDirective) {
1441                      cspDirective->toString(dest);
1442                    });
1443 }
1444 
toDomCSPStruct(mozilla::dom::CSP & outCSP) const1445 void nsCSPPolicy::toDomCSPStruct(mozilla::dom::CSP& outCSP) const {
1446   outCSP.mReport_only = mReportOnly;
1447 
1448   for (uint32_t i = 0; i < mDirectives.Length(); ++i) {
1449     mDirectives[i]->toDomCSPStruct(outCSP);
1450   }
1451 }
1452 
hasDirective(CSPDirective aDir) const1453 bool nsCSPPolicy::hasDirective(CSPDirective aDir) const {
1454   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1455     if (mDirectives[i]->equals(aDir)) {
1456       return true;
1457     }
1458   }
1459   return false;
1460 }
1461 
allowsNavigateTo(nsIURI * aURI,bool aWasRedirected,bool aEnforceAllowlist) const1462 bool nsCSPPolicy::allowsNavigateTo(nsIURI* aURI, bool aWasRedirected,
1463                                    bool aEnforceAllowlist) const {
1464   bool allowsNavigateTo = true;
1465 
1466   for (unsigned long i = 0; i < mDirectives.Length(); i++) {
1467     if (mDirectives[i]->equals(
1468             nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE)) {
1469       // Early return if we can skip the allowlist AND 'unsafe-allow-redirects'
1470       // is present.
1471       if (!aEnforceAllowlist &&
1472           mDirectives[i]->allows(CSP_UNSAFE_ALLOW_REDIRECTS, u""_ns, false)) {
1473         return true;
1474       }
1475       // Otherwise, check against the allowlist.
1476       if (!mDirectives[i]->permits(aURI, u""_ns, aWasRedirected, false, false,
1477                                    false)) {
1478         allowsNavigateTo = false;
1479       }
1480     }
1481   }
1482 
1483   return allowsNavigateTo;
1484 }
1485 
1486 /*
1487  * Use this function only after ::allows() returned 'false'. Most and
1488  * foremost it's used to get the violated directive before sending reports.
1489  * The parameter outDirective is the equivalent of 'outViolatedDirective'
1490  * for the ::permits() function family.
1491  */
getDirectiveStringAndReportSampleForContentType(CSPDirective aDirective,nsAString & outDirective,bool * aReportSample) const1492 void nsCSPPolicy::getDirectiveStringAndReportSampleForContentType(
1493     CSPDirective aDirective, nsAString& outDirective,
1494     bool* aReportSample) const {
1495   MOZ_ASSERT(aReportSample);
1496   *aReportSample = false;
1497 
1498   nsCSPDirective* defaultDir = nullptr;
1499   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1500     if (mDirectives[i]->isDefaultDirective()) {
1501       defaultDir = mDirectives[i];
1502       continue;
1503     }
1504     if (mDirectives[i]->equals(aDirective)) {
1505       mDirectives[i]->getDirName(outDirective);
1506       *aReportSample = mDirectives[i]->hasReportSampleKeyword();
1507       return;
1508     }
1509   }
1510   // if we haven't found a matching directive yet,
1511   // the contentType must be restricted by the default directive
1512   if (defaultDir) {
1513     defaultDir->getDirName(outDirective);
1514     *aReportSample = defaultDir->hasReportSampleKeyword();
1515     return;
1516   }
1517   NS_ASSERTION(false, "Can not query directive string for contentType!");
1518   outDirective.AppendLiteral("couldNotQueryViolatedDirective");
1519 }
1520 
getDirectiveAsString(CSPDirective aDir,nsAString & outDirective) const1521 void nsCSPPolicy::getDirectiveAsString(CSPDirective aDir,
1522                                        nsAString& outDirective) const {
1523   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1524     if (mDirectives[i]->equals(aDir)) {
1525       mDirectives[i]->toString(outDirective);
1526       return;
1527     }
1528   }
1529 }
1530 
1531 /*
1532  * Helper function that returns the underlying bit representation of sandbox
1533  * flags. The function returns SANDBOXED_NONE if there are no sandbox
1534  * directives.
1535  */
getSandboxFlags() const1536 uint32_t nsCSPPolicy::getSandboxFlags() const {
1537   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1538     if (mDirectives[i]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
1539       nsAutoString flags;
1540       mDirectives[i]->toString(flags);
1541 
1542       if (flags.IsEmpty()) {
1543         return SANDBOX_ALL_FLAGS;
1544       }
1545 
1546       nsAttrValue attr;
1547       attr.ParseAtomArray(flags);
1548 
1549       return nsContentUtils::ParseSandboxAttributeToFlags(&attr);
1550     }
1551   }
1552 
1553   return SANDBOXED_NONE;
1554 }
1555 
getReportURIs(nsTArray<nsString> & outReportURIs) const1556 void nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const {
1557   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1558     if (mDirectives[i]->equals(
1559             nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
1560       mDirectives[i]->getReportURIs(outReportURIs);
1561       return;
1562     }
1563   }
1564 }
1565 
visitDirectiveSrcs(CSPDirective aDir,nsCSPSrcVisitor * aVisitor) const1566 bool nsCSPPolicy::visitDirectiveSrcs(CSPDirective aDir,
1567                                      nsCSPSrcVisitor* aVisitor) const {
1568   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1569     if (mDirectives[i]->equals(aDir)) {
1570       return mDirectives[i]->visitSrcs(aVisitor);
1571     }
1572   }
1573   return false;
1574 }
1575