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