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 #ifndef nsCSPParser_h___ 8 #define nsCSPParser_h___ 9 10 #include "nsCSPUtils.h" 11 #include "nsCSPContext.h" 12 #include "nsIURI.h" 13 #include "PolicyTokenizer.h" 14 15 bool isNumberToken(char16_t aSymbol); 16 bool isValidHexDig(char16_t aHexDig); 17 18 // clang-format off 19 const char16_t COLON = ':'; 20 const char16_t SEMICOLON = ';'; 21 const char16_t SLASH = '/'; 22 const char16_t PLUS = '+'; 23 const char16_t DASH = '-'; 24 const char16_t DOT = '.'; 25 const char16_t UNDERLINE = '_'; 26 const char16_t TILDE = '~'; 27 const char16_t WILDCARD = '*'; 28 const char16_t SINGLEQUOTE = '\''; 29 const char16_t NUMBER_SIGN = '#'; 30 const char16_t QUESTIONMARK = '?'; 31 const char16_t PERCENT_SIGN = '%'; 32 const char16_t EXCLAMATION = '!'; 33 const char16_t DOLLAR = '$'; 34 const char16_t AMPERSAND = '&'; 35 const char16_t OPENBRACE = '('; 36 const char16_t CLOSINGBRACE = ')'; 37 const char16_t EQUALS = '='; 38 const char16_t ATSYMBOL = '@'; 39 // clang-format on 40 41 class nsCSPParser { 42 public: 43 /** 44 * The CSP parser only has one publicly accessible function, which is 45 * parseContentSecurityPolicy. Internally the input string is separated into 46 * string tokens and policy() is called, which starts parsing the policy. The 47 * parser calls one function after the other according the the source-list 48 * from http://www.w3.org/TR/CSP11/#source-list. E.g., the parser can only 49 * call port() after the parser has already processed any possible host in 50 * host(), similar to a finite state machine. 51 */ 52 static nsCSPPolicy* parseContentSecurityPolicy(const nsAString& aPolicyString, 53 nsIURI* aSelfURI, 54 bool aReportOnly, 55 nsCSPContext* aCSPContext, 56 bool aDeliveredViaMetaTag); 57 58 private: 59 nsCSPParser(policyTokens& aTokens, nsIURI* aSelfURI, 60 nsCSPContext* aCSPContext, bool aDeliveredViaMetaTag); 61 62 ~nsCSPParser(); 63 64 // Parsing the CSP using the source-list from 65 // http://www.w3.org/TR/CSP11/#source-list 66 nsCSPPolicy* policy(); 67 void directive(); 68 nsCSPDirective* directiveName(); 69 void directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs); 70 void referrerDirectiveValue(nsCSPDirective* aDir); 71 void reportURIList(nsCSPDirective* aDir); 72 void sandboxFlagList(nsCSPDirective* aDir); 73 void sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs); 74 nsCSPBaseSrc* sourceExpression(); 75 nsCSPSchemeSrc* schemeSource(); 76 nsCSPHostSrc* hostSource(); 77 nsCSPBaseSrc* keywordSource(); 78 nsCSPNonceSrc* nonceSource(); 79 nsCSPHashSrc* hashSource(); 80 nsCSPHostSrc* host(); 81 bool hostChar(); 82 bool schemeChar(); 83 bool port(); 84 bool path(nsCSPHostSrc* aCspHost); 85 86 bool subHost(); // helper function to parse subDomains 87 bool atValidUnreservedChar(); // helper function to parse unreserved 88 bool atValidSubDelimChar(); // helper function to parse sub-delims 89 bool atValidPctEncodedChar(); // helper function to parse pct-encoded 90 bool subPath(nsCSPHostSrc* aCspHost); // helper function to parse paths 91 atEnd()92 inline bool atEnd() { return mCurChar >= mEndChar; } 93 accept(char16_t aSymbol)94 inline bool accept(char16_t aSymbol) { 95 if (atEnd()) { 96 return false; 97 } 98 return (*mCurChar == aSymbol) && advance(); 99 } 100 accept(bool (* aClassifier)(char16_t))101 inline bool accept(bool (*aClassifier)(char16_t)) { 102 if (atEnd()) { 103 return false; 104 } 105 return (aClassifier(*mCurChar)) && advance(); 106 } 107 peek(char16_t aSymbol)108 inline bool peek(char16_t aSymbol) { 109 if (atEnd()) { 110 return false; 111 } 112 return *mCurChar == aSymbol; 113 } 114 peek(bool (* aClassifier)(char16_t))115 inline bool peek(bool (*aClassifier)(char16_t)) { 116 if (atEnd()) { 117 return false; 118 } 119 return aClassifier(*mCurChar); 120 } 121 advance()122 inline bool advance() { 123 if (atEnd()) { 124 return false; 125 } 126 mCurValue.Append(*mCurChar++); 127 return true; 128 } 129 resetCurValue()130 inline void resetCurValue() { mCurValue.Truncate(); } 131 132 bool atEndOfPath(); 133 bool atValidPathChar(); 134 135 void resetCurChar(const nsAString& aToken); 136 137 void logWarningErrorToConsole(uint32_t aSeverityFlag, const char* aProperty, 138 const nsTArray<nsString>& aParams); 139 140 /** 141 * When parsing the policy, the parser internally uses the following helper 142 * variables/members which are used/reset during parsing. The following 143 * example explains how they are used. 144 * The tokenizer separats all input into arrays of arrays of strings, which 145 * are stored in mTokens, for example: 146 * mTokens = [ [ script-src, http://www.example.com, 'self' ], ... ] 147 * 148 * When parsing starts, mCurdir always holds the currently processed array of 149 * strings. 150 * In our example: 151 * mCurDir = [ script-src, http://www.example.com, 'self' ] 152 * 153 * During parsing, we process/consume one string at a time of that array. 154 * We set mCurToken to the string we are currently processing; in the first 155 * case that would be: mCurToken = script-src which allows to do simple string 156 * comparisons to see if mCurToken is a valid directive. 157 * 158 * Continuing parsing, the parser consumes the next string of that array, 159 * resetting: 160 * mCurToken = "http://www.example.com" 161 * ^ ^ 162 * mCurChar mEndChar (points *after* the 'm') 163 * mCurValue = "" 164 * 165 * After calling advance() the first time, helpers would hold the following 166 * values: 167 * mCurToken = "http://www.example.com" 168 * ^ ^ 169 * mCurChar mEndChar (points *after* the 'm') 170 * mCurValue = "h" 171 * 172 * We continue parsing till all strings of one directive are consumed, then we 173 * reset mCurDir to hold the next array of strings and start the process all 174 * over. 175 */ 176 177 const char16_t* mCurChar; 178 const char16_t* mEndChar; 179 nsString mCurValue; 180 nsString mCurToken; 181 nsTArray<nsString> mCurDir; 182 183 // helpers to allow invalidation of srcs within script-src and style-src 184 // if either 'strict-dynamic' or at least a hash or nonce is present. 185 bool mHasHashOrNonce; // false, if no hash or nonce is defined 186 bool mStrictDynamic; // false, if 'strict-dynamic' is not defined 187 nsCSPKeywordSrc* mUnsafeInlineKeywordSrc; // null, otherwise invlidate() 188 189 // cache variables for child-src, frame-src and worker-src handling; 190 // in CSP 3 child-src is deprecated. For backwards compatibility 191 // child-src needs to restrict: 192 // (*) frames, in case frame-src is not expicitly specified 193 // (*) workers, in case worker-src is not expicitly specified 194 // If neither worker-src, nor child-src is present, then script-src 195 // needs to govern workers. 196 nsCSPChildSrcDirective* mChildSrc; 197 nsCSPDirective* mFrameSrc; 198 nsCSPDirective* mWorkerSrc; 199 nsCSPScriptSrcDirective* mScriptSrc; 200 201 // cache variable to let nsCSPHostSrc know that it's within 202 // the frame-ancestors directive. 203 bool mParsingFrameAncestorsDir; 204 205 policyTokens mTokens; 206 nsIURI* mSelfURI; 207 nsCSPPolicy* mPolicy; 208 nsCSPContext* mCSPContext; // used for console logging 209 bool mDeliveredViaMetaTag; 210 }; 211 212 #endif /* nsCSPParser_h___ */ 213