Backed out changeset e4bf227a6224 (bug 1801501) for causing mochitest plain failures...
[gecko.git] / dom / security / nsCSPParser.cpp
blobb2b6573a842feef1bae8adccdf1bb6232de4e990
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"
12 #include "nsCOMPtr.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)
45 : mCurChar(nullptr),
46 mEndChar(nullptr),
47 mHasHashOrNonce(false),
48 mHasAnyUnsafeEval(false),
49 mStrictDynamic(false),
50 mUnsafeInlineKeywordSrc(nullptr),
51 mChildSrc(nullptr),
52 mFrameSrc(nullptr),
53 mWorkerSrc(nullptr),
54 mScriptSrc(nullptr),
55 mStyleSrc(nullptr),
56 mParsingFrameAncestorsDir(false),
57 mTokens(aTokens.Clone()),
58 mSelfURI(aSelfURI),
59 mPolicy(nullptr),
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) {
83 // Using grammar at
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 =
91 if (end == cur) {
92 return false;
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)) {
99 return false;
103 return true;
106 void nsCSPParser::resetCurChar(const nsAString& aToken) {
107 mCurChar = aToken.BeginReading();
108 mEndChar = aToken.EndReading();
109 resetCurValue();
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.
143 return false;
146 // Any valid pct-encoding must follow the following format:
147 // "% HEXDIG HEXDIG"
148 if (PERCENT_SIGN != *pctCurChar || !isValidHexDig(*(pctCurChar + 1)) ||
149 !isValidHexDig(*(pctCurChar + 2))) {
150 return false;
152 return true;
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) {
168 return;
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
176 0, // aLineNumber
177 0, // aColumnNumber
178 aSeverityFlag); // aFlags
181 bool nsCSPParser::hostChar() {
182 if (atEnd()) {
183 return false;
185 return accept(isCharacterToken) || accept(isNumberToken) || accept(DASH);
188 // (ALPHA / DIGIT / "+" / "-" / "." )
189 bool nsCSPParser::schemeChar() {
190 if (atEnd()) {
191 return false;
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
204 accept(COLON);
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) ":";
209 resetCurValue();
211 // Port might be "*"
212 if (accept(WILDCARD)) {
213 return true;
216 // Port must start with a number
217 if (!accept(isNumberToken)) {
218 AutoTArray<nsString, 1> params = {mCurToken};
219 logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParsePort",
220 params);
221 return false;
223 // Consume more numbers and set parsed port to the nsCSPHost
224 while (accept(isNumberToken)) { /* consume */
226 return true;
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()) {
241 if (peek(SLASH)) {
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"
251 resetCurValue();
252 } else if (!atValidPathChar()) {
253 AutoTArray<nsString, 1> params = {mCurToken};
254 logWarningErrorToConsole(nsIScriptError::warningFlag,
255 "couldntParseInvalidSource", params);
256 return false;
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)) {
262 advance();
263 advance();
265 advance();
266 if (++charCounter > kSubHostPathCharacterCutoff) {
267 return false;
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);
275 resetCurValue();
276 return true;
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
287 // forget about it.
288 resetCurValue();
290 if (!accept(SLASH)) {
291 AutoTArray<nsString, 1> params = {mCurToken};
292 logWarningErrorToConsole(nsIScriptError::warningFlag,
293 "couldntParseInvalidSource", params);
294 return false;
296 if (atEndOfPath()) {
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);
302 return true;
304 // path can begin with "/" but not "//"
305 // see http://tools.ietf.org/html/rfc3986#section-3.3
306 if (peek(SLASH)) {
307 AutoTArray<nsString, 1> params = {mCurToken};
308 logWarningErrorToConsole(nsIScriptError::warningFlag,
309 "couldntParseInvalidSource", params);
310 return false;
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)) {
326 ++charCounter;
327 while (hostChar()) {
328 /* consume */
329 ++charCounter;
331 if (accept(DOT) && !hostChar()) {
332 return false;
334 if (charCounter > kSubHostPathCharacterCutoff) {
335 return false;
338 return true;
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
357 if (!accept(DOT)) {
358 AutoTArray<nsString, 1> params = {mCurToken};
359 logWarningErrorToConsole(nsIScriptError::warningFlag,
360 "couldntParseInvalidHost", params);
361 return nullptr;
365 // Expecting at least one host-char
366 if (!hostChar()) {
367 AutoTArray<nsString, 1> params = {mCurToken};
368 logWarningErrorToConsole(nsIScriptError::warningFlag,
369 "couldntParseInvalidHost", params);
370 return nullptr;
373 // There might be several sub hosts defined.
374 if (!subHost()) {
375 AutoTArray<nsString, 1> params = {mCurToken};
376 logWarningErrorToConsole(nsIScriptError::warningFlag,
377 "couldntParseInvalidHost", params);
378 return nullptr;
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 !CSP_IsDirective(mCurDir[0],
419 nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE)) {
420 AutoTArray<nsString, 1> params = {u"strict-dynamic"_ns};
421 logWarningErrorToConsole(nsIScriptError::warningFlag,
422 "ignoringStrictDynamic", params);
425 mStrictDynamic = true;
426 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
429 if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) {
430 // make sure script-src only contains 'unsafe-inline' once;
431 // ignore duplicates and log warning
432 if (mUnsafeInlineKeywordSrc) {
433 AutoTArray<nsString, 1> params = {mCurToken};
434 logWarningErrorToConsole(nsIScriptError::warningFlag,
435 "ignoringDuplicateSrc", params);
436 return nullptr;
438 // cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
439 // case that script-src directive also contains hash- or nonce-.
440 mUnsafeInlineKeywordSrc =
441 new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
442 return mUnsafeInlineKeywordSrc;
445 if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
446 mHasAnyUnsafeEval = true;
447 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
450 if (StaticPrefs::security_csp_wasm_unsafe_eval_enabled() &&
451 CSP_IsKeyword(mCurToken, CSP_WASM_UNSAFE_EVAL)) {
452 mHasAnyUnsafeEval = true;
453 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
456 if (StaticPrefs::security_csp_unsafe_hashes_enabled() &&
457 CSP_IsKeyword(mCurToken, CSP_UNSAFE_HASHES)) {
458 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
461 if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_ALLOW_REDIRECTS)) {
462 if (!CSP_IsDirective(mCurDir[0],
463 nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE)) {
464 // Only allow 'unsafe-allow-redirects' within navigate-to.
465 AutoTArray<nsString, 2> params = {u"unsafe-allow-redirects"_ns,
466 u"navigate-to"_ns};
467 logWarningErrorToConsole(nsIScriptError::warningFlag,
468 "IgnoringSourceWithinDirective", params);
469 return nullptr;
472 return new nsCSPKeywordSrc(CSP_UTF16KeywordToEnum(mCurToken));
475 return nullptr;
478 // host-source = [ scheme "://" ] host [ port ] [ path ]
479 nsCSPHostSrc* nsCSPParser::hostSource() {
480 CSPPARSERLOG(("nsCSPParser::hostSource, mCurToken: %s, mCurValue: %s",
481 NS_ConvertUTF16toUTF8(mCurToken).get(),
482 NS_ConvertUTF16toUTF8(mCurValue).get()));
484 nsCSPHostSrc* cspHost = host();
485 if (!cspHost) {
486 // Error was reported in host()
487 return nullptr;
490 // Calling port() to see if there is a port to parse, if an error
491 // occurs, port() reports the error, if port() returns true;
492 // we have a valid port, so we add it to cspHost.
493 if (peek(COLON)) {
494 if (!port()) {
495 delete cspHost;
496 return nullptr;
498 cspHost->setPort(mCurValue);
501 if (atEndOfPath()) {
502 return cspHost;
505 // Calling path() to see if there is a path to parse, if an error
506 // occurs, path() reports the error; handing cspHost as an argument
507 // which simplifies parsing of several paths.
508 if (!path(cspHost)) {
509 // If the host [port] is followed by a path, it has to be a valid path,
510 // otherwise we pass the nullptr, indicating an error, up the callstack.
511 // see also http://www.w3.org/TR/CSP11/#source-list
512 delete cspHost;
513 return nullptr;
515 return cspHost;
518 // scheme-source = scheme ":"
519 nsCSPSchemeSrc* nsCSPParser::schemeSource() {
520 CSPPARSERLOG(("nsCSPParser::schemeSource, mCurToken: %s, mCurValue: %s",
521 NS_ConvertUTF16toUTF8(mCurToken).get(),
522 NS_ConvertUTF16toUTF8(mCurValue).get()));
524 if (!accept(isCharacterToken)) {
525 return nullptr;
527 while (schemeChar()) { /* consume */
529 nsString scheme = mCurValue;
531 // If the potential scheme is not followed by ":" - it's not a scheme
532 if (!accept(COLON)) {
533 return nullptr;
536 // If the chraracter following the ":" is a number or the "*"
537 // then we are not parsing a scheme; but rather a host;
538 if (peek(isNumberToken) || peek(WILDCARD)) {
539 return nullptr;
542 return new nsCSPSchemeSrc(scheme);
545 // nonce-source = "'nonce-" nonce-value "'"
546 nsCSPNonceSrc* nsCSPParser::nonceSource() {
547 CSPPARSERLOG(("nsCSPParser::nonceSource, mCurToken: %s, mCurValue: %s",
548 NS_ConvertUTF16toUTF8(mCurToken).get(),
549 NS_ConvertUTF16toUTF8(mCurValue).get()));
551 // Check if mCurToken begins with "'nonce-" and ends with "'"
552 if (!StringBeginsWith(mCurToken,
553 nsDependentString(CSP_EnumToUTF16Keyword(CSP_NONCE)),
554 nsASCIICaseInsensitiveStringComparator) ||
555 mCurToken.Last() != SINGLEQUOTE) {
556 return nullptr;
559 // Trim surrounding single quotes
560 const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
562 int32_t dashIndex = expr.FindChar(DASH);
563 if (dashIndex < 0) {
564 return nullptr;
566 if (!isValidBase64Value(expr.BeginReading() + dashIndex + 1,
567 expr.EndReading())) {
568 return nullptr;
571 // cache if encountering hash or nonce to invalidate unsafe-inline
572 mHasHashOrNonce = true;
573 return new nsCSPNonceSrc(
574 Substring(expr, dashIndex + 1, expr.Length() - dashIndex + 1));
577 // hash-source = "'" hash-algo "-" base64-value "'"
578 nsCSPHashSrc* nsCSPParser::hashSource() {
579 CSPPARSERLOG(("nsCSPParser::hashSource, mCurToken: %s, mCurValue: %s",
580 NS_ConvertUTF16toUTF8(mCurToken).get(),
581 NS_ConvertUTF16toUTF8(mCurValue).get()));
583 // Check if mCurToken starts and ends with "'"
584 if (mCurToken.First() != SINGLEQUOTE || mCurToken.Last() != SINGLEQUOTE) {
585 return nullptr;
588 // Trim surrounding single quotes
589 const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
591 int32_t dashIndex = expr.FindChar(DASH);
592 if (dashIndex < 0) {
593 return nullptr;
596 if (!isValidBase64Value(expr.BeginReading() + dashIndex + 1,
597 expr.EndReading())) {
598 return nullptr;
601 nsAutoString algo(Substring(expr, 0, dashIndex));
602 nsAutoString hash(
603 Substring(expr, dashIndex + 1, expr.Length() - dashIndex + 1));
605 for (uint32_t i = 0; i < kHashSourceValidFnsLen; i++) {
606 if (algo.LowerCaseEqualsASCII(kHashSourceValidFns[i])) {
607 // cache if encountering hash or nonce to invalidate unsafe-inline
608 mHasHashOrNonce = true;
609 return new nsCSPHashSrc(algo, hash);
612 return nullptr;
615 // source-expression = scheme-source / host-source / keyword-source
616 // / nonce-source / hash-source
617 nsCSPBaseSrc* nsCSPParser::sourceExpression() {
618 CSPPARSERLOG(("nsCSPParser::sourceExpression, mCurToken: %s, mCurValue: %s",
619 NS_ConvertUTF16toUTF8(mCurToken).get(),
620 NS_ConvertUTF16toUTF8(mCurValue).get()));
622 // Check if it is a keyword
623 if (nsCSPBaseSrc* cspKeyword = keywordSource()) {
624 return cspKeyword;
627 // Check if it is a nonce-source
628 if (nsCSPNonceSrc* cspNonce = nonceSource()) {
629 return cspNonce;
632 // Check if it is a hash-source
633 if (nsCSPHashSrc* cspHash = hashSource()) {
634 return cspHash;
637 // We handle a single "*" as host here, to avoid any confusion when applying
638 // the default scheme. However, we still would need to apply the default
639 // scheme in case we would parse "*:80".
640 if (mCurToken.EqualsASCII("*")) {
641 return new nsCSPHostSrc(u"*"_ns);
644 // Calling resetCurChar allows us to use mCurChar and mEndChar
645 // to parse mCurToken; e.g. mCurToken = "http://www.example.com", then
646 // mCurChar = 'h'
647 // mEndChar = points just after the last 'm'
648 // mCurValue = ""
649 resetCurChar(mCurToken);
651 // Check if mCurToken starts with a scheme
652 nsAutoString parsedScheme;
653 if (nsCSPSchemeSrc* cspScheme = schemeSource()) {
654 // mCurToken might only enforce a specific scheme
655 if (atEnd()) {
656 return cspScheme;
658 // If something follows the scheme, we do not create
659 // a nsCSPSchemeSrc, but rather a nsCSPHostSrc, which
660 // needs to know the scheme to enforce; remember the
661 // scheme and delete cspScheme;
662 cspScheme->toString(parsedScheme);
663 parsedScheme.Trim(":", false, true);
664 delete cspScheme;
666 // If mCurToken provides not only a scheme, but also a host, we have to
667 // check if two slashes follow the scheme.
668 if (!accept(SLASH) || !accept(SLASH)) {
669 AutoTArray<nsString, 1> params = {mCurToken};
670 logWarningErrorToConsole(nsIScriptError::warningFlag,
671 "failedToParseUnrecognizedSource", params);
672 return nullptr;
676 // Calling resetCurValue allows us to keep pointers for mCurChar and mEndChar
677 // alive, but resets mCurValue; e.g. mCurToken = "http://www.example.com",
678 // then mCurChar = 'w' mEndChar = 'm' mCurValue = ""
679 resetCurValue();
681 // If mCurToken does not provide a scheme (scheme-less source), we apply the
682 // scheme from selfURI
683 if (parsedScheme.IsEmpty()) {
684 // Resetting internal helpers, because we might already have parsed some of
685 // the host when trying to parse a scheme.
686 resetCurChar(mCurToken);
687 nsAutoCString selfScheme;
688 mSelfURI->GetScheme(selfScheme);
689 parsedScheme.AssignASCII(selfScheme.get());
692 // At this point we are expecting a host to be parsed.
693 // Trying to create a new nsCSPHost.
694 if (nsCSPHostSrc* cspHost = hostSource()) {
695 // Do not forget to set the parsed scheme.
696 cspHost->setScheme(parsedScheme);
697 cspHost->setWithinFrameAncestorsDir(mParsingFrameAncestorsDir);
698 return cspHost;
700 // Error was reported in hostSource()
701 return nullptr;
704 // source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ]
705 // / *WSP "'none'" *WSP
706 void nsCSPParser::sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs) {
707 bool isNone = false;
709 // remember, srcs start at index 1
710 for (uint32_t i = 1; i < mCurDir.Length(); i++) {
711 // mCurToken is only set here and remains the current token
712 // to be processed, which avoid passing arguments between functions.
713 mCurToken = mCurDir[i];
714 resetCurValue();
716 CSPPARSERLOG(("nsCSPParser::sourceList, mCurToken: %s, mCurValue: %s",
717 NS_ConvertUTF16toUTF8(mCurToken).get(),
718 NS_ConvertUTF16toUTF8(mCurValue).get()));
720 // Special case handling for none:
721 // Ignore 'none' if any other src is available.
722 // (See http://www.w3.org/TR/CSP11/#parsing)
723 if (CSP_IsKeyword(mCurToken, CSP_NONE)) {
724 isNone = true;
725 continue;
727 // Must be a regular source expression
728 nsCSPBaseSrc* src = sourceExpression();
729 if (src) {
730 outSrcs.AppendElement(src);
734 // Check if the directive contains a 'none'
735 if (isNone) {
736 // If the directive contains no other srcs, then we set the 'none'
737 if (outSrcs.IsEmpty() ||
738 (outSrcs.Length() == 1 && outSrcs[0]->isReportSample())) {
739 nsCSPKeywordSrc* keyword = new nsCSPKeywordSrc(CSP_NONE);
740 outSrcs.InsertElementAt(0, keyword);
742 // Otherwise, we ignore 'none' and report a warning
743 else {
744 AutoTArray<nsString, 1> params;
745 params.AppendElement(CSP_EnumToUTF16Keyword(CSP_NONE));
746 logWarningErrorToConsole(nsIScriptError::warningFlag,
747 "ignoringUnknownOption", params);
752 void nsCSPParser::reportURIList(nsCSPDirective* aDir) {
753 CSPPARSERLOG(("nsCSPParser::reportURIList"));
755 nsTArray<nsCSPBaseSrc*> srcs;
756 nsCOMPtr<nsIURI> uri;
757 nsresult rv;
759 // remember, srcs start at index 1
760 for (uint32_t i = 1; i < mCurDir.Length(); i++) {
761 mCurToken = mCurDir[i];
763 CSPPARSERLOG(("nsCSPParser::reportURIList, mCurToken: %s, mCurValue: %s",
764 NS_ConvertUTF16toUTF8(mCurToken).get(),
765 NS_ConvertUTF16toUTF8(mCurValue).get()));
767 rv = NS_NewURI(getter_AddRefs(uri), mCurToken, "", mSelfURI);
769 // If creating the URI casued an error, skip this URI
770 if (NS_FAILED(rv)) {
771 AutoTArray<nsString, 1> params = {mCurToken};
772 logWarningErrorToConsole(nsIScriptError::warningFlag,
773 "couldNotParseReportURI", params);
774 continue;
777 // Create new nsCSPReportURI and append to the list.
778 nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
779 srcs.AppendElement(reportURI);
782 if (srcs.Length() == 0) {
783 AutoTArray<nsString, 1> directiveName = {mCurToken};
784 logWarningErrorToConsole(nsIScriptError::warningFlag,
785 "ignoringDirectiveWithNoValues", directiveName);
786 delete aDir;
787 return;
790 aDir->addSrcs(srcs);
791 mPolicy->addDirective(aDir);
794 /* Helper function for parsing sandbox flags. This function solely concatenates
795 * all the source list tokens (the sandbox flags) so the attribute parser
796 * (nsContentUtils::ParseSandboxAttributeToFlags) can parse them.
798 void nsCSPParser::sandboxFlagList(nsCSPDirective* aDir) {
799 CSPPARSERLOG(("nsCSPParser::sandboxFlagList"));
801 nsAutoString flags;
803 // remember, srcs start at index 1
804 for (uint32_t i = 1; i < mCurDir.Length(); i++) {
805 mCurToken = mCurDir[i];
807 CSPPARSERLOG(("nsCSPParser::sandboxFlagList, mCurToken: %s, mCurValue: %s",
808 NS_ConvertUTF16toUTF8(mCurToken).get(),
809 NS_ConvertUTF16toUTF8(mCurValue).get()));
811 if (!nsContentUtils::IsValidSandboxFlag(mCurToken)) {
812 AutoTArray<nsString, 1> params = {mCurToken};
813 logWarningErrorToConsole(nsIScriptError::warningFlag,
814 "couldntParseInvalidSandboxFlag", params);
815 continue;
818 flags.Append(mCurToken);
819 if (i != mCurDir.Length() - 1) {
820 flags.AppendLiteral(" ");
824 // Please note that the sandbox directive can exist
825 // by itself (not containing any flags).
826 nsTArray<nsCSPBaseSrc*> srcs;
827 srcs.AppendElement(new nsCSPSandboxFlags(flags));
828 aDir->addSrcs(srcs);
829 mPolicy->addDirective(aDir);
832 // directive-value = *( WSP / <VCHAR except ";" and ","> )
833 void nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs) {
834 CSPPARSERLOG(("nsCSPParser::directiveValue"));
836 // Just forward to sourceList
837 sourceList(outSrcs);
840 // directive-name = 1*( ALPHA / DIGIT / "-" )
841 nsCSPDirective* nsCSPParser::directiveName() {
842 CSPPARSERLOG(("nsCSPParser::directiveName, mCurToken: %s, mCurValue: %s",
843 NS_ConvertUTF16toUTF8(mCurToken).get(),
844 NS_ConvertUTF16toUTF8(mCurValue).get()));
846 // Check if it is a valid directive
847 CSPDirective directive = CSP_StringToCSPDirective(mCurToken);
848 if (directive == nsIContentSecurityPolicy::NO_DIRECTIVE) {
849 AutoTArray<nsString, 1> params = {mCurToken};
850 logWarningErrorToConsole(nsIScriptError::warningFlag,
851 "couldNotProcessUnknownDirective", params);
852 return nullptr;
855 // The directive 'reflected-xss' is part of CSP 1.1, see:
856 // http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss
857 // Currently we are not supporting that directive, hence we log a
858 // warning to the console and ignore the directive including its values.
859 if (directive == nsIContentSecurityPolicy::REFLECTED_XSS_DIRECTIVE) {
860 AutoTArray<nsString, 1> params = {mCurToken};
861 logWarningErrorToConsole(nsIScriptError::warningFlag,
862 "notSupportingDirective", params);
863 return nullptr;
866 // script-src-attr and script-scr-elem might have been disabled.
867 // Similarly style-src-{attr, elem}.
868 if (((directive == nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE ||
869 directive == nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) &&
870 !StaticPrefs::security_csp_script_src_attr_elem_enabled()) ||
871 ((directive == nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE ||
872 directive == nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) &&
873 !StaticPrefs::security_csp_style_src_attr_elem_enabled())) {
874 AutoTArray<nsString, 1> params = {mCurToken};
875 logWarningErrorToConsole(nsIScriptError::warningFlag,
876 "notSupportingDirective", params);
877 return nullptr;
880 // Bug 1529068: Implement navigate-to directive.
881 // Once all corner cases are resolved we can remove that special
882 // if-handling here and let the parser just fall through to
883 // return new nsCSPDirective.
884 if (directive == nsIContentSecurityPolicy::NAVIGATE_TO_DIRECTIVE &&
885 !StaticPrefs::security_csp_enableNavigateTo()) {
886 AutoTArray<nsString, 1> params = {mCurToken};
887 logWarningErrorToConsole(nsIScriptError::warningFlag,
888 "couldNotProcessUnknownDirective", params);
889 return nullptr;
892 // Make sure the directive does not already exist
893 // (see http://www.w3.org/TR/CSP11/#parsing)
894 if (mPolicy->hasDirective(directive)) {
895 AutoTArray<nsString, 1> params = {mCurToken};
896 logWarningErrorToConsole(nsIScriptError::warningFlag, "duplicateDirective",
897 params);
898 return nullptr;
901 // CSP delivered via meta tag should ignore the following directives:
902 // report-uri, frame-ancestors, and sandbox, see:
903 // http://www.w3.org/TR/CSP11/#delivery-html-meta-element
904 if (mDeliveredViaMetaTag &&
905 ((directive == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE) ||
906 (directive == nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE) ||
907 (directive == nsIContentSecurityPolicy::SANDBOX_DIRECTIVE))) {
908 // log to the console to indicate that meta CSP is ignoring the directive
909 AutoTArray<nsString, 1> params = {mCurToken};
910 logWarningErrorToConsole(nsIScriptError::warningFlag,
911 "ignoringSrcFromMetaCSP", params);
912 return nullptr;
915 // special case handling for block-all-mixed-content
916 if (directive == nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT) {
917 // If mixed content upgrade is enabled for all types block-all-mixed-content
918 // is obsolete
919 if (mozilla::StaticPrefs::
920 security_mixed_content_upgrade_display_content() &&
921 mozilla::StaticPrefs::
922 security_mixed_content_upgrade_display_content_image() &&
923 mozilla::StaticPrefs::
924 security_mixed_content_upgrade_display_content_audio() &&
925 mozilla::StaticPrefs::
926 security_mixed_content_upgrade_display_content_video()) {
927 // log to the console that if mixed content display upgrading is enabled
928 // block-all-mixed-content is obsolete.
929 AutoTArray<nsString, 1> params = {mCurToken};
930 logWarningErrorToConsole(nsIScriptError::warningFlag,
931 "obsoleteBlockAllMixedContent", params);
933 return new nsBlockAllMixedContentDirective(directive);
936 // special case handling for upgrade-insecure-requests
937 if (directive == nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE) {
938 return new nsUpgradeInsecureDirective(directive);
941 // if we have a child-src, cache it as a fallback for
942 // * workers (if worker-src is not explicitly specified)
943 // * frames (if frame-src is not explicitly specified)
944 if (directive == nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE) {
945 mChildSrc = new nsCSPChildSrcDirective(directive);
946 return mChildSrc;
949 // if we have a frame-src, cache it so we can discard child-src for frames
950 if (directive == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) {
951 mFrameSrc = new nsCSPDirective(directive);
952 return mFrameSrc;
955 // if we have a worker-src, cache it so we can discard child-src for workers
956 if (directive == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) {
957 mWorkerSrc = new nsCSPDirective(directive);
958 return mWorkerSrc;
961 // if we have a script-src, cache it as a fallback for worker-src
962 // in case child-src is not present. It is also used as a fallback for
963 // script-src-elem and script-src-attr.
964 if (directive == nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) {
965 mScriptSrc = new nsCSPScriptSrcDirective(directive);
966 return mScriptSrc;
969 // If we have a style-src, cache it as a fallback for style-src-elem and
970 // style-src-attr.
971 if (directive == nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) {
972 mStyleSrc = new nsCSPStyleSrcDirective(directive);
973 return mStyleSrc;
976 return new nsCSPDirective(directive);
979 // directive = *WSP [ directive-name [ WSP directive-value ] ]
980 void nsCSPParser::directive() {
981 // Set the directiveName to mCurToken
982 // Remember, the directive name is stored at index 0
983 mCurToken = mCurDir[0];
985 CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
986 NS_ConvertUTF16toUTF8(mCurToken).get(),
987 NS_ConvertUTF16toUTF8(mCurValue).get()));
989 // Make sure that the directive-srcs-array contains at least
990 // one directive.
991 if (mCurDir.Length() == 0) {
992 AutoTArray<nsString, 1> params = {u"directive missing"_ns};
993 logWarningErrorToConsole(nsIScriptError::warningFlag,
994 "failedToParseUnrecognizedSource", params);
995 return;
998 if (CSP_IsEmptyDirective(mCurValue, mCurToken)) {
999 return;
1002 // Try to create a new CSPDirective
1003 nsCSPDirective* cspDir = directiveName();
1004 if (!cspDir) {
1005 // if we can not create a CSPDirective, we can skip parsing the srcs for
1006 // that array
1007 return;
1010 // special case handling for block-all-mixed-content, which is only specified
1011 // by a directive name but does not include any srcs.
1012 if (cspDir->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
1013 if (mCurDir.Length() > 1) {
1014 AutoTArray<nsString, 1> params = {u"block-all-mixed-content"_ns};
1015 logWarningErrorToConsole(nsIScriptError::warningFlag,
1016 "ignoreSrcForDirective", params);
1018 // add the directive and return
1019 mPolicy->addDirective(cspDir);
1020 return;
1023 // special case handling for upgrade-insecure-requests, which is only
1024 // specified by a directive name but does not include any srcs.
1025 if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
1026 if (mCurDir.Length() > 1) {
1027 AutoTArray<nsString, 1> params = {u"upgrade-insecure-requests"_ns};
1028 logWarningErrorToConsole(nsIScriptError::warningFlag,
1029 "ignoreSrcForDirective", params);
1031 // add the directive and return
1032 mPolicy->addUpgradeInsecDir(
1033 static_cast<nsUpgradeInsecureDirective*>(cspDir));
1034 return;
1037 // special case handling for report-uri directive (since it doesn't contain
1038 // a valid source list but rather actual URIs)
1039 if (CSP_IsDirective(mCurDir[0],
1040 nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
1041 reportURIList(cspDir);
1042 return;
1045 // special case handling for sandbox directive (since it doe4sn't contain
1046 // a valid source list but rather special sandbox flags)
1047 if (CSP_IsDirective(mCurDir[0],
1048 nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
1049 sandboxFlagList(cspDir);
1050 return;
1053 // make sure to reset cache variables when trying to invalidate unsafe-inline;
1054 // unsafe-inline might not only appear in script-src, but also in default-src
1055 mHasHashOrNonce = false;
1056 mHasAnyUnsafeEval = false;
1057 mStrictDynamic = false;
1058 mUnsafeInlineKeywordSrc = nullptr;
1060 mParsingFrameAncestorsDir = CSP_IsDirective(
1061 mCurDir[0], nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE);
1063 // Try to parse all the srcs by handing the array off to directiveValue
1064 nsTArray<nsCSPBaseSrc*> srcs;
1065 directiveValue(srcs);
1067 // If we can not parse any srcs; we let the source expression be the empty set
1068 // ('none') see, http://www.w3.org/TR/CSP11/#source-list-parsing
1069 if (srcs.IsEmpty() || (srcs.Length() == 1 && srcs[0]->isReportSample())) {
1070 nsCSPKeywordSrc* keyword = new nsCSPKeywordSrc(CSP_NONE);
1071 srcs.InsertElementAt(0, keyword);
1074 // If policy contains 'strict-dynamic' warn about ignored sources.
1075 if (mStrictDynamic &&
1076 !CSP_IsDirective(mCurDir[0],
1077 nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE)) {
1078 for (uint32_t i = 0; i < srcs.Length(); i++) {
1079 nsAutoString srcStr;
1080 srcs[i]->toString(srcStr);
1081 // Hashes and nonces continue to apply with 'strict-dynamic', as well as
1082 // 'unsafe-eval', 'wasm-unsafe-eval' and 'unsafe-hashes'.
1083 if (!srcs[i]->isKeyword(CSP_STRICT_DYNAMIC) &&
1084 !srcs[i]->isKeyword(CSP_UNSAFE_EVAL) &&
1085 !srcs[i]->isKeyword(CSP_WASM_UNSAFE_EVAL) &&
1086 !srcs[i]->isKeyword(CSP_UNSAFE_HASHES) && !srcs[i]->isNonce() &&
1087 !srcs[i]->isHash()) {
1088 AutoTArray<nsString, 2> params = {srcStr, mCurDir[0]};
1089 logWarningErrorToConsole(nsIScriptError::warningFlag,
1090 "ignoringScriptSrcForStrictDynamic", params);
1094 // Log a warning that all scripts might be blocked because the policy
1095 // contains 'strict-dynamic' but no valid nonce or hash.
1096 if (!mHasHashOrNonce) {
1097 AutoTArray<nsString, 1> params = {mCurDir[0]};
1098 logWarningErrorToConsole(nsIScriptError::warningFlag,
1099 "strictDynamicButNoHashOrNonce", params);
1103 // From https://w3c.github.io/webappsec-csp/#allow-all-inline
1104 // follows that when either a hash or nonce is specified, 'unsafe-inline'
1105 // should not apply.
1106 if (mHasHashOrNonce && mUnsafeInlineKeywordSrc &&
1107 (cspDir->isDefaultDirective() ||
1108 cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
1109 cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
1110 cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE) ||
1111 cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE) ||
1112 cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE) ||
1113 cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE))) {
1114 // Log to the console that unsafe-inline will be ignored.
1115 AutoTArray<nsString, 2> params = {u"'unsafe-inline'"_ns, mCurDir[0]};
1116 logWarningErrorToConsole(nsIScriptError::warningFlag,
1117 "ignoringSrcWithinNonceOrHashDirective", params);
1120 if (mHasAnyUnsafeEval &&
1121 (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE) ||
1122 cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE))) {
1123 // Log to the console that (wasm-)unsafe-eval will be ignored.
1124 AutoTArray<nsString, 1> params = {mCurDir[0]};
1125 logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnsafeEval",
1126 params);
1129 // Add the newly created srcs to the directive and add the directive to the
1130 // policy
1131 cspDir->addSrcs(srcs);
1132 mPolicy->addDirective(cspDir);
1135 // policy = [ directive *( ";" [ directive ] ) ]
1136 nsCSPPolicy* nsCSPParser::policy() {
1137 CSPPARSERLOG(("nsCSPParser::policy"));
1139 mPolicy = new nsCSPPolicy();
1140 for (uint32_t i = 0; i < mTokens.Length(); i++) {
1141 // https://w3c.github.io/webappsec-csp/#parse-serialized-policy
1142 // Step 2.2. ..., or if token is not an ASCII string, continue.
1144 // Note: In the spec the token isn't split by whitespace yet.
1145 bool isAscii = true;
1146 for (const auto& token : mTokens[i]) {
1147 if (!IsAscii(token)) {
1148 AutoTArray<nsString, 1> params = {mTokens[i][0], token};
1149 logWarningErrorToConsole(nsIScriptError::warningFlag,
1150 "ignoringNonAsciiToken", params);
1151 isAscii = false;
1152 break;
1155 if (!isAscii) {
1156 continue;
1159 // All input is already tokenized; set one tokenized array in the form of
1160 // [ name, src, src, ... ]
1161 // to mCurDir and call directive which processes the current directive.
1162 mCurDir = mTokens[i].Clone();
1163 directive();
1166 if (mChildSrc) {
1167 if (!mFrameSrc) {
1168 // if frame-src is specified explicitly for that policy than child-src
1169 // should not restrict frames; if not, than child-src needs to restrict
1170 // frames.
1171 mChildSrc->setRestrictFrames();
1173 if (!mWorkerSrc) {
1174 // if worker-src is specified explicitly for that policy than child-src
1175 // should not restrict workers; if not, than child-src needs to restrict
1176 // workers.
1177 mChildSrc->setRestrictWorkers();
1181 // if script-src is specified, but not worker-src and also no child-src, then
1182 // script-src has to govern workers.
1183 if (mScriptSrc && !mWorkerSrc && !mChildSrc) {
1184 mScriptSrc->setRestrictWorkers();
1187 // If script-src is specified and script-src-elem is not specified, then
1188 // script-src has to govern script requests and script blocks.
1189 if (mScriptSrc && !mPolicy->hasDirective(
1190 nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE)) {
1191 mScriptSrc->setRestrictScriptElem();
1194 // If script-src is specified and script-src-attr is not specified, then
1195 // script-src has to govern script attr (event handlers).
1196 if (mScriptSrc && !mPolicy->hasDirective(
1197 nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE)) {
1198 mScriptSrc->setRestrictScriptAttr();
1201 // If style-src is specified and style-src-elem is not specified, then
1202 // style-src serves as a fallback.
1203 if (mStyleSrc && !mPolicy->hasDirective(
1204 nsIContentSecurityPolicy::STYLE_SRC_ELEM_DIRECTIVE)) {
1205 mStyleSrc->setRestrictStyleElem();
1208 // If style-src is specified and style-attr-elem is not specified, then
1209 // style-src serves as a fallback.
1210 if (mStyleSrc && !mPolicy->hasDirective(
1211 nsIContentSecurityPolicy::STYLE_SRC_ATTR_DIRECTIVE)) {
1212 mStyleSrc->setRestrictStyleAttr();
1215 return mPolicy;
1218 nsCSPPolicy* nsCSPParser::parseContentSecurityPolicy(
1219 const nsAString& aPolicyString, nsIURI* aSelfURI, bool aReportOnly,
1220 nsCSPContext* aCSPContext, bool aDeliveredViaMetaTag,
1221 bool aSuppressLogMessages) {
1222 if (CSPPARSERLOGENABLED()) {
1223 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, policy: %s",
1224 NS_ConvertUTF16toUTF8(aPolicyString).get()));
1225 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, selfURI: %s",
1226 aSelfURI->GetSpecOrDefault().get()));
1227 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, reportOnly: %s",
1228 (aReportOnly ? "true" : "false")));
1229 CSPPARSERLOG(
1230 ("nsCSPParser::parseContentSecurityPolicy, deliveredViaMetaTag: %s",
1231 (aDeliveredViaMetaTag ? "true" : "false")));
1234 NS_ASSERTION(aSelfURI, "Can not parseContentSecurityPolicy without aSelfURI");
1236 // Separate all input into tokens and store them in the form of:
1237 // [ [ name, src, src, ... ], [ name, src, src, ... ], ... ]
1238 // The tokenizer itself can not fail; all eventual errors
1239 // are detected in the parser itself.
1241 nsTArray<CopyableTArray<nsString> > tokens;
1242 PolicyTokenizer::tokenizePolicy(aPolicyString, tokens);
1244 nsCSPParser parser(tokens, aSelfURI, aCSPContext, aDeliveredViaMetaTag,
1245 aSuppressLogMessages);
1247 // Start the parser to generate a new CSPPolicy using the generated tokens.
1248 nsCSPPolicy* policy = parser.policy();
1250 // Check that report-only policies define a report-uri, otherwise log warning.
1251 if (aReportOnly) {
1252 policy->setReportOnlyFlag(true);
1253 if (!policy->hasDirective(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
1254 nsAutoCString prePath;
1255 nsresult rv = aSelfURI->GetPrePath(prePath);
1256 NS_ENSURE_SUCCESS(rv, policy);
1257 AutoTArray<nsString, 1> params;
1258 CopyUTF8toUTF16(prePath, *params.AppendElement());
1259 parser.logWarningErrorToConsole(nsIScriptError::warningFlag,
1260 "reportURInotInReportOnlyHeader", params);
1264 policy->setDeliveredViaMetaTagFlag(aDeliveredViaMetaTag);
1266 if (policy->getNumDirectives() == 0) {
1267 // Individual errors were already reported in the parser, but if
1268 // we do not have an enforcable directive at all, we return null.
1269 delete policy;
1270 return nullptr;
1273 if (CSPPARSERLOGENABLED()) {
1274 nsString parsedPolicy;
1275 policy->toString(parsedPolicy);
1276 CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, parsedPolicy: %s",
1277 NS_ConvertUTF16toUTF8(parsedPolicy).get()));
1280 return policy;