/src/mozilla-central/dom/security/nsCSPUtils.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 "nsAttrValue.h" |
8 | | #include "nsCharSeparatedTokenizer.h" |
9 | | #include "nsContentUtils.h" |
10 | | #include "nsCSPUtils.h" |
11 | | #include "nsDebug.h" |
12 | | #include "nsIConsoleService.h" |
13 | | #include "nsICryptoHash.h" |
14 | | #include "nsIScriptError.h" |
15 | | #include "nsIServiceManager.h" |
16 | | #include "nsIStringBundle.h" |
17 | | #include "nsIURL.h" |
18 | | #include "nsReadableUtils.h" |
19 | | #include "nsSandboxFlags.h" |
20 | | |
21 | 0 | #define DEFAULT_PORT -1 |
22 | | |
23 | | static mozilla::LogModule* |
24 | | GetCspUtilsLog() |
25 | 11.5k | { |
26 | 11.5k | static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils"); |
27 | 11.5k | return gCspUtilsPRLog; |
28 | 11.5k | } |
29 | | |
30 | 11.5k | #define CSPUTILSLOG(args) MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args) |
31 | 0 | #define CSPUTILSLOGENABLED() MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug) |
32 | | |
33 | | void |
34 | | CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr) |
35 | 11.2k | { |
36 | 11.2k | outDecStr.Truncate(); |
37 | 11.2k | |
38 | 11.2k | // helper function that should not be visible outside this methods scope |
39 | 11.2k | struct local { |
40 | 11.2k | static inline char16_t convertHexDig(char16_t aHexDig) { |
41 | 2.64k | if (isNumberToken(aHexDig)) { |
42 | 2.04k | return aHexDig - '0'; |
43 | 2.04k | } |
44 | 605 | if (aHexDig >= 'A' && aHexDig <= 'F') { |
45 | 310 | return aHexDig - 'A' + 10; |
46 | 310 | } |
47 | 295 | // must be a lower case character |
48 | 295 | // (aHexDig >= 'a' && aHexDig <= 'f') |
49 | 295 | return aHexDig - 'a' + 10; |
50 | 295 | } |
51 | 11.2k | }; |
52 | 11.2k | |
53 | 11.2k | const char16_t *cur, *end, *hexDig1, *hexDig2; |
54 | 11.2k | cur = aEncStr.BeginReading(); |
55 | 11.2k | end = aEncStr.EndReading(); |
56 | 11.2k | |
57 | 122k | while (cur != end) { |
58 | 111k | // if it's not a percent sign then there is |
59 | 111k | // nothing to do for that character |
60 | 111k | if (*cur != PERCENT_SIGN) { |
61 | 109k | outDecStr.Append(*cur); |
62 | 109k | cur++; |
63 | 109k | continue; |
64 | 109k | } |
65 | 1.32k | |
66 | 1.32k | // get the two hexDigs following the '%'-sign |
67 | 1.32k | hexDig1 = cur + 1; |
68 | 1.32k | hexDig2 = cur + 2; |
69 | 1.32k | |
70 | 1.32k | // if there are no hexdigs after the '%' then |
71 | 1.32k | // there is nothing to do for us. |
72 | 1.32k | if (hexDig1 == end || hexDig2 == end || |
73 | 1.32k | !isValidHexDig(*hexDig1) || |
74 | 1.32k | !isValidHexDig(*hexDig2)) { |
75 | 0 | outDecStr.Append(PERCENT_SIGN); |
76 | 0 | cur++; |
77 | 0 | continue; |
78 | 0 | } |
79 | 1.32k | |
80 | 1.32k | // decode "% hexDig1 hexDig2" into a character. |
81 | 1.32k | char16_t decChar = (local::convertHexDig(*hexDig1) << 4) + |
82 | 1.32k | local::convertHexDig(*hexDig2); |
83 | 1.32k | outDecStr.Append(decChar); |
84 | 1.32k | |
85 | 1.32k | // increment 'cur' to after the second hexDig |
86 | 1.32k | cur = ++hexDig2; |
87 | 1.32k | } |
88 | 11.2k | } |
89 | | |
90 | | void |
91 | | CSP_GetLocalizedStr(const char* aName, |
92 | | const char16_t** aParams, |
93 | | uint32_t aLength, |
94 | | nsAString& outResult) |
95 | 0 | { |
96 | 0 | nsCOMPtr<nsIStringBundle> keyStringBundle; |
97 | 0 | nsCOMPtr<nsIStringBundleService> stringBundleService = |
98 | 0 | mozilla::services::GetStringBundleService(); |
99 | 0 |
|
100 | 0 | NS_ASSERTION(stringBundleService, "String bundle service must be present!"); |
101 | 0 | stringBundleService->CreateBundle("chrome://global/locale/security/csp.properties", |
102 | 0 | getter_AddRefs(keyStringBundle)); |
103 | 0 |
|
104 | 0 | NS_ASSERTION(keyStringBundle, "Key string bundle must be available!"); |
105 | 0 |
|
106 | 0 | if (!keyStringBundle) { |
107 | 0 | return; |
108 | 0 | } |
109 | 0 | keyStringBundle->FormatStringFromName(aName, aParams, aLength, |
110 | 0 | outResult); |
111 | 0 | } |
112 | | |
113 | | void |
114 | | CSP_LogStrMessage(const nsAString& aMsg) |
115 | 0 | { |
116 | 0 | nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1")); |
117 | 0 |
|
118 | 0 | if (!console) { |
119 | 0 | return; |
120 | 0 | } |
121 | 0 | nsString msg(aMsg); |
122 | 0 | console->LogStringMessage(msg.get()); |
123 | 0 | } |
124 | | |
125 | | void |
126 | | CSP_LogMessage(const nsAString& aMessage, |
127 | | const nsAString& aSourceName, |
128 | | const nsAString& aSourceLine, |
129 | | uint32_t aLineNumber, |
130 | | uint32_t aColumnNumber, |
131 | | uint32_t aFlags, |
132 | | const nsACString& aCategory, |
133 | | uint64_t aInnerWindowID, |
134 | | bool aFromPrivateWindow) |
135 | 0 | { |
136 | 0 | nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); |
137 | 0 |
|
138 | 0 | nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); |
139 | 0 |
|
140 | 0 | if (!console || !error) { |
141 | 0 | return; |
142 | 0 | } |
143 | 0 | |
144 | 0 | // Prepending CSP to the outgoing console message |
145 | 0 | nsString cspMsg; |
146 | 0 | cspMsg.AppendLiteral(u"Content Security Policy: "); |
147 | 0 | cspMsg.Append(aMessage); |
148 | 0 |
|
149 | 0 | // Currently 'aSourceLine' is not logged to the console, because similar |
150 | 0 | // information is already included within the source link of the message. |
151 | 0 | // For inline violations however, the line and column number are 0 and |
152 | 0 | // information contained within 'aSourceLine' can be really useful for devs. |
153 | 0 | // E.g. 'aSourceLine' might be: 'onclick attribute on DIV element'. |
154 | 0 | // In such cases we append 'aSourceLine' directly to the error message. |
155 | 0 | if (!aSourceLine.IsEmpty()) { |
156 | 0 | cspMsg.AppendLiteral(u" Source: "); |
157 | 0 | cspMsg.Append(aSourceLine); |
158 | 0 | cspMsg.AppendLiteral(u"."); |
159 | 0 | } |
160 | 0 |
|
161 | 0 | // Since we are leveraging csp errors as the category names which |
162 | 0 | // we pass to devtools, we should prepend them with "CSP_" to |
163 | 0 | // allow easy distincution in devtools code. e.g. |
164 | 0 | // upgradeInsecureRequest -> CSP_upgradeInsecureRequest |
165 | 0 | nsCString category("CSP_"); |
166 | 0 | category.Append(aCategory); |
167 | 0 |
|
168 | 0 | nsresult rv; |
169 | 0 | if (aInnerWindowID > 0) { |
170 | 0 | rv = error->InitWithWindowID(cspMsg, aSourceName, |
171 | 0 | aSourceLine, aLineNumber, |
172 | 0 | aColumnNumber, aFlags, |
173 | 0 | category, aInnerWindowID); |
174 | 0 | } |
175 | 0 | else { |
176 | 0 | rv = error->Init(cspMsg, aSourceName, |
177 | 0 | aSourceLine, aLineNumber, |
178 | 0 | aColumnNumber, aFlags, |
179 | 0 | category.get(), aFromPrivateWindow); |
180 | 0 | } |
181 | 0 | if (NS_FAILED(rv)) { |
182 | 0 | return; |
183 | 0 | } |
184 | 0 | console->LogMessage(error); |
185 | 0 | } |
186 | | |
187 | | /** |
188 | | * Combines CSP_LogMessage and CSP_GetLocalizedStr into one call. |
189 | | */ |
190 | | void |
191 | | CSP_LogLocalizedStr(const char* aName, |
192 | | const char16_t** aParams, |
193 | | uint32_t aLength, |
194 | | const nsAString& aSourceName, |
195 | | const nsAString& aSourceLine, |
196 | | uint32_t aLineNumber, |
197 | | uint32_t aColumnNumber, |
198 | | uint32_t aFlags, |
199 | | const nsACString& aCategory, |
200 | | uint64_t aInnerWindowID, |
201 | | bool aFromPrivateWindow) |
202 | 0 | { |
203 | 0 | nsAutoString logMsg; |
204 | 0 | CSP_GetLocalizedStr(aName, aParams, aLength, logMsg); |
205 | 0 | CSP_LogMessage(logMsg, aSourceName, aSourceLine, |
206 | 0 | aLineNumber, aColumnNumber, aFlags, |
207 | 0 | aCategory, aInnerWindowID, aFromPrivateWindow); |
208 | 0 | } |
209 | | |
210 | | /* ===== Helpers ============================ */ |
211 | | CSPDirective |
212 | | CSP_ContentTypeToDirective(nsContentPolicyType aType) |
213 | 0 | { |
214 | 0 | switch (aType) { |
215 | 0 | case nsIContentPolicy::TYPE_IMAGE: |
216 | 0 | case nsIContentPolicy::TYPE_IMAGESET: |
217 | 0 | return nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE; |
218 | 0 |
|
219 | 0 | // BLock XSLT as script, see bug 910139 |
220 | 0 | case nsIContentPolicy::TYPE_XSLT: |
221 | 0 | case nsIContentPolicy::TYPE_SCRIPT: |
222 | 0 | case nsIContentPolicy::TYPE_INTERNAL_SCRIPT: |
223 | 0 | case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD: |
224 | 0 | case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS: |
225 | 0 | return nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE; |
226 | 0 |
|
227 | 0 | case nsIContentPolicy::TYPE_STYLESHEET: |
228 | 0 | return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE; |
229 | 0 |
|
230 | 0 | case nsIContentPolicy::TYPE_FONT: |
231 | 0 | return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE; |
232 | 0 |
|
233 | 0 | case nsIContentPolicy::TYPE_MEDIA: |
234 | 0 | return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE; |
235 | 0 |
|
236 | 0 | case nsIContentPolicy::TYPE_WEB_MANIFEST: |
237 | 0 | return nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE; |
238 | 0 |
|
239 | 0 | case nsIContentPolicy::TYPE_INTERNAL_WORKER: |
240 | 0 | case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: |
241 | 0 | case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER: |
242 | 0 | return nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE; |
243 | 0 |
|
244 | 0 | case nsIContentPolicy::TYPE_SUBDOCUMENT: |
245 | 0 | return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE; |
246 | 0 |
|
247 | 0 | case nsIContentPolicy::TYPE_WEBSOCKET: |
248 | 0 | case nsIContentPolicy::TYPE_XMLHTTPREQUEST: |
249 | 0 | case nsIContentPolicy::TYPE_BEACON: |
250 | 0 | case nsIContentPolicy::TYPE_PING: |
251 | 0 | case nsIContentPolicy::TYPE_FETCH: |
252 | 0 | return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE; |
253 | 0 |
|
254 | 0 | case nsIContentPolicy::TYPE_OBJECT: |
255 | 0 | case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: |
256 | 0 | return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE; |
257 | 0 |
|
258 | 0 | case nsIContentPolicy::TYPE_XBL: |
259 | 0 | case nsIContentPolicy::TYPE_DTD: |
260 | 0 | case nsIContentPolicy::TYPE_OTHER: |
261 | 0 | case nsIContentPolicy::TYPE_SPECULATIVE: |
262 | 0 | return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE; |
263 | 0 |
|
264 | 0 | // csp shold not block top level loads, e.g. in case |
265 | 0 | // of a redirect. |
266 | 0 | case nsIContentPolicy::TYPE_DOCUMENT: |
267 | 0 | // CSP can not block csp reports |
268 | 0 | case nsIContentPolicy::TYPE_CSP_REPORT: |
269 | 0 | return nsIContentSecurityPolicy::NO_DIRECTIVE; |
270 | 0 |
|
271 | 0 | case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: |
272 | 0 | return nsIContentSecurityPolicy::NO_DIRECTIVE; |
273 | 0 |
|
274 | 0 | // Fall through to error for all other directives |
275 | 0 | default: |
276 | 0 | MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective"); |
277 | 0 | } |
278 | 0 | return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE; |
279 | 0 | } |
280 | | |
281 | | nsCSPHostSrc* |
282 | | CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI) |
283 | 289 | { |
284 | 289 | // Create the host first |
285 | 289 | nsCString host; |
286 | 289 | aSelfURI->GetAsciiHost(host); |
287 | 289 | nsCSPHostSrc *hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host)); |
288 | 289 | hostsrc->setGeneratedFromSelfKeyword(); |
289 | 289 | |
290 | 289 | // Add the scheme. |
291 | 289 | nsCString scheme; |
292 | 289 | aSelfURI->GetScheme(scheme); |
293 | 289 | hostsrc->setScheme(NS_ConvertUTF8toUTF16(scheme)); |
294 | 289 | |
295 | 289 | // An empty host (e.g. for data:) indicates it's effectively a unique origin. |
296 | 289 | // Please note that we still need to set the scheme on hostsrc (see above), |
297 | 289 | // because it's used for reporting. |
298 | 289 | if (host.EqualsLiteral("")) { |
299 | 0 | hostsrc->setIsUniqueOrigin(); |
300 | 0 | // no need to query the port in that case. |
301 | 0 | return hostsrc; |
302 | 0 | } |
303 | 289 | |
304 | 289 | int32_t port; |
305 | 289 | aSelfURI->GetPort(&port); |
306 | 289 | // Only add port if it's not default port. |
307 | 289 | if (port > 0) { |
308 | 0 | nsAutoString portStr; |
309 | 0 | portStr.AppendInt(port); |
310 | 0 | hostsrc->setPort(portStr); |
311 | 0 | } |
312 | 289 | return hostsrc; |
313 | 289 | } |
314 | | |
315 | | bool |
316 | | CSP_IsEmptyDirective(const nsAString& aValue, const nsAString& aDir) |
317 | 315k | { |
318 | 315k | return (aDir.Length() == 0 && |
319 | 315k | aValue.Length() == 0); |
320 | 315k | } |
321 | | bool |
322 | | CSP_IsValidDirective(const nsAString& aDir) |
323 | 315k | { |
324 | 315k | uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0])); |
325 | 315k | |
326 | 7.12M | for (uint32_t i = 0; i < numDirs; i++) { |
327 | 6.82M | if (aDir.LowerCaseEqualsASCII(CSPStrDirectives[i])) { |
328 | 12.3k | return true; |
329 | 12.3k | } |
330 | 6.82M | } |
331 | 315k | return false; |
332 | 315k | } |
333 | | bool |
334 | | CSP_IsDirective(const nsAString& aValue, CSPDirective aDir) |
335 | 96.3k | { |
336 | 96.3k | return aValue.LowerCaseEqualsASCII(CSP_CSPDirectiveToString(aDir)); |
337 | 96.3k | } |
338 | | |
339 | | bool |
340 | | CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey) |
341 | 1.22M | { |
342 | 1.22M | return aValue.LowerCaseEqualsASCII(CSP_EnumToUTF8Keyword(aKey)); |
343 | 1.22M | } |
344 | | |
345 | | bool |
346 | | CSP_IsQuotelessKeyword(const nsAString& aKey) |
347 | 176k | { |
348 | 176k | nsString lowerKey; |
349 | 176k | ToLowerCase(aKey, lowerKey); |
350 | 176k | |
351 | 176k | nsAutoString keyword; |
352 | 1.58M | for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) { |
353 | 1.40M | // skipping the leading ' and trimming the trailing ' |
354 | 1.40M | keyword.AssignASCII(gCSPUTF8Keywords[i] + 1); |
355 | 1.40M | keyword.Trim("'", false, true); |
356 | 1.40M | if (lowerKey.Equals(keyword)) { |
357 | 76 | return true; |
358 | 76 | } |
359 | 1.40M | } |
360 | 176k | return false; |
361 | 176k | } |
362 | | |
363 | | /* |
364 | | * Checks whether the current directive permits a specific |
365 | | * scheme. This function is called from nsCSPSchemeSrc() and |
366 | | * also nsCSPHostSrc. |
367 | | * @param aEnforcementScheme |
368 | | * The scheme that this directive allows |
369 | | * @param aUri |
370 | | * The uri of the subresource load. |
371 | | * @param aReportOnly |
372 | | * Whether the enforced policy is report only or not. |
373 | | * @param aUpgradeInsecure |
374 | | * Whether the policy makes use of the directive |
375 | | * 'upgrade-insecure-requests'. |
376 | | * @param aFromSelfURI |
377 | | * Whether a scheme was generated from the keyword 'self' |
378 | | * which then allows schemeless sources to match ws and wss. |
379 | | */ |
380 | | |
381 | | bool |
382 | | permitsScheme(const nsAString& aEnforcementScheme, |
383 | | nsIURI* aUri, |
384 | | bool aReportOnly, |
385 | | bool aUpgradeInsecure, |
386 | | bool aFromSelfURI) |
387 | 0 | { |
388 | 0 | nsAutoCString scheme; |
389 | 0 | nsresult rv = aUri->GetScheme(scheme); |
390 | 0 | NS_ENSURE_SUCCESS(rv, false); |
391 | 0 |
|
392 | 0 | // no scheme to enforce, let's allow the load (e.g. script-src *) |
393 | 0 | if (aEnforcementScheme.IsEmpty()) { |
394 | 0 | return true; |
395 | 0 | } |
396 | 0 | |
397 | 0 | // if the scheme matches, all good - allow the load |
398 | 0 | if (aEnforcementScheme.EqualsASCII(scheme.get())) { |
399 | 0 | return true; |
400 | 0 | } |
401 | 0 | |
402 | 0 | // allow scheme-less sources where the protected resource is http |
403 | 0 | // and the load is https, see: |
404 | 0 | // http://www.w3.org/TR/CSP2/#match-source-expression |
405 | 0 | if (aEnforcementScheme.EqualsASCII("http")) { |
406 | 0 | if (scheme.EqualsASCII("https")) { |
407 | 0 | return true; |
408 | 0 | } |
409 | 0 | if ((scheme.EqualsASCII("ws") || scheme.EqualsASCII("wss")) && aFromSelfURI) { |
410 | 0 | return true; |
411 | 0 | } |
412 | 0 | } |
413 | 0 | if (aEnforcementScheme.EqualsASCII("https")) { |
414 | 0 | if (scheme.EqualsLiteral("wss") && aFromSelfURI) { |
415 | 0 | return true; |
416 | 0 | } |
417 | 0 | } |
418 | 0 | if (aEnforcementScheme.EqualsASCII("ws") && scheme.EqualsASCII("wss")) { |
419 | 0 | return true; |
420 | 0 | } |
421 | 0 | |
422 | 0 | // Allow the load when enforcing upgrade-insecure-requests with the |
423 | 0 | // promise the request gets upgraded from http to https and ws to wss. |
424 | 0 | // See nsHttpChannel::Connect() and also WebSocket.cpp. Please note, |
425 | 0 | // the report only policies should not allow the load and report |
426 | 0 | // the error back to the page. |
427 | 0 | return ((aUpgradeInsecure && !aReportOnly) && |
428 | 0 | ((scheme.EqualsASCII("http") && aEnforcementScheme.EqualsASCII("https")) || |
429 | 0 | (scheme.EqualsASCII("ws") && aEnforcementScheme.EqualsASCII("wss")))); |
430 | 0 | } |
431 | | |
432 | | /* |
433 | | * A helper function for appending a CSP header to an existing CSP |
434 | | * policy. |
435 | | * |
436 | | * @param aCsp the CSP policy |
437 | | * @param aHeaderValue the header |
438 | | * @param aReportOnly is this a report-only header? |
439 | | */ |
440 | | |
441 | | nsresult |
442 | | CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp, |
443 | | const nsAString& aHeaderValue, |
444 | | bool aReportOnly) |
445 | 0 | { |
446 | 0 | NS_ENSURE_ARG(aCsp); |
447 | 0 |
|
448 | 0 | // Need to tokenize the header value since multiple headers could be |
449 | 0 | // concatenated into one comma-separated list of policies. |
450 | 0 | // See RFC2616 section 4.2 (last paragraph) |
451 | 0 | nsresult rv = NS_OK; |
452 | 0 | nsCharSeparatedTokenizer tokenizer(aHeaderValue, ','); |
453 | 0 | while (tokenizer.hasMoreTokens()) { |
454 | 0 | const nsAString& policy = tokenizer.nextToken(); |
455 | 0 | rv = aCsp->AppendPolicy(policy, aReportOnly, false); |
456 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
457 | 0 | { |
458 | 0 | CSPUTILSLOG(("CSP refined with policy: \"%s\"", |
459 | 0 | NS_ConvertUTF16toUTF8(policy).get())); |
460 | 0 | } |
461 | 0 | } |
462 | 0 | return NS_OK; |
463 | 0 | } |
464 | | |
465 | | /* ===== nsCSPSrc ============================ */ |
466 | | |
467 | | nsCSPBaseSrc::nsCSPBaseSrc() |
468 | | : mInvalidated(false) |
469 | 3.18M | { |
470 | 3.18M | } |
471 | | |
472 | | nsCSPBaseSrc::~nsCSPBaseSrc() |
473 | 3.18M | { |
474 | 3.18M | } |
475 | | |
476 | | // ::permits is only called for external load requests, therefore: |
477 | | // nsCSPKeywordSrc and nsCSPHashSource fall back to this base class |
478 | | // implementation which will never allow the load. |
479 | | bool |
480 | | nsCSPBaseSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, |
481 | | bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const |
482 | 0 | { |
483 | 0 | if (CSPUTILSLOGENABLED()) { |
484 | 0 | CSPUTILSLOG(("nsCSPBaseSrc::permits, aUri: %s", |
485 | 0 | aUri->GetSpecOrDefault().get())); |
486 | 0 | } |
487 | 0 | return false; |
488 | 0 | } |
489 | | |
490 | | // ::allows is only called for inlined loads, therefore: |
491 | | // nsCSPSchemeSrc, nsCSPHostSrc fall back |
492 | | // to this base class implementation which will never allow the load. |
493 | | bool |
494 | | nsCSPBaseSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, |
495 | | bool aParserCreated) const |
496 | 0 | { |
497 | 0 | CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s", |
498 | 0 | aKeyword == CSP_HASH ? "hash" : CSP_EnumToUTF8Keyword(aKeyword), |
499 | 0 | NS_ConvertUTF16toUTF8(aHashOrNonce).get())); |
500 | 0 | return false; |
501 | 0 | } |
502 | | |
503 | | /* ====== nsCSPSchemeSrc ===================== */ |
504 | | |
505 | | nsCSPSchemeSrc::nsCSPSchemeSrc(const nsAString& aScheme) |
506 | | : mScheme(aScheme) |
507 | 10.1k | { |
508 | 10.1k | ToLowerCase(mScheme); |
509 | 10.1k | } |
510 | | |
511 | | nsCSPSchemeSrc::~nsCSPSchemeSrc() |
512 | 10.1k | { |
513 | 10.1k | } |
514 | | |
515 | | bool |
516 | | nsCSPSchemeSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, |
517 | | bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const |
518 | 0 | { |
519 | 0 | if (CSPUTILSLOGENABLED()) { |
520 | 0 | CSPUTILSLOG(("nsCSPSchemeSrc::permits, aUri: %s", |
521 | 0 | aUri->GetSpecOrDefault().get())); |
522 | 0 | } |
523 | 0 | MOZ_ASSERT((!mScheme.EqualsASCII("")), "scheme can not be the empty string"); |
524 | 0 | if (mInvalidated) { |
525 | 0 | return false; |
526 | 0 | } |
527 | 0 | return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, false); |
528 | 0 | } |
529 | | |
530 | | bool |
531 | | nsCSPSchemeSrc::visit(nsCSPSrcVisitor* aVisitor) const |
532 | 0 | { |
533 | 0 | return aVisitor->visitSchemeSrc(*this); |
534 | 0 | } |
535 | | |
536 | | void |
537 | | nsCSPSchemeSrc::toString(nsAString& outStr) const |
538 | 6.00k | { |
539 | 6.00k | outStr.Append(mScheme); |
540 | 6.00k | outStr.AppendLiteral(":"); |
541 | 6.00k | } |
542 | | |
543 | | /* ===== nsCSPHostSrc ======================== */ |
544 | | |
545 | | nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost) |
546 | | : mHost(aHost) |
547 | | , mGeneratedFromSelfKeyword(false) |
548 | | , mIsUniqueOrigin(false) |
549 | | , mWithinFrameAncstorsDir(false) |
550 | 180k | { |
551 | 180k | ToLowerCase(mHost); |
552 | 180k | } |
553 | | |
554 | | nsCSPHostSrc::~nsCSPHostSrc() |
555 | 180k | { |
556 | 180k | } |
557 | | |
558 | | /* |
559 | | * Checks whether the current directive permits a specific port. |
560 | | * @param aEnforcementScheme |
561 | | * The scheme that this directive allows |
562 | | * (used to query the default port for that scheme) |
563 | | * @param aEnforcementPort |
564 | | * The port that this directive allows |
565 | | * @param aResourceURI |
566 | | * The uri of the subresource load |
567 | | */ |
568 | | bool |
569 | | permitsPort(const nsAString& aEnforcementScheme, |
570 | | const nsAString& aEnforcementPort, |
571 | | nsIURI* aResourceURI) |
572 | 0 | { |
573 | 0 | // If enforcement port is the wildcard, don't block the load. |
574 | 0 | if (aEnforcementPort.EqualsASCII("*")) { |
575 | 0 | return true; |
576 | 0 | } |
577 | 0 | |
578 | 0 | int32_t resourcePort; |
579 | 0 | nsresult rv = aResourceURI->GetPort(&resourcePort); |
580 | 0 | NS_ENSURE_SUCCESS(rv, false); |
581 | 0 |
|
582 | 0 | // Avoid unnecessary string creation/manipulation and don't block the |
583 | 0 | // load if the resource to be loaded uses the default port for that |
584 | 0 | // scheme and there is no port to be enforced. |
585 | 0 | // Note, this optimization relies on scheme checks within permitsScheme(). |
586 | 0 | if (resourcePort == DEFAULT_PORT && aEnforcementPort.IsEmpty()) { |
587 | 0 | return true; |
588 | 0 | } |
589 | 0 | |
590 | 0 | // By now we know at that either the resourcePort does not use the default |
591 | 0 | // port or there is a port restriction to be enforced. A port value of -1 |
592 | 0 | // corresponds to the protocol's default port (eg. -1 implies port 80 for |
593 | 0 | // http URIs), in such a case we have to query the default port of the |
594 | 0 | // resource to be loaded. |
595 | 0 | if (resourcePort == DEFAULT_PORT) { |
596 | 0 | nsAutoCString resourceScheme; |
597 | 0 | rv = aResourceURI->GetScheme(resourceScheme); |
598 | 0 | NS_ENSURE_SUCCESS(rv, false); |
599 | 0 | resourcePort = NS_GetDefaultPort(resourceScheme.get()); |
600 | 0 | } |
601 | 0 |
|
602 | 0 | // If there is a port to be enforced and the ports match, then |
603 | 0 | // don't block the load. |
604 | 0 | nsString resourcePortStr; |
605 | 0 | resourcePortStr.AppendInt(resourcePort); |
606 | 0 | if (aEnforcementPort.Equals(resourcePortStr)) { |
607 | 0 | return true; |
608 | 0 | } |
609 | 0 | |
610 | 0 | // If there is no port to be enforced, query the default port for the load. |
611 | 0 | nsString enforcementPort(aEnforcementPort); |
612 | 0 | if (enforcementPort.IsEmpty()) { |
613 | 0 | // For scheme less sources, our parser always generates a scheme |
614 | 0 | // which is the scheme of the protected resource. |
615 | 0 | MOZ_ASSERT(!aEnforcementScheme.IsEmpty(), |
616 | 0 | "need a scheme to query default port"); |
617 | 0 | int32_t defaultEnforcementPort = |
618 | 0 | NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get()); |
619 | 0 | enforcementPort.Truncate(); |
620 | 0 | enforcementPort.AppendInt(defaultEnforcementPort); |
621 | 0 | } |
622 | 0 |
|
623 | 0 | // If default ports match, don't block the load |
624 | 0 | if (enforcementPort.Equals(resourcePortStr)) { |
625 | 0 | return true; |
626 | 0 | } |
627 | 0 | |
628 | 0 | // Additional port matching where the regular URL matching algorithm |
629 | 0 | // treats insecure ports as matching their secure variants. |
630 | 0 | // default port for http is :80 |
631 | 0 | // default port for https is :443 |
632 | 0 | if (enforcementPort.EqualsLiteral("80") && |
633 | 0 | resourcePortStr.EqualsLiteral("443")) { |
634 | 0 | return true; |
635 | 0 | } |
636 | 0 | |
637 | 0 | // ports do not match, block the load. |
638 | 0 | return false; |
639 | 0 | } |
640 | | |
641 | | bool |
642 | | nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, |
643 | | bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const |
644 | 0 | { |
645 | 0 | if (CSPUTILSLOGENABLED()) { |
646 | 0 | CSPUTILSLOG(("nsCSPHostSrc::permits, aUri: %s", |
647 | 0 | aUri->GetSpecOrDefault().get())); |
648 | 0 | } |
649 | 0 |
|
650 | 0 | if (mInvalidated || mIsUniqueOrigin) { |
651 | 0 | return false; |
652 | 0 | } |
653 | 0 | |
654 | 0 | // we are following the enforcement rules from the spec, see: |
655 | 0 | // http://www.w3.org/TR/CSP11/#match-source-expression |
656 | 0 | |
657 | 0 | // 4.3) scheme matching: Check if the scheme matches. |
658 | 0 | if (!permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, mGeneratedFromSelfKeyword)) { |
659 | 0 | return false; |
660 | 0 | } |
661 | 0 | |
662 | 0 | // The host in nsCSpHostSrc should never be empty. In case we are enforcing |
663 | 0 | // just a specific scheme, the parser should generate a nsCSPSchemeSource. |
664 | 0 | NS_ASSERTION((!mHost.IsEmpty()), "host can not be the empty string"); |
665 | 0 |
|
666 | 0 | // 2) host matching: Enforce a single * |
667 | 0 | if (mHost.EqualsASCII("*")) { |
668 | 0 | // The single ASTERISK character (*) does not match a URI's scheme of a type |
669 | 0 | // designating a globally unique identifier (such as blob:, data:, or filesystem:) |
670 | 0 | // At the moment firefox does not support filesystem; but for future compatibility |
671 | 0 | // we support it in CSP according to the spec, see: 4.2.2 Matching Source Expressions |
672 | 0 | // Note, that whitelisting any of these schemes would call nsCSPSchemeSrc::permits(). |
673 | 0 | bool isBlobScheme = |
674 | 0 | (NS_SUCCEEDED(aUri->SchemeIs("blob", &isBlobScheme)) && isBlobScheme); |
675 | 0 | bool isDataScheme = |
676 | 0 | (NS_SUCCEEDED(aUri->SchemeIs("data", &isDataScheme)) && isDataScheme); |
677 | 0 | bool isFileScheme = |
678 | 0 | (NS_SUCCEEDED(aUri->SchemeIs("filesystem", &isFileScheme)) && isFileScheme); |
679 | 0 |
|
680 | 0 | if (isBlobScheme || isDataScheme || isFileScheme) { |
681 | 0 | return false; |
682 | 0 | } |
683 | 0 | return true; |
684 | 0 | } |
685 | 0 | |
686 | 0 | // Before we can check if the host matches, we have to |
687 | 0 | // extract the host part from aUri. |
688 | 0 | nsAutoCString uriHost; |
689 | 0 | nsresult rv = aUri->GetAsciiHost(uriHost); |
690 | 0 | NS_ENSURE_SUCCESS(rv, false); |
691 | 0 |
|
692 | 0 | nsString decodedUriHost; |
693 | 0 | CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost); |
694 | 0 |
|
695 | 0 | // 4.5) host matching: Check if the allowed host starts with a wilcard. |
696 | 0 | if (mHost.First() == '*') { |
697 | 0 | NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'"); |
698 | 0 |
|
699 | 0 | // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before checking |
700 | 0 | // if the remaining characters match |
701 | 0 | nsString wildCardHost = mHost; |
702 | 0 | wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1); |
703 | 0 | if (!StringEndsWith(decodedUriHost, wildCardHost)) { |
704 | 0 | return false; |
705 | 0 | } |
706 | 0 | } |
707 | 0 | // 4.6) host matching: Check if hosts match. |
708 | 0 | else if (!mHost.Equals(decodedUriHost)) { |
709 | 0 | return false; |
710 | 0 | } |
711 | 0 | |
712 | 0 | // Port matching: Check if the ports match. |
713 | 0 | if (!permitsPort(mScheme, mPort, aUri)) { |
714 | 0 | return false; |
715 | 0 | } |
716 | 0 | |
717 | 0 | // 4.9) Path matching: If there is a path, we have to enforce |
718 | 0 | // path-level matching, unless the channel got redirected, see: |
719 | 0 | // http://www.w3.org/TR/CSP11/#source-list-paths-and-redirects |
720 | 0 | if (!aWasRedirected && !mPath.IsEmpty()) { |
721 | 0 | // converting aUri into nsIURL so we can strip query and ref |
722 | 0 | // example.com/test#foo -> example.com/test |
723 | 0 | // example.com/test?val=foo -> example.com/test |
724 | 0 | nsCOMPtr<nsIURL> url = do_QueryInterface(aUri); |
725 | 0 | if (!url) { |
726 | 0 | NS_ASSERTION(false, "can't QI into nsIURI"); |
727 | 0 | return false; |
728 | 0 | } |
729 | 0 | nsAutoCString uriPath; |
730 | 0 | rv = url->GetFilePath(uriPath); |
731 | 0 | NS_ENSURE_SUCCESS(rv, false); |
732 | 0 |
|
733 | 0 | if (mWithinFrameAncstorsDir) { |
734 | 0 | // no path matching for frame-ancestors to not leak any path information. |
735 | 0 | return true; |
736 | 0 | } |
737 | 0 | |
738 | 0 | nsString decodedUriPath; |
739 | 0 | CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath); |
740 | 0 |
|
741 | 0 | // check if the last character of mPath is '/'; if so |
742 | 0 | // we just have to check loading resource is within |
743 | 0 | // the allowed path. |
744 | 0 | if (mPath.Last() == '/') { |
745 | 0 | if (!StringBeginsWith(decodedUriPath, mPath)) { |
746 | 0 | return false; |
747 | 0 | } |
748 | 0 | } |
749 | 0 | // otherwise mPath whitelists a specific file, and we have to |
750 | 0 | // check if the loading resource matches that whitelisted file. |
751 | 0 | else { |
752 | 0 | if (!mPath.Equals(decodedUriPath)) { |
753 | 0 | return false; |
754 | 0 | } |
755 | 0 | } |
756 | 0 | } |
757 | 0 | |
758 | 0 | // At the end: scheme, host, port and path match -> allow the load. |
759 | 0 | return true; |
760 | 0 | } |
761 | | |
762 | | bool |
763 | | nsCSPHostSrc::visit(nsCSPSrcVisitor* aVisitor) const |
764 | 0 | { |
765 | 0 | return aVisitor->visitHostSrc(*this); |
766 | 0 | } |
767 | | |
768 | | void |
769 | | nsCSPHostSrc::toString(nsAString& outStr) const |
770 | 879 | { |
771 | 879 | if (mGeneratedFromSelfKeyword) { |
772 | 25 | outStr.AppendLiteral("'self'"); |
773 | 25 | return; |
774 | 25 | } |
775 | 854 | |
776 | 854 | // If mHost is a single "*", we append the wildcard and return. |
777 | 854 | if (mHost.EqualsASCII("*") && |
778 | 854 | mScheme.IsEmpty() && |
779 | 854 | mPort.IsEmpty()) { |
780 | 58 | outStr.Append(mHost); |
781 | 58 | return; |
782 | 58 | } |
783 | 796 | |
784 | 796 | // append scheme |
785 | 796 | outStr.Append(mScheme); |
786 | 796 | |
787 | 796 | // append host |
788 | 796 | outStr.AppendLiteral("://"); |
789 | 796 | outStr.Append(mHost); |
790 | 796 | |
791 | 796 | // append port |
792 | 796 | if (!mPort.IsEmpty()) { |
793 | 77 | outStr.AppendLiteral(":"); |
794 | 77 | outStr.Append(mPort); |
795 | 77 | } |
796 | 796 | |
797 | 796 | // append path |
798 | 796 | outStr.Append(mPath); |
799 | 796 | } |
800 | | |
801 | | void |
802 | | nsCSPHostSrc::setScheme(const nsAString& aScheme) |
803 | 21.3k | { |
804 | 21.3k | mScheme = aScheme; |
805 | 21.3k | ToLowerCase(mScheme); |
806 | 21.3k | } |
807 | | |
808 | | void |
809 | | nsCSPHostSrc::setPort(const nsAString& aPort) |
810 | 531 | { |
811 | 531 | mPort = aPort; |
812 | 531 | } |
813 | | |
814 | | void |
815 | | nsCSPHostSrc::appendPath(const nsAString& aPath) |
816 | 14.7k | { |
817 | 14.7k | mPath.Append(aPath); |
818 | 14.7k | } |
819 | | |
820 | | /* ===== nsCSPKeywordSrc ===================== */ |
821 | | |
822 | | nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword) |
823 | | : mKeyword(aKeyword) |
824 | 917 | { |
825 | 917 | NS_ASSERTION((aKeyword != CSP_SELF), |
826 | 917 | "'self' should have been replaced in the parser"); |
827 | 917 | } |
828 | | |
829 | | nsCSPKeywordSrc::~nsCSPKeywordSrc() |
830 | | { |
831 | | } |
832 | | |
833 | | bool |
834 | | nsCSPKeywordSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, |
835 | | bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const |
836 | 0 | { |
837 | 0 | // no need to check for invalidated, this will always return false unless |
838 | 0 | // it is an nsCSPKeywordSrc for 'strict-dynamic', which should allow non |
839 | 0 | // parser created scripts. |
840 | 0 | return ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated); |
841 | 0 | } |
842 | | |
843 | | bool |
844 | | nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, |
845 | | bool aParserCreated) const |
846 | 0 | { |
847 | 0 | CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: %s", |
848 | 0 | CSP_EnumToUTF8Keyword(aKeyword), |
849 | 0 | NS_ConvertUTF16toUTF8(aHashOrNonce).get(), |
850 | 0 | mInvalidated ? "yes" : "false")); |
851 | 0 |
|
852 | 0 | if (mInvalidated) { |
853 | 0 | // only 'self' and 'unsafe-inline' are keywords that can be ignored. Please note that |
854 | 0 | // the parser already translates 'self' into a uri (see assertion in constructor). |
855 | 0 | MOZ_ASSERT(mKeyword == CSP_UNSAFE_INLINE, |
856 | 0 | "should only invalidate unsafe-inline"); |
857 | 0 | return false; |
858 | 0 | } |
859 | 0 | // either the keyword allows the load or the policy contains 'strict-dynamic', in which |
860 | 0 | // case we have to make sure the script is not parser created before allowing the load |
861 | 0 | // and also eval should be blocked even if 'strict-dynamic' is present. Should be |
862 | 0 | // allowed only if 'unsafe-eval' is present. |
863 | 0 | return ((mKeyword == aKeyword) || |
864 | 0 | ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated && |
865 | 0 | aKeyword != CSP_UNSAFE_EVAL)); |
866 | 0 | } |
867 | | |
868 | | bool |
869 | | nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const |
870 | 0 | { |
871 | 0 | return aVisitor->visitKeywordSrc(*this); |
872 | 0 | } |
873 | | |
874 | | void |
875 | | nsCSPKeywordSrc::toString(nsAString& outStr) const |
876 | 279 | { |
877 | 279 | outStr.Append(CSP_EnumToUTF16Keyword(mKeyword)); |
878 | 279 | } |
879 | | |
880 | | /* ===== nsCSPNonceSrc ==================== */ |
881 | | |
882 | | nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce) |
883 | | : mNonce(aNonce) |
884 | 316 | { |
885 | 316 | } |
886 | | |
887 | | nsCSPNonceSrc::~nsCSPNonceSrc() |
888 | 316 | { |
889 | 316 | } |
890 | | |
891 | | bool |
892 | | nsCSPNonceSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, |
893 | | bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const |
894 | 0 | { |
895 | 0 | if (CSPUTILSLOGENABLED()) { |
896 | 0 | CSPUTILSLOG(("nsCSPNonceSrc::permits, aUri: %s, aNonce: %s", |
897 | 0 | aUri->GetSpecOrDefault().get(), |
898 | 0 | NS_ConvertUTF16toUTF8(aNonce).get())); |
899 | 0 | } |
900 | 0 |
|
901 | 0 | // nonces can not be invalidated by strict-dynamic |
902 | 0 | return mNonce.Equals(aNonce); |
903 | 0 | } |
904 | | |
905 | | bool |
906 | | nsCSPNonceSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, |
907 | | bool aParserCreated) const |
908 | 0 | { |
909 | 0 | CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s", |
910 | 0 | CSP_EnumToUTF8Keyword(aKeyword), |
911 | 0 | NS_ConvertUTF16toUTF8(aHashOrNonce).get())); |
912 | 0 |
|
913 | 0 | if (aKeyword != CSP_NONCE) { |
914 | 0 | return false; |
915 | 0 | } |
916 | 0 | // nonces can not be invalidated by strict-dynamic |
917 | 0 | return mNonce.Equals(aHashOrNonce); |
918 | 0 | } |
919 | | |
920 | | bool |
921 | | nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const |
922 | 0 | { |
923 | 0 | return aVisitor->visitNonceSrc(*this); |
924 | 0 | } |
925 | | |
926 | | void |
927 | | nsCSPNonceSrc::toString(nsAString& outStr) const |
928 | 6 | { |
929 | 6 | outStr.Append(CSP_EnumToUTF16Keyword(CSP_NONCE)); |
930 | 6 | outStr.Append(mNonce); |
931 | 6 | outStr.AppendLiteral("'"); |
932 | 6 | } |
933 | | |
934 | | /* ===== nsCSPHashSrc ===================== */ |
935 | | |
936 | | nsCSPHashSrc::nsCSPHashSrc(const nsAString& aAlgo, const nsAString& aHash) |
937 | | : mAlgorithm(aAlgo) |
938 | | , mHash(aHash) |
939 | 408 | { |
940 | 408 | // Only the algo should be rewritten to lowercase, the hash must remain the same. |
941 | 408 | ToLowerCase(mAlgorithm); |
942 | 408 | } |
943 | | |
944 | | nsCSPHashSrc::~nsCSPHashSrc() |
945 | 408 | { |
946 | 408 | } |
947 | | |
948 | | bool |
949 | | nsCSPHashSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, |
950 | | bool aParserCreated) const |
951 | 0 | { |
952 | 0 | CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s", |
953 | 0 | CSP_EnumToUTF8Keyword(aKeyword), |
954 | 0 | NS_ConvertUTF16toUTF8(aHashOrNonce).get())); |
955 | 0 |
|
956 | 0 | if (aKeyword != CSP_HASH) { |
957 | 0 | return false; |
958 | 0 | } |
959 | 0 | |
960 | 0 | // hashes can not be invalidated by strict-dynamic |
961 | 0 | |
962 | 0 | // Convert aHashOrNonce to UTF-8 |
963 | 0 | NS_ConvertUTF16toUTF8 utf8_hash(aHashOrNonce); |
964 | 0 |
|
965 | 0 | nsresult rv; |
966 | 0 | nsCOMPtr<nsICryptoHash> hasher; |
967 | 0 | hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv); |
968 | 0 | NS_ENSURE_SUCCESS(rv, false); |
969 | 0 |
|
970 | 0 | rv = hasher->InitWithString(NS_ConvertUTF16toUTF8(mAlgorithm)); |
971 | 0 | NS_ENSURE_SUCCESS(rv, false); |
972 | 0 |
|
973 | 0 | rv = hasher->Update((uint8_t *)utf8_hash.get(), utf8_hash.Length()); |
974 | 0 | NS_ENSURE_SUCCESS(rv, false); |
975 | 0 |
|
976 | 0 | nsAutoCString hash; |
977 | 0 | rv = hasher->Finish(true, hash); |
978 | 0 | NS_ENSURE_SUCCESS(rv, false); |
979 | 0 |
|
980 | 0 | return NS_ConvertUTF16toUTF8(mHash).Equals(hash); |
981 | 0 | } |
982 | | |
983 | | bool |
984 | | nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const |
985 | 0 | { |
986 | 0 | return aVisitor->visitHashSrc(*this); |
987 | 0 | } |
988 | | |
989 | | void |
990 | | nsCSPHashSrc::toString(nsAString& outStr) const |
991 | 156 | { |
992 | 156 | outStr.AppendLiteral("'"); |
993 | 156 | outStr.Append(mAlgorithm); |
994 | 156 | outStr.AppendLiteral("-"); |
995 | 156 | outStr.Append(mHash); |
996 | 156 | outStr.AppendLiteral("'"); |
997 | 156 | } |
998 | | |
999 | | /* ===== nsCSPReportURI ===================== */ |
1000 | | |
1001 | | nsCSPReportURI::nsCSPReportURI(nsIURI *aURI) |
1002 | | :mReportURI(aURI) |
1003 | 2.99M | { |
1004 | 2.99M | } |
1005 | | |
1006 | | nsCSPReportURI::~nsCSPReportURI() |
1007 | 2.99M | { |
1008 | 2.99M | } |
1009 | | |
1010 | | bool |
1011 | | nsCSPReportURI::visit(nsCSPSrcVisitor* aVisitor) const |
1012 | 0 | { |
1013 | 0 | return false; |
1014 | 0 | } |
1015 | | |
1016 | | void |
1017 | | nsCSPReportURI::toString(nsAString& outStr) const |
1018 | 0 | { |
1019 | 0 | nsAutoCString spec; |
1020 | 0 | nsresult rv = mReportURI->GetSpec(spec); |
1021 | 0 | if (NS_FAILED(rv)) { |
1022 | 0 | return; |
1023 | 0 | } |
1024 | 0 | outStr.AppendASCII(spec.get()); |
1025 | 0 | } |
1026 | | |
1027 | | /* ===== nsCSPSandboxFlags ===================== */ |
1028 | | |
1029 | | nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags) |
1030 | | : mFlags(aFlags) |
1031 | 103 | { |
1032 | 103 | ToLowerCase(mFlags); |
1033 | 103 | } |
1034 | | |
1035 | | nsCSPSandboxFlags::~nsCSPSandboxFlags() |
1036 | 103 | { |
1037 | 103 | } |
1038 | | |
1039 | | bool |
1040 | | nsCSPSandboxFlags::visit(nsCSPSrcVisitor* aVisitor) const |
1041 | 0 | { |
1042 | 0 | return false; |
1043 | 0 | } |
1044 | | |
1045 | | void |
1046 | | nsCSPSandboxFlags::toString(nsAString& outStr) const |
1047 | 0 | { |
1048 | 0 | outStr.Append(mFlags); |
1049 | 0 | } |
1050 | | |
1051 | | /* ===== nsCSPDirective ====================== */ |
1052 | | |
1053 | | nsCSPDirective::nsCSPDirective(CSPDirective aDirective) |
1054 | 8.72k | { |
1055 | 8.72k | mDirective = aDirective; |
1056 | 8.72k | } |
1057 | | |
1058 | | nsCSPDirective::~nsCSPDirective() |
1059 | 8.72k | { |
1060 | 3.03M | for (uint32_t i = 0; i < mSrcs.Length(); i++) { |
1061 | 3.02M | delete mSrcs[i]; |
1062 | 3.02M | } |
1063 | 8.72k | } |
1064 | | |
1065 | | bool |
1066 | | nsCSPDirective::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected, |
1067 | | bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const |
1068 | 0 | { |
1069 | 0 | if (CSPUTILSLOGENABLED()) { |
1070 | 0 | CSPUTILSLOG(("nsCSPDirective::permits, aUri: %s", |
1071 | 0 | aUri->GetSpecOrDefault().get())); |
1072 | 0 | } |
1073 | 0 |
|
1074 | 0 | for (uint32_t i = 0; i < mSrcs.Length(); i++) { |
1075 | 0 | if (mSrcs[i]->permits(aUri, aNonce, aWasRedirected, aReportOnly, aUpgradeInsecure, aParserCreated)) { |
1076 | 0 | return true; |
1077 | 0 | } |
1078 | 0 | } |
1079 | 0 | return false; |
1080 | 0 | } |
1081 | | |
1082 | | bool |
1083 | | nsCSPDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, |
1084 | | bool aParserCreated) const |
1085 | 0 | { |
1086 | 0 | CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, a HashOrNonce: %s", |
1087 | 0 | CSP_EnumToUTF8Keyword(aKeyword), |
1088 | 0 | NS_ConvertUTF16toUTF8(aHashOrNonce).get())); |
1089 | 0 |
|
1090 | 0 | for (uint32_t i = 0; i < mSrcs.Length(); i++) { |
1091 | 0 | if (mSrcs[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) { |
1092 | 0 | return true; |
1093 | 0 | } |
1094 | 0 | } |
1095 | 0 | return false; |
1096 | 0 | } |
1097 | | |
1098 | | void |
1099 | | nsCSPDirective::toString(nsAString& outStr) const |
1100 | 0 | { |
1101 | 0 | // Append directive name |
1102 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective)); |
1103 | 0 | outStr.AppendLiteral(" "); |
1104 | 0 |
|
1105 | 0 | // Append srcs |
1106 | 0 | uint32_t length = mSrcs.Length(); |
1107 | 0 | for (uint32_t i = 0; i < length; i++) { |
1108 | 0 | mSrcs[i]->toString(outStr); |
1109 | 0 | if (i != (length - 1)) { |
1110 | 0 | outStr.AppendLiteral(" "); |
1111 | 0 | } |
1112 | 0 | } |
1113 | 0 | } |
1114 | | |
1115 | | void |
1116 | | nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const |
1117 | 0 | { |
1118 | 0 | mozilla::dom::Sequence<nsString> srcs; |
1119 | 0 | nsString src; |
1120 | 0 | for (uint32_t i = 0; i < mSrcs.Length(); i++) { |
1121 | 0 | src.Truncate(); |
1122 | 0 | mSrcs[i]->toString(src); |
1123 | 0 | srcs.AppendElement(src, mozilla::fallible); |
1124 | 0 | } |
1125 | 0 |
|
1126 | 0 | switch(mDirective) { |
1127 | 0 | case nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE: |
1128 | 0 | outCSP.mDefault_src.Construct(); |
1129 | 0 | outCSP.mDefault_src.Value() = std::move(srcs); |
1130 | 0 | return; |
1131 | 0 |
|
1132 | 0 | case nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE: |
1133 | 0 | outCSP.mScript_src.Construct(); |
1134 | 0 | outCSP.mScript_src.Value() = std::move(srcs); |
1135 | 0 | return; |
1136 | 0 |
|
1137 | 0 | case nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE: |
1138 | 0 | outCSP.mObject_src.Construct(); |
1139 | 0 | outCSP.mObject_src.Value() = std::move(srcs); |
1140 | 0 | return; |
1141 | 0 |
|
1142 | 0 | case nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE: |
1143 | 0 | outCSP.mStyle_src.Construct(); |
1144 | 0 | outCSP.mStyle_src.Value() = std::move(srcs); |
1145 | 0 | return; |
1146 | 0 |
|
1147 | 0 | case nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE: |
1148 | 0 | outCSP.mImg_src.Construct(); |
1149 | 0 | outCSP.mImg_src.Value() = std::move(srcs); |
1150 | 0 | return; |
1151 | 0 |
|
1152 | 0 | case nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE: |
1153 | 0 | outCSP.mMedia_src.Construct(); |
1154 | 0 | outCSP.mMedia_src.Value() = std::move(srcs); |
1155 | 0 | return; |
1156 | 0 |
|
1157 | 0 | case nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE: |
1158 | 0 | outCSP.mFrame_src.Construct(); |
1159 | 0 | outCSP.mFrame_src.Value() = std::move(srcs); |
1160 | 0 | return; |
1161 | 0 |
|
1162 | 0 | case nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE: |
1163 | 0 | outCSP.mFont_src.Construct(); |
1164 | 0 | outCSP.mFont_src.Value() = std::move(srcs); |
1165 | 0 | return; |
1166 | 0 |
|
1167 | 0 | case nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE: |
1168 | 0 | outCSP.mConnect_src.Construct(); |
1169 | 0 | outCSP.mConnect_src.Value() = std::move(srcs); |
1170 | 0 | return; |
1171 | 0 |
|
1172 | 0 | case nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE: |
1173 | 0 | outCSP.mReport_uri.Construct(); |
1174 | 0 | outCSP.mReport_uri.Value() = std::move(srcs); |
1175 | 0 | return; |
1176 | 0 |
|
1177 | 0 | case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE: |
1178 | 0 | outCSP.mFrame_ancestors.Construct(); |
1179 | 0 | outCSP.mFrame_ancestors.Value() = std::move(srcs); |
1180 | 0 | return; |
1181 | 0 |
|
1182 | 0 | case nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE: |
1183 | 0 | outCSP.mManifest_src.Construct(); |
1184 | 0 | outCSP.mManifest_src.Value() = std::move(srcs); |
1185 | 0 | return; |
1186 | 0 | // not supporting REFLECTED_XSS_DIRECTIVE |
1187 | 0 |
|
1188 | 0 | case nsIContentSecurityPolicy::BASE_URI_DIRECTIVE: |
1189 | 0 | outCSP.mBase_uri.Construct(); |
1190 | 0 | outCSP.mBase_uri.Value() = std::move(srcs); |
1191 | 0 | return; |
1192 | 0 |
|
1193 | 0 | case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE: |
1194 | 0 | outCSP.mForm_action.Construct(); |
1195 | 0 | outCSP.mForm_action.Value() = std::move(srcs); |
1196 | 0 | return; |
1197 | 0 |
|
1198 | 0 | case nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT: |
1199 | 0 | outCSP.mBlock_all_mixed_content.Construct(); |
1200 | 0 | // does not have any srcs |
1201 | 0 | return; |
1202 | 0 |
|
1203 | 0 | case nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE: |
1204 | 0 | outCSP.mUpgrade_insecure_requests.Construct(); |
1205 | 0 | // does not have any srcs |
1206 | 0 | return; |
1207 | 0 |
|
1208 | 0 | case nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE: |
1209 | 0 | outCSP.mChild_src.Construct(); |
1210 | 0 | outCSP.mChild_src.Value() = std::move(srcs); |
1211 | 0 | return; |
1212 | 0 |
|
1213 | 0 | case nsIContentSecurityPolicy::SANDBOX_DIRECTIVE: |
1214 | 0 | outCSP.mSandbox.Construct(); |
1215 | 0 | outCSP.mSandbox.Value() = std::move(srcs); |
1216 | 0 | return; |
1217 | 0 |
|
1218 | 0 | case nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE: |
1219 | 0 | outCSP.mWorker_src.Construct(); |
1220 | 0 | outCSP.mWorker_src.Value() = std::move(srcs); |
1221 | 0 | return; |
1222 | 0 |
|
1223 | 0 | // REQUIRE_SRI_FOR is handled in nsCSPPolicy::toDomCSPStruct() |
1224 | 0 |
|
1225 | 0 | default: |
1226 | 0 | NS_ASSERTION(false, "cannot find directive to convert CSP to JSON"); |
1227 | 0 | } |
1228 | 0 | } |
1229 | | |
1230 | | |
1231 | | bool |
1232 | | nsCSPDirective::restrictsContentType(nsContentPolicyType aContentType) const |
1233 | 0 | { |
1234 | 0 | // make sure we do not check for the default src before any other sources |
1235 | 0 | if (isDefaultDirective()) { |
1236 | 0 | return false; |
1237 | 0 | } |
1238 | 0 | return mDirective == CSP_ContentTypeToDirective(aContentType); |
1239 | 0 | } |
1240 | | |
1241 | | void |
1242 | | nsCSPDirective::getReportURIs(nsTArray<nsString> &outReportURIs) const |
1243 | 0 | { |
1244 | 0 | NS_ASSERTION((mDirective == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE), "not a report-uri directive"); |
1245 | 0 |
|
1246 | 0 | // append uris |
1247 | 0 | nsString tmpReportURI; |
1248 | 0 | for (uint32_t i = 0; i < mSrcs.Length(); i++) { |
1249 | 0 | tmpReportURI.Truncate(); |
1250 | 0 | mSrcs[i]->toString(tmpReportURI); |
1251 | 0 | outReportURIs.AppendElement(tmpReportURI); |
1252 | 0 | } |
1253 | 0 | } |
1254 | | |
1255 | | bool |
1256 | | nsCSPDirective::visitSrcs(nsCSPSrcVisitor* aVisitor) const |
1257 | 0 | { |
1258 | 0 | for (uint32_t i = 0; i < mSrcs.Length(); i++) { |
1259 | 0 | if (!mSrcs[i]->visit(aVisitor)) { |
1260 | 0 | return false; |
1261 | 0 | } |
1262 | 0 | } |
1263 | 0 | return true; |
1264 | 0 | } |
1265 | | |
1266 | | bool nsCSPDirective::equals(CSPDirective aDirective) const |
1267 | 39.6k | { |
1268 | 39.6k | return (mDirective == aDirective); |
1269 | 39.6k | } |
1270 | | |
1271 | | void |
1272 | | nsCSPDirective::getDirName(nsAString& outStr) const |
1273 | 0 | { |
1274 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective)); |
1275 | 0 | } |
1276 | | |
1277 | | bool |
1278 | | nsCSPDirective::hasReportSampleKeyword() const |
1279 | 0 | { |
1280 | 0 | for (nsCSPBaseSrc* src : mSrcs) { |
1281 | 0 | if (src->isReportSample()) { |
1282 | 0 | return true; |
1283 | 0 | } |
1284 | 0 | } |
1285 | 0 |
|
1286 | 0 | return false; |
1287 | 0 | } |
1288 | | |
1289 | | /* =============== nsCSPChildSrcDirective ============= */ |
1290 | | |
1291 | | nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective) |
1292 | | : nsCSPDirective(aDirective) |
1293 | | , mRestrictFrames(false) |
1294 | | , mRestrictWorkers(false) |
1295 | 44 | { |
1296 | 44 | } |
1297 | | |
1298 | | nsCSPChildSrcDirective::~nsCSPChildSrcDirective() |
1299 | | { |
1300 | | } |
1301 | | |
1302 | | bool nsCSPChildSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const |
1303 | 0 | { |
1304 | 0 | if (aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT) { |
1305 | 0 | return mRestrictFrames; |
1306 | 0 | } |
1307 | 0 | if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER || |
1308 | 0 | aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER || |
1309 | 0 | aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) { |
1310 | 0 | return mRestrictWorkers; |
1311 | 0 | } |
1312 | 0 | return false; |
1313 | 0 | } |
1314 | | |
1315 | | bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const |
1316 | 3.11k | { |
1317 | 3.11k | if (aDirective == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) { |
1318 | 501 | return mRestrictFrames; |
1319 | 501 | } |
1320 | 2.61k | if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) { |
1321 | 1.07k | return mRestrictWorkers; |
1322 | 1.07k | } |
1323 | 1.54k | return (mDirective == aDirective); |
1324 | 1.54k | } |
1325 | | |
1326 | | /* =============== nsCSPScriptSrcDirective ============= */ |
1327 | | |
1328 | | nsCSPScriptSrcDirective::nsCSPScriptSrcDirective(CSPDirective aDirective) |
1329 | | : nsCSPDirective(aDirective) |
1330 | | , mRestrictWorkers(false) |
1331 | 89 | { |
1332 | 89 | } |
1333 | | |
1334 | | nsCSPScriptSrcDirective::~nsCSPScriptSrcDirective() |
1335 | | { |
1336 | | } |
1337 | | |
1338 | | bool nsCSPScriptSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const |
1339 | 0 | { |
1340 | 0 | if (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER || |
1341 | 0 | aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER || |
1342 | 0 | aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER) { |
1343 | 0 | return mRestrictWorkers; |
1344 | 0 | } |
1345 | 0 | return mDirective == CSP_ContentTypeToDirective(aContentType); |
1346 | 0 | } |
1347 | | |
1348 | | bool nsCSPScriptSrcDirective::equals(CSPDirective aDirective) const |
1349 | 1.28k | { |
1350 | 1.28k | if (aDirective == nsIContentSecurityPolicy::WORKER_SRC_DIRECTIVE) { |
1351 | 560 | return mRestrictWorkers; |
1352 | 560 | } |
1353 | 728 | return (mDirective == aDirective); |
1354 | 728 | } |
1355 | | |
1356 | | /* =============== nsBlockAllMixedContentDirective ============= */ |
1357 | | |
1358 | | nsBlockAllMixedContentDirective::nsBlockAllMixedContentDirective(CSPDirective aDirective) |
1359 | | : nsCSPDirective(aDirective) |
1360 | 3 | { |
1361 | 3 | } |
1362 | | |
1363 | | nsBlockAllMixedContentDirective::~nsBlockAllMixedContentDirective() |
1364 | | { |
1365 | | } |
1366 | | |
1367 | | void |
1368 | | nsBlockAllMixedContentDirective::toString(nsAString& outStr) const |
1369 | 0 | { |
1370 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString( |
1371 | 0 | nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)); |
1372 | 0 | } |
1373 | | |
1374 | | void |
1375 | | nsBlockAllMixedContentDirective::getDirName(nsAString& outStr) const |
1376 | 0 | { |
1377 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString( |
1378 | 0 | nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)); |
1379 | 0 | } |
1380 | | |
1381 | | /* =============== nsUpgradeInsecureDirective ============= */ |
1382 | | |
1383 | | nsUpgradeInsecureDirective::nsUpgradeInsecureDirective(CSPDirective aDirective) |
1384 | | : nsCSPDirective(aDirective) |
1385 | 5 | { |
1386 | 5 | } |
1387 | | |
1388 | | nsUpgradeInsecureDirective::~nsUpgradeInsecureDirective() |
1389 | | { |
1390 | | } |
1391 | | |
1392 | | void |
1393 | | nsUpgradeInsecureDirective::toString(nsAString& outStr) const |
1394 | 0 | { |
1395 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString( |
1396 | 0 | nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)); |
1397 | 0 | } |
1398 | | |
1399 | | void |
1400 | | nsUpgradeInsecureDirective::getDirName(nsAString& outStr) const |
1401 | 0 | { |
1402 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString( |
1403 | 0 | nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)); |
1404 | 0 | } |
1405 | | |
1406 | | /* ===== nsRequireSRIForDirective ========================= */ |
1407 | | |
1408 | | nsRequireSRIForDirective::nsRequireSRIForDirective(CSPDirective aDirective) |
1409 | | : nsCSPDirective(aDirective) |
1410 | 0 | { |
1411 | 0 | } |
1412 | | |
1413 | | nsRequireSRIForDirective::~nsRequireSRIForDirective() |
1414 | 0 | { |
1415 | 0 | } |
1416 | | |
1417 | | void |
1418 | | nsRequireSRIForDirective::toString(nsAString &outStr) const |
1419 | 0 | { |
1420 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString( |
1421 | 0 | nsIContentSecurityPolicy::REQUIRE_SRI_FOR)); |
1422 | 0 | for (uint32_t i = 0; i < mTypes.Length(); i++) { |
1423 | 0 | if (mTypes[i] == nsIContentPolicy::TYPE_SCRIPT) { |
1424 | 0 | outStr.AppendLiteral(" script"); |
1425 | 0 | } |
1426 | 0 | else if (mTypes[i] == nsIContentPolicy::TYPE_STYLESHEET) { |
1427 | 0 | outStr.AppendLiteral(" style"); |
1428 | 0 | } |
1429 | 0 | } |
1430 | 0 | } |
1431 | | |
1432 | | bool |
1433 | | nsRequireSRIForDirective::hasType(nsContentPolicyType aType) const |
1434 | 0 | { |
1435 | 0 | for (uint32_t i = 0; i < mTypes.Length(); i++) { |
1436 | 0 | if (mTypes[i] == aType) { |
1437 | 0 | return true; |
1438 | 0 | } |
1439 | 0 | } |
1440 | 0 | return false; |
1441 | 0 | } |
1442 | | |
1443 | | bool |
1444 | | nsRequireSRIForDirective::restrictsContentType(const nsContentPolicyType aType) const |
1445 | 0 | { |
1446 | 0 | return this->hasType(aType); |
1447 | 0 | } |
1448 | | |
1449 | | bool |
1450 | | nsRequireSRIForDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce, |
1451 | | bool aParserCreated) const |
1452 | 0 | { |
1453 | 0 | // can only disallow CSP_REQUIRE_SRI_FOR. |
1454 | 0 | return (aKeyword != CSP_REQUIRE_SRI_FOR); |
1455 | 0 | } |
1456 | | |
1457 | | void |
1458 | | nsRequireSRIForDirective::getDirName(nsAString& outStr) const |
1459 | 0 | { |
1460 | 0 | outStr.AppendASCII(CSP_CSPDirectiveToString( |
1461 | 0 | nsIContentSecurityPolicy::REQUIRE_SRI_FOR)); |
1462 | 0 | } |
1463 | | |
1464 | | /* ===== nsCSPPolicy ========================= */ |
1465 | | |
1466 | | nsCSPPolicy::nsCSPPolicy() |
1467 | | : mUpgradeInsecDir(nullptr) |
1468 | | , mReportOnly(false) |
1469 | 5.77k | { |
1470 | 5.77k | CSPUTILSLOG(("nsCSPPolicy::nsCSPPolicy")); |
1471 | 5.77k | } |
1472 | | |
1473 | | nsCSPPolicy::~nsCSPPolicy() |
1474 | 5.77k | { |
1475 | 5.77k | CSPUTILSLOG(("nsCSPPolicy::~nsCSPPolicy")); |
1476 | 5.77k | |
1477 | 11.0k | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1478 | 5.27k | delete mDirectives[i]; |
1479 | 5.27k | } |
1480 | 5.77k | } |
1481 | | |
1482 | | bool |
1483 | | nsCSPPolicy::permits(CSPDirective aDir, |
1484 | | nsIURI* aUri, |
1485 | | bool aSpecific) const |
1486 | 0 | { |
1487 | 0 | nsString outp; |
1488 | 0 | return this->permits(aDir, aUri, EmptyString(), false, aSpecific, false, outp); |
1489 | 0 | } |
1490 | | |
1491 | | bool |
1492 | | nsCSPPolicy::permits(CSPDirective aDir, |
1493 | | nsIURI* aUri, |
1494 | | const nsAString& aNonce, |
1495 | | bool aWasRedirected, |
1496 | | bool aSpecific, |
1497 | | bool aParserCreated, |
1498 | | nsAString& outViolatedDirective) const |
1499 | 0 | { |
1500 | 0 | if (CSPUTILSLOGENABLED()) { |
1501 | 0 | CSPUTILSLOG(("nsCSPPolicy::permits, aUri: %s, aDir: %d, aSpecific: %s", |
1502 | 0 | aUri->GetSpecOrDefault().get(), aDir, |
1503 | 0 | aSpecific ? "true" : "false")); |
1504 | 0 | } |
1505 | 0 |
|
1506 | 0 | NS_ASSERTION(aUri, "permits needs an uri to perform the check!"); |
1507 | 0 | outViolatedDirective.Truncate(); |
1508 | 0 |
|
1509 | 0 | nsCSPDirective* defaultDir = nullptr; |
1510 | 0 |
|
1511 | 0 | // Try to find a relevant directive |
1512 | 0 | // These directive arrays are short (1-5 elements), not worth using a hashtable. |
1513 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1514 | 0 | if (mDirectives[i]->equals(aDir)) { |
1515 | 0 | if (!mDirectives[i]->permits(aUri, aNonce, aWasRedirected, mReportOnly, |
1516 | 0 | mUpgradeInsecDir, aParserCreated)) { |
1517 | 0 | mDirectives[i]->getDirName(outViolatedDirective); |
1518 | 0 | return false; |
1519 | 0 | } |
1520 | 0 | return true; |
1521 | 0 | } |
1522 | 0 | if (mDirectives[i]->isDefaultDirective()) { |
1523 | 0 | defaultDir = mDirectives[i]; |
1524 | 0 | } |
1525 | 0 | } |
1526 | 0 |
|
1527 | 0 | // If the above loop runs through, we haven't found a matching directive. |
1528 | 0 | // Avoid relooping, just store the result of default-src while looping. |
1529 | 0 | if (!aSpecific && defaultDir) { |
1530 | 0 | if (!defaultDir->permits(aUri, aNonce, aWasRedirected, mReportOnly, |
1531 | 0 | mUpgradeInsecDir, aParserCreated)) { |
1532 | 0 | defaultDir->getDirName(outViolatedDirective); |
1533 | 0 | return false; |
1534 | 0 | } |
1535 | 0 | return true; |
1536 | 0 | } |
1537 | 0 | |
1538 | 0 | // Nothing restricts this, so we're allowing the load |
1539 | 0 | // See bug 764937 |
1540 | 0 | return true; |
1541 | 0 | } |
1542 | | |
1543 | | bool |
1544 | | nsCSPPolicy::allows(nsContentPolicyType aContentType, |
1545 | | enum CSPKeyword aKeyword, |
1546 | | const nsAString& aHashOrNonce, |
1547 | | bool aParserCreated) const |
1548 | 0 | { |
1549 | 0 | CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s", |
1550 | 0 | CSP_EnumToUTF8Keyword(aKeyword), |
1551 | 0 | NS_ConvertUTF16toUTF8(aHashOrNonce).get())); |
1552 | 0 |
|
1553 | 0 | nsCSPDirective* defaultDir = nullptr; |
1554 | 0 |
|
1555 | 0 | // Try to find a matching directive |
1556 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1557 | 0 | if (mDirectives[i]->restrictsContentType(aContentType)) { |
1558 | 0 | if (mDirectives[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) { |
1559 | 0 | return true; |
1560 | 0 | } |
1561 | 0 | return false; |
1562 | 0 | } |
1563 | 0 | if (mDirectives[i]->isDefaultDirective()) { |
1564 | 0 | defaultDir = mDirectives[i]; |
1565 | 0 | } |
1566 | 0 | } |
1567 | 0 |
|
1568 | 0 | // {nonce,hash}-source should not consult default-src: |
1569 | 0 | // * return false if default-src is specified |
1570 | 0 | // * but allow the load if default-src is *not* specified (Bug 1198422) |
1571 | 0 | if (aKeyword == CSP_NONCE || aKeyword == CSP_HASH) { |
1572 | 0 | if (!defaultDir) { |
1573 | 0 | return true; |
1574 | 0 | } |
1575 | 0 | return false; |
1576 | 0 | } |
1577 | 0 | |
1578 | 0 | // If the above loop runs through, we haven't found a matching directive. |
1579 | 0 | // Avoid relooping, just store the result of default-src while looping. |
1580 | 0 | if (defaultDir) { |
1581 | 0 | return defaultDir->allows(aKeyword, aHashOrNonce, aParserCreated); |
1582 | 0 | } |
1583 | 0 | |
1584 | 0 | // Allowing the load; see Bug 885433 |
1585 | 0 | // a) inline scripts (also unsafe eval) should only be blocked |
1586 | 0 | // if there is a [script-src] or [default-src] |
1587 | 0 | // b) inline styles should only be blocked |
1588 | 0 | // if there is a [style-src] or [default-src] |
1589 | 0 | return true; |
1590 | 0 | } |
1591 | | |
1592 | | bool |
1593 | | nsCSPPolicy::allows(nsContentPolicyType aContentType, |
1594 | | enum CSPKeyword aKeyword) const |
1595 | 0 | { |
1596 | 0 | return allows(aContentType, aKeyword, NS_LITERAL_STRING(""), false); |
1597 | 0 | } |
1598 | | |
1599 | | void |
1600 | | nsCSPPolicy::toString(nsAString& outStr) const |
1601 | 0 | { |
1602 | 0 | uint32_t length = mDirectives.Length(); |
1603 | 0 | for (uint32_t i = 0; i < length; ++i) { |
1604 | 0 | mDirectives[i]->toString(outStr); |
1605 | 0 | if (i != (length - 1)) { |
1606 | 0 | outStr.AppendLiteral("; "); |
1607 | 0 | } |
1608 | 0 | } |
1609 | 0 | } |
1610 | | |
1611 | | void |
1612 | | nsCSPPolicy::toDomCSPStruct(mozilla::dom::CSP& outCSP) const |
1613 | 0 | { |
1614 | 0 | outCSP.mReport_only = mReportOnly; |
1615 | 0 |
|
1616 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); ++i) { |
1617 | 0 | mDirectives[i]->toDomCSPStruct(outCSP); |
1618 | 0 | } |
1619 | 0 | } |
1620 | | |
1621 | | bool |
1622 | | nsCSPPolicy::hasDirective(CSPDirective aDir) const |
1623 | 17.3k | { |
1624 | 31.6k | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1625 | 17.8k | if (mDirectives[i]->equals(aDir)) { |
1626 | 3.57k | return true; |
1627 | 3.57k | } |
1628 | 17.8k | } |
1629 | 17.3k | return false; |
1630 | 17.3k | } |
1631 | | |
1632 | | /* |
1633 | | * Use this function only after ::allows() returned 'false'. Most and |
1634 | | * foremost it's used to get the violated directive before sending reports. |
1635 | | * The parameter outDirective is the equivalent of 'outViolatedDirective' |
1636 | | * for the ::permits() function family. |
1637 | | */ |
1638 | | void |
1639 | | nsCSPPolicy::getDirectiveStringAndReportSampleForContentType(nsContentPolicyType aContentType, |
1640 | | nsAString& outDirective, |
1641 | | bool* aReportSample) const |
1642 | 0 | { |
1643 | 0 | MOZ_ASSERT(aReportSample); |
1644 | 0 | *aReportSample = false; |
1645 | 0 |
|
1646 | 0 | nsCSPDirective* defaultDir = nullptr; |
1647 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1648 | 0 | if (mDirectives[i]->restrictsContentType(aContentType)) { |
1649 | 0 | mDirectives[i]->getDirName(outDirective); |
1650 | 0 | *aReportSample = mDirectives[i]->hasReportSampleKeyword(); |
1651 | 0 | return; |
1652 | 0 | } |
1653 | 0 | if (mDirectives[i]->isDefaultDirective()) { |
1654 | 0 | defaultDir = mDirectives[i]; |
1655 | 0 | } |
1656 | 0 | } |
1657 | 0 | // if we haven't found a matching directive yet, |
1658 | 0 | // the contentType must be restricted by the default directive |
1659 | 0 | if (defaultDir) { |
1660 | 0 | defaultDir->getDirName(outDirective); |
1661 | 0 | *aReportSample = defaultDir->hasReportSampleKeyword(); |
1662 | 0 | return; |
1663 | 0 | } |
1664 | 0 | NS_ASSERTION(false, "Can not query directive string for contentType!"); |
1665 | 0 | outDirective.AppendLiteral("couldNotQueryViolatedDirective"); |
1666 | 0 | } |
1667 | | |
1668 | | void |
1669 | | nsCSPPolicy::getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const |
1670 | 0 | { |
1671 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1672 | 0 | if (mDirectives[i]->equals(aDir)) { |
1673 | 0 | mDirectives[i]->toString(outDirective); |
1674 | 0 | return; |
1675 | 0 | } |
1676 | 0 | } |
1677 | 0 | } |
1678 | | |
1679 | | /* |
1680 | | * Helper function that returns the underlying bit representation of sandbox |
1681 | | * flags. The function returns SANDBOXED_NONE if there are no sandbox |
1682 | | * directives. |
1683 | | */ |
1684 | | uint32_t |
1685 | | nsCSPPolicy::getSandboxFlags() const |
1686 | 0 | { |
1687 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1688 | 0 | if (mDirectives[i]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) { |
1689 | 0 | nsAutoString flags; |
1690 | 0 | mDirectives[i]->toString(flags); |
1691 | 0 |
|
1692 | 0 | if (flags.IsEmpty()) { |
1693 | 0 | return SANDBOX_ALL_FLAGS; |
1694 | 0 | } |
1695 | 0 | |
1696 | 0 | nsAttrValue attr; |
1697 | 0 | attr.ParseAtomArray(flags); |
1698 | 0 |
|
1699 | 0 | return nsContentUtils::ParseSandboxAttributeToFlags(&attr); |
1700 | 0 | } |
1701 | 0 | } |
1702 | 0 |
|
1703 | 0 | return SANDBOXED_NONE; |
1704 | 0 | } |
1705 | | |
1706 | | void |
1707 | | nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const |
1708 | 0 | { |
1709 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1710 | 0 | if (mDirectives[i]->equals(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) { |
1711 | 0 | mDirectives[i]->getReportURIs(outReportURIs); |
1712 | 0 | return; |
1713 | 0 | } |
1714 | 0 | } |
1715 | 0 | } |
1716 | | |
1717 | | bool |
1718 | | nsCSPPolicy::visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const |
1719 | 0 | { |
1720 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1721 | 0 | if (mDirectives[i]->equals(aDir)) { |
1722 | 0 | return mDirectives[i]->visitSrcs(aVisitor); |
1723 | 0 | } |
1724 | 0 | } |
1725 | 0 | return false; |
1726 | 0 | } |
1727 | | |
1728 | | bool |
1729 | | nsCSPPolicy::requireSRIForType(nsContentPolicyType aContentType) |
1730 | 0 | { |
1731 | 0 | for (uint32_t i = 0; i < mDirectives.Length(); i++) { |
1732 | 0 | if (mDirectives[i]->equals(nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) { |
1733 | 0 | return static_cast<nsRequireSRIForDirective*>(mDirectives[i])->hasType(aContentType); |
1734 | 0 | } |
1735 | 0 | } |
1736 | 0 | return false; |
1737 | 0 | } |