1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/TextUtils.h"
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/StaticPrefs_security.h"
13 #include "nsContentUtils.h"
14 #include "nsCSPParser.h"
15 #include "nsCSPUtils.h"
16 #include "nsIScriptError.h"
17 #include "nsNetUtil.h"
18 #include "nsReadableUtils.h"
19 #include "nsServiceManagerUtils.h"
20 #include "nsUnicharUtils.h"
22 using namespace mozilla
;
23 using namespace mozilla::dom
;
25 static LogModule
* GetCspParserLog() {
26 static LazyLogModule
gCspParserPRLog("CSPParser");
27 return gCspParserPRLog
;
30 #define CSPPARSERLOG(args) \
31 MOZ_LOG(GetCspParserLog(), mozilla::LogLevel::Debug, args)
32 #define CSPPARSERLOGENABLED() \
33 MOZ_LOG_TEST(GetCspParserLog(), mozilla::LogLevel::Debug)
35 static const uint32_t kSubHostPathCharacterCutoff
= 512;
37 static const char* const kHashSourceValidFns
[] = {"sha256", "sha384", "sha512"};
38 static const uint32_t kHashSourceValidFnsLen
= 3;
40 /* ===== nsCSPParser ==================== */
42 nsCSPParser::nsCSPParser(policyTokens
& aTokens
, nsIURI
* aSelfURI
,
43 nsCSPContext
* aCSPContext
, bool aDeliveredViaMetaTag
,
44 bool aSuppressLogMessages
)
47 mHasHashOrNonce(false),
48 mHasAnyUnsafeEval(false),
49 mStrictDynamic(false),
50 mUnsafeInlineKeywordSrc(nullptr),
56 mParsingFrameAncestorsDir(false),
57 mTokens(aTokens
.Clone()),
60 mCSPContext(aCSPContext
),
61 mDeliveredViaMetaTag(aDeliveredViaMetaTag
),
62 mSuppressLogMessages(aSuppressLogMessages
) {
63 CSPPARSERLOG(("nsCSPParser::nsCSPParser"));
66 nsCSPParser::~nsCSPParser() { CSPPARSERLOG(("nsCSPParser::~nsCSPParser")); }
68 static bool isCharacterToken(char16_t aSymbol
) {
69 return (aSymbol
>= 'a' && aSymbol
<= 'z') ||
70 (aSymbol
>= 'A' && aSymbol
<= 'Z');
73 bool isNumberToken(char16_t aSymbol
) {
74 return (aSymbol
>= '0' && aSymbol
<= '9');
77 bool isValidHexDig(char16_t aHexDig
) {
78 return (isNumberToken(aHexDig
) || (aHexDig
>= 'A' && aHexDig
<= 'F') ||
79 (aHexDig
>= 'a' && aHexDig
<= 'f'));
82 static bool isValidBase64Value(const char16_t
* cur
, const char16_t
* end
) {
84 // https://w3c.github.io/webappsec-csp/#grammardef-nonce-source
86 // May end with one or two =
87 if (end
> cur
&& *(end
- 1) == EQUALS
) end
--;
88 if (end
> cur
&& *(end
- 1) == EQUALS
) end
--;
90 // Must have at least one character aside from any =
95 // Rest must all be A-Za-z0-9+/-_
96 for (; cur
< end
; ++cur
) {
97 if (!(isCharacterToken(*cur
) || isNumberToken(*cur
) || *cur
== PLUS
||
98 *cur
== SLASH
|| *cur
== DASH
|| *cur
== UNDERLINE
)) {
106 void nsCSPParser::resetCurChar(const nsAString
& aToken
) {
107 mCurChar
= aToken
.BeginReading();
108 mEndChar
= aToken
.EndReading();
112 // The path is terminated by the first question mark ("?") or
113 // number sign ("#") character, or by the end of the URI.
114 // http://tools.ietf.org/html/rfc3986#section-3.3
115 bool nsCSPParser::atEndOfPath() {
116 return (atEnd() || peek(QUESTIONMARK
) || peek(NUMBER_SIGN
));
119 // unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
120 bool nsCSPParser::atValidUnreservedChar() {
121 return (peek(isCharacterToken
) || peek(isNumberToken
) || peek(DASH
) ||
122 peek(DOT
) || peek(UNDERLINE
) || peek(TILDE
));
125 // sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
126 // / "*" / "+" / "," / ";" / "="
127 // Please note that even though ',' and ';' appear to be
128 // valid sub-delims according to the RFC production of paths,
129 // both can not appear here by itself, they would need to be
130 // pct-encoded in order to be part of the path.
131 bool nsCSPParser::atValidSubDelimChar() {
132 return (peek(EXCLAMATION
) || peek(DOLLAR
) || peek(AMPERSAND
) ||
133 peek(SINGLEQUOTE
) || peek(OPENBRACE
) || peek(CLOSINGBRACE
) ||
134 peek(WILDCARD
) || peek(PLUS
) || peek(EQUALS
));
137 // pct-encoded = "%" HEXDIG HEXDIG
138 bool nsCSPParser::atValidPctEncodedChar() {
139 const char16_t
* pctCurChar
= mCurChar
;
141 if ((pctCurChar
+ 2) >= mEndChar
) {
142 // string too short, can't be a valid pct-encoded char.
146 // Any valid pct-encoding must follow the following format:
148 if (PERCENT_SIGN
!= *pctCurChar
|| !isValidHexDig(*(pctCurChar
+ 1)) ||
149 !isValidHexDig(*(pctCurChar
+ 2))) {
155 // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
156 // http://tools.ietf.org/html/rfc3986#section-3.3
157 bool nsCSPParser::atValidPathChar() {
158 return (atValidUnreservedChar() || atValidSubDelimChar() ||
159 atValidPctEncodedChar() || peek(COLON
) || peek(ATSYMBOL
));
162 void nsCSPParser::logWarningErrorToConsole(uint32_t aSeverityFlag
,
163 const char* aProperty
,
164 const nsTArray
<nsString
>& aParams
) {
165 CSPPARSERLOG(("nsCSPParser::logWarningErrorToConsole: %s", aProperty
));
167 if (mSuppressLogMessages
) {
171 // send console messages off to the context and let the context
172 // deal with it (potentially messages need to be queued up)
173 mCSPContext
->logToConsole(aProperty
, aParams
,
174 u
""_ns
, // aSourceName
175 u
""_ns
, // aSourceLine
178 aSeverityFlag
); // aFlags
181 bool nsCSPParser::hostChar() {
185 return accept(isCharacterToken
) || accept(isNumberToken
) || accept(DASH
);
188 // (ALPHA / DIGIT / "+" / "-" / "." )
189 bool nsCSPParser::schemeChar() {
193 return accept(isCharacterToken
) || accept(isNumberToken
) || accept(PLUS
) ||
194 accept(DASH
) || accept(DOT
);
197 // port = ":" ( 1*DIGIT / "*" )
198 bool nsCSPParser::port() {
199 CSPPARSERLOG(("nsCSPParser::port, mCurToken: %s, mCurValue: %s",
200 NS_ConvertUTF16toUTF8(mCurToken
).get(),
201 NS_ConvertUTF16toUTF8(mCurValue
).get()));
203 // Consume the COLON we just peeked at in houstSource
206 // Resetting current value since we start to parse a port now.
207 // e.g; "http://www.example.com:8888" then we have already parsed
208 // everything up to (including) ":";
212 if (accept(WILDCARD
)) {
216 // Port must start with a number
217 if (!accept(isNumberToken
)) {
218 AutoTArray
<nsString
, 1> params
= {mCurToken
};
219 logWarningErrorToConsole(nsIScriptError::warningFlag
, "couldntParsePort",
223 // Consume more numbers and set parsed port to the nsCSPHost
224 while (accept(isNumberToken
)) { /* consume */
229 bool nsCSPParser::subPath(nsCSPHostSrc
* aCspHost
) {
230 CSPPARSERLOG(("nsCSPParser::subPath, mCurToken: %s, mCurValue: %s",
231 NS_ConvertUTF16toUTF8(mCurToken
).get(),
232 NS_ConvertUTF16toUTF8(mCurValue
).get()));
234 // Emergency exit to avoid endless loops in case a path in a CSP policy
235 // is longer than 512 characters, or also to avoid endless loops
236 // in case we are parsing unrecognized characters in the following loop.
237 uint32_t charCounter
= 0;
238 nsString pctDecodedSubPath
;
240 while (!atEndOfPath()) {
242 // before appendig any additional portion of a subpath we have to
243 // pct-decode that portion of the subpath. atValidPathChar() already
244 // verified a correct pct-encoding, now we can safely decode and append
245 // the decoded-sub path.
246 CSP_PercentDecodeStr(mCurValue
, pctDecodedSubPath
);
247 aCspHost
->appendPath(pctDecodedSubPath
);
248 // Resetting current value since we are appending parts of the path
249 // to aCspHost, e.g; "http://www.example.com/path1/path2" then the
250 // first part is "/path1", second part "/path2"
252 } else if (!atValidPathChar()) {
253 AutoTArray
<nsString
, 1> params
= {mCurToken
};
254 logWarningErrorToConsole(nsIScriptError::warningFlag
,
255 "couldntParseInvalidSource", params
);
258 // potentially we have encountred a valid pct-encoded character in
259 // atValidPathChar(); if so, we have to account for "% HEXDIG HEXDIG" and
260 // advance the pointer past the pct-encoded char.
261 if (peek(PERCENT_SIGN
)) {
266 if (++charCounter
> kSubHostPathCharacterCutoff
) {
270 // before appendig any additional portion of a subpath we have to pct-decode
271 // that portion of the subpath. atValidPathChar() already verified a correct
272 // pct-encoding, now we can safely decode and append the decoded-sub path.
273 CSP_PercentDecodeStr(mCurValue
, pctDecodedSubPath
);
274 aCspHost
->appendPath(pctDecodedSubPath
);
279 bool nsCSPParser::path(nsCSPHostSrc
* aCspHost
) {
280 CSPPARSERLOG(("nsCSPParser::path, mCurToken: %s, mCurValue: %s",
281 NS_ConvertUTF16toUTF8(mCurToken
).get(),
282 NS_ConvertUTF16toUTF8(mCurValue
).get()));
284 // Resetting current value and forgetting everything we have parsed so far
285 // e.g. parsing "http://www.example.com/path1/path2", then
286 // "http://www.example.com" has already been parsed so far
290 if (!accept(SLASH
)) {
291 AutoTArray
<nsString
, 1> params
= {mCurToken
};
292 logWarningErrorToConsole(nsIScriptError::warningFlag
,
293 "couldntParseInvalidSource", params
);
297 // one slash right after host [port] is also considered a path, e.g.
298 // www.example.com/ should result in www.example.com/
299 // please note that we do not have to perform any pct-decoding here
300 // because we are just appending a '/' and not any actual chars.
301 aCspHost
->appendPath(u
"/"_ns
);
304 // path can begin with "/" but not "//"
305 // see http://tools.ietf.org/html/rfc3986#section-3.3
307 AutoTArray
<nsString
, 1> params
= {mCurToken
};
308 logWarningErrorToConsole(nsIScriptError::warningFlag
,
309 "couldntParseInvalidSource", params
);
312 return subPath(aCspHost
);
315 bool nsCSPParser::subHost() {
316 CSPPARSERLOG(("nsCSPParser::subHost, mCurToken: %s, mCurValue: %s",
317 NS_ConvertUTF16toUTF8(mCurToken
).get(),
318 NS_ConvertUTF16toUTF8(mCurValue
).get()));
320 // Emergency exit to avoid endless loops in case a host in a CSP policy
321 // is longer than 512 characters, or also to avoid endless loops
322 // in case we are parsing unrecognized characters in the following loop.
323 uint32_t charCounter
= 0;
325 while (!atEndOfPath() && !peek(COLON
) && !peek(SLASH
)) {
331 if (accept(DOT
) && !hostChar()) {
334 if (charCounter
> kSubHostPathCharacterCutoff
) {
341 // host = "*" / [ "*." ] 1*host-char *( "." 1*host-char )
342 nsCSPHostSrc
* nsCSPParser::host() {
343 CSPPARSERLOG(("nsCSPParser::host, mCurToken: %s, mCurValue: %s",
344 NS_ConvertUTF16toUTF8(mCurToken
).get(),
345 NS_ConvertUTF16toUTF8(mCurValue
).get()));
347 // Check if the token starts with "*"; please remember that we handle
348 // a single "*" as host in sourceExpression, but we still have to handle
349 // the case where a scheme was defined, e.g., as:
350 // "https://*", "*.example.com", "*:*", etc.
351 if (accept(WILDCARD
)) {
352 // Might solely be the wildcard
353 if (atEnd() || peek(COLON
)) {
354 return new nsCSPHostSrc(mCurValue
);
356 // If the token is not only the "*", a "." must follow right after
358 AutoTArray
<nsString
, 1> params
= {mCurToken
};
359 logWarningErrorToConsole(nsIScriptError::warningFlag
,
360 "couldntParseInvalidHost", params
);
365 // Expecting at least one host-char
367 AutoTArray
<nsString
, 1> params
= {mCurToken
};
368 logWarningErrorToConsole(nsIScriptError::warningFlag
,
369 "couldntParseInvalidHost", params
);
373 // There might be several sub hosts defined.
375 AutoTArray
<nsString
, 1> params
= {mCurToken
};
376 logWarningErrorToConsole(nsIScriptError::warningFlag
,
377 "couldntParseInvalidHost", params
);
381 // HostName might match a keyword, log to the console.
382 if (CSP_IsQuotelessKeyword(mCurValue
)) {
383 nsString keyword
= mCurValue
;
384 ToLowerCase(keyword
);
385 AutoTArray
<nsString
, 2> params
= {mCurToken
, keyword
};
386 logWarningErrorToConsole(nsIScriptError::warningFlag
,
387 "hostNameMightBeKeyword", params
);
390 // Create a new nsCSPHostSrc with the parsed host.
391 return new nsCSPHostSrc(mCurValue
);
394 // keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'" /
395 // "'wasm-unsafe-eval'"
396 nsCSPBaseSrc
* nsCSPParser::keywordSource() {
397 CSPPARSERLOG(("nsCSPParser::keywordSource, mCurToken: %s, mCurValue: %s",
398 NS_ConvertUTF16toUTF8(mCurToken
).get(),
399 NS_ConvertUTF16toUTF8(mCurValue
).get()));
401 // Special case handling for 'self' which is not stored internally as a
402 // keyword, but rather creates a nsCSPHostSrc using the selfURI
403 if (CSP_IsKeyword(mCurToken
, CSP_SELF
)) {
404 return CSP_CreateHostSrcFromSelfURI(mSelfURI
);
407 if (CSP_IsKeyword(mCurToken
, CSP_REPORT_SAMPLE
)) {
408 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken
));
411 if (CSP_IsKeyword(mCurToken
, CSP_STRICT_DYNAMIC
)) {
412 if (!CSP_IsDirective(mCurDir
[0],
413 nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE
) &&
414 !CSP_IsDirective(mCurDir
[0],
415 nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
) &&
416 !CSP_IsDirective(mCurDir
[0],
417 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
)) {
418 // Todo: Enforce 'strict-dynamic' within default-src; see Bug 1313937
419 AutoTArray
<nsString
, 1> params
= {u
"strict-dynamic"_ns
};
420 logWarningErrorToConsole(nsIScriptError::warningFlag
,
421 "ignoringStrictDynamic", params
);
424 mStrictDynamic
= true;
425 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken
));
428 if (CSP_IsKeyword(mCurToken
, CSP_UNSAFE_INLINE
)) {
429 nsWeakPtr ctx
= mCSPContext
->GetLoadingContext();
430 nsCOMPtr
<Document
> doc
= do_QueryReferent(ctx
);
432 doc
->SetHasUnsafeInlineCSP(true);
434 // make sure script-src only contains 'unsafe-inline' once;
435 // ignore duplicates and log warning
436 if (mUnsafeInlineKeywordSrc
) {
437 AutoTArray
<nsString
, 1> params
= {mCurToken
};
438 logWarningErrorToConsole(nsIScriptError::warningFlag
,
439 "ignoringDuplicateSrc", params
);
442 // cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
443 // case that script-src directive also contains hash- or nonce-.
444 mUnsafeInlineKeywordSrc
=
445 new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken
));
446 return mUnsafeInlineKeywordSrc
;
449 if (CSP_IsKeyword(mCurToken
, CSP_UNSAFE_EVAL
)) {
450 nsWeakPtr ctx
= mCSPContext
->GetLoadingContext();
451 nsCOMPtr
<Document
> doc
= do_QueryReferent(ctx
);
453 doc
->SetHasUnsafeEvalCSP(true);
455 mHasAnyUnsafeEval
= true;
456 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken
));
459 if (StaticPrefs::security_csp_wasm_unsafe_eval_enabled() &&
460 CSP_IsKeyword(mCurToken
, CSP_WASM_UNSAFE_EVAL
)) {
461 mHasAnyUnsafeEval
= true;
462 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken
));
465 if (StaticPrefs::security_csp_unsafe_hashes_enabled() &&
466 CSP_IsKeyword(mCurToken
, CSP_UNSAFE_HASHES
)) {
467 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken
));
470 if (CSP_IsKeyword(mCurToken
, CSP_UNSAFE_ALLOW_REDIRECTS
)) {
471 if (!CSP_IsDirective(mCurDir
[0],
472 nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE
)) {
473 // Only allow 'unsafe-allow-redirects' within navigate-to.
474 AutoTArray
<nsString
, 2> params
= {u
"unsafe-allow-redirects"_ns
,
476 logWarningErrorToConsole(nsIScriptError::warningFlag
,
477 "IgnoringSourceWithinDirective", params
);
481 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken
));
487 // host-source = [ scheme "://" ] host [ port ] [ path ]
488 nsCSPHostSrc
* nsCSPParser::hostSource() {
489 CSPPARSERLOG(("nsCSPParser::hostSource, mCurToken: %s, mCurValue: %s",
490 NS_ConvertUTF16toUTF8(mCurToken
).get(),
491 NS_ConvertUTF16toUTF8(mCurValue
).get()));
493 nsCSPHostSrc
* cspHost
= host();
495 // Error was reported in host()
499 // Calling port() to see if there is a port to parse, if an error
500 // occurs, port() reports the error, if port() returns true;
501 // we have a valid port, so we add it to cspHost.
507 cspHost
->setPort(mCurValue
);
514 // Calling path() to see if there is a path to parse, if an error
515 // occurs, path() reports the error; handing cspHost as an argument
516 // which simplifies parsing of several paths.
517 if (!path(cspHost
)) {
518 // If the host [port] is followed by a path, it has to be a valid path,
519 // otherwise we pass the nullptr, indicating an error, up the callstack.
520 // see also http://www.w3.org/TR/CSP11/#source-list
527 // scheme-source = scheme ":"
528 nsCSPSchemeSrc
* nsCSPParser::schemeSource() {
529 CSPPARSERLOG(("nsCSPParser::schemeSource, mCurToken: %s, mCurValue: %s",
530 NS_ConvertUTF16toUTF8(mCurToken
).get(),
531 NS_ConvertUTF16toUTF8(mCurValue
).get()));
533 if (!accept(isCharacterToken
)) {
536 while (schemeChar()) { /* consume */
538 nsString scheme
= mCurValue
;
540 // If the potential scheme is not followed by ":" - it's not a scheme
541 if (!accept(COLON
)) {
545 // If the chraracter following the ":" is a number or the "*"
546 // then we are not parsing a scheme; but rather a host;
547 if (peek(isNumberToken
) || peek(WILDCARD
)) {
551 return new nsCSPSchemeSrc(scheme
);
554 // nonce-source = "'nonce-" nonce-value "'"
555 nsCSPNonceSrc
* nsCSPParser::nonceSource() {
556 CSPPARSERLOG(("nsCSPParser::nonceSource, mCurToken: %s, mCurValue: %s",
557 NS_ConvertUTF16toUTF8(mCurToken
).get(),
558 NS_ConvertUTF16toUTF8(mCurValue
).get()));
560 // Check if mCurToken begins with "'nonce-" and ends with "'"
561 if (!StringBeginsWith(mCurToken
,
562 nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE
)),
563 nsASCIICaseInsensitiveStringComparator
) ||
564 mCurToken
.Last() != SINGLEQUOTE
) {
568 // Trim surrounding single quotes
569 const nsAString
& expr
= Substring(mCurToken
, 1, mCurToken
.Length() - 2);
571 int32_t dashIndex
= expr
.FindChar(DASH
);
575 if (!isValidBase64Value(expr
.BeginReading() + dashIndex
+ 1,
576 expr
.EndReading())) {
580 // cache if encountering hash or nonce to invalidate unsafe-inline
581 mHasHashOrNonce
= true;
582 return new nsCSPNonceSrc(
583 Substring(expr
, dashIndex
+ 1, expr
.Length() - dashIndex
+ 1));
586 // hash-source = "'" hash-algo "-" base64-value "'"
587 nsCSPHashSrc
* nsCSPParser::hashSource() {
588 CSPPARSERLOG(("nsCSPParser::hashSource, mCurToken: %s, mCurValue: %s",
589 NS_ConvertUTF16toUTF8(mCurToken
).get(),
590 NS_ConvertUTF16toUTF8(mCurValue
).get()));
592 // Check if mCurToken starts and ends with "'"
593 if (mCurToken
.First() != SINGLEQUOTE
|| mCurToken
.Last() != SINGLEQUOTE
) {
597 // Trim surrounding single quotes
598 const nsAString
& expr
= Substring(mCurToken
, 1, mCurToken
.Length() - 2);
600 int32_t dashIndex
= expr
.FindChar(DASH
);
605 if (!isValidBase64Value(expr
.BeginReading() + dashIndex
+ 1,
606 expr
.EndReading())) {
610 nsAutoString
algo(Substring(expr
, 0, dashIndex
));
612 Substring(expr
, dashIndex
+ 1, expr
.Length() - dashIndex
+ 1));
614 for (uint32_t i
= 0; i
< kHashSourceValidFnsLen
; i
++) {
615 if (algo
.LowerCaseEqualsASCII(kHashSourceValidFns
[i
])) {
616 // cache if encountering hash or nonce to invalidate unsafe-inline
617 mHasHashOrNonce
= true;
618 return new nsCSPHashSrc(algo
, hash
);
624 // source-expression = scheme-source / host-source / keyword-source
625 // / nonce-source / hash-source
626 nsCSPBaseSrc
* nsCSPParser::sourceExpression() {
627 CSPPARSERLOG(("nsCSPParser::sourceExpression, mCurToken: %s, mCurValue: %s",
628 NS_ConvertUTF16toUTF8(mCurToken
).get(),
629 NS_ConvertUTF16toUTF8(mCurValue
).get()));
631 // Check if it is a keyword
632 if (nsCSPBaseSrc
* cspKeyword
= keywordSource()) {
636 // Check if it is a nonce-source
637 if (nsCSPNonceSrc
* cspNonce
= nonceSource()) {
641 // Check if it is a hash-source
642 if (nsCSPHashSrc
* cspHash
= hashSource()) {
646 // We handle a single "*" as host here, to avoid any confusion when applying
647 // the default scheme. However, we still would need to apply the default
648 // scheme in case we would parse "*:80".
649 if (mCurToken
.EqualsASCII("*")) {
650 return new nsCSPHostSrc(u
"*"_ns
);
653 // Calling resetCurChar allows us to use mCurChar and mEndChar
654 // to parse mCurToken; e.g. mCurToken = "http://www.example.com", then
656 // mEndChar = points just after the last 'm'
658 resetCurChar(mCurToken
);
660 // Check if mCurToken starts with a scheme
661 nsAutoString parsedScheme
;
662 if (nsCSPSchemeSrc
* cspScheme
= schemeSource()) {
663 // mCurToken might only enforce a specific scheme
667 // If something follows the scheme, we do not create
668 // a nsCSPSchemeSrc, but rather a nsCSPHostSrc, which
669 // needs to know the scheme to enforce; remember the
670 // scheme and delete cspScheme;
671 cspScheme
->toString(parsedScheme
);
672 parsedScheme
.Trim(":", false, true);
675 // If mCurToken provides not only a scheme, but also a host, we have to
676 // check if two slashes follow the scheme.
677 if (!accept(SLASH
) || !accept(SLASH
)) {
678 AutoTArray
<nsString
, 1> params
= {mCurToken
};
679 logWarningErrorToConsole(nsIScriptError::warningFlag
,
680 "failedToParseUnrecognizedSource", params
);
685 // Calling resetCurValue allows us to keep pointers for mCurChar and mEndChar
686 // alive, but resets mCurValue; e.g. mCurToken = "http://www.example.com",
687 // then mCurChar = 'w' mEndChar = 'm' mCurValue = ""
690 // If mCurToken does not provide a scheme (scheme-less source), we apply the
691 // scheme from selfURI
692 if (parsedScheme
.IsEmpty()) {
693 // Resetting internal helpers, because we might already have parsed some of
694 // the host when trying to parse a scheme.
695 resetCurChar(mCurToken
);
696 nsAutoCString selfScheme
;
697 mSelfURI
->GetScheme(selfScheme
);
698 parsedScheme
.AssignASCII(selfScheme
.get());
701 // At this point we are expecting a host to be parsed.
702 // Trying to create a new nsCSPHost.
703 if (nsCSPHostSrc
* cspHost
= hostSource()) {
704 // Do not forget to set the parsed scheme.
705 cspHost
->setScheme(parsedScheme
);
706 cspHost
->setWithinFrameAncestorsDir(mParsingFrameAncestorsDir
);
709 // Error was reported in hostSource()
713 // source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ]
714 // / *WSP "'none'" *WSP
715 void nsCSPParser::sourceList(nsTArray
<nsCSPBaseSrc
*>& outSrcs
) {
718 // remember, srcs start at index 1
719 for (uint32_t i
= 1; i
< mCurDir
.Length(); i
++) {
720 // mCurToken is only set here and remains the current token
721 // to be processed, which avoid passing arguments between functions.
722 mCurToken
= mCurDir
[i
];
725 CSPPARSERLOG(("nsCSPParser::sourceList, mCurToken: %s, mCurValue: %s",
726 NS_ConvertUTF16toUTF8(mCurToken
).get(),
727 NS_ConvertUTF16toUTF8(mCurValue
).get()));
729 // Special case handling for none:
730 // Ignore 'none' if any other src is available.
731 // (See http://www.w3.org/TR/CSP11/#parsing)
732 if (CSP_IsKeyword(mCurToken
, CSP_NONE
)) {
736 // Must be a regular source expression
737 nsCSPBaseSrc
* src
= sourceExpression();
739 outSrcs
.AppendElement(src
);
743 // Check if the directive contains a 'none'
745 // If the directive contains no other srcs, then we set the 'none'
746 if (outSrcs
.IsEmpty() ||
747 (outSrcs
.Length() == 1 && outSrcs
[0]->isReportSample())) {
748 nsCSPKeywordSrc
* keyword
= new nsCSPKeywordSrc(CSP_NONE
);
749 outSrcs
.InsertElementAt(0, keyword
);
751 // Otherwise, we ignore 'none' and report a warning
753 AutoTArray
<nsString
, 1> params
;
754 params
.AppendElement(CSP_EnumToUTF16Keyword(CSP_NONE
));
755 logWarningErrorToConsole(nsIScriptError::warningFlag
,
756 "ignoringUnknownOption", params
);
761 void nsCSPParser::reportURIList(nsCSPDirective
* aDir
) {
762 CSPPARSERLOG(("nsCSPParser::reportURIList"));
764 nsTArray
<nsCSPBaseSrc
*> srcs
;
765 nsCOMPtr
<nsIURI
> uri
;
768 // remember, srcs start at index 1
769 for (uint32_t i
= 1; i
< mCurDir
.Length(); i
++) {
770 mCurToken
= mCurDir
[i
];
772 CSPPARSERLOG(("nsCSPParser::reportURIList, mCurToken: %s, mCurValue: %s",
773 NS_ConvertUTF16toUTF8(mCurToken
).get(),
774 NS_ConvertUTF16toUTF8(mCurValue
).get()));
776 rv
= NS_NewURI(getter_AddRefs(uri
), mCurToken
, "", mSelfURI
);
778 // If creating the URI casued an error, skip this URI
780 AutoTArray
<nsString
, 1> params
= {mCurToken
};
781 logWarningErrorToConsole(nsIScriptError::warningFlag
,
782 "couldNotParseReportURI", params
);
786 // Create new nsCSPReportURI and append to the list.
787 nsCSPReportURI
* reportURI
= new nsCSPReportURI(uri
);
788 srcs
.AppendElement(reportURI
);
791 if (srcs
.Length() == 0) {
792 AutoTArray
<nsString
, 1> directiveName
= {mCurToken
};
793 logWarningErrorToConsole(nsIScriptError::warningFlag
,
794 "ignoringDirectiveWithNoValues", directiveName
);
800 mPolicy
->addDirective(aDir
);
803 /* Helper function for parsing sandbox flags. This function solely concatenates
804 * all the source list tokens (the sandbox flags) so the attribute parser
805 * (nsContentUtils::ParseSandboxAttributeToFlags) can parse them.
807 void nsCSPParser::sandboxFlagList(nsCSPDirective
* aDir
) {
808 CSPPARSERLOG(("nsCSPParser::sandboxFlagList"));
812 // remember, srcs start at index 1
813 for (uint32_t i
= 1; i
< mCurDir
.Length(); i
++) {
814 mCurToken
= mCurDir
[i
];
816 CSPPARSERLOG(("nsCSPParser::sandboxFlagList, mCurToken: %s, mCurValue: %s",
817 NS_ConvertUTF16toUTF8(mCurToken
).get(),
818 NS_ConvertUTF16toUTF8(mCurValue
).get()));
820 if (!nsContentUtils::IsValidSandboxFlag(mCurToken
)) {
821 AutoTArray
<nsString
, 1> params
= {mCurToken
};
822 logWarningErrorToConsole(nsIScriptError::warningFlag
,
823 "couldntParseInvalidSandboxFlag", params
);
827 flags
.Append(mCurToken
);
828 if (i
!= mCurDir
.Length() - 1) {
829 flags
.AppendLiteral(" ");
833 // Please note that the sandbox directive can exist
834 // by itself (not containing any flags).
835 nsTArray
<nsCSPBaseSrc
*> srcs
;
836 srcs
.AppendElement(new nsCSPSandboxFlags(flags
));
838 mPolicy
->addDirective(aDir
);
841 // directive-value = *( WSP / <VCHAR except ";" and ","> )
842 void nsCSPParser::directiveValue(nsTArray
<nsCSPBaseSrc
*>& outSrcs
) {
843 CSPPARSERLOG(("nsCSPParser::directiveValue"));
845 // Just forward to sourceList
849 // directive-name = 1*( ALPHA / DIGIT / "-" )
850 nsCSPDirective
* nsCSPParser::directiveName() {
851 CSPPARSERLOG(("nsCSPParser::directiveName, mCurToken: %s, mCurValue: %s",
852 NS_ConvertUTF16toUTF8(mCurToken
).get(),
853 NS_ConvertUTF16toUTF8(mCurValue
).get()));
855 // Check if it is a valid directive
856 CSPDirective directive
= CSP_StringToCSPDirective(mCurToken
);
857 if (directive
== nsIContentSecurityPolicy::NO_DIRECTIVE
) {
858 AutoTArray
<nsString
, 1> params
= {mCurToken
};
859 logWarningErrorToConsole(nsIScriptError::warningFlag
,
860 "couldNotProcessUnknownDirective", params
);
864 // The directive 'reflected-xss' is part of CSP 1.1, see:
865 // http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss
866 // Currently we are not supporting that directive, hence we log a
867 // warning to the console and ignore the directive including its values.
868 if (directive
== nsIContentSecurityPolicy::REFLECTED_XSS_DIRECTIVE
) {
869 AutoTArray
<nsString
, 1> params
= {mCurToken
};
870 logWarningErrorToConsole(nsIScriptError::warningFlag
,
871 "notSupportingDirective", params
);
875 // script-src-attr and script-scr-elem might have been disabled.
876 // Similarly style-src-{attr, elem}.
877 if (((directive
== nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
||
878 directive
== nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
) &&
879 !StaticPrefs::security_csp_script_src_attr_elem_enabled()) ||
880 ((directive
== nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE
||
881 directive
== nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE
) &&
882 !StaticPrefs::security_csp_style_src_attr_elem_enabled())) {
883 AutoTArray
<nsString
, 1> params
= {mCurToken
};
884 logWarningErrorToConsole(nsIScriptError::warningFlag
,
885 "notSupportingDirective", params
);
889 // Bug 1529068: Implement navigate-to directive.
890 // Once all corner cases are resolved we can remove that special
891 // if-handling here and let the parser just fall through to
892 // return new nsCSPDirective.
893 if (directive
== nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE
&&
894 !StaticPrefs::security_csp_enableNavigateTo()) {
895 AutoTArray
<nsString
, 1> params
= {mCurToken
};
896 logWarningErrorToConsole(nsIScriptError::warningFlag
,
897 "couldNotProcessUnknownDirective", params
);
901 // Make sure the directive does not already exist
902 // (see http://www.w3.org/TR/CSP11/#parsing)
903 if (mPolicy
->hasDirective(directive
)) {
904 AutoTArray
<nsString
, 1> params
= {mCurToken
};
905 logWarningErrorToConsole(nsIScriptError::warningFlag
, "duplicateDirective",
910 // CSP delivered via meta tag should ignore the following directives:
911 // report-uri, frame-ancestors, and sandbox, see:
912 // http://www.w3.org/TR/CSP11/#delivery-html-meta-element
913 if (mDeliveredViaMetaTag
&&
914 ((directive
== nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE
) ||
915 (directive
== nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE
) ||
916 (directive
== nsIContentSecurityPolicy::SANDBOX_DIRECTIVE
))) {
917 // log to the console to indicate that meta CSP is ignoring the directive
918 AutoTArray
<nsString
, 1> params
= {mCurToken
};
919 logWarningErrorToConsole(nsIScriptError::warningFlag
,
920 "ignoringSrcFromMetaCSP", params
);
924 // special case handling for block-all-mixed-content
925 if (directive
== nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT
) {
926 // If mixed content upgrade is enabled block-all-mixed content is obsolete
927 if (mozilla::StaticPrefs::
928 security_mixed_content_upgrade_display_content()) {
929 // log to the console that if mixed content display upgrading is enabled
930 // block-all-mixed-content is obsolete.
931 AutoTArray
<nsString
, 1> params
= {mCurToken
};
932 logWarningErrorToConsole(nsIScriptError::warningFlag
,
933 "obsoleteBlockAllMixedContent", params
);
935 return new nsBlockAllMixedContentDirective(directive
);
938 // special case handling for upgrade-insecure-requests
939 if (directive
== nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE
) {
940 return new nsUpgradeInsecureDirective(directive
);
943 // if we have a child-src, cache it as a fallback for
944 // * workers (if worker-src is not explicitly specified)
945 // * frames (if frame-src is not explicitly specified)
946 if (directive
== nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE
) {
947 mChildSrc
= new nsCSPChildSrcDirective(directive
);
951 // if we have a frame-src, cache it so we can discard child-src for frames
952 if (directive
== nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE
) {
953 mFrameSrc
= new nsCSPDirective(directive
);
957 // if we have a worker-src, cache it so we can discard child-src for workers
958 if (directive
== nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE
) {
959 mWorkerSrc
= new nsCSPDirective(directive
);
963 // if we have a script-src, cache it as a fallback for worker-src
964 // in case child-src is not present. It is also used as a fallback for
965 // script-src-elem and script-src-attr.
966 if (directive
== nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE
) {
967 mScriptSrc
= new nsCSPScriptSrcDirective(directive
);
971 // If we have a style-src, cache it as a fallback for style-src-elem and
973 if (directive
== nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE
) {
974 mStyleSrc
= new nsCSPStyleSrcDirective(directive
);
978 return new nsCSPDirective(directive
);
981 // directive = *WSP [ directive-name [ WSP directive-value ] ]
982 void nsCSPParser::directive() {
983 // Set the directiveName to mCurToken
984 // Remember, the directive name is stored at index 0
985 mCurToken
= mCurDir
[0];
987 CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
988 NS_ConvertUTF16toUTF8(mCurToken
).get(),
989 NS_ConvertUTF16toUTF8(mCurValue
).get()));
991 // Make sure that the directive-srcs-array contains at least
993 if (mCurDir
.Length() == 0) {
994 AutoTArray
<nsString
, 1> params
= {u
"directive missing"_ns
};
995 logWarningErrorToConsole(nsIScriptError::warningFlag
,
996 "failedToParseUnrecognizedSource", params
);
1000 if (CSP_IsEmptyDirective(mCurValue
, mCurToken
)) {
1004 // Try to create a new CSPDirective
1005 nsCSPDirective
* cspDir
= directiveName();
1007 // if we can not create a CSPDirective, we can skip parsing the srcs for
1012 // special case handling for block-all-mixed-content, which is only specified
1013 // by a directive name but does not include any srcs.
1014 if (cspDir
->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT
)) {
1015 if (mCurDir
.Length() > 1) {
1016 AutoTArray
<nsString
, 1> params
= {u
"block-all-mixed-content"_ns
};
1017 logWarningErrorToConsole(nsIScriptError::warningFlag
,
1018 "ignoreSrcForDirective", params
);
1020 // add the directive and return
1021 mPolicy
->addDirective(cspDir
);
1025 // special case handling for upgrade-insecure-requests, which is only
1026 // specified by a directive name but does not include any srcs.
1027 if (cspDir
->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE
)) {
1028 if (mCurDir
.Length() > 1) {
1029 AutoTArray
<nsString
, 1> params
= {u
"upgrade-insecure-requests"_ns
};
1030 logWarningErrorToConsole(nsIScriptError::warningFlag
,
1031 "ignoreSrcForDirective", params
);
1033 // add the directive and return
1034 mPolicy
->addUpgradeInsecDir(
1035 static_cast<nsUpgradeInsecureDirective
*>(cspDir
));
1039 // special case handling for report-uri directive (since it doesn't contain
1040 // a valid source list but rather actual URIs)
1041 if (CSP_IsDirective(mCurDir
[0],
1042 nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE
)) {
1043 reportURIList(cspDir
);
1047 // special case handling for sandbox directive (since it doe4sn't contain
1048 // a valid source list but rather special sandbox flags)
1049 if (CSP_IsDirective(mCurDir
[0],
1050 nsIContentSecurityPolicy::SANDBOX_DIRECTIVE
)) {
1051 sandboxFlagList(cspDir
);
1055 // make sure to reset cache variables when trying to invalidate unsafe-inline;
1056 // unsafe-inline might not only appear in script-src, but also in default-src
1057 mHasHashOrNonce
= false;
1058 mHasAnyUnsafeEval
= false;
1059 mStrictDynamic
= false;
1060 mUnsafeInlineKeywordSrc
= nullptr;
1062 mParsingFrameAncestorsDir
= CSP_IsDirective(
1063 mCurDir
[0], nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE
);
1065 // Try to parse all the srcs by handing the array off to directiveValue
1066 nsTArray
<nsCSPBaseSrc
*> srcs
;
1067 directiveValue(srcs
);
1069 // If we can not parse any srcs; we let the source expression be the empty set
1070 // ('none') see, http://www.w3.org/TR/CSP11/#source-list-parsing
1071 if (srcs
.IsEmpty() || (srcs
.Length() == 1 && srcs
[0]->isReportSample())) {
1072 nsCSPKeywordSrc
* keyword
= new nsCSPKeywordSrc(CSP_NONE
);
1073 srcs
.InsertElementAt(0, keyword
);
1076 // If policy contains 'strict-dynamic' invalidate all srcs within script-src.
1077 if (mStrictDynamic
) {
1079 cspDir
->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE
) ||
1081 nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
) ||
1082 cspDir
->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
),
1083 "strict-dynamic only allowed within script-src(-elem|attr)");
1084 for (uint32_t i
= 0; i
< srcs
.Length(); i
++) {
1085 // Please note that nsCSPNonceSrc as well as nsCSPHashSrc overwrite
1086 // invalidate(), so it's fine to just call invalidate() on all srcs.
1087 // Please also note that nsCSPKeywordSrc() can not be invalidated and
1088 // always returns false unless the keyword is 'strict-dynamic' in which
1089 // case we allow the load if the script is not parser created!
1090 srcs
[i
]->invalidate();
1091 // Log a message to the console that src will be ignored.
1092 nsAutoString srcStr
;
1093 srcs
[i
]->toString(srcStr
);
1094 // Even though we invalidate all of the srcs internally, we don't want to
1095 // log messages for the srcs: 'strict-dynamic', 'unsafe-inline',
1096 // 'unsafe-hashes', nonces, and hashes, because those still apply even
1097 // with 'strict-dynamic'.
1098 // TODO the comment seems wrong 'unsafe-eval' vs 'unsafe-inline'.
1099 if (!srcStr
.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_STRICT_DYNAMIC
)) &&
1100 !srcStr
.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_UNSAFE_EVAL
)) &&
1101 !srcStr
.EqualsASCII(CSP_EnumToUTF8Keyword(CSP_UNSAFE_HASHES
)) &&
1103 srcStr
, nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE
))) &&
1104 !StringBeginsWith(srcStr
, u
"'sha"_ns
)) {
1105 AutoTArray
<nsString
, 2> params
= {srcStr
, mCurDir
[0]};
1106 logWarningErrorToConsole(nsIScriptError::warningFlag
,
1107 "ignoringScriptSrcForStrictDynamic", params
);
1110 // Log a warning that all scripts might be blocked because the policy
1111 // contains 'strict-dynamic' but no valid nonce or hash.
1112 if (!mHasHashOrNonce
) {
1113 AutoTArray
<nsString
, 1> params
= {mCurDir
[0]};
1114 logWarningErrorToConsole(nsIScriptError::warningFlag
,
1115 "strictDynamicButNoHashOrNonce", params
);
1119 // From https://w3c.github.io/webappsec-csp/#allow-all-inline
1120 // follows that when either a hash or nonce is specified, 'unsafe-inline'
1121 // should not apply.
1122 if (mHasHashOrNonce
&& mUnsafeInlineKeywordSrc
&&
1123 (cspDir
->isDefaultDirective() ||
1124 cspDir
->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE
) ||
1125 cspDir
->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
) ||
1126 cspDir
->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
) ||
1127 cspDir
->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE
) ||
1128 cspDir
->equals(nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE
) ||
1129 cspDir
->equals(nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE
))) {
1130 mUnsafeInlineKeywordSrc
->invalidate();
1132 // Log to the console that unsafe-inline will be ignored.
1133 AutoTArray
<nsString
, 2> params
= {u
"'unsafe-inline'"_ns
, mCurDir
[0]};
1134 logWarningErrorToConsole(nsIScriptError::warningFlag
,
1135 "ignoringSrcWithinNonceOrHashDirective", params
);
1138 if (mHasAnyUnsafeEval
&&
1139 (cspDir
->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
) ||
1140 cspDir
->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
))) {
1141 // Log to the console that (wasm-)unsafe-eval will be ignored.
1142 AutoTArray
<nsString
, 1> params
= {mCurDir
[0]};
1143 logWarningErrorToConsole(nsIScriptError::warningFlag
, "ignoringUnsafeEval",
1147 // Add the newly created srcs to the directive and add the directive to the
1149 cspDir
->addSrcs(srcs
);
1150 mPolicy
->addDirective(cspDir
);
1153 // policy = [ directive *( ";" [ directive ] ) ]
1154 nsCSPPolicy
* nsCSPParser::policy() {
1155 CSPPARSERLOG(("nsCSPParser::policy"));
1157 mPolicy
= new nsCSPPolicy();
1158 for (uint32_t i
= 0; i
< mTokens
.Length(); i
++) {
1159 // https://w3c.github.io/webappsec-csp/#parse-serialized-policy
1160 // Step 2.2. ..., or if token is not an ASCII string, continue.
1162 // Note: In the spec the token isn't split by whitespace yet.
1163 bool isAscii
= true;
1164 for (const auto& token
: mTokens
[i
]) {
1165 if (!IsAscii(token
)) {
1166 AutoTArray
<nsString
, 1> params
= {mTokens
[i
][0], token
};
1167 logWarningErrorToConsole(nsIScriptError::warningFlag
,
1168 "ignoringNonAsciiToken", params
);
1177 // All input is already tokenized; set one tokenized array in the form of
1178 // [ name, src, src, ... ]
1179 // to mCurDir and call directive which processes the current directive.
1180 mCurDir
= mTokens
[i
].Clone();
1186 // if frame-src is specified explicitly for that policy than child-src
1187 // should not restrict frames; if not, than child-src needs to restrict
1189 mChildSrc
->setRestrictFrames();
1192 // if worker-src is specified explicitly for that policy than child-src
1193 // should not restrict workers; if not, than child-src needs to restrict
1195 mChildSrc
->setRestrictWorkers();
1199 // if script-src is specified, but not worker-src and also no child-src, then
1200 // script-src has to govern workers.
1201 if (mScriptSrc
&& !mWorkerSrc
&& !mChildSrc
) {
1202 mScriptSrc
->setRestrictWorkers();
1205 // If script-src is specified and script-src-elem is not specified, then
1206 // script-src has to govern script requests and script blocks.
1207 if (mScriptSrc
&& !mPolicy
->hasDirective(
1208 nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE
)) {
1209 mScriptSrc
->setRestrictScriptElem();
1212 // If script-src is specified and script-src-attr is not specified, then
1213 // script-src has to govern script attr (event handlers).
1214 if (mScriptSrc
&& !mPolicy
->hasDirective(
1215 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE
)) {
1216 mScriptSrc
->setRestrictScriptAttr();
1219 // If style-src is specified and style-src-elem is not specified, then
1220 // style-src serves as a fallback.
1221 if (mStyleSrc
&& !mPolicy
->hasDirective(
1222 nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE
)) {
1223 mStyleSrc
->setRestrictStyleElem();
1226 // If style-src is specified and style-attr-elem is not specified, then
1227 // style-src serves as a fallback.
1228 if (mStyleSrc
&& !mPolicy
->hasDirective(
1229 nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE
)) {
1230 mStyleSrc
->setRestrictStyleAttr();
1236 nsCSPPolicy
* nsCSPParser::parseContentSecurityPolicy(
1237 const nsAString
& aPolicyString
, nsIURI
* aSelfURI
, bool aReportOnly
,
1238 nsCSPContext
* aCSPContext
, bool aDeliveredViaMetaTag
,
1239 bool aSuppressLogMessages
) {
1240 if (CSPPARSERLOGENABLED()) {
1241 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, policy: %s",
1242 NS_ConvertUTF16toUTF8(aPolicyString
).get()));
1243 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, selfURI: %s",
1244 aSelfURI
->GetSpecOrDefault().get()));
1245 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, reportOnly: %s",
1246 (aReportOnly
? "true" : "false")));
1248 ("nsCSPParser::parseContentSecurityPolicy, deliveredViaMetaTag: %s",
1249 (aDeliveredViaMetaTag
? "true" : "false")));
1252 NS_ASSERTION(aSelfURI
, "Can not parseContentSecurityPolicy without aSelfURI");
1254 // Separate all input into tokens and store them in the form of:
1255 // [ [ name, src, src, ... ], [ name, src, src, ... ], ... ]
1256 // The tokenizer itself can not fail; all eventual errors
1257 // are detected in the parser itself.
1259 nsTArray
<CopyableTArray
<nsString
> > tokens
;
1260 PolicyTokenizer::tokenizePolicy(aPolicyString
, tokens
);
1262 nsCSPParser
parser(tokens
, aSelfURI
, aCSPContext
, aDeliveredViaMetaTag
,
1263 aSuppressLogMessages
);
1265 // Start the parser to generate a new CSPPolicy using the generated tokens.
1266 nsCSPPolicy
* policy
= parser
.policy();
1268 // Check that report-only policies define a report-uri, otherwise log warning.
1270 policy
->setReportOnlyFlag(true);
1271 if (!policy
->hasDirective(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE
)) {
1272 nsAutoCString prePath
;
1273 nsresult rv
= aSelfURI
->GetPrePath(prePath
);
1274 NS_ENSURE_SUCCESS(rv
, policy
);
1275 AutoTArray
<nsString
, 1> params
;
1276 CopyUTF8toUTF16(prePath
, *params
.AppendElement());
1277 parser
.logWarningErrorToConsole(nsIScriptError::warningFlag
,
1278 "reportURInotInReportOnlyHeader", params
);
1282 policy
->setDeliveredViaMetaTagFlag(aDeliveredViaMetaTag
);
1284 if (policy
->getNumDirectives() == 0) {
1285 // Individual errors were already reported in the parser, but if
1286 // we do not have an enforcable directive at all, we return null.
1291 if (CSPPARSERLOGENABLED()) {
1292 nsString parsedPolicy
;
1293 policy
->toString(parsedPolicy
);
1294 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, parsedPolicy: %s",
1295 NS_ConvertUTF16toUTF8(parsedPolicy
).get()));