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