LCOV - code coverage report
Current view: top level - dom/security - nsCSPParser.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 599 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 42 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "mozilla/ArrayUtils.h"
       8             : #include "mozilla/Preferences.h"
       9             : #include "mozilla/SizePrintfMacros.h"
      10             : #include "nsCOMPtr.h"
      11             : #include "nsContentUtils.h"
      12             : #include "nsCSPParser.h"
      13             : #include "nsCSPUtils.h"
      14             : #include "nsIConsoleService.h"
      15             : #include "nsIContentPolicy.h"
      16             : #include "nsIScriptError.h"
      17             : #include "nsIStringBundle.h"
      18             : #include "nsNetUtil.h"
      19             : #include "nsReadableUtils.h"
      20             : #include "nsServiceManagerUtils.h"
      21             : #include "nsUnicharUtils.h"
      22             : #include "mozilla/net/ReferrerPolicy.h"
      23             : 
      24             : using namespace mozilla;
      25             : 
      26             : static LogModule*
      27           0 : GetCspParserLog()
      28             : {
      29             :   static LazyLogModule gCspParserPRLog("CSPParser");
      30           0 :   return gCspParserPRLog;
      31             : }
      32             : 
      33             : #define CSPPARSERLOG(args) MOZ_LOG(GetCspParserLog(), mozilla::LogLevel::Debug, args)
      34             : #define CSPPARSERLOGENABLED() MOZ_LOG_TEST(GetCspParserLog(), mozilla::LogLevel::Debug)
      35             : 
      36             : static const char16_t COLON        = ':';
      37             : static const char16_t SEMICOLON    = ';';
      38             : static const char16_t SLASH        = '/';
      39             : static const char16_t PLUS         = '+';
      40             : static const char16_t DASH         = '-';
      41             : static const char16_t DOT          = '.';
      42             : static const char16_t UNDERLINE    = '_';
      43             : static const char16_t TILDE        = '~';
      44             : static const char16_t WILDCARD     = '*';
      45             : static const char16_t SINGLEQUOTE  = '\'';
      46             : static const char16_t NUMBER_SIGN  = '#';
      47             : static const char16_t QUESTIONMARK = '?';
      48             : static const char16_t PERCENT_SIGN = '%';
      49             : static const char16_t EXCLAMATION  = '!';
      50             : static const char16_t DOLLAR       = '$';
      51             : static const char16_t AMPERSAND    = '&';
      52             : static const char16_t OPENBRACE    = '(';
      53             : static const char16_t CLOSINGBRACE = ')';
      54             : static const char16_t EQUALS       = '=';
      55             : static const char16_t ATSYMBOL     = '@';
      56             : 
      57             : static const uint32_t kSubHostPathCharacterCutoff = 512;
      58             : 
      59             : static const char *const kHashSourceValidFns [] = { "sha256", "sha384", "sha512" };
      60             : static const uint32_t kHashSourceValidFnsLen = 3;
      61             : 
      62             : static const char* const kStyle    = "style";
      63             : static const char* const kScript   = "script";
      64             : 
      65             : /* ===== nsCSPTokenizer ==================== */
      66             : 
      67           0 : nsCSPTokenizer::nsCSPTokenizer(const char16_t* aStart,
      68           0 :                                const char16_t* aEnd)
      69             :   : mCurChar(aStart)
      70           0 :   , mEndChar(aEnd)
      71             : {
      72           0 :   CSPPARSERLOG(("nsCSPTokenizer::nsCSPTokenizer"));
      73           0 : }
      74             : 
      75           0 : nsCSPTokenizer::~nsCSPTokenizer()
      76             : {
      77           0 :   CSPPARSERLOG(("nsCSPTokenizer::~nsCSPTokenizer"));
      78           0 : }
      79             : 
      80             : void
      81           0 : nsCSPTokenizer::generateNextToken()
      82             : {
      83           0 :   skipWhiteSpaceAndSemicolon();
      84           0 :   while (!atEnd() &&
      85           0 :          !nsContentUtils::IsHTMLWhitespace(*mCurChar) &&
      86           0 :          *mCurChar != SEMICOLON) {
      87           0 :     mCurToken.Append(*mCurChar++);
      88             :   }
      89           0 :   CSPPARSERLOG(("nsCSPTokenizer::generateNextToken: %s", NS_ConvertUTF16toUTF8(mCurToken).get()));
      90           0 : }
      91             : 
      92             : void
      93           0 : nsCSPTokenizer::generateTokens(cspTokens& outTokens)
      94             : {
      95           0 :   CSPPARSERLOG(("nsCSPTokenizer::generateTokens"));
      96             : 
      97             :   // dirAndSrcs holds one set of [ name, src, src, src, ... ]
      98           0 :   nsTArray <nsString> dirAndSrcs;
      99             : 
     100           0 :   while (!atEnd()) {
     101           0 :     generateNextToken();
     102           0 :     dirAndSrcs.AppendElement(mCurToken);
     103           0 :     skipWhiteSpace();
     104           0 :     if (atEnd() || accept(SEMICOLON)) {
     105           0 :       outTokens.AppendElement(dirAndSrcs);
     106           0 :       dirAndSrcs.Clear();
     107             :     }
     108             :   }
     109           0 : }
     110             : 
     111             : void
     112           0 : nsCSPTokenizer::tokenizeCSPPolicy(const nsAString &aPolicyString,
     113             :                                   cspTokens& outTokens)
     114             : {
     115           0 :   CSPPARSERLOG(("nsCSPTokenizer::tokenizeCSPPolicy"));
     116             : 
     117             :   nsCSPTokenizer tokenizer(aPolicyString.BeginReading(),
     118           0 :                            aPolicyString.EndReading());
     119             : 
     120           0 :   tokenizer.generateTokens(outTokens);
     121           0 : }
     122             : 
     123             : /* ===== nsCSPParser ==================== */
     124             : bool nsCSPParser::sCSPExperimentalEnabled = false;
     125             : bool nsCSPParser::sStrictDynamicEnabled = false;
     126             : 
     127           0 : nsCSPParser::nsCSPParser(cspTokens& aTokens,
     128             :                          nsIURI* aSelfURI,
     129             :                          nsCSPContext* aCSPContext,
     130           0 :                          bool aDeliveredViaMetaTag)
     131             :  : mCurChar(nullptr)
     132             :  , mEndChar(nullptr)
     133             :  , mHasHashOrNonce(false)
     134             :  , mStrictDynamic(false)
     135             :  , mUnsafeInlineKeywordSrc(nullptr)
     136             :  , mChildSrc(nullptr)
     137             :  , mFrameSrc(nullptr)
     138             :  , mParsingFrameAncestorsDir(false)
     139             :  , mTokens(aTokens)
     140             :  , mSelfURI(aSelfURI)
     141             :  , mPolicy(nullptr)
     142             :  , mCSPContext(aCSPContext)
     143           0 :  , mDeliveredViaMetaTag(aDeliveredViaMetaTag)
     144             : {
     145             :   static bool initialized = false;
     146           0 :   if (!initialized) {
     147           0 :     initialized = true;
     148           0 :     Preferences::AddBoolVarCache(&sCSPExperimentalEnabled, "security.csp.experimentalEnabled");
     149           0 :     Preferences::AddBoolVarCache(&sStrictDynamicEnabled, "security.csp.enableStrictDynamic");
     150             :   }
     151           0 :   CSPPARSERLOG(("nsCSPParser::nsCSPParser"));
     152           0 : }
     153             : 
     154           0 : nsCSPParser::~nsCSPParser()
     155             : {
     156           0 :   CSPPARSERLOG(("nsCSPParser::~nsCSPParser"));
     157           0 : }
     158             : 
     159             : static bool
     160           0 : isCharacterToken(char16_t aSymbol)
     161             : {
     162           0 :   return (aSymbol >= 'a' && aSymbol <= 'z') ||
     163           0 :          (aSymbol >= 'A' && aSymbol <= 'Z');
     164             : }
     165             : 
     166             : static bool
     167           0 : isNumberToken(char16_t aSymbol)
     168             : {
     169           0 :   return (aSymbol >= '0' && aSymbol <= '9');
     170             : }
     171             : 
     172             : static bool
     173           0 : isValidHexDig(char16_t aHexDig)
     174             : {
     175           0 :   return (isNumberToken(aHexDig) ||
     176           0 :           (aHexDig >= 'A' && aHexDig <= 'F') ||
     177           0 :           (aHexDig >= 'a' && aHexDig <= 'f'));
     178             : }
     179             : 
     180             : static bool
     181           0 : isValidBase64Value(const char16_t* cur, const char16_t* end)
     182             : {
     183             :   // Using grammar at https://w3c.github.io/webappsec-csp/#grammardef-nonce-source
     184             : 
     185             :   // May end with one or two =
     186           0 :   if (end > cur && *(end-1) == EQUALS) end--;
     187           0 :   if (end > cur && *(end-1) == EQUALS) end--;
     188             : 
     189             :   // Must have at least one character aside from any =
     190           0 :   if (end == cur) {
     191           0 :     return false;
     192             :   }
     193             : 
     194             :   // Rest must all be A-Za-z0-9+/-_
     195           0 :   for (; cur < end; ++cur) {
     196           0 :     if (!(isCharacterToken(*cur) || isNumberToken(*cur) ||
     197           0 :           *cur == PLUS || *cur == SLASH ||
     198           0 :           *cur == DASH || *cur == UNDERLINE)) {
     199           0 :       return false;
     200             :     }
     201             :   }
     202             : 
     203           0 :   return true;
     204             : }
     205             : 
     206             : void
     207           0 : nsCSPParser::resetCurChar(const nsAString& aToken)
     208             : {
     209           0 :   mCurChar = aToken.BeginReading();
     210           0 :   mEndChar = aToken.EndReading();
     211           0 :   resetCurValue();
     212           0 : }
     213             : 
     214             : // The path is terminated by the first question mark ("?") or
     215             : // number sign ("#") character, or by the end of the URI.
     216             : // http://tools.ietf.org/html/rfc3986#section-3.3
     217             : bool
     218           0 : nsCSPParser::atEndOfPath()
     219             : {
     220           0 :   return (atEnd() || peek(QUESTIONMARK) || peek(NUMBER_SIGN));
     221             : }
     222             : 
     223             : // unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
     224             : bool
     225           0 : nsCSPParser::atValidUnreservedChar()
     226             : {
     227           0 :   return (peek(isCharacterToken) || peek(isNumberToken) ||
     228           0 :           peek(DASH) || peek(DOT) ||
     229           0 :           peek(UNDERLINE) || peek(TILDE));
     230             : }
     231             : 
     232             : // sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
     233             : //                 / "*" / "+" / "," / ";" / "="
     234             : // Please note that even though ',' and ';' appear to be
     235             : // valid sub-delims according to the RFC production of paths,
     236             : // both can not appear here by itself, they would need to be
     237             : // pct-encoded in order to be part of the path.
     238             : bool
     239           0 : nsCSPParser::atValidSubDelimChar()
     240             : {
     241           0 :   return (peek(EXCLAMATION) || peek(DOLLAR) || peek(AMPERSAND) ||
     242           0 :           peek(SINGLEQUOTE) || peek(OPENBRACE) || peek(CLOSINGBRACE) ||
     243           0 :           peek(WILDCARD) || peek(PLUS) || peek(EQUALS));
     244             : }
     245             : 
     246             : // pct-encoded   = "%" HEXDIG HEXDIG
     247             : bool
     248           0 : nsCSPParser::atValidPctEncodedChar()
     249             : {
     250           0 :   const char16_t* pctCurChar = mCurChar;
     251             : 
     252           0 :   if ((pctCurChar + 2) >= mEndChar) {
     253             :     // string too short, can't be a valid pct-encoded char.
     254           0 :     return false;
     255             :   }
     256             : 
     257             :   // Any valid pct-encoding must follow the following format:
     258             :   // "% HEXDIG HEXDIG"
     259           0 :   if (PERCENT_SIGN != *pctCurChar ||
     260           0 :      !isValidHexDig(*(pctCurChar+1)) ||
     261           0 :      !isValidHexDig(*(pctCurChar+2))) {
     262           0 :     return false;
     263             :   }
     264           0 :   return true;
     265             : }
     266             : 
     267             : // pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
     268             : // http://tools.ietf.org/html/rfc3986#section-3.3
     269             : bool
     270           0 : nsCSPParser::atValidPathChar()
     271             : {
     272           0 :   return (atValidUnreservedChar() ||
     273           0 :           atValidSubDelimChar() ||
     274           0 :           atValidPctEncodedChar() ||
     275           0 :           peek(COLON) || peek(ATSYMBOL));
     276             : }
     277             : 
     278             : void
     279           0 : nsCSPParser::logWarningErrorToConsole(uint32_t aSeverityFlag,
     280             :                                       const char* aProperty,
     281             :                                       const char16_t* aParams[],
     282             :                                       uint32_t aParamsLength)
     283             : {
     284           0 :   CSPPARSERLOG(("nsCSPParser::logWarningErrorToConsole: %s", aProperty));
     285             :   // send console messages off to the context and let the context
     286             :   // deal with it (potentially messages need to be queued up)
     287           0 :   mCSPContext->logToConsole(NS_ConvertUTF8toUTF16(aProperty).get(),
     288             :                             aParams,
     289             :                             aParamsLength,
     290           0 :                             EmptyString(), // aSourceName
     291           0 :                             EmptyString(), // aSourceLine
     292             :                             0,             // aLineNumber
     293             :                             0,             // aColumnNumber
     294           0 :                             aSeverityFlag); // aFlags
     295           0 : }
     296             : 
     297             : bool
     298           0 : nsCSPParser::hostChar()
     299             : {
     300           0 :   if (atEnd()) {
     301           0 :     return false;
     302             :   }
     303           0 :   return accept(isCharacterToken) ||
     304           0 :          accept(isNumberToken) ||
     305           0 :          accept(DASH);
     306             : }
     307             : 
     308             : // (ALPHA / DIGIT / "+" / "-" / "." )
     309             : bool
     310           0 : nsCSPParser::schemeChar()
     311             : {
     312           0 :   if (atEnd()) {
     313           0 :     return false;
     314             :   }
     315           0 :   return accept(isCharacterToken) ||
     316           0 :          accept(isNumberToken) ||
     317           0 :          accept(PLUS) ||
     318           0 :          accept(DASH) ||
     319           0 :          accept(DOT);
     320             : }
     321             : 
     322             : // port = ":" ( 1*DIGIT / "*" )
     323             : bool
     324           0 : nsCSPParser::port()
     325             : {
     326           0 :   CSPPARSERLOG(("nsCSPParser::port, mCurToken: %s, mCurValue: %s",
     327             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     328             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     329             : 
     330             :   // Consume the COLON we just peeked at in houstSource
     331           0 :   accept(COLON);
     332             : 
     333             :   // Resetting current value since we start to parse a port now.
     334             :   // e.g; "http://www.example.com:8888" then we have already parsed
     335             :   // everything up to (including) ":";
     336           0 :   resetCurValue();
     337             : 
     338             :   // Port might be "*"
     339           0 :   if (accept(WILDCARD)) {
     340           0 :     return true;
     341             :   }
     342             : 
     343             :   // Port must start with a number
     344           0 :   if (!accept(isNumberToken)) {
     345           0 :     const char16_t* params[] = { mCurToken.get() };
     346           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParsePort",
     347           0 :                              params, ArrayLength(params));
     348           0 :     return false;
     349             :   }
     350             :   // Consume more numbers and set parsed port to the nsCSPHost
     351           0 :   while (accept(isNumberToken)) { /* consume */ }
     352           0 :   return true;
     353             : }
     354             : 
     355             : bool
     356           0 : nsCSPParser::subPath(nsCSPHostSrc* aCspHost)
     357             : {
     358           0 :   CSPPARSERLOG(("nsCSPParser::subPath, mCurToken: %s, mCurValue: %s",
     359             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     360             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     361             : 
     362             :   // Emergency exit to avoid endless loops in case a path in a CSP policy
     363             :   // is longer than 512 characters, or also to avoid endless loops
     364             :   // in case we are parsing unrecognized characters in the following loop.
     365           0 :   uint32_t charCounter = 0;
     366           0 :   nsString pctDecodedSubPath;
     367             : 
     368           0 :   while (!atEndOfPath()) {
     369           0 :     if (peek(SLASH)) {
     370             :       // before appendig any additional portion of a subpath we have to pct-decode
     371             :       // that portion of the subpath. atValidPathChar() already verified a correct
     372             :       // pct-encoding, now we can safely decode and append the decoded-sub path.
     373           0 :       CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
     374           0 :       aCspHost->appendPath(pctDecodedSubPath);
     375             :       // Resetting current value since we are appending parts of the path
     376             :       // to aCspHost, e.g; "http://www.example.com/path1/path2" then the
     377             :       // first part is "/path1", second part "/path2"
     378           0 :       resetCurValue();
     379             :     }
     380           0 :     else if (!atValidPathChar()) {
     381           0 :       const char16_t* params[] = { mCurToken.get() };
     382           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag,
     383             :                                "couldntParseInvalidSource",
     384           0 :                                params, ArrayLength(params));
     385           0 :       return false;
     386             :     }
     387             :     // potentially we have encountred a valid pct-encoded character in atValidPathChar();
     388             :     // if so, we have to account for "% HEXDIG HEXDIG" and advance the pointer past
     389             :     // the pct-encoded char.
     390           0 :     if (peek(PERCENT_SIGN)) {
     391           0 :       advance();
     392           0 :       advance();
     393             :     }
     394           0 :     advance();
     395           0 :     if (++charCounter > kSubHostPathCharacterCutoff) {
     396           0 :       return false;
     397             :     }
     398             :   }
     399             :   // before appendig any additional portion of a subpath we have to pct-decode
     400             :   // that portion of the subpath. atValidPathChar() already verified a correct
     401             :   // pct-encoding, now we can safely decode and append the decoded-sub path.
     402           0 :   CSP_PercentDecodeStr(mCurValue, pctDecodedSubPath);
     403           0 :   aCspHost->appendPath(pctDecodedSubPath);
     404           0 :   resetCurValue();
     405           0 :   return true;
     406             : }
     407             : 
     408             : bool
     409           0 : nsCSPParser::path(nsCSPHostSrc* aCspHost)
     410             : {
     411           0 :   CSPPARSERLOG(("nsCSPParser::path, mCurToken: %s, mCurValue: %s",
     412             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     413             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     414             : 
     415             :   // Resetting current value and forgetting everything we have parsed so far
     416             :   // e.g. parsing "http://www.example.com/path1/path2", then
     417             :   // "http://www.example.com" has already been parsed so far
     418             :   // forget about it.
     419           0 :   resetCurValue();
     420             : 
     421           0 :   if (!accept(SLASH)) {
     422           0 :     const char16_t* params[] = { mCurToken.get() };
     423           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource",
     424           0 :                              params, ArrayLength(params));
     425           0 :     return false;
     426             :   }
     427           0 :   if (atEndOfPath()) {
     428             :     // one slash right after host [port] is also considered a path, e.g.
     429             :     // www.example.com/ should result in www.example.com/
     430             :     // please note that we do not have to perform any pct-decoding here
     431             :     // because we are just appending a '/' and not any actual chars.
     432           0 :     aCspHost->appendPath(NS_LITERAL_STRING("/"));
     433           0 :     return true;
     434             :   }
     435             :   // path can begin with "/" but not "//"
     436             :   // see http://tools.ietf.org/html/rfc3986#section-3.3
     437           0 :   if (peek(SLASH)) {
     438           0 :     const char16_t* params[] = { mCurToken.get() };
     439           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidSource",
     440           0 :                              params, ArrayLength(params));
     441           0 :     return false;
     442             :   }
     443           0 :   return subPath(aCspHost);
     444             : }
     445             : 
     446             : bool
     447           0 : nsCSPParser::subHost()
     448             : {
     449           0 :   CSPPARSERLOG(("nsCSPParser::subHost, mCurToken: %s, mCurValue: %s",
     450             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     451             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     452             : 
     453             :   // Emergency exit to avoid endless loops in case a host in a CSP policy
     454             :   // is longer than 512 characters, or also to avoid endless loops
     455             :   // in case we are parsing unrecognized characters in the following loop.
     456           0 :   uint32_t charCounter = 0;
     457             : 
     458           0 :   while (!atEndOfPath() && !peek(COLON) && !peek(SLASH)) {
     459           0 :     ++charCounter;
     460           0 :     while (hostChar()) {
     461             :       /* consume */
     462           0 :       ++charCounter;
     463             :     }
     464           0 :     if (accept(DOT) && !hostChar()) {
     465           0 :       return false;
     466             :     }
     467           0 :     if (charCounter > kSubHostPathCharacterCutoff) {
     468           0 :       return false;
     469             :     }
     470             :   }
     471           0 :   return true;
     472             : }
     473             : 
     474             : // host = "*" / [ "*." ] 1*host-char *( "." 1*host-char )
     475             : nsCSPHostSrc*
     476           0 : nsCSPParser::host()
     477             : {
     478           0 :   CSPPARSERLOG(("nsCSPParser::host, mCurToken: %s, mCurValue: %s",
     479             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     480             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     481             : 
     482             :   // Check if the token starts with "*"; please remember that we handle
     483             :   // a single "*" as host in sourceExpression, but we still have to handle
     484             :   // the case where a scheme was defined, e.g., as:
     485             :   // "https://*", "*.example.com", "*:*", etc.
     486           0 :   if (accept(WILDCARD)) {
     487             :     // Might solely be the wildcard
     488           0 :     if (atEnd() || peek(COLON)) {
     489           0 :       return new nsCSPHostSrc(mCurValue);
     490             :     }
     491             :     // If the token is not only the "*", a "." must follow right after
     492           0 :     if (!accept(DOT)) {
     493           0 :       const char16_t* params[] = { mCurToken.get() };
     494           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost",
     495           0 :                                params, ArrayLength(params));
     496           0 :       return nullptr;
     497             :     }
     498             :   }
     499             : 
     500             :   // Expecting at least one host-char
     501           0 :   if (!hostChar()) {
     502           0 :     const char16_t* params[] = { mCurToken.get() };
     503           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost",
     504           0 :                              params, ArrayLength(params));
     505           0 :     return nullptr;
     506             :   }
     507             : 
     508             :   // There might be several sub hosts defined.
     509           0 :   if (!subHost()) {
     510           0 :     const char16_t* params[] = { mCurToken.get() };
     511           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldntParseInvalidHost",
     512           0 :                              params, ArrayLength(params));
     513           0 :     return nullptr;
     514             :   }
     515             : 
     516             :   // HostName might match a keyword, log to the console.
     517           0 :   if (CSP_IsQuotelessKeyword(mCurValue)) {
     518           0 :     nsString keyword = mCurValue;
     519           0 :     ToLowerCase(keyword);
     520           0 :     const char16_t* params[] = { mCurToken.get(), keyword.get() };
     521           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "hostNameMightBeKeyword",
     522           0 :                              params, ArrayLength(params));
     523             :   }
     524             : 
     525             :   // Create a new nsCSPHostSrc with the parsed host.
     526           0 :   return new nsCSPHostSrc(mCurValue);
     527             : }
     528             : 
     529             : // keyword-source = "'self'" / "'unsafe-inline'" / "'unsafe-eval'"
     530             : nsCSPBaseSrc*
     531           0 : nsCSPParser::keywordSource()
     532             : {
     533           0 :   CSPPARSERLOG(("nsCSPParser::keywordSource, mCurToken: %s, mCurValue: %s",
     534             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     535             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     536             : 
     537             :   // Special case handling for 'self' which is not stored internally as a keyword,
     538             :   // but rather creates a nsCSPHostSrc using the selfURI
     539           0 :   if (CSP_IsKeyword(mCurToken, CSP_SELF)) {
     540           0 :     return CSP_CreateHostSrcFromSelfURI(mSelfURI);
     541             :   }
     542             : 
     543           0 :   if (CSP_IsKeyword(mCurToken, CSP_STRICT_DYNAMIC)) {
     544             :     // make sure strict dynamic is enabled
     545           0 :     if (!sStrictDynamicEnabled) {
     546           0 :       return nullptr;
     547             :     }
     548           0 :     if (!CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE)) {
     549             :       // Todo: Enforce 'strict-dynamic' within default-src; see Bug 1313937
     550           0 :       const char16_t* params[] = { u"strict-dynamic" };
     551           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringStrictDynamic",
     552           0 :                                params, ArrayLength(params));
     553           0 :       return nullptr;
     554             :     }
     555           0 :     mStrictDynamic = true;
     556           0 :     return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
     557             :   }
     558             : 
     559           0 :   if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_INLINE)) {
     560           0 :       nsWeakPtr ctx = mCSPContext->GetLoadingContext();
     561           0 :       nsCOMPtr<nsIDocument> doc = do_QueryReferent(ctx);
     562           0 :       if (doc) {
     563           0 :         doc->SetHasUnsafeInlineCSP(true);
     564             :       }
     565             :     // make sure script-src only contains 'unsafe-inline' once;
     566             :     // ignore duplicates and log warning
     567           0 :     if (mUnsafeInlineKeywordSrc) {
     568           0 :       const char16_t* params[] = { mCurToken.get() };
     569           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDuplicateSrc",
     570           0 :                                params, ArrayLength(params));
     571           0 :       return nullptr;
     572             :     }
     573             :     // cache if we encounter 'unsafe-inline' so we can invalidate (ignore) it in
     574             :     // case that script-src directive also contains hash- or nonce-.
     575           0 :     mUnsafeInlineKeywordSrc = new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
     576           0 :     return mUnsafeInlineKeywordSrc;
     577             :   }
     578             : 
     579           0 :   if (CSP_IsKeyword(mCurToken, CSP_UNSAFE_EVAL)) {
     580           0 :     nsWeakPtr ctx = mCSPContext->GetLoadingContext();
     581           0 :     nsCOMPtr<nsIDocument> doc = do_QueryReferent(ctx);
     582           0 :     if (doc) {
     583           0 :       doc->SetHasUnsafeEvalCSP(true);
     584             :     }
     585           0 :     return new nsCSPKeywordSrc(CSP_KeywordToEnum(mCurToken));
     586             :   }
     587           0 :   return nullptr;
     588             : }
     589             : 
     590             : // host-source = [ scheme "://" ] host [ port ] [ path ]
     591             : nsCSPHostSrc*
     592           0 : nsCSPParser::hostSource()
     593             : {
     594           0 :   CSPPARSERLOG(("nsCSPParser::hostSource, mCurToken: %s, mCurValue: %s",
     595             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     596             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     597             : 
     598           0 :   nsCSPHostSrc* cspHost = host();
     599           0 :   if (!cspHost) {
     600             :     // Error was reported in host()
     601           0 :     return nullptr;
     602             :   }
     603             : 
     604             :   // Calling port() to see if there is a port to parse, if an error
     605             :   // occurs, port() reports the error, if port() returns true;
     606             :   // we have a valid port, so we add it to cspHost.
     607           0 :   if (peek(COLON)) {
     608           0 :     if (!port()) {
     609           0 :       delete cspHost;
     610           0 :       return nullptr;
     611             :     }
     612           0 :     cspHost->setPort(mCurValue);
     613             :   }
     614             : 
     615           0 :   if (atEndOfPath()) {
     616           0 :     return cspHost;
     617             :   }
     618             : 
     619             :   // Calling path() to see if there is a path to parse, if an error
     620             :   // occurs, path() reports the error; handing cspHost as an argument
     621             :   // which simplifies parsing of several paths.
     622           0 :   if (!path(cspHost)) {
     623             :     // If the host [port] is followed by a path, it has to be a valid path,
     624             :     // otherwise we pass the nullptr, indicating an error, up the callstack.
     625             :     // see also http://www.w3.org/TR/CSP11/#source-list
     626           0 :     delete cspHost;
     627           0 :     return nullptr;
     628             :   }
     629           0 :   return cspHost;
     630             : }
     631             : 
     632             : // scheme-source = scheme ":"
     633             : nsCSPSchemeSrc*
     634           0 : nsCSPParser::schemeSource()
     635             : {
     636           0 :   CSPPARSERLOG(("nsCSPParser::schemeSource, mCurToken: %s, mCurValue: %s",
     637             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     638             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     639             : 
     640           0 :   if (!accept(isCharacterToken)) {
     641           0 :     return nullptr;
     642             :   }
     643           0 :   while (schemeChar()) { /* consume */ }
     644           0 :   nsString scheme = mCurValue;
     645             : 
     646             :   // If the potential scheme is not followed by ":" - it's not a scheme
     647           0 :   if (!accept(COLON)) {
     648           0 :     return nullptr;
     649             :   }
     650             : 
     651             :   // If the chraracter following the ":" is a number or the "*"
     652             :   // then we are not parsing a scheme; but rather a host;
     653           0 :   if (peek(isNumberToken) || peek(WILDCARD)) {
     654           0 :     return nullptr;
     655             :   }
     656             : 
     657           0 :   return new nsCSPSchemeSrc(scheme);
     658             : }
     659             : 
     660             : // nonce-source = "'nonce-" nonce-value "'"
     661             : nsCSPNonceSrc*
     662           0 : nsCSPParser::nonceSource()
     663             : {
     664           0 :   CSPPARSERLOG(("nsCSPParser::nonceSource, mCurToken: %s, mCurValue: %s",
     665             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     666             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     667             : 
     668             :   // Check if mCurToken begins with "'nonce-" and ends with "'"
     669           0 :   if (!StringBeginsWith(mCurToken, NS_ConvertUTF8toUTF16(CSP_EnumToKeyword(CSP_NONCE)),
     670           0 :                         nsASCIICaseInsensitiveStringComparator()) ||
     671           0 :       mCurToken.Last() != SINGLEQUOTE) {
     672           0 :     return nullptr;
     673             :   }
     674             : 
     675             :   // Trim surrounding single quotes
     676           0 :   const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
     677             : 
     678           0 :   int32_t dashIndex = expr.FindChar(DASH);
     679           0 :   if (dashIndex < 0) {
     680           0 :     return nullptr;
     681             :   }
     682           0 :   if (!isValidBase64Value(expr.BeginReading() + dashIndex + 1, expr.EndReading())) {
     683           0 :     return nullptr;
     684             :   }
     685             : 
     686             :   // cache if encountering hash or nonce to invalidate unsafe-inline
     687           0 :   mHasHashOrNonce = true;
     688           0 :   return new nsCSPNonceSrc(Substring(expr,
     689           0 :                                      dashIndex + 1,
     690           0 :                                      expr.Length() - dashIndex + 1));
     691             : }
     692             : 
     693             : // hash-source = "'" hash-algo "-" base64-value "'"
     694             : nsCSPHashSrc*
     695           0 : nsCSPParser::hashSource()
     696             : {
     697           0 :   CSPPARSERLOG(("nsCSPParser::hashSource, mCurToken: %s, mCurValue: %s",
     698             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     699             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     700             : 
     701             :   // Check if mCurToken starts and ends with "'"
     702           0 :   if (mCurToken.First() != SINGLEQUOTE ||
     703           0 :       mCurToken.Last() != SINGLEQUOTE) {
     704           0 :     return nullptr;
     705             :   }
     706             : 
     707             :   // Trim surrounding single quotes
     708           0 :   const nsAString& expr = Substring(mCurToken, 1, mCurToken.Length() - 2);
     709             : 
     710           0 :   int32_t dashIndex = expr.FindChar(DASH);
     711           0 :   if (dashIndex < 0) {
     712           0 :     return nullptr;
     713             :   }
     714             : 
     715           0 :   if (!isValidBase64Value(expr.BeginReading() + dashIndex + 1, expr.EndReading())) {
     716           0 :     return nullptr;
     717             :   }
     718             : 
     719           0 :   nsAutoString algo(Substring(expr, 0, dashIndex));
     720           0 :   nsAutoString hash(Substring(expr, dashIndex + 1, expr.Length() - dashIndex + 1));
     721             : 
     722           0 :   for (uint32_t i = 0; i < kHashSourceValidFnsLen; i++) {
     723           0 :     if (algo.LowerCaseEqualsASCII(kHashSourceValidFns[i])) {
     724             :       // cache if encountering hash or nonce to invalidate unsafe-inline
     725           0 :       mHasHashOrNonce = true;
     726           0 :       return new nsCSPHashSrc(algo, hash);
     727             :     }
     728             :   }
     729           0 :   return nullptr;
     730             : }
     731             : 
     732             : // source-expression = scheme-source / host-source / keyword-source
     733             : //                     / nonce-source / hash-source
     734             : nsCSPBaseSrc*
     735           0 : nsCSPParser::sourceExpression()
     736             : {
     737           0 :   CSPPARSERLOG(("nsCSPParser::sourceExpression, mCurToken: %s, mCurValue: %s",
     738             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
     739             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
     740             : 
     741             :   // Check if it is a keyword
     742           0 :   if (nsCSPBaseSrc *cspKeyword = keywordSource()) {
     743           0 :     return cspKeyword;
     744             :   }
     745             : 
     746             :   // Check if it is a nonce-source
     747           0 :   if (nsCSPNonceSrc* cspNonce = nonceSource()) {
     748           0 :     return cspNonce;
     749             :   }
     750             : 
     751             :   // Check if it is a hash-source
     752           0 :   if (nsCSPHashSrc* cspHash = hashSource()) {
     753           0 :     return cspHash;
     754             :   }
     755             : 
     756             :   // We handle a single "*" as host here, to avoid any confusion when applying the default scheme.
     757             :   // However, we still would need to apply the default scheme in case we would parse "*:80".
     758           0 :   if (mCurToken.EqualsASCII("*")) {
     759           0 :     return new nsCSPHostSrc(NS_LITERAL_STRING("*"));
     760             :   }
     761             : 
     762             :   // Calling resetCurChar allows us to use mCurChar and mEndChar
     763             :   // to parse mCurToken; e.g. mCurToken = "http://www.example.com", then
     764             :   // mCurChar = 'h'
     765             :   // mEndChar = points just after the last 'm'
     766             :   // mCurValue = ""
     767           0 :   resetCurChar(mCurToken);
     768             : 
     769             :   // Check if mCurToken starts with a scheme
     770           0 :   nsAutoString parsedScheme;
     771           0 :   if (nsCSPSchemeSrc* cspScheme = schemeSource()) {
     772             :     // mCurToken might only enforce a specific scheme
     773           0 :     if (atEnd()) {
     774           0 :       return cspScheme;
     775             :     }
     776             :     // If something follows the scheme, we do not create
     777             :     // a nsCSPSchemeSrc, but rather a nsCSPHostSrc, which
     778             :     // needs to know the scheme to enforce; remember the
     779             :     // scheme and delete cspScheme;
     780           0 :     cspScheme->toString(parsedScheme);
     781           0 :     parsedScheme.Trim(":", false, true);
     782           0 :     delete cspScheme;
     783             : 
     784             :     // If mCurToken provides not only a scheme, but also a host, we have to check
     785             :     // if two slashes follow the scheme.
     786           0 :     if (!accept(SLASH) || !accept(SLASH)) {
     787           0 :       const char16_t* params[] = { mCurToken.get() };
     788           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
     789           0 :                                params, ArrayLength(params));
     790           0 :       return nullptr;
     791             :     }
     792             :   }
     793             : 
     794             :   // Calling resetCurValue allows us to keep pointers for mCurChar and mEndChar
     795             :   // alive, but resets mCurValue; e.g. mCurToken = "http://www.example.com", then
     796             :   // mCurChar = 'w'
     797             :   // mEndChar = 'm'
     798             :   // mCurValue = ""
     799           0 :   resetCurValue();
     800             : 
     801             :   // If mCurToken does not provide a scheme (scheme-less source), we apply the scheme
     802             :   // from selfURI
     803           0 :   if (parsedScheme.IsEmpty()) {
     804             :     // Resetting internal helpers, because we might already have parsed some of the host
     805             :     // when trying to parse a scheme.
     806           0 :     resetCurChar(mCurToken);
     807           0 :     nsAutoCString selfScheme;
     808           0 :     mSelfURI->GetScheme(selfScheme);
     809           0 :     parsedScheme.AssignASCII(selfScheme.get());
     810             :   }
     811             : 
     812             :   // At this point we are expecting a host to be parsed.
     813             :   // Trying to create a new nsCSPHost.
     814           0 :   if (nsCSPHostSrc *cspHost = hostSource()) {
     815             :     // Do not forget to set the parsed scheme.
     816           0 :     cspHost->setScheme(parsedScheme);
     817           0 :     cspHost->setWithinFrameAncestorsDir(mParsingFrameAncestorsDir);
     818           0 :     return cspHost;
     819             :   }
     820             :   // Error was reported in hostSource()
     821           0 :   return nullptr;
     822             : }
     823             : 
     824             : // source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ]
     825             : //               / *WSP "'none'" *WSP
     826             : void
     827           0 : nsCSPParser::sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs)
     828             : {
     829           0 :   bool isNone = false;
     830             : 
     831             :   // remember, srcs start at index 1
     832           0 :   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     833             :     // mCurToken is only set here and remains the current token
     834             :     // to be processed, which avoid passing arguments between functions.
     835           0 :     mCurToken = mCurDir[i];
     836           0 :     resetCurValue();
     837             : 
     838           0 :     CSPPARSERLOG(("nsCSPParser::sourceList, mCurToken: %s, mCurValue: %s",
     839             :                  NS_ConvertUTF16toUTF8(mCurToken).get(),
     840             :                  NS_ConvertUTF16toUTF8(mCurValue).get()));
     841             : 
     842             :     // Special case handling for none:
     843             :     // Ignore 'none' if any other src is available.
     844             :     // (See http://www.w3.org/TR/CSP11/#parsing)
     845           0 :     if (CSP_IsKeyword(mCurToken, CSP_NONE)) {
     846           0 :       isNone = true;
     847           0 :       continue;
     848             :     }
     849             :     // Must be a regular source expression
     850           0 :     nsCSPBaseSrc* src = sourceExpression();
     851           0 :     if (src) {
     852           0 :       outSrcs.AppendElement(src);
     853             :     }
     854             :   }
     855             : 
     856             :   // Check if the directive contains a 'none'
     857           0 :   if (isNone) {
     858             :     // If the directive contains no other srcs, then we set the 'none'
     859           0 :     if (outSrcs.Length() == 0) {
     860           0 :       nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
     861           0 :       outSrcs.AppendElement(keyword);
     862             :     }
     863             :     // Otherwise, we ignore 'none' and report a warning
     864             :     else {
     865           0 :       NS_ConvertUTF8toUTF16 unicodeNone(CSP_EnumToKeyword(CSP_NONE));
     866           0 :       const char16_t* params[] = { unicodeNone.get() };
     867           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringUnknownOption",
     868           0 :                                params, ArrayLength(params));
     869             :     }
     870             :   }
     871           0 : }
     872             : 
     873             : void
     874           0 : nsCSPParser::referrerDirectiveValue(nsCSPDirective* aDir)
     875             : {
     876             :   // directive-value   = "none" / "none-when-downgrade" / "origin" / "origin-when-cross-origin" / "unsafe-url"
     877             :   // directive name is token 0, we need to examine the remaining tokens (and
     878             :   // there should only be one token in the value).
     879           0 :   CSPPARSERLOG(("nsCSPParser::referrerDirectiveValue"));
     880             : 
     881           0 :   if (mCurDir.Length() != 2) {
     882           0 :     CSPPARSERLOG(("Incorrect number of tokens in referrer directive, got %" PRIuSIZE " expected 1",
     883             :                  mCurDir.Length() - 1));
     884           0 :     delete aDir;
     885           0 :     return;
     886             :   }
     887             : 
     888           0 :   if (!mozilla::net::IsValidReferrerPolicy(mCurDir[1])) {
     889           0 :     CSPPARSERLOG(("invalid value for referrer directive: %s",
     890             :                   NS_ConvertUTF16toUTF8(mCurDir[1]).get()));
     891           0 :     delete aDir;
     892           0 :     return;
     893             :   }
     894             : 
     895             :   //referrer-directive deprecation warning
     896           0 :   const char16_t* params[] = { mCurDir[1].get() };
     897           0 :   logWarningErrorToConsole(nsIScriptError::warningFlag, "deprecatedReferrerDirective",
     898           0 :                              params, ArrayLength(params));
     899             : 
     900             :   // the referrer policy is valid, so go ahead and use it.
     901           0 :   nsWeakPtr ctx = mCSPContext->GetLoadingContext();
     902           0 :   nsCOMPtr<nsIDocument> doc = do_QueryReferent(ctx);
     903           0 :   if (doc) {
     904           0 :     doc->SetHasReferrerPolicyCSP(true);
     905             :   }
     906           0 :   mPolicy->setReferrerPolicy(&mCurDir[1]);
     907           0 :   mPolicy->addDirective(aDir);
     908             : }
     909             : 
     910             : void
     911           0 : nsCSPParser::requireSRIForDirectiveValue(nsRequireSRIForDirective* aDir)
     912             : {
     913           0 :   CSPPARSERLOG(("nsCSPParser::requireSRIForDirectiveValue"));
     914             : 
     915             :   // directive-value = "style" / "script"
     916             :   // directive name is token 0, we need to examine the remaining tokens
     917           0 :   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     918             :     // mCurToken is only set here and remains the current token
     919             :     // to be processed, which avoid passing arguments between functions.
     920           0 :     mCurToken = mCurDir[i];
     921           0 :     resetCurValue();
     922           0 :     CSPPARSERLOG(("nsCSPParser:::directive (require-sri-for directive), "
     923             :                   "mCurToken: %s (valid), mCurValue: %s",
     924             :                   NS_ConvertUTF16toUTF8(mCurToken).get(),
     925             :                   NS_ConvertUTF16toUTF8(mCurValue).get()));
     926             :     // add contentPolicyTypes to the CSP's required-SRI list for this token
     927           0 :     if (mCurToken.LowerCaseEqualsASCII(kScript)) {
     928           0 :       aDir->addType(nsIContentPolicy::TYPE_SCRIPT);
     929             :     }
     930           0 :     else if (mCurToken.LowerCaseEqualsASCII(kStyle)) {
     931           0 :       aDir->addType(nsIContentPolicy::TYPE_STYLESHEET);
     932             :     } else {
     933           0 :       const char16_t* invalidTokenName[] = { mCurToken.get() };
     934           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
     935           0 :                             invalidTokenName, ArrayLength(invalidTokenName));
     936           0 :       CSPPARSERLOG(("nsCSPParser:::directive (require-sri-for directive), "
     937             :                     "mCurToken: %s (invalid), mCurValue: %s",
     938             :                     NS_ConvertUTF16toUTF8(mCurToken).get(),
     939             :                     NS_ConvertUTF16toUTF8(mCurValue).get()));
     940             :     }
     941             :   }
     942             : 
     943           0 :   if (!(aDir->hasType(nsIContentPolicy::TYPE_STYLESHEET)) &&
     944           0 :       !(aDir->hasType(nsIContentPolicy::TYPE_SCRIPT))) {
     945           0 :     const char16_t* directiveName[] = { mCurToken.get() };
     946           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDirectiveWithNoValues",
     947           0 :                                directiveName, ArrayLength(directiveName));
     948           0 :     delete aDir;
     949           0 :     return;
     950             :   }
     951             : 
     952           0 :   mPolicy->addDirective(aDir);
     953             : }
     954             : 
     955             : void
     956           0 : nsCSPParser::reportURIList(nsCSPDirective* aDir)
     957             : {
     958           0 :   CSPPARSERLOG(("nsCSPParser::reportURIList"));
     959             : 
     960           0 :   nsTArray<nsCSPBaseSrc*> srcs;
     961           0 :   nsCOMPtr<nsIURI> uri;
     962             :   nsresult rv;
     963             : 
     964             :   // remember, srcs start at index 1
     965           0 :   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     966           0 :     mCurToken = mCurDir[i];
     967             : 
     968           0 :     CSPPARSERLOG(("nsCSPParser::reportURIList, mCurToken: %s, mCurValue: %s",
     969             :                  NS_ConvertUTF16toUTF8(mCurToken).get(),
     970             :                  NS_ConvertUTF16toUTF8(mCurValue).get()));
     971             : 
     972           0 :     rv = NS_NewURI(getter_AddRefs(uri), mCurToken, "", mSelfURI);
     973             : 
     974             :     // If creating the URI casued an error, skip this URI
     975           0 :     if (NS_FAILED(rv)) {
     976           0 :       const char16_t* params[] = { mCurToken.get() };
     977           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotParseReportURI",
     978           0 :                                params, ArrayLength(params));
     979           0 :       continue;
     980             :     }
     981             : 
     982             :     // Create new nsCSPReportURI and append to the list.
     983           0 :     nsCSPReportURI* reportURI = new nsCSPReportURI(uri);
     984           0 :     srcs.AppendElement(reportURI);
     985             :   }
     986             : 
     987           0 :   if (srcs.Length() == 0) {
     988           0 :     const char16_t* directiveName[] = { mCurToken.get() };
     989           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDirectiveWithNoValues",
     990           0 :                              directiveName, ArrayLength(directiveName));
     991           0 :     delete aDir;
     992           0 :     return;
     993             :   }
     994             : 
     995           0 :   aDir->addSrcs(srcs);
     996           0 :   mPolicy->addDirective(aDir);
     997             : }
     998             : 
     999             : /* Helper function for parsing sandbox flags. This function solely concatenates
    1000             :  * all the source list tokens (the sandbox flags) so the attribute parser
    1001             :  * (nsContentUtils::ParseSandboxAttributeToFlags) can parse them.
    1002             :  */
    1003             : void
    1004           0 : nsCSPParser::sandboxFlagList(nsCSPDirective* aDir)
    1005             : {
    1006           0 :   CSPPARSERLOG(("nsCSPParser::sandboxFlagList"));
    1007             : 
    1008           0 :   nsAutoString flags;
    1009             : 
    1010             :   // remember, srcs start at index 1
    1011           0 :   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
    1012           0 :     mCurToken = mCurDir[i];
    1013             : 
    1014           0 :     CSPPARSERLOG(("nsCSPParser::sandboxFlagList, mCurToken: %s, mCurValue: %s",
    1015             :                  NS_ConvertUTF16toUTF8(mCurToken).get(),
    1016             :                  NS_ConvertUTF16toUTF8(mCurValue).get()));
    1017             : 
    1018           0 :     if (!nsContentUtils::IsValidSandboxFlag(mCurToken)) {
    1019           0 :       const char16_t* params[] = { mCurToken.get() };
    1020           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag,
    1021             :                                "couldntParseInvalidSandboxFlag",
    1022           0 :                                params, ArrayLength(params));
    1023           0 :       continue;
    1024             :     }
    1025             : 
    1026           0 :     flags.Append(mCurToken);
    1027           0 :     if (i != mCurDir.Length() - 1) {
    1028           0 :       flags.AppendASCII(" ");
    1029             :     }
    1030             :   }
    1031             : 
    1032             :   // Please note that the sandbox directive can exist
    1033             :   // by itself (not containing any flags).
    1034           0 :   nsTArray<nsCSPBaseSrc*> srcs;
    1035           0 :   srcs.AppendElement(new nsCSPSandboxFlags(flags));
    1036           0 :   aDir->addSrcs(srcs);
    1037           0 :   mPolicy->addDirective(aDir);
    1038           0 : }
    1039             : 
    1040             : // directive-value = *( WSP / <VCHAR except ";" and ","> )
    1041             : void
    1042           0 : nsCSPParser::directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs)
    1043             : {
    1044           0 :   CSPPARSERLOG(("nsCSPParser::directiveValue"));
    1045             : 
    1046             :   // Just forward to sourceList
    1047           0 :   sourceList(outSrcs);
    1048           0 : }
    1049             : 
    1050             : // directive-name = 1*( ALPHA / DIGIT / "-" )
    1051             : nsCSPDirective*
    1052           0 : nsCSPParser::directiveName()
    1053             : {
    1054           0 :   CSPPARSERLOG(("nsCSPParser::directiveName, mCurToken: %s, mCurValue: %s",
    1055             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
    1056             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
    1057             : 
    1058             :   // Check if it is a valid directive
    1059           0 :   if (!CSP_IsValidDirective(mCurToken) ||
    1060           0 :        (!sCSPExperimentalEnabled &&
    1061           0 :          CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REQUIRE_SRI_FOR))) {
    1062           0 :     const char16_t* params[] = { mCurToken.get() };
    1063           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "couldNotProcessUnknownDirective",
    1064           0 :                              params, ArrayLength(params));
    1065           0 :     return nullptr;
    1066             :   }
    1067             : 
    1068             :   // The directive 'reflected-xss' is part of CSP 1.1, see:
    1069             :   // http://www.w3.org/TR/2014/WD-CSP11-20140211/#reflected-xss
    1070             :   // Currently we are not supporting that directive, hence we log a
    1071             :   // warning to the console and ignore the directive including its values.
    1072           0 :   if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REFLECTED_XSS_DIRECTIVE)) {
    1073           0 :     const char16_t* params[] = { mCurToken.get() };
    1074           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "notSupportingDirective",
    1075           0 :                              params, ArrayLength(params));
    1076           0 :     return nullptr;
    1077             :   }
    1078             : 
    1079             :   // Make sure the directive does not already exist
    1080             :   // (see http://www.w3.org/TR/CSP11/#parsing)
    1081           0 :   if (mPolicy->hasDirective(CSP_StringToCSPDirective(mCurToken))) {
    1082           0 :     const char16_t* params[] = { mCurToken.get() };
    1083           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "duplicateDirective",
    1084           0 :                              params, ArrayLength(params));
    1085           0 :     return nullptr;
    1086             :   }
    1087             : 
    1088             :   // CSP delivered via meta tag should ignore the following directives:
    1089             :   // report-uri, frame-ancestors, and sandbox, see:
    1090             :   // http://www.w3.org/TR/CSP11/#delivery-html-meta-element
    1091           0 :   if (mDeliveredViaMetaTag &&
    1092           0 :        ((CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) ||
    1093           0 :         (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE)) ||
    1094           0 :         (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)))) {
    1095             :     // log to the console to indicate that meta CSP is ignoring the directive
    1096           0 :     const char16_t* params[] = { mCurToken.get() };
    1097           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag,
    1098             :                              "ignoringSrcFromMetaCSP",
    1099           0 :                              params, ArrayLength(params));
    1100           0 :     return nullptr;
    1101             :   }
    1102             : 
    1103             :   // special case handling for block-all-mixed-content
    1104           0 :   if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
    1105           0 :     return new nsBlockAllMixedContentDirective(CSP_StringToCSPDirective(mCurToken));
    1106             :   }
    1107             : 
    1108             :   // special case handling for upgrade-insecure-requests
    1109           0 :   if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
    1110           0 :     return new nsUpgradeInsecureDirective(CSP_StringToCSPDirective(mCurToken));
    1111             :   }
    1112             : 
    1113             :   // child-src has it's own class to handle frame-src if necessary
    1114           0 :   if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE)) {
    1115           0 :     mChildSrc = new nsCSPChildSrcDirective(CSP_StringToCSPDirective(mCurToken));
    1116           0 :     return mChildSrc;
    1117             :   }
    1118             : 
    1119             :   // if we have a frame-src, cache it so we can decide whether to use child-src
    1120           0 :   if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE)) {
    1121           0 :     const char16_t* params[] = { mCurToken.get(), u"child-src" };
    1122           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "deprecatedDirective",
    1123           0 :                              params, ArrayLength(params));
    1124           0 :     mFrameSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
    1125           0 :     return mFrameSrc;
    1126             :   }
    1127             : 
    1128           0 :   if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
    1129           0 :     return new nsRequireSRIForDirective(CSP_StringToCSPDirective(mCurToken));
    1130             :   }
    1131             : 
    1132           0 :   return new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
    1133             : }
    1134             : 
    1135             : // directive = *WSP [ directive-name [ WSP directive-value ] ]
    1136             : void
    1137           0 : nsCSPParser::directive()
    1138             : {
    1139             :   // Set the directiveName to mCurToken
    1140             :   // Remember, the directive name is stored at index 0
    1141           0 :   mCurToken = mCurDir[0];
    1142             : 
    1143           0 :   CSPPARSERLOG(("nsCSPParser::directive, mCurToken: %s, mCurValue: %s",
    1144             :                NS_ConvertUTF16toUTF8(mCurToken).get(),
    1145             :                NS_ConvertUTF16toUTF8(mCurValue).get()));
    1146             : 
    1147             :   // Make sure that the directive-srcs-array contains at least
    1148             :   // one directive and one src.
    1149           0 :   if (mCurDir.Length() < 1) {
    1150           0 :     const char16_t* params[] = { u"directive missing" };
    1151           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
    1152           0 :                              params, ArrayLength(params));
    1153           0 :     return;
    1154             :   }
    1155             : 
    1156             :   // Try to create a new CSPDirective
    1157           0 :   nsCSPDirective* cspDir = directiveName();
    1158           0 :   if (!cspDir) {
    1159             :     // if we can not create a CSPDirective, we can skip parsing the srcs for that array
    1160           0 :     return;
    1161             :   }
    1162             : 
    1163             :   // special case handling for block-all-mixed-content, which is only specified
    1164             :   // by a directive name but does not include any srcs.
    1165           0 :   if (cspDir->equals(nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
    1166           0 :     if (mCurDir.Length() > 1) {
    1167           0 :       const char16_t* params[] = { u"block-all-mixed-content" };
    1168           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag,
    1169             :                                "ignoreSrcForDirective",
    1170           0 :                                params, ArrayLength(params));
    1171             :     }
    1172             :     // add the directive and return
    1173           0 :     mPolicy->addDirective(cspDir);
    1174           0 :     return;
    1175             :   }
    1176             : 
    1177             :   // special case handling for upgrade-insecure-requests, which is only specified
    1178             :   // by a directive name but does not include any srcs.
    1179           0 :   if (cspDir->equals(nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
    1180           0 :     if (mCurDir.Length() > 1) {
    1181           0 :       const char16_t* params[] = { u"upgrade-insecure-requests" };
    1182           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag,
    1183             :                                "ignoreSrcForDirective",
    1184           0 :                                params, ArrayLength(params));
    1185             :     }
    1186             :     // add the directive and return
    1187           0 :     mPolicy->addUpgradeInsecDir(static_cast<nsUpgradeInsecureDirective*>(cspDir));
    1188           0 :     return;
    1189             :   }
    1190             : 
    1191             :   // special case handling for require-sri-for, which has directive values that
    1192             :   // are well-defined tokens but are not sources
    1193           0 :   if (cspDir->equals(nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
    1194           0 :     requireSRIForDirectiveValue(static_cast<nsRequireSRIForDirective*>(cspDir));
    1195           0 :     return;
    1196             :   }
    1197             : 
    1198             :   // special case handling of the referrer directive (since it doesn't contain
    1199             :   // source lists)
    1200           0 :   if (cspDir->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
    1201           0 :     referrerDirectiveValue(cspDir);
    1202           0 :     return;
    1203             :   }
    1204             : 
    1205             :   // special case handling for report-uri directive (since it doesn't contain
    1206             :   // a valid source list but rather actual URIs)
    1207           0 :   if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
    1208           0 :     reportURIList(cspDir);
    1209           0 :     return;
    1210             :   }
    1211             : 
    1212             :   // special case handling for sandbox directive (since it doe4sn't contain
    1213             :   // a valid source list but rather special sandbox flags)
    1214           0 :   if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
    1215           0 :     sandboxFlagList(cspDir);
    1216           0 :     return;
    1217             :   }
    1218             : 
    1219             :   // make sure to reset cache variables when trying to invalidate unsafe-inline;
    1220             :   // unsafe-inline might not only appear in script-src, but also in default-src
    1221           0 :   mHasHashOrNonce = false;
    1222           0 :   mStrictDynamic = false;
    1223           0 :   mUnsafeInlineKeywordSrc = nullptr;
    1224             : 
    1225           0 :   mParsingFrameAncestorsDir =
    1226           0 :     CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE);
    1227             : 
    1228             :   // Try to parse all the srcs by handing the array off to directiveValue
    1229           0 :   nsTArray<nsCSPBaseSrc*> srcs;
    1230           0 :   directiveValue(srcs);
    1231             : 
    1232             :   // If we can not parse any srcs; we let the source expression be the empty set ('none')
    1233             :   // see, http://www.w3.org/TR/CSP11/#source-list-parsing
    1234           0 :   if (srcs.Length() == 0) {
    1235           0 :     nsCSPKeywordSrc *keyword = new nsCSPKeywordSrc(CSP_NONE);
    1236           0 :     srcs.AppendElement(keyword);
    1237             :   }
    1238             : 
    1239             :   // If policy contains 'strict-dynamic' invalidate all srcs within script-src.
    1240           0 :   if (mStrictDynamic) {
    1241           0 :     MOZ_ASSERT(cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE),
    1242             :                "strict-dynamic only allowed within script-src");
    1243           0 :     for (uint32_t i = 0; i < srcs.Length(); i++) {
    1244             :       // Please note that nsCSPNonceSrc as well as nsCSPHashSrc overwrite invalidate(),
    1245             :       // so it's fine to just call invalidate() on all srcs. Please also note that
    1246             :       // nsCSPKeywordSrc() can not be invalidated and always returns false unless the
    1247             :       // keyword is 'strict-dynamic' in which case we allow the load if the script is
    1248             :       // not parser created!
    1249           0 :       srcs[i]->invalidate();
    1250             :       // Log a message to the console that src will be ignored.
    1251           0 :       nsAutoString srcStr;
    1252           0 :       srcs[i]->toString(srcStr);
    1253             :       // Even though we invalidate all of the srcs internally, we don't want to log
    1254             :       // messages for the srcs: (1) strict-dynamic, (2) unsafe-inline,
    1255             :       // (3) nonces, and (4) hashes
    1256           0 :       if (!srcStr.EqualsASCII(CSP_EnumToKeyword(CSP_STRICT_DYNAMIC)) &&
    1257           0 :           !srcStr.EqualsASCII(CSP_EnumToKeyword(CSP_UNSAFE_EVAL)) &&
    1258           0 :           !StringBeginsWith(NS_ConvertUTF16toUTF8(srcStr), NS_LITERAL_CSTRING("'nonce-")) &&
    1259           0 :           !StringBeginsWith(NS_ConvertUTF16toUTF8(srcStr), NS_LITERAL_CSTRING("'sha")))
    1260             :       {
    1261           0 :         const char16_t* params[] = { srcStr.get() };
    1262           0 :         logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcForStrictDynamic",
    1263           0 :                                  params, ArrayLength(params));
    1264             :       }
    1265             :     }
    1266             :     // Log a warning that all scripts might be blocked because the policy contains
    1267             :     // 'strict-dynamic' but no valid nonce or hash.
    1268           0 :     if (!mHasHashOrNonce) {
    1269           0 :       const char16_t* params[] = { mCurDir[0].get() };
    1270           0 :       logWarningErrorToConsole(nsIScriptError::warningFlag, "strictDynamicButNoHashOrNonce",
    1271           0 :                                params, ArrayLength(params));
    1272             :     }
    1273             :   }
    1274           0 :   else if (mHasHashOrNonce && mUnsafeInlineKeywordSrc &&
    1275           0 :            (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) ||
    1276           0 :             cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE))) {
    1277           0 :     mUnsafeInlineKeywordSrc->invalidate();
    1278             :     // log to the console that unsafe-inline will be ignored
    1279           0 :     const char16_t* params[] = { u"'unsafe-inline'" };
    1280           0 :     logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptStyleSrc",
    1281           0 :                              params, ArrayLength(params));
    1282             :   }
    1283             : 
    1284             :   // Add the newly created srcs to the directive and add the directive to the policy
    1285           0 :   cspDir->addSrcs(srcs);
    1286           0 :   mPolicy->addDirective(cspDir);
    1287             : }
    1288             : 
    1289             : // policy = [ directive *( ";" [ directive ] ) ]
    1290             : nsCSPPolicy*
    1291           0 : nsCSPParser::policy()
    1292             : {
    1293           0 :   CSPPARSERLOG(("nsCSPParser::policy"));
    1294             : 
    1295           0 :   mPolicy = new nsCSPPolicy();
    1296           0 :   for (uint32_t i = 0; i < mTokens.Length(); i++) {
    1297             :     // All input is already tokenized; set one tokenized array in the form of
    1298             :     // [ name, src, src, ... ]
    1299             :     // to mCurDir and call directive which processes the current directive.
    1300           0 :     mCurDir = mTokens[i];
    1301           0 :     directive();
    1302             :   }
    1303             : 
    1304           0 :   if (mChildSrc && !mFrameSrc) {
    1305             :     // if we have a child-src, it handles frame-src too, unless frame-src is set
    1306           0 :     mChildSrc->setHandleFrameSrc();
    1307             :   }
    1308             : 
    1309           0 :   return mPolicy;
    1310             : }
    1311             : 
    1312             : nsCSPPolicy*
    1313           0 : nsCSPParser::parseContentSecurityPolicy(const nsAString& aPolicyString,
    1314             :                                         nsIURI *aSelfURI,
    1315             :                                         bool aReportOnly,
    1316             :                                         nsCSPContext* aCSPContext,
    1317             :                                         bool aDeliveredViaMetaTag)
    1318             : {
    1319           0 :   if (CSPPARSERLOGENABLED()) {
    1320           0 :     CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, policy: %s",
    1321             :                  NS_ConvertUTF16toUTF8(aPolicyString).get()));
    1322           0 :     CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, selfURI: %s",
    1323             :                  aSelfURI->GetSpecOrDefault().get()));
    1324           0 :     CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, reportOnly: %s",
    1325             :                  (aReportOnly ? "true" : "false")));
    1326           0 :     CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, deliveredViaMetaTag: %s",
    1327             :                  (aDeliveredViaMetaTag ? "true" : "false")));
    1328             :   }
    1329             : 
    1330           0 :   NS_ASSERTION(aSelfURI, "Can not parseContentSecurityPolicy without aSelfURI");
    1331             : 
    1332             :   // Separate all input into tokens and store them in the form of:
    1333             :   // [ [ name, src, src, ... ], [ name, src, src, ... ], ... ]
    1334             :   // The tokenizer itself can not fail; all eventual errors
    1335             :   // are detected in the parser itself.
    1336             : 
    1337           0 :   nsTArray< nsTArray<nsString> > tokens;
    1338           0 :   nsCSPTokenizer::tokenizeCSPPolicy(aPolicyString, tokens);
    1339             : 
    1340           0 :   nsCSPParser parser(tokens, aSelfURI, aCSPContext, aDeliveredViaMetaTag);
    1341             : 
    1342             :   // Start the parser to generate a new CSPPolicy using the generated tokens.
    1343           0 :   nsCSPPolicy* policy = parser.policy();
    1344             : 
    1345             :   // Check that report-only policies define a report-uri, otherwise log warning.
    1346           0 :   if (aReportOnly) {
    1347           0 :     policy->setReportOnlyFlag(true);
    1348           0 :     if (!policy->hasDirective(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
    1349           0 :       nsAutoCString prePath;
    1350           0 :       nsresult rv = aSelfURI->GetPrePath(prePath);
    1351           0 :       NS_ENSURE_SUCCESS(rv, policy);
    1352           0 :       NS_ConvertUTF8toUTF16 unicodePrePath(prePath);
    1353           0 :       const char16_t* params[] = { unicodePrePath.get() };
    1354           0 :       parser.logWarningErrorToConsole(nsIScriptError::warningFlag, "reportURInotInReportOnlyHeader",
    1355           0 :                                       params, ArrayLength(params));
    1356             :     }
    1357             :   }
    1358             : 
    1359           0 :   if (policy->getNumDirectives() == 0) {
    1360             :     // Individual errors were already reported in the parser, but if
    1361             :     // we do not have an enforcable directive at all, we return null.
    1362           0 :     delete policy;
    1363           0 :     return nullptr;
    1364             :   }
    1365             : 
    1366           0 :   if (CSPPARSERLOGENABLED()) {
    1367           0 :     nsString parsedPolicy;
    1368           0 :     policy->toString(parsedPolicy);
    1369           0 :     CSPPARSERLOG(("nsCSPParser::parseContentSecurityPolicy, parsedPolicy: %s",
    1370             :                  NS_ConvertUTF16toUTF8(parsedPolicy).get()));
    1371             :   }
    1372             : 
    1373           0 :   return policy;
    1374             : }

Generated by: LCOV version 1.13