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