Source code

Revision control

Other Tools

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 <string>
8
#include <unordered_set>
9
10
#include "nsCOMPtr.h"
11
#include "nsContentPolicyUtils.h"
12
#include "nsContentUtils.h"
13
#include "nsCSPContext.h"
14
#include "nsCSPParser.h"
15
#include "nsCSPService.h"
16
#include "nsError.h"
17
#include "nsIAsyncVerifyRedirectCallback.h"
18
#include "nsIClassInfoImpl.h"
19
#include "nsIDocShell.h"
20
#include "nsIDocShellTreeItem.h"
21
#include "mozilla/dom/Document.h"
22
#include "nsIHttpChannel.h"
23
#include "nsIInterfaceRequestor.h"
24
#include "nsIInterfaceRequestorUtils.h"
25
#include "nsIObjectInputStream.h"
26
#include "nsIObjectOutputStream.h"
27
#include "nsIObserver.h"
28
#include "nsIObserverService.h"
29
#include "nsIStringStream.h"
30
#include "nsISupportsPrimitives.h"
31
#include "nsIUploadChannel.h"
32
#include "nsIURIMutator.h"
33
#include "nsIScriptError.h"
34
#include "nsIWebNavigation.h"
35
#include "nsMimeTypes.h"
36
#include "nsNetUtil.h"
37
#include "nsIContentPolicy.h"
38
#include "nsSupportsPrimitives.h"
39
#include "nsThreadUtils.h"
40
#include "nsString.h"
41
#include "nsScriptSecurityManager.h"
42
#include "nsStringStream.h"
43
#include "mozilla/Logging.h"
44
#include "mozilla/Preferences.h"
45
#include "mozilla/dom/CSPReportBinding.h"
46
#include "mozilla/dom/CSPDictionariesBinding.h"
47
#include "mozilla/ipc/PBackgroundSharedTypes.h"
48
#include "nsINetworkInterceptController.h"
49
#include "nsSandboxFlags.h"
50
#include "nsIScriptElement.h"
51
#include "nsIEventTarget.h"
52
#include "mozilla/dom/DocGroup.h"
53
#include "mozilla/dom/Element.h"
54
#include "nsXULAppAPI.h"
55
#include "nsJSUtils.h"
56
57
using namespace mozilla;
58
using namespace mozilla::dom;
59
60
static LogModule* GetCspContextLog() {
61
static LazyLogModule gCspContextPRLog("CSPContext");
62
return gCspContextPRLog;
63
}
64
65
#define CSPCONTEXTLOG(args) \
66
MOZ_LOG(GetCspContextLog(), mozilla::LogLevel::Debug, args)
67
#define CSPCONTEXTLOGENABLED() \
68
MOZ_LOG_TEST(GetCspContextLog(), mozilla::LogLevel::Debug)
69
70
static const uint32_t CSP_CACHE_URI_CUTOFF_SIZE = 512;
71
72
#ifdef DEBUG
73
/**
74
* This function is only used for verification purposes within
75
* GatherSecurityPolicyViolationEventData.
76
*/
77
static bool ValidateDirectiveName(const nsAString& aDirective) {
78
static const auto directives = []() {
79
std::unordered_set<std::string> directives;
80
constexpr size_t dirLen =
81
sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]);
82
for (size_t i = 0; i < dirLen; ++i) {
83
directives.insert(CSPStrDirectives[i]);
84
}
85
return directives;
86
}();
87
88
nsAutoString directive(aDirective);
89
auto itr = directives.find(NS_ConvertUTF16toUTF8(directive).get());
90
return itr != directives.end();
91
}
92
#endif // DEBUG
93
94
static void BlockedContentSourceToString(
95
nsCSPContext::BlockedContentSource aSource, nsACString& aString) {
96
switch (aSource) {
97
case nsCSPContext::BlockedContentSource::eUnknown:
98
aString.Truncate();
99
break;
100
101
case nsCSPContext::BlockedContentSource::eInline:
102
aString.AssignLiteral("inline");
103
break;
104
105
case nsCSPContext::BlockedContentSource::eEval:
106
aString.AssignLiteral("eval");
107
break;
108
109
case nsCSPContext::BlockedContentSource::eSelf:
110
aString.AssignLiteral("self");
111
break;
112
}
113
}
114
115
/* ===== nsIContentSecurityPolicy impl ====== */
116
117
NS_IMETHODIMP
118
nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
119
nsICSPEventListener* aCSPEventListener,
120
nsIURI* aContentLocation, nsIURI* aRequestOrigin,
121
nsISupports* aRequestContext,
122
const nsACString& aMimeTypeGuess,
123
nsIURI* aOriginalURIIfRedirect,
124
bool aSendViolationReports, const nsAString& aNonce,
125
int16_t* outDecision) {
126
if (CSPCONTEXTLOGENABLED()) {
127
CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s",
128
aContentLocation->GetSpecOrDefault().get()));
129
CSPCONTEXTLOG((">>>> aContentType: %d", aContentType));
130
}
131
132
bool isPreload = nsContentUtils::IsPreloadType(aContentType);
133
134
// Since we know whether we are dealing with a preload, we have to convert
135
// the internal policytype ot the external policy type before moving on.
136
// We still need to know if this is a worker so child-src can handle that
137
// case correctly.
138
aContentType =
139
nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(aContentType);
140
141
// This ShouldLoad function is called from nsCSPService::ShouldLoad,
142
// which already checked a number of things, including:
143
// * aContentLocation is not null; we can consume this without further checks
144
// * scheme is not a whitelisted scheme (about: chrome:, etc).
145
// * CSP is enabled
146
// * Content Type is not whitelisted (CSP Reports, TYPE_DOCUMENT, etc).
147
// * Fast Path for Apps
148
149
// Default decision, CSP can revise it if there's a policy to enforce
150
*outDecision = nsIContentPolicy::ACCEPT;
151
152
// If the content type doesn't map to a CSP directive, there's nothing for
153
// CSP to do.
154
CSPDirective dir = CSP_ContentTypeToDirective(aContentType);
155
if (dir == nsIContentSecurityPolicy::NO_DIRECTIVE) {
156
return NS_OK;
157
}
158
159
bool parserCreated = false;
160
if (!isPreload) {
161
nsCOMPtr<nsIScriptElement> script = do_QueryInterface(aRequestContext);
162
if (script && script->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER) {
163
parserCreated = true;
164
}
165
}
166
167
bool permitted =
168
permitsInternal(dir,
169
nullptr, // aTriggeringElement
170
aCSPEventListener, aContentLocation,
171
aOriginalURIIfRedirect, aNonce, isPreload,
172
false, // allow fallback to default-src
173
aSendViolationReports,
174
true, // send blocked URI in violation reports
175
parserCreated);
176
177
*outDecision =
178
permitted ? nsIContentPolicy::ACCEPT : nsIContentPolicy::REJECT_SERVER;
179
180
if (CSPCONTEXTLOGENABLED()) {
181
CSPCONTEXTLOG(
182
("nsCSPContext::ShouldLoad, decision: %s, "
183
"aContentLocation: %s",
184
*outDecision > 0 ? "load" : "deny",
185
aContentLocation->GetSpecOrDefault().get()));
186
}
187
return NS_OK;
188
}
189
190
bool nsCSPContext::permitsInternal(
191
CSPDirective aDir, Element* aTriggeringElement,
192
nsICSPEventListener* aCSPEventListener, nsIURI* aContentLocation,
193
nsIURI* aOriginalURIIfRedirect, const nsAString& aNonce, bool aIsPreload,
194
bool aSpecific, bool aSendViolationReports,
195
bool aSendContentLocationInViolationReports, bool aParserCreated) {
196
bool permits = true;
197
198
nsAutoString violatedDirective;
199
for (uint32_t p = 0; p < mPolicies.Length(); p++) {
200
if (!mPolicies[p]->permits(aDir, aContentLocation, aNonce,
201
!!aOriginalURIIfRedirect, aSpecific,
202
aParserCreated, violatedDirective)) {
203
// If the policy is violated and not report-only, reject the load and
204
// report to the console
205
if (!mPolicies[p]->getReportOnlyFlag()) {
206
CSPCONTEXTLOG(("nsCSPContext::permitsInternal, false"));
207
permits = false;
208
}
209
210
// Do not send a report or notify observers if this is a preload - the
211
// decision may be wrong due to the inability to get the nonce, and will
212
// incorrectly fail the unit tests.
213
if (!aIsPreload && aSendViolationReports) {
214
uint32_t lineNumber = 0;
215
uint32_t columnNumber = 0;
216
nsAutoString spec;
217
JSContext* cx = nsContentUtils::GetCurrentJSContext();
218
if (cx) {
219
nsJSUtils::GetCallingLocation(cx, spec, &lineNumber, &columnNumber);
220
// If GetCallingLocation fails linenumber & columnNumber are set to 0
221
// anyway so we can skip checking if that is the case.
222
}
223
AsyncReportViolation(
224
aTriggeringElement, aCSPEventListener,
225
(aSendContentLocationInViolationReports ? aContentLocation
226
: nullptr),
227
BlockedContentSource::eUnknown, /* a BlockedContentSource */
228
aOriginalURIIfRedirect, /* in case of redirect originalURI is not
229
null */
230
violatedDirective, p, /* policy index */
231
EmptyString(), /* no observer subject */
232
spec, /* source file */
233
EmptyString(), /* no script sample */
234
lineNumber, /* line number */
235
columnNumber); /* column number */
236
}
237
}
238
}
239
240
return permits;
241
}
242
243
/* ===== nsISupports implementation ========== */
244
245
NS_IMPL_CLASSINFO(nsCSPContext, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
246
NS_CSPCONTEXT_CID)
247
248
NS_IMPL_ISUPPORTS_CI(nsCSPContext, nsIContentSecurityPolicy, nsISerializable)
249
250
nsCSPContext::nsCSPContext()
251
: mInnerWindowID(0),
252
mSkipAllowInlineStyleCheck(false),
253
mLoadingContext(nullptr),
254
mLoadingPrincipal(nullptr),
255
mQueueUpMessages(true) {
256
CSPCONTEXTLOG(("nsCSPContext::nsCSPContext"));
257
}
258
259
nsCSPContext::~nsCSPContext() {
260
CSPCONTEXTLOG(("nsCSPContext::~nsCSPContext"));
261
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
262
delete mPolicies[i];
263
}
264
}
265
266
/* static */
267
bool nsCSPContext::Equals(nsIContentSecurityPolicy* aCSP,
268
nsIContentSecurityPolicy* aOtherCSP) {
269
if (aCSP == aOtherCSP) {
270
// fast path for pointer equality
271
return true;
272
}
273
274
uint32_t policyCount = 0;
275
if (aCSP) {
276
aCSP->GetPolicyCount(&policyCount);
277
}
278
279
uint32_t otherPolicyCount = 0;
280
if (aOtherCSP) {
281
aOtherCSP->GetPolicyCount(&otherPolicyCount);
282
}
283
284
if (policyCount != otherPolicyCount) {
285
return false;
286
}
287
288
nsAutoString policyStr, otherPolicyStr;
289
for (uint32_t i = 0; i < policyCount; ++i) {
290
aCSP->GetPolicyString(i, policyStr);
291
aOtherCSP->GetPolicyString(i, otherPolicyStr);
292
if (!policyStr.Equals(otherPolicyStr)) {
293
return false;
294
}
295
}
296
297
return true;
298
}
299
300
nsresult nsCSPContext::InitFromOther(nsCSPContext* aOtherContext) {
301
NS_ENSURE_ARG(aOtherContext);
302
303
nsresult rv = NS_OK;
304
nsCOMPtr<Document> doc = do_QueryReferent(aOtherContext->mLoadingContext);
305
if (doc) {
306
rv = SetRequestContextWithDocument(doc);
307
} else {
308
rv = SetRequestContextWithPrincipal(
309
aOtherContext->mLoadingPrincipal, aOtherContext->mSelfURI,
310
aOtherContext->mReferrer, aOtherContext->mInnerWindowID);
311
}
312
NS_ENSURE_SUCCESS(rv, rv);
313
314
mSkipAllowInlineStyleCheck = aOtherContext->mSkipAllowInlineStyleCheck;
315
316
for (auto policy : aOtherContext->mPolicies) {
317
nsAutoString policyStr;
318
policy->toString(policyStr);
319
AppendPolicy(policyStr, policy->getReportOnlyFlag(),
320
policy->getDeliveredViaMetaTagFlag());
321
}
322
mIPCPolicies = aOtherContext->mIPCPolicies;
323
return NS_OK;
324
}
325
326
void nsCSPContext::EnsureIPCPoliciesRead() {
327
if (mIPCPolicies.Length() > 0) {
328
nsresult rv;
329
for (auto& policy : mIPCPolicies) {
330
rv = AppendPolicy(policy.policy(), policy.reportOnlyFlag(),
331
policy.deliveredViaMetaTagFlag());
332
Unused << NS_WARN_IF(NS_FAILED(rv));
333
}
334
mIPCPolicies.Clear();
335
}
336
}
337
338
NS_IMETHODIMP
339
nsCSPContext::GetPolicyString(uint32_t aIndex, nsAString& outStr) {
340
outStr.Truncate();
341
EnsureIPCPoliciesRead();
342
if (aIndex >= mPolicies.Length()) {
343
return NS_ERROR_ILLEGAL_VALUE;
344
}
345
mPolicies[aIndex]->toString(outStr);
346
return NS_OK;
347
}
348
349
const nsCSPPolicy* nsCSPContext::GetPolicy(uint32_t aIndex) {
350
EnsureIPCPoliciesRead();
351
if (aIndex >= mPolicies.Length()) {
352
return nullptr;
353
}
354
return mPolicies[aIndex];
355
}
356
357
NS_IMETHODIMP
358
nsCSPContext::GetPolicyCount(uint32_t* outPolicyCount) {
359
EnsureIPCPoliciesRead();
360
*outPolicyCount = mPolicies.Length();
361
return NS_OK;
362
}
363
364
NS_IMETHODIMP
365
nsCSPContext::GetUpgradeInsecureRequests(bool* outUpgradeRequest) {
366
EnsureIPCPoliciesRead();
367
*outUpgradeRequest = false;
368
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
369
if (mPolicies[i]->hasDirective(
370
nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
371
*outUpgradeRequest = true;
372
return NS_OK;
373
}
374
}
375
return NS_OK;
376
}
377
378
NS_IMETHODIMP
379
nsCSPContext::GetBlockAllMixedContent(bool* outBlockAllMixedContent) {
380
EnsureIPCPoliciesRead();
381
*outBlockAllMixedContent = false;
382
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
383
if (!mPolicies[i]->getReportOnlyFlag() &&
384
mPolicies[i]->hasDirective(
385
nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
386
*outBlockAllMixedContent = true;
387
return NS_OK;
388
}
389
}
390
return NS_OK;
391
}
392
393
NS_IMETHODIMP
394
nsCSPContext::GetEnforcesFrameAncestors(bool* outEnforcesFrameAncestors) {
395
EnsureIPCPoliciesRead();
396
*outEnforcesFrameAncestors = false;
397
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
398
if (!mPolicies[i]->getReportOnlyFlag() &&
399
mPolicies[i]->hasDirective(
400
nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE)) {
401
*outEnforcesFrameAncestors = true;
402
return NS_OK;
403
}
404
}
405
return NS_OK;
406
}
407
408
NS_IMETHODIMP
409
nsCSPContext::AppendPolicy(const nsAString& aPolicyString, bool aReportOnly,
410
bool aDeliveredViaMetaTag) {
411
CSPCONTEXTLOG(("nsCSPContext::AppendPolicy: %s",
412
NS_ConvertUTF16toUTF8(aPolicyString).get()));
413
414
// Use mSelfURI from setRequestContextWith{Document,Principal} (bug 991474)
415
MOZ_ASSERT(
416
mLoadingPrincipal,
417
"did you forget to call setRequestContextWith{Document,Principal}?");
418
MOZ_ASSERT(
419
mSelfURI,
420
"did you forget to call setRequestContextWith{Document,Principal}?");
421
NS_ENSURE_TRUE(mLoadingPrincipal, NS_ERROR_UNEXPECTED);
422
NS_ENSURE_TRUE(mSelfURI, NS_ERROR_UNEXPECTED);
423
424
nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(
425
aPolicyString, mSelfURI, aReportOnly, this, aDeliveredViaMetaTag);
426
if (policy) {
427
if (policy->hasDirective(
428
nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
429
nsAutoCString selfURIspec, referrer;
430
if (mSelfURI) {
431
mSelfURI->GetAsciiSpec(selfURIspec);
432
}
433
referrer = NS_ConvertUTF16toUTF8(mReferrer);
434
CSPCONTEXTLOG(
435
("nsCSPContext::AppendPolicy added UPGRADE_IF_INSECURE_DIRECTIVE "
436
"self-uri=%s referrer=%s",
437
selfURIspec.get(), referrer.get()));
438
}
439
440
mPolicies.AppendElement(policy);
441
442
// set the flag on the document for CSP telemetry
443
nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
444
if (doc) {
445
doc->SetHasCSP(true);
446
}
447
}
448
449
return NS_OK;
450
}
451
452
NS_IMETHODIMP
453
nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
454
bool* outAllowsEval) {
455
EnsureIPCPoliciesRead();
456
*outShouldReportViolation = false;
457
*outAllowsEval = true;
458
459
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
460
if (!mPolicies[i]->allows(nsIContentPolicy::TYPE_SCRIPT, CSP_UNSAFE_EVAL,
461
EmptyString(), false)) {
462
// policy is violated: must report the violation and allow the inline
463
// script if the policy is report-only.
464
*outShouldReportViolation = true;
465
if (!mPolicies[i]->getReportOnlyFlag()) {
466
*outAllowsEval = false;
467
}
468
}
469
}
470
return NS_OK;
471
}
472
473
// Helper function to report inline violations
474
void nsCSPContext::reportInlineViolation(
475
nsContentPolicyType aContentType, Element* aTriggeringElement,
476
nsICSPEventListener* aCSPEventListener, const nsAString& aNonce,
477
const nsAString& aContent, const nsAString& aViolatedDirective,
478
uint32_t aViolatedPolicyIndex, // TODO, use report only flag for that
479
uint32_t aLineNumber, uint32_t aColumnNumber) {
480
nsString observerSubject;
481
// if the nonce is non empty, then we report the nonce error, otherwise
482
// let's report the hash error; no need to report the unsafe-inline error
483
// anymore.
484
if (!aNonce.IsEmpty()) {
485
observerSubject =
486
(aContentType == nsIContentPolicy::TYPE_SCRIPT)
487
? NS_LITERAL_STRING(SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC)
488
: NS_LITERAL_STRING(STYLE_NONCE_VIOLATION_OBSERVER_TOPIC);
489
} else {
490
observerSubject =
491
(aContentType == nsIContentPolicy::TYPE_SCRIPT)
492
? NS_LITERAL_STRING(SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC)
493
: NS_LITERAL_STRING(STYLE_HASH_VIOLATION_OBSERVER_TOPIC);
494
}
495
496
nsAutoString sourceFile;
497
uint32_t lineNumber;
498
uint32_t columnNumber;
499
500
JSContext* cx = nsContentUtils::GetCurrentJSContext();
501
if (!cx || !nsJSUtils::GetCallingLocation(cx, sourceFile, &lineNumber,
502
&columnNumber)) {
503
// use selfURI as the sourceFile
504
if (mSelfURI) {
505
nsAutoCString cSourceFile;
506
mSelfURI->GetSpec(cSourceFile);
507
sourceFile.Assign(NS_ConvertUTF8toUTF16(cSourceFile));
508
}
509
lineNumber = aLineNumber;
510
columnNumber = aColumnNumber;
511
}
512
513
AsyncReportViolation(aTriggeringElement, aCSPEventListener,
514
nullptr, // aBlockedURI
515
BlockedContentSource::eInline, // aBlockedSource
516
mSelfURI, // aOriginalURI
517
aViolatedDirective, // aViolatedDirective
518
aViolatedPolicyIndex, // aViolatedPolicyIndex
519
observerSubject, // aObserverSubject
520
sourceFile, // aSourceFile
521
aContent, // aScriptSample
522
lineNumber, // aLineNum
523
columnNumber); // aColumnNum
524
}
525
526
NS_IMETHODIMP
527
nsCSPContext::GetAllowsInline(nsContentPolicyType aContentType,
528
const nsAString& aNonce, bool aParserCreated,
529
Element* aTriggeringElement,
530
nsICSPEventListener* aCSPEventListener,
531
const nsAString& aContentOfPseudoScript,
532
uint32_t aLineNumber, uint32_t aColumnNumber,
533
bool* outAllowsInline) {
534
*outAllowsInline = true;
535
536
MOZ_ASSERT(
537
aContentType ==
538
nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
539
"We should only see external content policy types here.");
540
541
if (aContentType != nsIContentPolicy::TYPE_SCRIPT &&
542
aContentType != nsIContentPolicy::TYPE_STYLESHEET) {
543
MOZ_ASSERT(false, "can only allow inline for script or style");
544
return NS_OK;
545
}
546
547
EnsureIPCPoliciesRead();
548
nsAutoString content(EmptyString());
549
550
// always iterate all policies, otherwise we might not send out all reports
551
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
552
bool allowed =
553
mPolicies[i]->allows(aContentType, CSP_UNSAFE_INLINE, EmptyString(),
554
aParserCreated) ||
555
mPolicies[i]->allows(aContentType, CSP_NONCE, aNonce, aParserCreated);
556
557
// If the inlined script or style is allowed by either unsafe-inline or the
558
// nonce, go ahead and shortcut this loop so we can avoid allocating
559
// unecessary strings
560
if (allowed) {
561
continue;
562
}
563
564
// Check the content length to ensure the content is not allocated more than
565
// once. Even though we are in a for loop, it is probable that there is only
566
// one policy, so this check may be unnecessary.
567
if (content.IsEmpty() && aTriggeringElement) {
568
nsCOMPtr<nsIScriptElement> element =
569
do_QueryInterface(aTriggeringElement);
570
if (element) {
571
element->GetScriptText(content);
572
}
573
}
574
575
// Check if the csp-hash matches against the hash of the script or
576
// pseudoscript. If we can't get any content to check, block the script.
577
if (!content.IsEmpty() || !aContentOfPseudoScript.IsEmpty()) {
578
if (content.IsEmpty()) {
579
content = aContentOfPseudoScript;
580
}
581
allowed =
582
mPolicies[i]->allows(aContentType, CSP_HASH, content, aParserCreated);
583
}
584
585
if (!allowed) {
586
// policy is violoated: deny the load unless policy is report only and
587
// report the violation.
588
if (!mPolicies[i]->getReportOnlyFlag()) {
589
*outAllowsInline = false;
590
}
591
nsAutoString violatedDirective;
592
bool reportSample = false;
593
mPolicies[i]->getDirectiveStringAndReportSampleForContentType(
594
aContentType, violatedDirective, &reportSample);
595
reportInlineViolation(aContentType, aTriggeringElement, aCSPEventListener,
596
aNonce, reportSample ? content : EmptyString(),
597
violatedDirective, i, aLineNumber, aColumnNumber);
598
}
599
}
600
return NS_OK;
601
}
602
603
NS_IMETHODIMP
604
nsCSPContext::GetAllowsNavigateTo(nsIURI* aURI, nsILoadInfo* aLoadInfo,
605
bool aWasRedirected, bool aEnforceWhitelist,
606
bool* outAllowsNavigateTo) {
607
/*
608
* The matrix below shows the different values of (aWasRedirect,
609
* aEnforceWhitelist) for the three different checks we do.
610
*
611
* Navigation | Start Loading | Initiate Redirect | Document
612
* | (nsDocShell) | (nsCSPService) |
613
* -----------------------------------------------------------------
614
* A -> B (false,false) - (false,true)
615
* A -> ... -> B (false,false) (true,false) (true,true)
616
*/
617
*outAllowsNavigateTo = false;
618
619
EnsureIPCPoliciesRead();
620
// The 'form-action' directive overrules 'navigate-to' for form submissions.
621
// So in case this is a form submission and the directive 'form-action' is
622
// present then there is nothing for us to do here, see: 6.3.3.1.2
624
if (aLoadInfo->GetIsFormSubmission()) {
625
for (unsigned long i = 0; i < mPolicies.Length(); i++) {
626
if (mPolicies[i]->hasDirective(
627
nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE)) {
628
*outAllowsNavigateTo = true;
629
return NS_OK;
630
}
631
}
632
}
633
634
bool atLeastOneBlock = false;
635
for (unsigned long i = 0; i < mPolicies.Length(); i++) {
636
if (!mPolicies[i]->allowsNavigateTo(aURI, aWasRedirected,
637
aEnforceWhitelist)) {
638
if (!mPolicies[i]->getReportOnlyFlag()) {
639
atLeastOneBlock = true;
640
}
641
642
// If the load encountered a server side redirect, the spec suggests to
643
// remove the path component from the URI, see:
645
nsCOMPtr<nsIURI> blockedURIForReporting = aURI;
646
if (aWasRedirected) {
647
nsAutoCString prePathStr;
648
nsCOMPtr<nsIURI> prePathURI;
649
nsresult rv = aURI->GetPrePath(prePathStr);
650
NS_ENSURE_SUCCESS(rv, rv);
651
rv = NS_NewURI(getter_AddRefs(blockedURIForReporting), prePathStr);
652
NS_ENSURE_SUCCESS(rv, rv);
653
}
654
655
// Lines numbers and source file for the violation report
656
uint32_t lineNumber = 0;
657
uint32_t columnNumber = 0;
658
nsAutoCString spec;
659
JSContext* cx = nsContentUtils::GetCurrentJSContext();
660
if (cx) {
661
nsJSUtils::GetCallingLocation(cx, spec, &lineNumber, &columnNumber);
662
// If GetCallingLocation fails linenumber & columnNumber are set to 0
663
// anyway so we can skip checking if that is the case.
664
}
665
666
// Report the violation
667
nsresult rv = AsyncReportViolation(
668
nullptr, // aTriggeringElement
669
nullptr, // aCSPEventListener
670
blockedURIForReporting, // aBlockedURI
671
nsCSPContext::BlockedContentSource::eSelf, // aBlockedSource
672
nullptr, // aOriginalURI
673
NS_LITERAL_STRING("navigate-to"), // aViolatedDirective
674
i, // aViolatedPolicyIndex
675
EmptyString(), // aObserverSubject
676
NS_ConvertUTF8toUTF16(spec), // aSourceFile
677
EmptyString(), // aScriptSample
678
lineNumber, // aLineNum
679
columnNumber); // aColumnNum
680
NS_ENSURE_SUCCESS(rv, rv);
681
}
682
}
683
684
*outAllowsNavigateTo = !atLeastOneBlock;
685
return NS_OK;
686
}
687
688
/**
689
* Reduces some code repetition for the various logging situations in
690
* LogViolationDetails.
691
*
692
* Call-sites for the eval/inline checks recieve two return values: allows
693
* and violates. Based on those, they must choose whether to call
694
* LogViolationDetails or not. Policies that are report-only allow the
695
* loads/compilations but violations should still be reported. Not all
696
* policies in this nsIContentSecurityPolicy instance will be violated,
697
* which is why we must check allows() again here.
698
*
699
* Note: This macro uses some parameters from its caller's context:
700
* p, mPolicies, this, aSourceFile, aScriptSample, aLineNum, aColumnNum,
701
* blockedContentSource
702
*
703
* @param violationType: the VIOLATION_TYPE_* constant (partial symbol)
704
* such as INLINE_SCRIPT
705
* @param contentPolicyType: a constant from nsIContentPolicy such as
706
* TYPE_STYLESHEET
707
* @param nonceOrHash: for NONCE and HASH violations, it's the nonce or content
708
* string. For other violations, it is an empty string.
709
* @param keyword: the keyword corresponding to violation (UNSAFE_INLINE for
710
* most)
711
* @param observerTopic: the observer topic string to send with the CSP
712
* observer notifications.
713
*
714
* Please note that inline violations for scripts are reported within
715
* GetAllowsInline() and do not call this macro, hence we can pass 'false'
716
* as the argument _aParserCreated_ to allows().
717
*/
718
#define CASE_CHECK_AND_REPORT(violationType, contentPolicyType, nonceOrHash, \
719
keyword, observerTopic) \
720
case nsIContentSecurityPolicy::VIOLATION_TYPE_##violationType: \
721
PR_BEGIN_MACRO \
722
if (!mPolicies[p]->allows(nsIContentPolicy::TYPE_##contentPolicyType, \
723
keyword, nonceOrHash, false)) { \
724
nsAutoString violatedDirective; \
725
bool reportSample = false; \
726
mPolicies[p]->getDirectiveStringAndReportSampleForContentType( \
727
nsIContentPolicy::TYPE_##contentPolicyType, violatedDirective, \
728
&reportSample); \
729
AsyncReportViolation(aTriggeringElement, aCSPEventListener, nullptr, \
730
blockedContentSource, nullptr, violatedDirective, \
731
p, NS_LITERAL_STRING(observerTopic), aSourceFile, \
732
reportSample ? aScriptSample : EmptyString(), \
733
aLineNum, aColumnNum); \
734
} \
735
PR_END_MACRO; \
736
break
737
738
/**
739
* For each policy, log any violation on the Error Console and send a report
740
* if a report-uri is present in the policy
741
*
742
* @param aViolationType
743
* one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
744
* @param aSourceFile
745
* name of the source file containing the violation (if available)
746
* @param aContentSample
747
* sample of the violating content (to aid debugging)
748
* @param aLineNum
749
* source line number of the violation (if available)
750
* @param aColumnNum
751
* source column number of the violation (if available)
752
* @param aNonce
753
* (optional) If this is a nonce violation, include the nonce so we can
754
* recheck to determine which policies were violated and send the
755
* appropriate reports.
756
* @param aContent
757
* (optional) If this is a hash violation, include contents of the inline
758
* resource in the question so we can recheck the hash in order to
759
* determine which policies were violated and send the appropriate
760
* reports.
761
*/
762
NS_IMETHODIMP
763
nsCSPContext::LogViolationDetails(
764
uint16_t aViolationType, Element* aTriggeringElement,
765
nsICSPEventListener* aCSPEventListener, const nsAString& aSourceFile,
766
const nsAString& aScriptSample, int32_t aLineNum, int32_t aColumnNum,
767
const nsAString& aNonce, const nsAString& aContent) {
768
EnsureIPCPoliciesRead();
769
for (uint32_t p = 0; p < mPolicies.Length(); p++) {
770
NS_ASSERTION(mPolicies[p], "null pointer in nsTArray<nsCSPPolicy>");
771
772
BlockedContentSource blockedContentSource = BlockedContentSource::eUnknown;
773
if (aViolationType == nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL) {
774
blockedContentSource = BlockedContentSource::eEval;
775
} else if (aViolationType ==
776
nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT ||
777
aViolationType ==
778
nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE) {
779
blockedContentSource = BlockedContentSource::eInline;
780
} else {
781
// All the other types should have a URL, but just in case, let's use
782
// 'self' here.
783
blockedContentSource = BlockedContentSource::eSelf;
784
}
785
786
switch (aViolationType) {
787
CASE_CHECK_AND_REPORT(EVAL, SCRIPT, NS_LITERAL_STRING(""),
788
CSP_UNSAFE_EVAL, EVAL_VIOLATION_OBSERVER_TOPIC);
789
CASE_CHECK_AND_REPORT(INLINE_STYLE, STYLESHEET, NS_LITERAL_STRING(""),
790
CSP_UNSAFE_INLINE,
791
INLINE_STYLE_VIOLATION_OBSERVER_TOPIC);
792
CASE_CHECK_AND_REPORT(INLINE_SCRIPT, SCRIPT, NS_LITERAL_STRING(""),
793
CSP_UNSAFE_INLINE,
794
INLINE_SCRIPT_VIOLATION_OBSERVER_TOPIC);
795
CASE_CHECK_AND_REPORT(NONCE_SCRIPT, SCRIPT, aNonce, CSP_UNSAFE_INLINE,
796
SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC);
797
CASE_CHECK_AND_REPORT(NONCE_STYLE, STYLESHEET, aNonce, CSP_UNSAFE_INLINE,
798
STYLE_NONCE_VIOLATION_OBSERVER_TOPIC);
799
CASE_CHECK_AND_REPORT(HASH_SCRIPT, SCRIPT, aContent, CSP_UNSAFE_INLINE,
800
SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC);
801
CASE_CHECK_AND_REPORT(HASH_STYLE, STYLESHEET, aContent, CSP_UNSAFE_INLINE,
802
STYLE_HASH_VIOLATION_OBSERVER_TOPIC);
803
804
default:
805
NS_ASSERTION(false, "LogViolationDetails with invalid type");
806
break;
807
}
808
}
809
return NS_OK;
810
}
811
812
#undef CASE_CHECK_AND_REPORT
813
814
NS_IMETHODIMP
815
nsCSPContext::SetRequestContextWithDocument(Document* aDocument) {
816
MOZ_ASSERT(aDocument, "Can't set context without doc");
817
NS_ENSURE_ARG(aDocument);
818
819
mLoadingContext = do_GetWeakReference(aDocument);
820
mSelfURI = aDocument->GetDocumentURI();
821
mLoadingPrincipal = aDocument->NodePrincipal();
822
aDocument->GetReferrer(mReferrer);
823
mInnerWindowID = aDocument->InnerWindowID();
824
// the innerWindowID is not available for CSPs delivered through the
825
// header at the time setReqeustContext is called - let's queue up
826
// console messages until it becomes available, see flushConsoleMessages
827
mQueueUpMessages = !mInnerWindowID;
828
mCallingChannelLoadGroup = aDocument->GetDocumentLoadGroup();
829
// set the flag on the document for CSP telemetry
830
mEventTarget = aDocument->EventTargetFor(TaskCategory::Other);
831
832
MOZ_ASSERT(mLoadingPrincipal, "need a valid requestPrincipal");
833
MOZ_ASSERT(mSelfURI, "need mSelfURI to translate 'self' into actual URI");
834
return NS_OK;
835
}
836
837
NS_IMETHODIMP
838
nsCSPContext::SetRequestContextWithPrincipal(nsIPrincipal* aRequestPrincipal,
839
nsIURI* aSelfURI,
840
const nsAString& aReferrer,
841
uint64_t aInnerWindowId) {
842
NS_ENSURE_ARG(aRequestPrincipal);
843
844
mLoadingPrincipal = aRequestPrincipal;
845
mSelfURI = aSelfURI;
846
mReferrer = aReferrer;
847
mInnerWindowID = aInnerWindowId;
848
// if no document is available, then it also does not make sense to queue
849
// console messages sending messages to the browser console instead of the web
850
// console in that case.
851
mQueueUpMessages = false;
852
mCallingChannelLoadGroup = nullptr;
853
mEventTarget = nullptr;
854
855
MOZ_ASSERT(mLoadingPrincipal, "need a valid requestPrincipal");
856
MOZ_ASSERT(mSelfURI, "need mSelfURI to translate 'self' into actual URI");
857
return NS_OK;
858
}
859
860
nsIPrincipal* nsCSPContext::GetRequestPrincipal() { return mLoadingPrincipal; }
861
862
nsIURI* nsCSPContext::GetSelfURI() { return mSelfURI; }
863
864
NS_IMETHODIMP
865
nsCSPContext::GetReferrer(nsAString& outReferrer) {
866
outReferrer.Truncate();
867
outReferrer.Append(mReferrer);
868
return NS_OK;
869
}
870
871
uint64_t nsCSPContext::GetInnerWindowID() { return mInnerWindowID; }
872
873
bool nsCSPContext::GetSkipAllowInlineStyleCheck() {
874
return mSkipAllowInlineStyleCheck;
875
}
876
877
void nsCSPContext::SetSkipAllowInlineStyleCheck(
878
bool aSkipAllowInlineStyleCheck) {
879
mSkipAllowInlineStyleCheck = aSkipAllowInlineStyleCheck;
880
}
881
882
NS_IMETHODIMP
883
nsCSPContext::EnsureEventTarget(nsIEventTarget* aEventTarget) {
884
NS_ENSURE_ARG(aEventTarget);
885
// Don't bother if we did have a valid event target (if the csp object is
886
// tied to a document in SetRequestContextWithDocument)
887
if (mEventTarget) {
888
return NS_OK;
889
}
890
891
mEventTarget = aEventTarget;
892
return NS_OK;
893
}
894
895
struct ConsoleMsgQueueElem {
896
nsString mMsg;
897
nsString mSourceName;
898
nsString mSourceLine;
899
uint32_t mLineNumber;
900
uint32_t mColumnNumber;
901
uint32_t mSeverityFlag;
902
nsCString mCategory;
903
};
904
905
void nsCSPContext::flushConsoleMessages() {
906
bool privateWindow = false;
907
908
// should flush messages even if doc is not available
909
nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
910
if (doc) {
911
mInnerWindowID = doc->InnerWindowID();
912
privateWindow =
913
!!doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
914
}
915
916
mQueueUpMessages = false;
917
918
for (uint32_t i = 0; i < mConsoleMsgQueue.Length(); i++) {
919
ConsoleMsgQueueElem& elem = mConsoleMsgQueue[i];
920
CSP_LogMessage(elem.mMsg, elem.mSourceName, elem.mSourceLine,
921
elem.mLineNumber, elem.mColumnNumber, elem.mSeverityFlag,
922
elem.mCategory, mInnerWindowID, privateWindow);
923
}
924
mConsoleMsgQueue.Clear();
925
}
926
927
void nsCSPContext::logToConsole(const char* aName,
928
const nsTArray<nsString>& aParams,
929
const nsAString& aSourceName,
930
const nsAString& aSourceLine,
931
uint32_t aLineNumber, uint32_t aColumnNumber,
932
uint32_t aSeverityFlag) {
933
// we are passing aName as the category so we can link to the
934
// appropriate MDN docs depending on the specific error.
935
nsDependentCString category(aName);
936
937
// let's check if we have to queue up console messages
938
if (mQueueUpMessages) {
939
nsAutoString msg;
940
CSP_GetLocalizedStr(aName, aParams, msg);
941
ConsoleMsgQueueElem& elem = *mConsoleMsgQueue.AppendElement();
942
elem.mMsg = msg;
943
elem.mSourceName = PromiseFlatString(aSourceName);
944
elem.mSourceLine = PromiseFlatString(aSourceLine);
945
elem.mLineNumber = aLineNumber;
946
elem.mColumnNumber = aColumnNumber;
947
elem.mSeverityFlag = aSeverityFlag;
948
elem.mCategory = category;
949
return;
950
}
951
952
bool privateWindow = false;
953
nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
954
if (doc) {
955
privateWindow =
956
!!doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId;
957
}
958
959
CSP_LogLocalizedStr(aName, aParams, aSourceName, aSourceLine, aLineNumber,
960
aColumnNumber, aSeverityFlag, category, mInnerWindowID,
961
privateWindow);
962
}
963
964
/**
965
* Strip URI for reporting according to:
967
*
968
* @param aURI
969
* The uri to be stripped for reporting
970
* @param aSelfURI
971
* The uri of the protected resource
972
* which is needed to enforce the SOP.
973
* @return ASCII serialization of the uri to be reported.
974
*/
975
void StripURIForReporting(nsIURI* aURI, nsIURI* aSelfURI,
976
nsACString& outStrippedURI) {
977
// 1) If the origin of uri is a globally unique identifier (for example,
978
// aURI has a scheme of data, blob, or filesystem), then return the
979
// ASCII serialization of uri’s scheme.
980
bool isHttpFtpOrWs =
981
(aURI->SchemeIs("http") || aURI->SchemeIs("https") ||
982
aURI->SchemeIs("ftp") || aURI->SchemeIs("ws") || aURI->SchemeIs("wss"));
983
984
if (!isHttpFtpOrWs) {
985
// not strictly spec compliant, but what we really care about is
986
// http/https and also ftp. If it's not http/https or ftp, then treat aURI
987
// as if it's a globally unique identifier and just return the scheme.
988
aURI->GetScheme(outStrippedURI);
989
return;
990
}
991
992
// Return uri, with any fragment component removed.
993
aURI->GetSpecIgnoringRef(outStrippedURI);
994
}
995
996
nsresult nsCSPContext::GatherSecurityPolicyViolationEventData(
997
nsIURI* aBlockedURI, const nsACString& aBlockedString, nsIURI* aOriginalURI,
998
nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex,
999
nsAString& aSourceFile, nsAString& aScriptSample, uint32_t aLineNum,
1000
uint32_t aColumnNum,
1001
mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
1002
EnsureIPCPoliciesRead();
1003
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
1004
1005
MOZ_ASSERT(ValidateDirectiveName(aViolatedDirective),
1006
"Invalid directive name");
1007
1008
nsresult rv;
1009
1010
// document-uri
1011
nsAutoCString reportDocumentURI;
1012
StripURIForReporting(mSelfURI, mSelfURI, reportDocumentURI);
1013
aViolationEventInit.mDocumentURI = NS_ConvertUTF8toUTF16(reportDocumentURI);
1014
1015
// referrer
1016
aViolationEventInit.mReferrer = mReferrer;
1017
1018
// blocked-uri
1019
if (aBlockedURI) {
1020
nsAutoCString reportBlockedURI;
1021
StripURIForReporting(aOriginalURI ? aOriginalURI : aBlockedURI, mSelfURI,
1022
reportBlockedURI);
1023
aViolationEventInit.mBlockedURI = NS_ConvertUTF8toUTF16(reportBlockedURI);
1024
} else {
1025
aViolationEventInit.mBlockedURI = NS_ConvertUTF8toUTF16(aBlockedString);
1026
}
1027
1028
// effective-directive
1029
// The name of the policy directive that was violated.
1030
aViolationEventInit.mEffectiveDirective = aViolatedDirective;
1031
1032
// violated-directive
1033
// In CSP2, the policy directive that was violated, as it appears in the
1034
// policy. In CSP3, the same as effective-directive.
1035
aViolationEventInit.mViolatedDirective = aViolatedDirective;
1036
1037
// original-policy
1038
nsAutoString originalPolicy;
1039
rv = this->GetPolicyString(aViolatedPolicyIndex, originalPolicy);
1040
NS_ENSURE_SUCCESS(rv, rv);
1041
aViolationEventInit.mOriginalPolicy = originalPolicy;
1042
1043
// source-file
1044
if (!aSourceFile.IsEmpty()) {
1045
// if aSourceFile is a URI, we have to make sure to strip fragments
1046
nsCOMPtr<nsIURI> sourceURI;
1047
NS_NewURI(getter_AddRefs(sourceURI), aSourceFile);
1048
if (sourceURI) {
1049
nsAutoCString spec;
1050
sourceURI->GetSpecIgnoringRef(spec);
1051
aSourceFile = NS_ConvertUTF8toUTF16(spec);
1052
}
1053
aViolationEventInit.mSourceFile = aSourceFile;
1054
}
1055
1056
// sample, max 40 chars.
1057
aViolationEventInit.mSample = aScriptSample;
1058
uint32_t length = aViolationEventInit.mSample.Length();
1059
if (length > ScriptSampleMaxLength()) {
1060
uint32_t desiredLength = ScriptSampleMaxLength();
1061
// Don't cut off right before a low surrogate. Just include it.
1062
if (NS_IS_LOW_SURROGATE(aViolationEventInit.mSample[desiredLength])) {
1063
desiredLength++;
1064
}
1065
aViolationEventInit.mSample.Replace(ScriptSampleMaxLength(),
1066
length - desiredLength,
1067
nsContentUtils::GetLocalizedEllipsis());
1068
}
1069
1070
// disposition
1071
aViolationEventInit.mDisposition =
1072
mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag()
1073
? mozilla::dom::SecurityPolicyViolationEventDisposition::Report
1074
: mozilla::dom::SecurityPolicyViolationEventDisposition::Enforce;
1075
1076
// status-code
1077
uint16_t statusCode = 0;
1078
{
1079
nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
1080
if (doc) {
1081
nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(doc->GetChannel());
1082
if (channel) {
1083
uint32_t responseStatus = 0;
1084
nsresult rv = channel->GetResponseStatus(&responseStatus);
1085
if (NS_SUCCEEDED(rv) && (responseStatus <= UINT16_MAX)) {
1086
statusCode = static_cast<uint16_t>(responseStatus);
1087
}
1088
}
1089
}
1090
}
1091
aViolationEventInit.mStatusCode = statusCode;
1092
1093
// line-number
1094
aViolationEventInit.mLineNumber = aLineNum;
1095
1096
// column-number
1097
aViolationEventInit.mColumnNumber = aColumnNum;
1098
1099
aViolationEventInit.mBubbles = true;
1100
aViolationEventInit.mComposed = true;
1101
1102
return NS_OK;
1103
}
1104
1105
nsresult nsCSPContext::SendReports(
1106
const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit,
1107
uint32_t aViolatedPolicyIndex) {
1108
EnsureIPCPoliciesRead();
1109
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
1110
1111
dom::CSPReport report;
1112
1113
// blocked-uri
1114
report.mCsp_report.mBlocked_uri = aViolationEventInit.mBlockedURI;
1115
1116
// document-uri
1117
report.mCsp_report.mDocument_uri = aViolationEventInit.mDocumentURI;
1118
1119
// original-policy
1120
report.mCsp_report.mOriginal_policy = aViolationEventInit.mOriginalPolicy;
1121
1122
// referrer
1123
report.mCsp_report.mReferrer = aViolationEventInit.mReferrer;
1124
1125
// violated-directive
1126
report.mCsp_report.mViolated_directive =
1127
aViolationEventInit.mViolatedDirective;
1128
1129
// source-file
1130
if (!aViolationEventInit.mSourceFile.IsEmpty()) {
1131
report.mCsp_report.mSource_file.Construct();
1132
report.mCsp_report.mSource_file.Value() = aViolationEventInit.mSourceFile;
1133
}
1134
1135
// script-sample
1136
if (!aViolationEventInit.mSample.IsEmpty()) {
1137
report.mCsp_report.mScript_sample.Construct();
1138
report.mCsp_report.mScript_sample.Value() = aViolationEventInit.mSample;
1139
}
1140
1141
// line-number
1142
if (aViolationEventInit.mLineNumber != 0) {
1143
report.mCsp_report.mLine_number.Construct();
1144
report.mCsp_report.mLine_number.Value() = aViolationEventInit.mLineNumber;
1145
}
1146
1147
if (aViolationEventInit.mColumnNumber != 0) {
1148
report.mCsp_report.mColumn_number.Construct();
1149
report.mCsp_report.mColumn_number.Value() =
1150
aViolationEventInit.mColumnNumber;
1151
}
1152
1153
nsString csp_report;
1154
if (!report.ToJSON(csp_report)) {
1155
return NS_ERROR_FAILURE;
1156
}
1157
1158
// ---------- Assembled, now send it to all the report URIs ----------- //
1159
1160
nsTArray<nsString> reportURIs;
1161
mPolicies[aViolatedPolicyIndex]->getReportURIs(reportURIs);
1162
1163
nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
1164
nsCOMPtr<nsIURI> reportURI;
1165
nsCOMPtr<nsIChannel> reportChannel;
1166
1167
nsresult rv;
1168
for (uint32_t r = 0; r < reportURIs.Length(); r++) {
1169
nsAutoCString reportURICstring = NS_ConvertUTF16toUTF8(reportURIs[r]);
1170
// try to create a new uri from every report-uri string
1171
rv = NS_NewURI(getter_AddRefs(reportURI), reportURIs[r]);
1172
if (NS_FAILED(rv)) {
1173
AutoTArray<nsString, 1> params = {reportURIs[r]};
1174
CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
1175
reportURICstring.get()));
1176
logToConsole("triedToSendReport", params, aViolationEventInit.mSourceFile,
1177
aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
1178
aViolationEventInit.mColumnNumber,
1179
nsIScriptError::errorFlag);
1180
continue; // don't return yet, there may be more URIs
1181
}
1182
1183
// try to create a new channel for every report-uri
1184
if (doc) {
1185
rv = NS_NewChannel(getter_AddRefs(reportChannel), reportURI, doc,
1186
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1187
nsIContentPolicy::TYPE_CSP_REPORT);
1188
} else {
1189
rv = NS_NewChannel(getter_AddRefs(reportChannel), reportURI,
1190
mLoadingPrincipal,
1191
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1192
nsIContentPolicy::TYPE_CSP_REPORT);
1193
}
1194
1195
if (NS_FAILED(rv)) {
1196
CSPCONTEXTLOG(("Could not create new channel for report URI %s",
1197
reportURICstring.get()));
1198
continue; // don't return yet, there may be more URIs
1199
}
1200
1201
// log a warning to console if scheme is not http or https
1202
bool isHttpScheme =
1203
reportURI->SchemeIs("http") || reportURI->SchemeIs("https");
1204
1205
if (!isHttpScheme) {
1206
AutoTArray<nsString, 1> params = {reportURIs[r]};
1207
logToConsole(
1208
"reportURInotHttpsOrHttp2", params, aViolationEventInit.mSourceFile,
1209
aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
1210
aViolationEventInit.mColumnNumber, nsIScriptError::errorFlag);
1211
continue;
1212
}
1213
1214
// make sure this is an anonymous request (no cookies) so in case the
1215
// policy URI is injected, it can't be abused for CSRF.
1216
nsLoadFlags flags;
1217
rv = reportChannel->GetLoadFlags(&flags);
1218
NS_ENSURE_SUCCESS(rv, rv);
1219
flags |= nsIRequest::LOAD_ANONYMOUS;
1220
rv = reportChannel->SetLoadFlags(flags);
1221
NS_ENSURE_SUCCESS(rv, rv);
1222
1223
// we need to set an nsIChannelEventSink on the channel object
1224
// so we can tell it to not follow redirects when posting the reports
1225
RefPtr<CSPReportRedirectSink> reportSink = new CSPReportRedirectSink();
1226
if (doc && doc->GetDocShell()) {
1227
nsCOMPtr<nsINetworkInterceptController> interceptController =
1228
do_QueryInterface(doc->GetDocShell());
1229
reportSink->SetInterceptController(interceptController);
1230
}
1231
reportChannel->SetNotificationCallbacks(reportSink);
1232
1233
// apply the loadgroup taken by setRequestContextWithDocument. If there's
1234
// no loadgroup, AsyncOpen will fail on process-split necko (since the
1235
// channel cannot query the iBrowserChild).
1236
rv = reportChannel->SetLoadGroup(mCallingChannelLoadGroup);
1237
NS_ENSURE_SUCCESS(rv, rv);
1238
1239
// wire in the string input stream to send the report
1240
nsCOMPtr<nsIStringInputStream> sis(
1241
do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
1242
NS_ASSERTION(sis,
1243
"nsIStringInputStream is needed but not available to send CSP "
1244
"violation reports");
1245
nsAutoCString utf8CSPReport = NS_ConvertUTF16toUTF8(csp_report);
1246
rv = sis->SetData(utf8CSPReport.get(), utf8CSPReport.Length());
1247
NS_ENSURE_SUCCESS(rv, rv);
1248
1249
nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(reportChannel));
1250
if (!uploadChannel) {
1251
// It's possible the URI provided can't be uploaded to, in which case
1252
// we skip this one. We'll already have warned about a non-HTTP URI
1253
// earlier.
1254
continue;
1255
}
1256
1257
rv = uploadChannel->SetUploadStream(
1258
sis, NS_LITERAL_CSTRING("application/csp-report"), -1);
1259
NS_ENSURE_SUCCESS(rv, rv);
1260
1261
// if this is an HTTP channel, set the request method to post
1262
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(reportChannel));
1263
if (httpChannel) {
1264
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
1265
MOZ_ASSERT(NS_SUCCEEDED(rv));
1266
}
1267
1268
RefPtr<CSPViolationReportListener> listener =
1269
new CSPViolationReportListener();
1270
rv = reportChannel->AsyncOpen(listener);
1271
1272
// AsyncOpen should not fail, but could if there's no load group (like if
1273
// SetRequestContextWith{Document,Principal} is not given a channel). This
1274
// should fail quietly and not return an error since it's really ok if
1275
// reports don't go out, but it's good to log the error locally.
1276
1277
if (NS_FAILED(rv)) {
1278
AutoTArray<nsString, 1> params = {reportURIs[r]};
1279
CSPCONTEXTLOG(("AsyncOpen failed for report URI %s",
1280
NS_ConvertUTF16toUTF8(params[0]).get()));
1281
logToConsole("triedToSendReport", params, aViolationEventInit.mSourceFile,
1282
aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
1283
aViolationEventInit.mColumnNumber,
1284
nsIScriptError::errorFlag);
1285
} else {
1286
CSPCONTEXTLOG(
1287
("Sent violation report to URI %s", reportURICstring.get()));
1288
}
1289
}
1290
return NS_OK;
1291
}
1292
1293
nsresult nsCSPContext::FireViolationEvent(
1294
Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener,
1295
const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
1296
if (aCSPEventListener) {
1297
nsAutoString json;
1298
if (aViolationEventInit.ToJSON(json)) {
1299
aCSPEventListener->OnCSPViolationEvent(json);
1300
}
1301
}
1302
1303
// 1. If target is not null, and global is a Window, and target’s
1304
// shadow-including root is not global’s associated Document, set target to
1305
// null.
1306
RefPtr<EventTarget> eventTarget = aTriggeringElement;
1307
1308
nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
1309
if (doc && aTriggeringElement &&
1310
aTriggeringElement->GetComposedDoc() != doc) {
1311
eventTarget = nullptr;
1312
}
1313
1314
if (!eventTarget) {
1315
// If target is a Window, set target to target’s associated Document.
1316
eventTarget = doc;
1317
}
1318
1319
if (!eventTarget) {
1320
// If we are here, we are probably dealing with workers. Those are handled
1321
// via nsICSPEventListener. Nothing to do here.
1322
return NS_OK;
1323
}
1324
1325
RefPtr<mozilla::dom::Event> event =
1326
mozilla::dom::SecurityPolicyViolationEvent::Constructor(
1327
eventTarget, NS_LITERAL_STRING("securitypolicyviolation"),
1328
aViolationEventInit);
1329
event->SetTrusted(true);
1330
1331
ErrorResult rv;
1332
eventTarget->DispatchEvent(*event, rv);
1333
return rv.StealNSResult();
1334
}
1335
1336
/**
1337
* Dispatched from the main thread to send reports for one CSP violation.
1338
*/
1339
class CSPReportSenderRunnable final : public Runnable {
1340
public:
1341
CSPReportSenderRunnable(
1342
Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener,
1343
nsIURI* aBlockedURI,
1344
nsCSPContext::BlockedContentSource aBlockedContentSource,
1345
nsIURI* aOriginalURI, uint32_t aViolatedPolicyIndex, bool aReportOnlyFlag,
1346
const nsAString& aViolatedDirective, const nsAString& aObserverSubject,
1347
const nsAString& aSourceFile, const nsAString& aScriptSample,
1348
uint32_t aLineNum, uint32_t aColumnNum, nsCSPContext* aCSPContext)
1349
: mozilla::Runnable("CSPReportSenderRunnable"),
1350
mTriggeringElement(aTriggeringElement),
1351
mCSPEventListener(aCSPEventListener),
1352
mBlockedURI(aBlockedURI),
1353
mBlockedContentSource(aBlockedContentSource),
1354
mOriginalURI(aOriginalURI),
1355
mViolatedPolicyIndex(aViolatedPolicyIndex),
1356
mReportOnlyFlag(aReportOnlyFlag),
1357
mViolatedDirective(aViolatedDirective),
1358
mSourceFile(aSourceFile),
1359
mScriptSample(aScriptSample),
1360
mLineNum(aLineNum),
1361
mColumnNum(aColumnNum),
1362
mCSPContext(aCSPContext) {
1363
NS_ASSERTION(!aViolatedDirective.IsEmpty(),
1364
"Can not send reports without a violated directive");
1365
// the observer subject is an nsISupports: either an nsISupportsCString
1366
// from the arg passed in directly, or if that's empty, it's the blocked
1367
// source.
1368
if (aObserverSubject.IsEmpty() && mBlockedURI) {
1369
mObserverSubject = aBlockedURI;
1370
return;
1371
}
1372
1373
nsAutoCString subject;
1374
if (aObserverSubject.IsEmpty()) {
1375
BlockedContentSourceToString(aBlockedContentSource, subject);
1376
} else {
1377
CopyUTF16toUTF8(aObserverSubject, subject);
1378
}
1379
1380
nsCOMPtr<nsISupportsCString> supportscstr =
1381
do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
1382
if (supportscstr) {
1383
supportscstr->SetData(subject);
1384
mObserverSubject = do_QueryInterface(supportscstr);
1385
}
1386
}
1387
1388
NS_IMETHOD Run() override {
1389
MOZ_ASSERT(NS_IsMainThread());
1390
1391
nsresult rv;
1392
1393
// 0) prepare violation data
1394
mozilla::dom::SecurityPolicyViolationEventInit init;
1395
1396
nsAutoCString blockedContentSource;
1397
BlockedContentSourceToString(mBlockedContentSource, blockedContentSource);
1398
1399
rv = mCSPContext->GatherSecurityPolicyViolationEventData(
1400
mBlockedURI, blockedContentSource, mOriginalURI, mViolatedDirective,
1401
mViolatedPolicyIndex, mSourceFile, mScriptSample, mLineNum, mColumnNum,
1402
init);
1403
NS_ENSURE_SUCCESS(rv, rv);
1404
1405
// 1) notify observers
1406
nsCOMPtr<nsIObserverService> observerService =
1407
mozilla::services::GetObserverService();
1408
if (mObserverSubject && observerService) {
1409
rv = observerService->NotifyObservers(
1410
mObserverSubject, CSP_VIOLATION_TOPIC, mViolatedDirective.get());
1411
NS_ENSURE_SUCCESS(rv, rv);
1412
}
1413
1414
// 2) send reports for the policy that was violated
1415
mCSPContext->SendReports(init, mViolatedPolicyIndex);
1416
1417
// 3) log to console (one per policy violation)
1418
1419
if (mBlockedURI) {
1420
mBlockedURI->GetSpec(blockedContentSource);
1421
if (blockedContentSource.Length() >
1422
nsCSPContext::ScriptSampleMaxLength()) {
1423
bool isData = mBlockedURI->SchemeIs("data");
1424
if (NS_SUCCEEDED(rv) && isData &&
1425
blockedContentSource.Length() >
1426
nsCSPContext::ScriptSampleMaxLength()) {
1427
blockedContentSource.Truncate(nsCSPContext::ScriptSampleMaxLength());
1428
blockedContentSource.Append(
1429
NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis()));
1430
}
1431
}
1432
}
1433
1434
if (blockedContentSource.Length() > 0) {
1435
nsString blockedContentSource16 =
1436
NS_ConvertUTF8toUTF16(blockedContentSource);
1437
AutoTArray<nsString, 2> params = {mViolatedDirective,
1438
blockedContentSource16};
1439
mCSPContext->logToConsole(
1440
mReportOnlyFlag ? "CSPROViolationWithURI" : "CSPViolationWithURI",
1441
params, mSourceFile, mScriptSample, mLineNum, mColumnNum,
1442
nsIScriptError::errorFlag);
1443
}
1444
1445
// 4) fire violation event
1446
mCSPContext->FireViolationEvent(mTriggeringElement, mCSPEventListener,
1447
init);
1448
1449
return NS_OK;
1450
}
1451
1452
private:
1453
RefPtr<Element> mTriggeringElement;
1454
nsCOMPtr<nsICSPEventListener> mCSPEventListener;
1455
nsCOMPtr<nsIURI> mBlockedURI;
1456
nsCSPContext::BlockedContentSource mBlockedContentSource;
1457
nsCOMPtr<nsIURI> mOriginalURI;
1458
uint32_t mViolatedPolicyIndex;
1459
bool mReportOnlyFlag;
1460
nsString mViolatedDirective;
1461
nsCOMPtr<nsISupports> mObserverSubject;
1462
nsString mSourceFile;
1463
nsString mScriptSample;
1464
uint32_t mLineNum;
1465
uint32_t mColumnNum;
1466
RefPtr<nsCSPContext> mCSPContext;
1467
};
1468
1469
/**
1470
* Asynchronously notifies any nsIObservers listening to the CSP violation
1471
* topic that a violation occurred. Also triggers report sending and console
1472
* logging. All asynchronous on the main thread.
1473
*
1474
* @param aTriggeringElement
1475
* The element that triggered this report violation. It can be null.
1476
* @param aBlockedContentSource
1477
* Either a CSP Source (like 'self', as string) or nsIURI: the source
1478
* of the violation.
1479
* @param aOriginalUri
1480
* The original URI if the blocked content is a redirect, else null
1481
* @param aViolatedDirective
1482
* the directive that was violated (string).
1483
* @param aViolatedPolicyIndex
1484
* the index of the policy that was violated (so we know where to send
1485
* the reports).
1486
* @param aObserverSubject
1487
* optional, subject sent to the nsIObservers listening to the CSP
1488
* violation topic.
1489
* @param aSourceFile
1490
* name of the file containing the inline script violation
1491
* @param aScriptSample
1492
* a sample of the violating inline script
1493
* @param aLineNum
1494
* source line number of the violation (if available)
1495
* @param aColumnNum
1496
* source column number of the violation (if available)
1497
*/
1498
nsresult nsCSPContext::AsyncReportViolation(
1499
Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener,
1500
nsIURI* aBlockedURI, BlockedContentSource aBlockedContentSource,
1501
nsIURI* aOriginalURI, const nsAString& aViolatedDirective,
1502
uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject,
1503
const nsAString& aSourceFile, const nsAString& aScriptSample,
1504
uint32_t aLineNum, uint32_t aColumnNum) {
1505
EnsureIPCPoliciesRead();
1506
NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
1507
1508
nsCOMPtr<nsIRunnable> task = new CSPReportSenderRunnable(
1509
aTriggeringElement, aCSPEventListener, aBlockedURI, aBlockedContentSource,
1510
aOriginalURI, aViolatedPolicyIndex,
1511
mPolicies[aViolatedPolicyIndex]->getReportOnlyFlag(), aViolatedDirective,
1512
aObserverSubject, aSourceFile, aScriptSample, aLineNum, aColumnNum, this);
1513
1514
if (XRE_IsContentProcess()) {
1515
if (mEventTarget) {
1516
mEventTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
1517
return NS_OK;
1518
}
1519
}
1520
1521
NS_DispatchToMainThread(task.forget());
1522
return NS_OK;
1523
}
1524
1525
/**
1526
* Based on the given docshell, determines if this CSP context allows the
1527
* ancestry.
1528
*
1529
* In order to determine the URI of the parent document (one causing the load
1530
* of this protected document), this function obtains the docShellTreeItem,
1531
* then walks up the hierarchy until it finds a privileged (chrome) tree item.
1532
* Getting the a tree item's URI looks like this in pseudocode:
1533
*
1534
* nsIDocShellTreeItem->GetDocument()->GetDocumentURI();
1535
*
1536
* aDocShell is the docShell for the protected document.
1537
*/
1538
NS_IMETHODIMP
1539
nsCSPContext::PermitsAncestry(nsIDocShell* aDocShell,
1540
bool* outPermitsAncestry) {
1541
nsresult rv;
1542
1543
// Can't check ancestry without a docShell.
1544
if (aDocShell == nullptr) {
1545
return NS_ERROR_FAILURE;
1546
}
1547
1548
*outPermitsAncestry = true;
1549
1550
// extract the ancestry as an array
1551
nsCOMArray<nsIURI> ancestorsArray;
1552
1553
nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(aDocShell));
1554
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_GetInterface(ir));
1555
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
1556
nsCOMPtr<nsIURI> currentURI;
1557
nsCOMPtr<nsIURI> uriClone;
1558
1559
// iterate through each docShell parent item
1560
while (NS_SUCCEEDED(
1561
treeItem->GetInProcessParent(getter_AddRefs(parentTreeItem))) &&
1562
parentTreeItem != nullptr) {
1563
// stop when reaching chrome
1564
if (parentTreeItem->ItemType() == nsIDocShellTreeItem::typeChrome) {
1565
break;
1566
}
1567
1568
Document* doc = parentTreeItem->GetDocument();
1569
NS_ASSERTION(doc,
1570
"Could not get Document from nsIDocShellTreeItem in "
1571
"nsCSPContext::PermitsAncestry");
1572
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1573
1574
currentURI = doc->GetDocumentURI();
1575
1576
if (currentURI) {
1577
// delete the userpass from the URI.
1578
rv = NS_MutateURI(currentURI)
1579
.SetRef(EmptyCString())
1580
.SetUserPass(EmptyCString())
1581
.Finalize(uriClone);
1582
1583
// If setUserPass fails for some reason, just return a clone of the
1584
// current URI
1585
if (NS_FAILED(rv)) {
1586
rv = NS_GetURIWithoutRef(currentURI, getter_AddRefs(uriClone));
1587
NS_ENSURE_SUCCESS(rv, rv);
1588
}
1589
1590
if (CSPCONTEXTLOGENABLED()) {
1591
CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, found ancestor: %s",
1592
uriClone->GetSpecOrDefault().get()));
1593
}
1594
ancestorsArray.AppendElement(uriClone);
1595
}
1596
1597
// next ancestor
1598
treeItem = parentTreeItem;
1599
}
1600
1601
nsAutoString violatedDirective;
1602
1603
// Now that we've got the ancestry chain in ancestorsArray, time to check
1604
// them against any CSP.
1605
// NOTE: the ancestors are not allowed to be sent cross origin; this is a
1606
// restriction not placed on subresource loads.
1607
1608
for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
1609
if (CSPCONTEXTLOGENABLED()) {
1610
CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, checking ancestor: %s",
1611
ancestorsArray[a]->GetSpecOrDefault().get()));
1612
}
1613
// omit the ancestor URI in violation reports if cross-origin as per spec
1614
// (it is a violation of the same-origin policy).
1615
bool okToSendAncestor =
1616
NS_SecurityCompareURIs(ancestorsArray[a], mSelfURI, true);
1617
1618
bool permits =
1619
permitsInternal(nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE,
1620
nullptr, // triggering element
1621
nullptr, // nsICSPEventListener
1622
ancestorsArray[a],
1623
nullptr, // no redirect here.
1624
EmptyString(), // no nonce
1625
false, // not a preload.
1626
true, // specific, do not use default-src
1627
true, // send violation reports
1628
okToSendAncestor,
1629
false); // not parser created
1630
if (!permits) {
1631
*outPermitsAncestry = false;
1632
}
1633
}
1634
return NS_OK;
1635
}
1636
1637
NS_IMETHODIMP
1638
nsCSPContext::Permits(Element* aTriggeringElement,
1639
nsICSPEventListener* aCSPEventListener, nsIURI* aURI,
1640
CSPDirective aDir, bool aSpecific, bool* outPermits) {
1641
// Can't perform check without aURI
1642
if (aURI == nullptr) {
1643
return NS_ERROR_FAILURE;
1644
}
1645
1646
*outPermits =
1647
permitsInternal(aDir, aTriggeringElement, aCSPEventListener, aURI,
1648
nullptr, // no original (pre-redirect) URI
1649
EmptyString(), // no nonce
1650
false, // not a preload.
1651
aSpecific,
1652
true, // send violation reports
1653
true, // send blocked URI in violation reports
1654
false); // not parser created
1655
1656
if (CSPCONTEXTLOGENABLED()) {
1657
CSPCONTEXTLOG(("nsCSPContext::Permits, aUri: %s, aDir: %d, isAllowed: %s",
1658
aURI->GetSpecOrDefault().get(), aDir,
1659
*outPermits ? "allow" : "deny"));
1660
}
1661
1662
return NS_OK;
1663
}
1664
1665
NS_IMETHODIMP
1666
nsCSPContext::ToJSON(nsAString& outCSPinJSON) {
1667
outCSPinJSON.Truncate();
1668
dom::CSPPolicies jsonPolicies;
1669
jsonPolicies.mCsp_policies.Construct();
1670
EnsureIPCPoliciesRead();
1671
1672
for (uint32_t p = 0; p < mPolicies.Length(); p++) {
1673
dom::CSP jsonCSP;
1674
mPolicies[p]->toDomCSPStruct(jsonCSP);
1675
jsonPolicies.mCsp_policies.Value().AppendElement(jsonCSP, fallible);
1676
}
1677
1678
// convert the gathered information to JSON
1679
if (!jsonPolicies.ToJSON(outCSPinJSON)) {
1680
return NS_ERROR_FAILURE;
1681
}
1682
return NS_OK;
1683
}
1684
1685
NS_IMETHODIMP
1686
nsCSPContext::GetCSPSandboxFlags(uint32_t* aOutSandboxFlags) {
1687
if (!aOutSandboxFlags) {
1688
return NS_ERROR_FAILURE;
1689
}
1690
*aOutSandboxFlags = SANDBOXED_NONE;
1691
1692
EnsureIPCPoliciesRead();
1693
for (uint32_t i = 0; i < mPolicies.Length(); i++) {
1694
uint32_t flags = mPolicies[i]->getSandboxFlags();
1695
1696
// current policy doesn't have sandbox flag, check next policy
1697
if (!flags) {
1698
continue;
1699
}
1700
1701
// current policy has sandbox flags, if the policy is in enforcement-mode
1702
// (i.e. not report-only) set these flags and check for policies with more
1703
// restrictions
1704
if (!mPolicies[i]->getReportOnlyFlag()) {
1705
*aOutSandboxFlags |= flags;
1706
} else {
1707
// sandbox directive is ignored in report-only mode, warn about it and
1708
// continue the loop checking for an enforcement policy.
1709
nsAutoString policy;
1710
mPolicies[i]->toString(policy);
1711
1712
CSPCONTEXTLOG(
1713
("nsCSPContext::GetCSPSandboxFlags, report only policy, ignoring "
1714
"sandbox in: %s",
1715
NS_ConvertUTF16toUTF8(policy).get()));
1716
1717
AutoTArray<nsString, 1> params = {policy};
1718
logToConsole("ignoringReportOnlyDirective", params, EmptyString(),
1719
EmptyString(), 0, 0, nsIScriptError::warningFlag);
1720
}
1721
}
1722
1723
return NS_OK;
1724
}
1725
1726
/* ========== CSPViolationReportListener implementation ========== */
1727
1728
NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener,
1729
nsIRequestObserver, nsISupports);
1730
1731
CSPViolationReportListener::CSPViolationReportListener() {}
1732
1733
CSPViolationReportListener::~CSPViolationReportListener() {}
1734
1735
nsresult AppendSegmentToString(nsIInputStream* aInputStream, void* aClosure,
1736
const char* aRawSegment, uint32_t aToOffset,
1737
uint32_t aCount, uint32_t* outWrittenCount) {
1738
nsCString* decodedData = static_cast<nsCString*>(aClosure);
1739
decodedData->Append(aRawSegment, aCount);
1740
*outWrittenCount = aCount;
1741
return NS_OK;
1742
}
1743
1744
NS_IMETHODIMP
1745
CSPViolationReportListener::OnDataAvailable(nsIRequest* aRequest,
1746
nsIInputStream* aInputStream,
1747
uint64_t aOffset, uint32_t aCount) {
1748
uint32_t read;
1749
nsCString decodedData;
1750
return aInputStream->ReadSegments(AppendSegmentToString, &decodedData, aCount,
1751
&read);
1752
}
1753
1754
NS_IMETHODIMP
1755
CSPViolationReportListener::OnStopRequest(nsIRequest* aRequest,
1756
nsresult aStatus) {
1757
return NS_OK;
1758
}
1759
1760
NS_IMETHODIMP
1761
CSPViolationReportListener::OnStartRequest(nsIRequest* aRequest) {
1762
return NS_OK;
1763
}
1764
1765
/* ========== CSPReportRedirectSink implementation ========== */
1766
1767
NS_IMPL_ISUPPORTS(CSPReportRedirectSink, nsIChannelEventSink,
1768
nsIInterfaceRequestor);
1769
1770
CSPReportRedirectSink::CSPReportRedirectSink() {}
1771
1772
CSPReportRedirectSink::~CSPReportRedirectSink() {}
1773
1774
NS_IMETHODIMP
1775
CSPReportRedirectSink::AsyncOnChannelRedirect(
1776
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aRedirFlags,
1777
nsIAsyncVerifyRedirectCallback* aCallback) {
1778
if (aRedirFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
1779
aCallback->OnRedirectVerifyCallback(NS_OK);
1780
return NS_OK;
1781
}
1782
1783
// cancel the old channel so XHR failure callback happens
1784
nsresult rv = aOldChannel->Cancel(NS_ERROR_ABORT);
1785
NS_ENSURE_SUCCESS(rv, rv);
1786
1787
// notify an observer that we have blocked the report POST due to a redirect,
1788
// used in testing, do this async since we're in an async call now to begin
1789
// with
1790
nsCOMPtr<nsIURI> uri;
1791
rv = aOldChannel->GetURI(getter_AddRefs(uri));
1792
NS_ENSURE_SUCCESS(rv, rv);
1793
1794
nsCOMPtr<nsIObserverService> observerService =
1795
mozilla::services::GetObserverService();
1796
NS_ASSERTION(observerService,
1797
"Observer service required to log CSP violations");
1798
observerService->NotifyObservers(
1799
uri, CSP_VIOLATION_TOPIC,
1800
u"denied redirect while sending violation report");
1801
1802
return NS_BINDING_REDIRECTED;
1803
}
1804
1805
NS_IMETHODIMP
1806
CSPRepor