Coverage Report

Created: 2018-09-25 14:53

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