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