tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsCSPContext.cpp (86605B)


      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 "nsCSPContext.h"
      8 
      9 #include <string>
     10 #include <unordered_set>
     11 #include <utility>
     12 
     13 #include "mozilla/Logging.h"
     14 #include "mozilla/Preferences.h"
     15 #include "mozilla/StaticPrefs_security.h"
     16 #include "mozilla/dom/CSPDictionariesBinding.h"
     17 #include "mozilla/dom/CSPReportBinding.h"
     18 #include "mozilla/dom/CSPViolationReportBody.h"
     19 #include "mozilla/dom/DocGroup.h"
     20 #include "mozilla/dom/Document.h"
     21 #include "mozilla/dom/Element.h"
     22 #include "mozilla/dom/ReportingUtils.h"
     23 #include "mozilla/dom/WindowGlobalParent.h"
     24 #include "mozilla/glean/DomSecurityMetrics.h"
     25 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     26 #include "nsCOMPtr.h"
     27 #include "nsCSPParser.h"
     28 #include "nsCSPService.h"
     29 #include "nsCSPUtils.h"
     30 #include "nsContentPolicyUtils.h"
     31 #include "nsContentSecurityUtils.h"
     32 #include "nsContentUtils.h"
     33 #include "nsError.h"
     34 #include "nsGlobalWindowOuter.h"
     35 #include "nsIAsyncVerifyRedirectCallback.h"
     36 #include "nsIClassInfoImpl.h"
     37 #include "nsIContentPolicy.h"
     38 #include "nsIEventTarget.h"
     39 #include "nsIHttpChannel.h"
     40 #include "nsIInterfaceRequestor.h"
     41 #include "nsIInterfaceRequestorUtils.h"
     42 #include "nsINetworkInterceptController.h"
     43 #include "nsIObjectInputStream.h"
     44 #include "nsIObjectOutputStream.h"
     45 #include "nsIObserver.h"
     46 #include "nsIObserverService.h"
     47 #include "nsIScriptElement.h"
     48 #include "nsIScriptError.h"
     49 #include "nsIStringStream.h"
     50 #include "nsISupportsPrimitives.h"
     51 #include "nsIURIMutator.h"
     52 #include "nsIUploadChannel.h"
     53 #include "nsJSUtils.h"
     54 #include "nsMimeTypes.h"
     55 #include "nsNetUtil.h"
     56 #include "nsSandboxFlags.h"
     57 #include "nsScriptSecurityManager.h"
     58 #include "nsStreamUtils.h"
     59 #include "nsString.h"
     60 #include "nsStringStream.h"
     61 #include "nsSupportsPrimitives.h"
     62 #include "nsThreadUtils.h"
     63 #include "nsXULAppAPI.h"
     64 
     65 using namespace mozilla;
     66 using namespace mozilla::dom;
     67 using namespace mozilla::ipc;
     68 
     69 static LogModule* GetCspContextLog() {
     70  static LazyLogModule gCspContextPRLog("CSPContext");
     71  return gCspContextPRLog;
     72 }
     73 
     74 #define CSPCONTEXTLOG(args) \
     75  MOZ_LOG(GetCspContextLog(), mozilla::LogLevel::Debug, args)
     76 #define CSPCONTEXTLOGENABLED() \
     77  MOZ_LOG_TEST(GetCspContextLog(), mozilla::LogLevel::Debug)
     78 
     79 static LogModule* GetCspOriginLogLog() {
     80  static LazyLogModule gCspOriginPRLog("CSPOrigin");
     81  return gCspOriginPRLog;
     82 }
     83 
     84 #define CSPORIGINLOG(args) \
     85  MOZ_LOG(GetCspOriginLogLog(), mozilla::LogLevel::Debug, args)
     86 #define CSPORIGINLOGENABLED() \
     87  MOZ_LOG_TEST(GetCspOriginLogLog(), mozilla::LogLevel::Debug)
     88 
     89 #ifdef DEBUG
     90 /**
     91 * This function is only used for verification purposes within
     92 * GatherSecurityPolicyViolationEventData.
     93 */
     94 static bool ValidateDirectiveName(const nsAString& aDirective) {
     95  static const auto directives = []() {
     96    std::unordered_set<std::string> directives;
     97    constexpr size_t dirLen =
     98        sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]);
     99    for (size_t i = 0; i < dirLen; ++i) {
    100      directives.insert(CSPStrDirectives[i]);
    101    }
    102    return directives;
    103  }();
    104 
    105  nsAutoString directive(aDirective);
    106  auto itr = directives.find(NS_ConvertUTF16toUTF8(directive).get());
    107  return itr != directives.end();
    108 }
    109 #endif  // DEBUG
    110 
    111 static void BlockedContentSourceToString(
    112    CSPViolationData::BlockedContentSource aSource, nsACString& aString) {
    113  switch (aSource) {
    114    case CSPViolationData::BlockedContentSource::Unknown:
    115      aString.Truncate();
    116      break;
    117 
    118    case CSPViolationData::BlockedContentSource::Inline:
    119      aString.AssignLiteral("inline");
    120      break;
    121 
    122    case CSPViolationData::BlockedContentSource::Eval:
    123      aString.AssignLiteral("eval");
    124      break;
    125 
    126    case CSPViolationData::BlockedContentSource::Self:
    127      aString.AssignLiteral("self");
    128      break;
    129 
    130    case CSPViolationData::BlockedContentSource::WasmEval:
    131      aString.AssignLiteral("wasm-eval");
    132      break;
    133    case CSPViolationData::BlockedContentSource::TrustedTypesPolicy:
    134      aString.AssignLiteral("trusted-types-policy");
    135      break;
    136    case CSPViolationData::BlockedContentSource::TrustedTypesSink:
    137      aString.AssignLiteral("trusted-types-sink");
    138      break;
    139  }
    140 }
    141 
    142 /* =====  nsIContentSecurityPolicy impl ====== */
    143 
    144 NS_IMETHODIMP
    145 nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
    146                         nsICSPEventListener* aCSPEventListener,
    147                         nsILoadInfo* aLoadInfo, nsIURI* aContentLocation,
    148                         nsIURI* aOriginalURIIfRedirect,
    149                         bool aSendViolationReports, int16_t* outDecision) {
    150  if (CSPCONTEXTLOGENABLED()) {
    151    CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, aContentLocation: %s",
    152                   aContentLocation->GetSpecOrDefault().get()));
    153    CSPCONTEXTLOG((">>>>                      aContentType: %s",
    154                   NS_CP_ContentTypeName(aContentType)));
    155  }
    156 
    157  // This ShouldLoad function is called from nsCSPService::ShouldLoad,
    158  // which already checked a number of things, including:
    159  // * aContentLocation is not null; we can consume this without further checks
    160  // * scheme is not a allowlisted scheme (about: chrome:, etc).
    161  // * CSP is enabled
    162  // * Content Type is not allowlisted (CSP Reports, TYPE_DOCUMENT, etc).
    163  // * Fast Path for Apps
    164 
    165  // Default decision, CSP can revise it if there's a policy to enforce
    166  *outDecision = nsIContentPolicy::ACCEPT;
    167 
    168  // If the content type doesn't map to a CSP directive, there's nothing for
    169  // CSP to do.
    170  CSPDirective dir = CSP_ContentTypeToDirective(aContentType);
    171  if (dir == nsIContentSecurityPolicy::NO_DIRECTIVE) {
    172    return NS_OK;
    173  }
    174 
    175  bool permitted = permitsInternal(
    176      dir,
    177      nullptr,  // aTriggeringElement
    178      aCSPEventListener, aLoadInfo, aContentLocation, aOriginalURIIfRedirect,
    179      false,  // allow fallback to default-src
    180      aSendViolationReports,
    181      true);  // send blocked URI in violation reports
    182 
    183  *outDecision =
    184      permitted ? nsIContentPolicy::ACCEPT : nsIContentPolicy::REJECT_SERVER;
    185 
    186  if (CSPCONTEXTLOGENABLED()) {
    187    CSPCONTEXTLOG(
    188        ("nsCSPContext::ShouldLoad, decision: %s, "
    189         "aContentLocation: %s",
    190         *outDecision > 0 ? "load" : "deny",
    191         aContentLocation->GetSpecOrDefault().get()));
    192  }
    193  return NS_OK;
    194 }
    195 
    196 bool nsCSPContext::permitsInternal(
    197    CSPDirective aDir, Element* aTriggeringElement,
    198    nsICSPEventListener* aCSPEventListener, nsILoadInfo* aLoadInfo,
    199    nsIURI* aContentLocation, nsIURI* aOriginalURIIfRedirect, bool aSpecific,
    200    bool aSendViolationReports, bool aSendContentLocationInViolationReports) {
    201  EnsureIPCPoliciesRead();
    202  bool permits = true;
    203 
    204  nsAutoString violatedDirective;
    205  nsAutoString violatedDirectiveString;
    206  for (uint32_t p = 0; p < mPolicies.Length(); p++) {
    207    if (!mPolicies[p]->permits(aDir, aLoadInfo, aContentLocation,
    208                               !!aOriginalURIIfRedirect, aSpecific,
    209                               violatedDirective, violatedDirectiveString)) {
    210      // If the policy is violated and not report-only, reject the load and
    211      // report to the console
    212      if (!mPolicies[p]->getReportOnlyFlag()) {
    213        CSPCONTEXTLOG(("nsCSPContext::permitsInternal, false"));
    214        permits = false;
    215      }
    216 
    217      // Callers should set |aSendViolationReports| to false if this is a
    218      // preload - the decision may be wrong due to the inability to get the
    219      // nonce, and will incorrectly fail the unit tests.
    220      if (aSendViolationReports) {
    221        auto loc = JSCallingLocation::Get();
    222 
    223        using Resource = CSPViolationData::Resource;
    224        Resource resource =
    225            aSendContentLocationInViolationReports
    226                ? Resource{nsCOMPtr<nsIURI>{aContentLocation}}
    227                : Resource{CSPViolationData::BlockedContentSource::Unknown};
    228 
    229        CSPViolationData cspViolationData{p,
    230                                          std::move(resource),
    231                                          aDir,
    232                                          loc.FileName(),
    233                                          loc.mLine,
    234                                          loc.mColumn,
    235                                          aTriggeringElement,
    236                                          /* aSample */ u""_ns};
    237 
    238        AsyncReportViolation(
    239            aCSPEventListener, std::move(cspViolationData),
    240            aOriginalURIIfRedirect, /* in case of redirect originalURI is not
    241                                       null */
    242            violatedDirective, violatedDirectiveString,
    243            u""_ns,  // no observer subject
    244            false);  // aReportSample (no sample)
    245      }
    246    }
    247  }
    248 
    249  return permits;
    250 }
    251 
    252 /* ===== nsISupports implementation ========== */
    253 
    254 NS_IMPL_CLASSINFO(nsCSPContext, nullptr, 0, NS_CSPCONTEXT_CID)
    255 
    256 NS_IMPL_ISUPPORTS_CI(nsCSPContext, nsIContentSecurityPolicy, nsISerializable)
    257 
    258 nsCSPContext::nsCSPContext()
    259    : mInnerWindowID(0),
    260      mSkipAllowInlineStyleCheck(false),
    261      mLoadingContext(nullptr),
    262      mLoadingPrincipal(nullptr),
    263      mQueueUpMessages(true) {
    264  CSPCONTEXTLOG(("nsCSPContext::nsCSPContext"));
    265 }
    266 
    267 nsCSPContext::~nsCSPContext() {
    268  CSPCONTEXTLOG(("nsCSPContext::~nsCSPContext"));
    269  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
    270    delete mPolicies[i];
    271  }
    272 }
    273 
    274 /* static */
    275 bool nsCSPContext::Equals(nsIContentSecurityPolicy* aCSP,
    276                          nsIContentSecurityPolicy* aOtherCSP) {
    277  if (aCSP == aOtherCSP) {
    278    // fast path for pointer equality
    279    return true;
    280  }
    281 
    282  uint32_t policyCount = 0;
    283  if (aCSP) {
    284    aCSP->GetPolicyCount(&policyCount);
    285  }
    286 
    287  uint32_t otherPolicyCount = 0;
    288  if (aOtherCSP) {
    289    aOtherCSP->GetPolicyCount(&otherPolicyCount);
    290  }
    291 
    292  if (policyCount != otherPolicyCount) {
    293    return false;
    294  }
    295 
    296  nsAutoString policyStr, otherPolicyStr;
    297  for (uint32_t i = 0; i < policyCount; ++i) {
    298    aCSP->GetPolicyString(i, policyStr);
    299    aOtherCSP->GetPolicyString(i, otherPolicyStr);
    300    if (!policyStr.Equals(otherPolicyStr)) {
    301      return false;
    302    }
    303  }
    304 
    305  return true;
    306 }
    307 
    308 nsresult nsCSPContext::InitFromOther(nsCSPContext* aOtherContext) {
    309  NS_ENSURE_ARG(aOtherContext);
    310 
    311  nsresult rv = NS_OK;
    312  nsCOMPtr<Document> doc = do_QueryReferent(aOtherContext->mLoadingContext);
    313  if (doc) {
    314    rv = SetRequestContextWithDocument(doc);
    315  } else {
    316    rv = SetRequestContextWithPrincipal(
    317        aOtherContext->mLoadingPrincipal, aOtherContext->mSelfURI,
    318        aOtherContext->mReferrer, aOtherContext->mInnerWindowID);
    319  }
    320  NS_ENSURE_SUCCESS(rv, rv);
    321 
    322  mSkipAllowInlineStyleCheck = aOtherContext->mSkipAllowInlineStyleCheck;
    323 
    324  // This policy was already parsed somewhere else, don't emit parsing errors.
    325  mSuppressParserLogMessages = true;
    326  for (auto policy : aOtherContext->mPolicies) {
    327    nsAutoString policyStr;
    328    policy->toString(policyStr);
    329    AppendPolicy(policyStr, policy->getReportOnlyFlag(),
    330                 policy->getDeliveredViaMetaTagFlag());
    331  }
    332 
    333  mSuppressParserLogMessages = aOtherContext->mSuppressParserLogMessages;
    334 
    335  mIPCPolicies = aOtherContext->mIPCPolicies.Clone();
    336  return NS_OK;
    337 }
    338 
    339 NS_IMETHODIMP
    340 nsCSPContext::EnsureIPCPoliciesRead() {
    341  // Most likely the parser errors already happened before serializing
    342  // the policy for IPC.
    343  bool previous = mSuppressParserLogMessages;
    344  mSuppressParserLogMessages = true;
    345 
    346  if (mIPCPolicies.Length() > 0) {
    347    nsresult rv;
    348    for (auto& policy : mIPCPolicies) {
    349      rv = AppendPolicy(policy.policy(), policy.reportOnlyFlag(),
    350                        policy.deliveredViaMetaTagFlag());
    351      (void)NS_WARN_IF(NS_FAILED(rv));
    352    }
    353    mIPCPolicies.Clear();
    354  }
    355 
    356  mSuppressParserLogMessages = previous;
    357  return NS_OK;
    358 }
    359 
    360 NS_IMETHODIMP
    361 nsCSPContext::GetPolicyString(uint32_t aIndex, nsAString& outStr) {
    362  outStr.Truncate();
    363  EnsureIPCPoliciesRead();
    364  if (aIndex >= mPolicies.Length()) {
    365    return NS_ERROR_ILLEGAL_VALUE;
    366  }
    367  mPolicies[aIndex]->toString(outStr);
    368  return NS_OK;
    369 }
    370 
    371 const nsCSPPolicy* nsCSPContext::GetPolicy(uint32_t aIndex) {
    372  EnsureIPCPoliciesRead();
    373  if (aIndex >= mPolicies.Length()) {
    374    return nullptr;
    375  }
    376  return mPolicies[aIndex];
    377 }
    378 
    379 NS_IMETHODIMP
    380 nsCSPContext::GetPolicyCount(uint32_t* outPolicyCount) {
    381  EnsureIPCPoliciesRead();
    382  *outPolicyCount = mPolicies.Length();
    383  return NS_OK;
    384 }
    385 
    386 NS_IMETHODIMP
    387 nsCSPContext::GetUpgradeInsecureRequests(bool* outUpgradeRequest) {
    388  EnsureIPCPoliciesRead();
    389  *outUpgradeRequest = false;
    390  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
    391    if (mPolicies[i]->hasDirective(
    392            nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE) &&
    393        !mPolicies[i]->getReportOnlyFlag()) {
    394      *outUpgradeRequest = true;
    395      return NS_OK;
    396    }
    397  }
    398  return NS_OK;
    399 }
    400 
    401 NS_IMETHODIMP
    402 nsCSPContext::GetBlockAllMixedContent(bool* outBlockAllMixedContent) {
    403  EnsureIPCPoliciesRead();
    404  *outBlockAllMixedContent = false;
    405  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
    406    if (!mPolicies[i]->getReportOnlyFlag() &&
    407        mPolicies[i]->hasDirective(
    408            nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT)) {
    409      *outBlockAllMixedContent = true;
    410      return NS_OK;
    411    }
    412  }
    413  return NS_OK;
    414 }
    415 
    416 NS_IMETHODIMP
    417 nsCSPContext::GetEnforcesFrameAncestors(bool* outEnforcesFrameAncestors) {
    418  EnsureIPCPoliciesRead();
    419  *outEnforcesFrameAncestors = false;
    420  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
    421    if (!mPolicies[i]->getReportOnlyFlag() &&
    422        mPolicies[i]->hasDirective(
    423            nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE)) {
    424      *outEnforcesFrameAncestors = true;
    425      return NS_OK;
    426    }
    427  }
    428  return NS_OK;
    429 }
    430 
    431 NS_IMETHODIMP
    432 nsCSPContext::AppendPolicy(const nsAString& aPolicyString, bool aReportOnly,
    433                           bool aDeliveredViaMetaTag) {
    434  CSPCONTEXTLOG(("nsCSPContext::AppendPolicy: %s",
    435                 NS_ConvertUTF16toUTF8(aPolicyString).get()));
    436 
    437  // Use mSelfURI from setRequestContextWith{Document,Principal} (bug 991474)
    438  MOZ_ASSERT(
    439      mLoadingPrincipal,
    440      "did you forget to call setRequestContextWith{Document,Principal}?");
    441  MOZ_ASSERT(
    442      mSelfURI,
    443      "did you forget to call setRequestContextWith{Document,Principal}?");
    444  NS_ENSURE_TRUE(mLoadingPrincipal, NS_ERROR_UNEXPECTED);
    445  NS_ENSURE_TRUE(mSelfURI, NS_ERROR_UNEXPECTED);
    446 
    447  if (CSPORIGINLOGENABLED()) {
    448    nsAutoCString selfURISpec;
    449    mSelfURI->GetSpec(selfURISpec);
    450    CSPORIGINLOG(("CSP - AppendPolicy"));
    451    CSPORIGINLOG((" * selfURI: %s", selfURISpec.get()));
    452    CSPORIGINLOG((" * reportOnly: %s", aReportOnly ? "yes" : "no"));
    453    CSPORIGINLOG(
    454        (" * deliveredViaMetaTag: %s", aDeliveredViaMetaTag ? "yes" : "no"));
    455    CSPORIGINLOG(
    456        (" * policy: %s\n", NS_ConvertUTF16toUTF8(aPolicyString).get()));
    457  }
    458 
    459  nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(
    460      aPolicyString, mSelfURI, aReportOnly, this, aDeliveredViaMetaTag,
    461      mSuppressParserLogMessages);
    462  if (policy) {
    463    if (policy->hasDirective(
    464            nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE)) {
    465      nsAutoCString selfURIspec;
    466      if (mSelfURI) {
    467        mSelfURI->GetAsciiSpec(selfURIspec);
    468      }
    469      CSPCONTEXTLOG(
    470          ("nsCSPContext::AppendPolicy added UPGRADE_IF_INSECURE_DIRECTIVE "
    471           "self-uri=%s referrer=%s",
    472           selfURIspec.get(), mReferrer.get()));
    473    }
    474    if (policy->hasDirective(
    475            nsIContentSecurityPolicy::REQUIRE_TRUSTED_TYPES_FOR_DIRECTIVE)) {
    476      if (mRequireTrustedTypesForDirectiveState !=
    477          RequireTrustedTypesForDirectiveState::ENFORCE) {
    478        mRequireTrustedTypesForDirectiveState =
    479            policy->getReportOnlyFlag()
    480                ? RequireTrustedTypesForDirectiveState::REPORT_ONLY
    481                : RequireTrustedTypesForDirectiveState::ENFORCE;
    482      }
    483      if (nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext)) {
    484        doc->SetHasPolicyWithRequireTrustedTypesForDirective(true);
    485      }
    486    }
    487 
    488    mPolicies.AppendElement(policy);
    489  }
    490 
    491  return NS_OK;
    492 }
    493 
    494 NS_IMETHODIMP
    495 nsCSPContext::GetRequireTrustedTypesForDirectiveState(
    496    RequireTrustedTypesForDirectiveState*
    497        aRequireTrustedTypesForDirectiveState) {
    498  *aRequireTrustedTypesForDirectiveState =
    499      mRequireTrustedTypesForDirectiveState;
    500  return NS_OK;
    501 }
    502 
    503 NS_IMETHODIMP
    504 nsCSPContext::GetAllowsEval(bool* outShouldReportViolation,
    505                            bool* outAllowsEval) {
    506  EnsureIPCPoliciesRead();
    507  *outShouldReportViolation = false;
    508  *outAllowsEval = true;
    509 
    510  if (CSP_IsBrowserXHTML(mSelfURI)) {
    511    // Allow eval in browser.xhtml, just like
    512    // nsContentSecurityUtils::IsEvalAllowed allows it for other privileged
    513    // contexts.
    514    if (StaticPrefs::
    515            security_allow_unsafe_dangerous_privileged_evil_eval_AtStartup()) {
    516      return NS_OK;
    517    }
    518  }
    519 
    520  bool trustedTypesRequired = (mRequireTrustedTypesForDirectiveState ==
    521                               RequireTrustedTypesForDirectiveState::ENFORCE);
    522 
    523  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
    524    if (!(trustedTypesRequired &&
    525          mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_TRUSTED_TYPES_EVAL,
    526                               u""_ns)) &&
    527        !mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_UNSAFE_EVAL, u""_ns)) {
    528      // policy is violated: must report the violation and allow the inline
    529      // script if the policy is report-only.
    530      *outShouldReportViolation = true;
    531      if (!mPolicies[i]->getReportOnlyFlag()) {
    532        *outAllowsEval = false;
    533      }
    534    }
    535  }
    536  return NS_OK;
    537 }
    538 
    539 NS_IMETHODIMP
    540 nsCSPContext::GetAllowsWasmEval(bool* outShouldReportViolation,
    541                                bool* outAllowsWasmEval) {
    542  EnsureIPCPoliciesRead();
    543  *outShouldReportViolation = false;
    544  *outAllowsWasmEval = true;
    545 
    546  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
    547    // Either 'unsafe-eval' or 'wasm-unsafe-eval' can allow this
    548    if (!mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_WASM_UNSAFE_EVAL,
    549                              u""_ns) &&
    550        !mPolicies[i]->allows(SCRIPT_SRC_DIRECTIVE, CSP_UNSAFE_EVAL, u""_ns)) {
    551      // policy is violated: must report the violation and allow the inline
    552      // script if the policy is report-only.
    553      *outShouldReportViolation = true;
    554      if (!mPolicies[i]->getReportOnlyFlag()) {
    555        *outAllowsWasmEval = false;
    556      }
    557    }
    558  }
    559 
    560  return NS_OK;
    561 }
    562 
    563 // Helper function to report inline violations
    564 void nsCSPContext::ReportInlineViolation(
    565    CSPDirective aDirective, Element* aTriggeringElement,
    566    nsICSPEventListener* aCSPEventListener, const nsAString& aNonce,
    567    bool aReportSample, const nsAString& aSourceCode,
    568    const nsAString& aViolatedDirective,
    569    const nsAString& aViolatedDirectiveString, CSPDirective aEffectiveDirective,
    570    uint32_t aViolatedPolicyIndex,  // TODO, use report only flag for that
    571    uint32_t aLineNumber, uint32_t aColumnNumber) {
    572  nsString observerSubject;
    573  // if the nonce is non empty, then we report the nonce error, otherwise
    574  // let's report the hash error; no need to report the unsafe-inline error
    575  // anymore.
    576  if (!aNonce.IsEmpty()) {
    577    observerSubject = (aDirective == SCRIPT_SRC_ELEM_DIRECTIVE ||
    578                       aDirective == SCRIPT_SRC_ATTR_DIRECTIVE)
    579                          ? NS_LITERAL_STRING_FROM_CSTRING(
    580                                SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC)
    581                          : NS_LITERAL_STRING_FROM_CSTRING(
    582                                STYLE_NONCE_VIOLATION_OBSERVER_TOPIC);
    583  } else {
    584    observerSubject = (aDirective == SCRIPT_SRC_ELEM_DIRECTIVE ||
    585                       aDirective == SCRIPT_SRC_ATTR_DIRECTIVE)
    586                          ? NS_LITERAL_STRING_FROM_CSTRING(
    587                                SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC)
    588                          : NS_LITERAL_STRING_FROM_CSTRING(
    589                                STYLE_HASH_VIOLATION_OBSERVER_TOPIC);
    590  }
    591 
    592  auto loc = JSCallingLocation::Get();
    593  if (!loc) {
    594    nsCString sourceFile;
    595    // use selfURI as the source
    596    if (mSelfURI) {
    597      mSelfURI->GetSpec(sourceFile);
    598      loc.mResource = AsVariant(std::move(sourceFile));
    599    }
    600    loc.mLine = aLineNumber;
    601    loc.mColumn = aColumnNumber;
    602  }
    603 
    604  nsAutoCString hashSHA256;
    605  // We optionally include the hash to create more helpful error messages.
    606  nsCOMPtr<nsICryptoHash> hasher;
    607  if (NS_SUCCEEDED(
    608          NS_NewCryptoHash(nsICryptoHash::SHA256, getter_AddRefs(hasher)))) {
    609    NS_ConvertUTF16toUTF8 source(aSourceCode);
    610    if (NS_SUCCEEDED(hasher->Update(
    611            reinterpret_cast<const uint8_t*>(source.get()), source.Length()))) {
    612      (void)hasher->Finish(true, hashSHA256);
    613    }
    614  }
    615 
    616  CSPViolationData cspViolationData{
    617      aViolatedPolicyIndex,
    618      CSPViolationData::Resource{
    619          CSPViolationData::BlockedContentSource::Inline},
    620      aEffectiveDirective,
    621      loc.FileName(),
    622      loc.mLine,
    623      loc.mColumn,
    624      aTriggeringElement,
    625      aSourceCode,
    626      hashSHA256};
    627 
    628  AsyncReportViolation(aCSPEventListener, std::move(cspViolationData),
    629                       mSelfURI,            // aOriginalURI
    630                       aViolatedDirective,  // aViolatedDirective
    631                       aViolatedDirectiveString,
    632                       observerSubject,  // aObserverSubject
    633                       aReportSample);   // aReportSample
    634 }
    635 
    636 NS_IMETHODIMP
    637 nsCSPContext::GetAllowsInline(CSPDirective aDirective, bool aHasUnsafeHash,
    638                              const nsAString& aNonce, bool aParserCreated,
    639                              Element* aTriggeringElement,
    640                              nsICSPEventListener* aCSPEventListener,
    641                              const nsAString& aSourceText,
    642                              uint32_t aLineNumber, uint32_t aColumnNumber,
    643                              bool* outAllowsInline) {
    644  *outAllowsInline = true;
    645 
    646  if (aDirective != SCRIPT_SRC_ELEM_DIRECTIVE &&
    647      aDirective != SCRIPT_SRC_ATTR_DIRECTIVE &&
    648      aDirective != STYLE_SRC_ELEM_DIRECTIVE &&
    649      aDirective != STYLE_SRC_ATTR_DIRECTIVE) {
    650    MOZ_ASSERT(false,
    651               "can only allow inline for (script/style)-src-(attr/elem)");
    652    return NS_OK;
    653  }
    654 
    655  EnsureIPCPoliciesRead();
    656  nsAutoString content;
    657 
    658  // always iterate all policies, otherwise we might not send out all reports
    659  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
    660    // https://w3c.github.io/webappsec-csp/#match-element-to-source-list
    661 
    662    // Step 1. If §6.7.3.2 Does a source list allow all inline behavior for
    663    // type? returns "Allows" given list and type, return "Matches".
    664    if (mPolicies[i]->allowsAllInlineBehavior(aDirective)) {
    665      continue;
    666    }
    667 
    668    // Step 2. If type is "script" or "style", and §6.7.3.1 Is element
    669    // nonceable? returns "Nonceable" when executed upon element:
    670    if ((aDirective == SCRIPT_SRC_ELEM_DIRECTIVE ||
    671         aDirective == STYLE_SRC_ELEM_DIRECTIVE) &&
    672        aTriggeringElement && !aNonce.IsEmpty()) {
    673 #ifdef DEBUG
    674      // NOTE: Folllowing Chrome "Is element nonceable?" doesn't apply to
    675      // <style>.
    676      if (aDirective == SCRIPT_SRC_ELEM_DIRECTIVE) {
    677        // Our callers should have checked this.
    678        MOZ_ASSERT(nsContentSecurityUtils::GetIsElementNonceableNonce(
    679                       *aTriggeringElement) == aNonce);
    680      }
    681 #endif
    682 
    683      // Step 2.1. For each expression of list: [...]
    684      if (mPolicies[i]->allows(aDirective, CSP_NONCE, aNonce)) {
    685        continue;
    686      }
    687    }
    688 
    689    // Check the content length to ensure the content is not allocated more than
    690    // once. Even though we are in a for loop, it is probable that there is only
    691    // one policy, so this check may be unnecessary.
    692    if (content.IsEmpty()) {
    693      if (aSourceText.IsVoid()) {
    694        // Lazily retrieve the text of inline script, see bug 1376651.
    695        nsCOMPtr<nsIScriptElement> element =
    696            do_QueryInterface(aTriggeringElement);
    697        MOZ_ASSERT(element);
    698        element->GetScriptText(content);
    699      } else {
    700        content = aSourceText;
    701      }
    702    }
    703 
    704    // Step 3. Let unsafe-hashes flag be false.
    705    // Step 4. For each expression of list: [...]
    706    bool unsafeHashesFlag =
    707        mPolicies[i]->allows(aDirective, CSP_UNSAFE_HASHES, u""_ns);
    708 
    709    // Step 5. If type is "script" or "style", or unsafe-hashes flag is true:
    710    //
    711    // aHasUnsafeHash is true for event handlers (type "script attribute"),
    712    // style= attributes (type "style attribute") and the javascript: protocol.
    713    if (!aHasUnsafeHash || unsafeHashesFlag) {
    714      if (mPolicies[i]->allows(aDirective, CSP_HASH, content)) {
    715        continue;
    716      }
    717    }
    718 
    719    // TODO(Bug 1844290): Figure out how/if strict-dynamic for inline scripts is
    720    // specified
    721    bool allowed = false;
    722    if ((aDirective == SCRIPT_SRC_ELEM_DIRECTIVE ||
    723         aDirective == SCRIPT_SRC_ATTR_DIRECTIVE) &&
    724        mPolicies[i]->allows(aDirective, CSP_STRICT_DYNAMIC, u""_ns)) {
    725      allowed = !aParserCreated;
    726    }
    727 
    728    if (!allowed) {
    729      // policy is violoated: deny the load unless policy is report only and
    730      // report the violation.
    731      if (!mPolicies[i]->getReportOnlyFlag()) {
    732        *outAllowsInline = false;
    733      }
    734      nsAutoString violatedDirective;
    735      nsAutoString violatedDirectiveString;
    736      bool reportSample = false;
    737      mPolicies[i]->getViolatedDirectiveInformation(
    738          aDirective, violatedDirective, violatedDirectiveString,
    739          &reportSample);
    740 
    741      ReportInlineViolation(aDirective, aTriggeringElement, aCSPEventListener,
    742                            aNonce, reportSample, content, violatedDirective,
    743                            violatedDirectiveString, aDirective, i, aLineNumber,
    744                            aColumnNumber);
    745    }
    746  }
    747 
    748  return NS_OK;
    749 }
    750 
    751 /**
    752 * For each policy, log any violation on the Error Console and send a report
    753 * if a report-uri is present in the policy
    754 *
    755 * @param aViolationType
    756 *     one of the VIOLATION_TYPE_* constants, e.g. inline-script or eval
    757 * @param aSourceFile
    758 *     name of the source file containing the violation (if available)
    759 * @param aContentSample
    760 *     sample of the violating content (to aid debugging)
    761 * @param aLineNum
    762 *     source line number of the violation (if available)
    763 * @param aColumnNum
    764 *     source column number of the violation (if available)
    765 * @param aNonce
    766 *     (optional) If this is a nonce violation, include the nonce so we can
    767 *     recheck to determine which policies were violated and send the
    768 *     appropriate reports.
    769 * @param aContent
    770 *     (optional) If this is a hash violation, include contents of the inline
    771 *     resource in the question so we can recheck the hash in order to
    772 *     determine which policies were violated and send the appropriate
    773 *     reports.
    774 */
    775 NS_IMETHODIMP
    776 nsCSPContext::LogViolationDetails(
    777    uint16_t aViolationType, Element* aTriggeringElement,
    778    nsICSPEventListener* aCSPEventListener, const nsACString& aSourceFile,
    779    const nsAString& aScriptSample, int32_t aLineNum, int32_t aColumnNum,
    780    const nsAString& aNonce, const nsAString& aContent) {
    781  EnsureIPCPoliciesRead();
    782 
    783  CSPViolationData::BlockedContentSource blockedContentSource;
    784  enum CSPKeyword keyword;
    785  nsAutoString observerSubject;
    786  if (aViolationType == nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL) {
    787    blockedContentSource = CSPViolationData::BlockedContentSource::Eval;
    788    keyword = CSP_UNSAFE_EVAL;
    789    observerSubject.AssignLiteral(EVAL_VIOLATION_OBSERVER_TOPIC);
    790  } else {
    791    NS_ASSERTION(
    792        aViolationType == nsIContentSecurityPolicy::VIOLATION_TYPE_WASM_EVAL,
    793        "unexpected aViolationType");
    794    blockedContentSource = CSPViolationData::BlockedContentSource::WasmEval;
    795    keyword = CSP_WASM_UNSAFE_EVAL;
    796    observerSubject.AssignLiteral(WASM_EVAL_VIOLATION_OBSERVER_TOPIC);
    797  }
    798 
    799  for (uint32_t p = 0; p < mPolicies.Length(); p++) {
    800    NS_ASSERTION(mPolicies[p], "null pointer in nsTArray<nsCSPPolicy>");
    801 
    802    if (mPolicies[p]->allows(SCRIPT_SRC_DIRECTIVE, keyword, u""_ns)) {
    803      continue;
    804    }
    805 
    806    CSPViolationData cspViolationData{
    807        p,
    808        CSPViolationData::Resource{blockedContentSource},
    809        /* aEffectiveDirective */ CSPDirective::SCRIPT_SRC_DIRECTIVE,
    810        aSourceFile,
    811        static_cast<uint32_t>(aLineNum),
    812        static_cast<uint32_t>(aColumnNum),
    813        aTriggeringElement,
    814        aScriptSample};
    815 
    816    LogViolationDetailsUnchecked(aCSPEventListener, std::move(cspViolationData),
    817                                 observerSubject, ForceReportSample::No);
    818  }
    819  return NS_OK;
    820 }
    821 
    822 void nsCSPContext::LogViolationDetailsUnchecked(
    823    nsICSPEventListener* aCSPEventListener,
    824    mozilla::dom::CSPViolationData&& aCSPViolationData,
    825    const nsAString& aObserverSubject, ForceReportSample aForceReportSample) {
    826  EnsureIPCPoliciesRead();
    827 
    828  nsAutoString violatedDirectiveName;
    829  nsAutoString violatedDirectiveNameAndValue;
    830  bool reportSample = false;
    831  mPolicies[aCSPViolationData.mViolatedPolicyIndex]
    832      ->getViolatedDirectiveInformation(
    833          aCSPViolationData.mEffectiveDirective, violatedDirectiveName,
    834          violatedDirectiveNameAndValue, &reportSample);
    835 
    836  if (aForceReportSample == ForceReportSample::Yes) {
    837    reportSample = true;
    838  }
    839 
    840  AsyncReportViolation(aCSPEventListener, std::move(aCSPViolationData), nullptr,
    841                       violatedDirectiveName, violatedDirectiveNameAndValue,
    842                       aObserverSubject, reportSample);
    843 }
    844 
    845 NS_IMETHODIMP nsCSPContext::LogTrustedTypesViolationDetailsUnchecked(
    846    CSPViolationData&& aCSPViolationData, const nsAString& aObserverSubject,
    847    nsICSPEventListener* aCSPEventListener) {
    848  EnsureIPCPoliciesRead();
    849 
    850  // Trusted types don't support the "report-sample" keyword
    851  // (https://github.com/w3c/trusted-types/issues/531#issuecomment-2194166146).
    852  LogViolationDetailsUnchecked(aCSPEventListener, std::move(aCSPViolationData),
    853                               aObserverSubject, ForceReportSample::Yes);
    854  return NS_OK;
    855 }
    856 
    857 #undef CASE_CHECK_AND_REPORT
    858 
    859 NS_IMETHODIMP
    860 nsCSPContext::SetRequestContextWithDocument(Document* aDocument) {
    861  MOZ_ASSERT(aDocument, "Can't set context without doc");
    862  NS_ENSURE_ARG(aDocument);
    863 
    864  mLoadingContext = do_GetWeakReference(aDocument);
    865  mSelfURI = aDocument->GetDocumentURI();
    866  mLoadingPrincipal = aDocument->NodePrincipal();
    867  aDocument->GetReferrer(mReferrer);
    868  mInnerWindowID = aDocument->InnerWindowID();
    869  // the innerWindowID is not available for CSPs delivered through the
    870  // header at the time setReqeustContext is called - let's queue up
    871  // console messages until it becomes available, see flushConsoleMessages
    872  mQueueUpMessages = !mInnerWindowID;
    873  mCallingChannelLoadGroup = aDocument->GetDocumentLoadGroup();
    874  // set the flag on the document for CSP telemetry
    875  mEventTarget = GetMainThreadSerialEventTarget();
    876 
    877  MOZ_ASSERT(mLoadingPrincipal, "need a valid requestPrincipal");
    878  MOZ_ASSERT(mSelfURI, "need mSelfURI to translate 'self' into actual URI");
    879  return NS_OK;
    880 }
    881 
    882 NS_IMETHODIMP
    883 nsCSPContext::SetRequestContextWithPrincipal(nsIPrincipal* aRequestPrincipal,
    884                                             nsIURI* aSelfURI,
    885                                             const nsACString& aReferrer,
    886                                             uint64_t aInnerWindowId) {
    887  NS_ENSURE_ARG(aRequestPrincipal);
    888 
    889  mLoadingPrincipal = aRequestPrincipal;
    890  mSelfURI = aSelfURI;
    891  mReferrer = aReferrer;
    892  mInnerWindowID = aInnerWindowId;
    893  // if no document is available, then it also does not make sense to queue
    894  // console messages sending messages to the browser console instead of the web
    895  // console in that case.
    896  mQueueUpMessages = false;
    897  mCallingChannelLoadGroup = nullptr;
    898  mEventTarget = nullptr;
    899 
    900  MOZ_ASSERT(mLoadingPrincipal, "need a valid requestPrincipal");
    901  MOZ_ASSERT(mSelfURI, "need mSelfURI to translate 'self' into actual URI");
    902  return NS_OK;
    903 }
    904 
    905 nsIPrincipal* nsCSPContext::GetRequestPrincipal() { return mLoadingPrincipal; }
    906 
    907 nsIURI* nsCSPContext::GetSelfURI() { return mSelfURI; }
    908 
    909 NS_IMETHODIMP
    910 nsCSPContext::GetReferrer(nsACString& outReferrer) {
    911  outReferrer.Assign(mReferrer);
    912  return NS_OK;
    913 }
    914 
    915 uint64_t nsCSPContext::GetInnerWindowID() { return mInnerWindowID; }
    916 
    917 bool nsCSPContext::GetSkipAllowInlineStyleCheck() {
    918  return mSkipAllowInlineStyleCheck;
    919 }
    920 
    921 void nsCSPContext::SetSkipAllowInlineStyleCheck(
    922    bool aSkipAllowInlineStyleCheck) {
    923  mSkipAllowInlineStyleCheck = aSkipAllowInlineStyleCheck;
    924 }
    925 
    926 NS_IMETHODIMP
    927 nsCSPContext::EnsureEventTarget(nsIEventTarget* aEventTarget) {
    928  NS_ENSURE_ARG(aEventTarget);
    929  // Don't bother if we did have a valid event target (if the csp object is
    930  // tied to a document in SetRequestContextWithDocument)
    931  if (mEventTarget) {
    932    return NS_OK;
    933  }
    934 
    935  mEventTarget = aEventTarget;
    936  return NS_OK;
    937 }
    938 
    939 struct ConsoleMsgQueueElem {
    940  nsString mMsg;
    941  nsCString mSourceName;
    942  nsString mSourceLine;
    943  uint32_t mLineNumber;
    944  uint32_t mColumnNumber;
    945  uint32_t mSeverityFlag;
    946  nsCString mCategory;
    947 };
    948 
    949 void nsCSPContext::flushConsoleMessages() {
    950  bool privateWindow = false;
    951 
    952  // should flush messages even if doc is not available
    953  nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
    954  if (doc) {
    955    mInnerWindowID = doc->InnerWindowID();
    956    privateWindow =
    957        doc->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing();
    958  }
    959 
    960  mQueueUpMessages = false;
    961 
    962  for (uint32_t i = 0; i < mConsoleMsgQueue.Length(); i++) {
    963    ConsoleMsgQueueElem& elem = mConsoleMsgQueue[i];
    964    CSP_LogMessage(elem.mMsg, elem.mSourceName, elem.mSourceLine,
    965                   elem.mLineNumber, elem.mColumnNumber, elem.mSeverityFlag,
    966                   elem.mCategory, mInnerWindowID, privateWindow);
    967  }
    968  mConsoleMsgQueue.Clear();
    969 }
    970 
    971 void nsCSPContext::logToConsole(const char* aName,
    972                                const nsTArray<nsString>& aParams,
    973                                const nsACString& aSourceName,
    974                                const nsAString& aSourceLine,
    975                                uint32_t aLineNumber, uint32_t aColumnNumber,
    976                                uint32_t aSeverityFlag) {
    977  // we are passing aName as the category so we can link to the
    978  // appropriate MDN docs depending on the specific error.
    979  nsDependentCString category(aName);
    980 
    981  // Fallback
    982  nsAutoCString spec;
    983  if (aSourceName.IsEmpty() && mSelfURI) {
    984    mSelfURI->GetSpec(spec);
    985  }
    986 
    987  const auto& sourceName = aSourceName.IsEmpty() ? spec : aSourceName;
    988 
    989  // let's check if we have to queue up console messages
    990  if (mQueueUpMessages) {
    991    nsAutoString msg;
    992    CSP_GetLocalizedStr(aName, aParams, msg);
    993    ConsoleMsgQueueElem& elem = *mConsoleMsgQueue.AppendElement();
    994    elem.mMsg = msg;
    995    elem.mSourceName = sourceName;
    996    elem.mSourceLine = PromiseFlatString(aSourceLine);
    997    elem.mLineNumber = aLineNumber;
    998    elem.mColumnNumber = aColumnNumber;
    999    elem.mSeverityFlag = aSeverityFlag;
   1000    elem.mCategory = category;
   1001    return;
   1002  }
   1003 
   1004  bool privateWindow = false;
   1005  if (nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext)) {
   1006    privateWindow =
   1007        doc->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing();
   1008  }
   1009 
   1010  CSP_LogLocalizedStr(aName, aParams, sourceName, aSourceLine, aLineNumber,
   1011                      aColumnNumber, aSeverityFlag, category, mInnerWindowID,
   1012                      privateWindow);
   1013 }
   1014 
   1015 /**
   1016 * Strip URI for reporting according to:
   1017 * https://w3c.github.io/webappsec-csp/#strip-url-for-use-in-reports
   1018 *
   1019 * @param aSelfURI
   1020 *        The URI of the CSP policy. Used for cross-origin checks.
   1021 * @param aURI
   1022 *        The URI of the blocked resource. In case of a redirect, this it the
   1023 *        initial URI the request started out with, not the redirected URI.
   1024 * @param aEffectiveDirective
   1025 *        The effective directive that triggered this report
   1026 * @return The ASCII serialization of the uri to be reported ignoring
   1027 *         the ref part of the URI.
   1028 */
   1029 void StripURIForReporting(nsIURI* aSelfURI, nsIURI* aURI,
   1030                          const nsAString& aEffectiveDirective,
   1031                          nsACString& outStrippedURI) {
   1032  // Non-standard: For reports going to internal chrome: documents include the
   1033  // whole URI.
   1034  if (aSelfURI->SchemeIs("chrome")) {
   1035    aURI->GetSpecIgnoringRef(outStrippedURI);
   1036    return;
   1037  }
   1038 
   1039  // Step 1. If url’s scheme is not an HTTP(S) scheme, then return url’s scheme.
   1040  // https://github.com/w3c/webappsec-csp/issues/735: We also allow WS(S)
   1041  // schemes.
   1042  if (!net::SchemeIsHttpOrHttps(aURI) &&
   1043      !(aURI->SchemeIs("ws") || aURI->SchemeIs("wss"))) {
   1044    aURI->GetScheme(outStrippedURI);
   1045    return;
   1046  }
   1047 
   1048  // Step 2. Set url’s fragment to the empty string.
   1049  // Step 3. Set url’s username to the empty string.
   1050  // Step 3. Set url’s password to the empty string.
   1051  nsCOMPtr<nsIURI> stripped;
   1052  if (NS_FAILED(NS_MutateURI(aURI).SetRef(""_ns).SetUserPass(""_ns).Finalize(
   1053          stripped))) {
   1054    // Mutating the URI failed for some reason, just return the scheme.
   1055    aURI->GetScheme(outStrippedURI);
   1056    return;
   1057  }
   1058 
   1059  // Non-standard: https://github.com/w3c/webappsec-csp/issues/735
   1060  // For cross-origin URIs in frame-src also strip the path.
   1061  // This prevents detailed tracking of pages loaded into an iframe
   1062  // by the embedding page using a report-only policy.
   1063  if (aEffectiveDirective.EqualsLiteral("frame-src") ||
   1064      aEffectiveDirective.EqualsLiteral("object-src")) {
   1065    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   1066    if (NS_FAILED(ssm->CheckSameOriginURI(aSelfURI, stripped, false, false))) {
   1067      stripped->GetPrePath(outStrippedURI);
   1068      return;
   1069    }
   1070  }
   1071 
   1072  // Step 4. Return the result of executing the URL serializer on url.
   1073  stripped->GetSpec(outStrippedURI);
   1074 }
   1075 
   1076 nsresult nsCSPContext::GatherSecurityPolicyViolationEventData(
   1077    nsIURI* aOriginalURI, const nsAString& aEffectiveDirective,
   1078    const mozilla::dom::CSPViolationData& aCSPViolationData, bool aReportSample,
   1079    mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
   1080  EnsureIPCPoliciesRead();
   1081  NS_ENSURE_ARG_MAX(aCSPViolationData.mViolatedPolicyIndex,
   1082                    mPolicies.Length() - 1);
   1083 
   1084  MOZ_ASSERT(ValidateDirectiveName(aEffectiveDirective),
   1085             "Invalid directive name");
   1086 
   1087  nsresult rv;
   1088 
   1089  // document-uri
   1090  nsAutoCString reportDocumentURI;
   1091  StripURIForReporting(mSelfURI, mSelfURI, aEffectiveDirective,
   1092                       reportDocumentURI);
   1093  CopyUTF8toUTF16(reportDocumentURI, aViolationEventInit.mDocumentURI);
   1094 
   1095  // referrer
   1096  CopyUTF8toUTF16(mReferrer, aViolationEventInit.mReferrer);
   1097 
   1098  // blocked-uri
   1099  // Corresponds to
   1100  // <https://w3c.github.io/webappsec-csp/#obtain-violation-blocked-uri>.
   1101  if (aCSPViolationData.mResource.is<nsCOMPtr<nsIURI>>()) {
   1102    nsAutoCString reportBlockedURI;
   1103    StripURIForReporting(
   1104        mSelfURI,
   1105        aOriginalURI ? aOriginalURI
   1106                     : aCSPViolationData.mResource.as<nsCOMPtr<nsIURI>>().get(),
   1107        aEffectiveDirective, reportBlockedURI);
   1108    CopyUTF8toUTF16(reportBlockedURI, aViolationEventInit.mBlockedURI);
   1109  } else {
   1110    nsAutoCString blockedContentSource;
   1111    BlockedContentSourceToString(
   1112        aCSPViolationData.mResource
   1113            .as<CSPViolationData::BlockedContentSource>(),
   1114        blockedContentSource);
   1115    CopyUTF8toUTF16(blockedContentSource, aViolationEventInit.mBlockedURI);
   1116  }
   1117 
   1118  // effective-directive
   1119  // The name of the policy directive that was violated.
   1120  aViolationEventInit.mEffectiveDirective = aEffectiveDirective;
   1121 
   1122  // violated-directive
   1123  // In CSP2, the policy directive that was violated, as it appears in the
   1124  // policy. In CSP3, the same as effective-directive.
   1125  aViolationEventInit.mViolatedDirective = aEffectiveDirective;
   1126 
   1127  // original-policy
   1128  nsAutoString originalPolicy;
   1129  rv = this->GetPolicyString(aCSPViolationData.mViolatedPolicyIndex,
   1130                             originalPolicy);
   1131  NS_ENSURE_SUCCESS(rv, rv);
   1132  aViolationEventInit.mOriginalPolicy = originalPolicy;
   1133 
   1134  // source-file
   1135  if (!aCSPViolationData.mSourceFile.IsEmpty()) {
   1136    // if aSourceFile is a URI, we have to make sure to strip fragments
   1137    nsCOMPtr<nsIURI> sourceURI;
   1138    NS_NewURI(getter_AddRefs(sourceURI), aCSPViolationData.mSourceFile);
   1139    if (sourceURI) {
   1140      nsAutoCString stripped;
   1141      StripURIForReporting(mSelfURI, sourceURI, aEffectiveDirective, stripped);
   1142      CopyUTF8toUTF16(stripped, aViolationEventInit.mSourceFile);
   1143    } else {
   1144      CopyUTF8toUTF16(aCSPViolationData.mSourceFile,
   1145                      aViolationEventInit.mSourceFile);
   1146    }
   1147  }
   1148 
   1149  // sample (already truncated)
   1150  aViolationEventInit.mSample =
   1151      aReportSample ? aCSPViolationData.mSample : EmptyString();
   1152 
   1153  // disposition
   1154  aViolationEventInit.mDisposition =
   1155      mPolicies[aCSPViolationData.mViolatedPolicyIndex]->getReportOnlyFlag()
   1156          ? mozilla::dom::SecurityPolicyViolationEventDisposition::Report
   1157          : mozilla::dom::SecurityPolicyViolationEventDisposition::Enforce;
   1158 
   1159  // status-code
   1160  uint16_t statusCode = 0;
   1161  {
   1162    nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
   1163    if (doc) {
   1164      nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(doc->GetChannel());
   1165      if (channel) {
   1166        uint32_t responseStatus = 0;
   1167        nsresult rv = channel->GetResponseStatus(&responseStatus);
   1168        if (NS_SUCCEEDED(rv) && (responseStatus <= UINT16_MAX)) {
   1169          statusCode = static_cast<uint16_t>(responseStatus);
   1170        }
   1171      }
   1172    }
   1173  }
   1174  aViolationEventInit.mStatusCode = statusCode;
   1175 
   1176  // line-number
   1177  aViolationEventInit.mLineNumber = aCSPViolationData.mLineNumber;
   1178 
   1179  // column-number
   1180  aViolationEventInit.mColumnNumber = aCSPViolationData.mColumnNumber;
   1181 
   1182  aViolationEventInit.mBubbles = true;
   1183  aViolationEventInit.mComposed = true;
   1184 
   1185  return NS_OK;
   1186 }
   1187 
   1188 bool nsCSPContext::ShouldThrottleReport(
   1189    const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
   1190  // Fetch the rate limit preference.
   1191  const uint32_t kLimitCount =
   1192      StaticPrefs::security_csp_reporting_limit_count();
   1193 
   1194  // Disable throttling if the preference is set to 0.
   1195  if (kLimitCount == 0) {
   1196    return false;
   1197  }
   1198 
   1199  const uint32_t kTimeSpanSeconds = 2;
   1200  TimeDuration throttleSpan = TimeDuration::FromSeconds(kTimeSpanSeconds);
   1201  if (mSendReportLimitSpanStart.IsNull() ||
   1202      ((TimeStamp::Now() - mSendReportLimitSpanStart) > throttleSpan)) {
   1203    // Initial call or timespan exceeded, reset counter and timespan.
   1204    mSendReportLimitSpanStart = TimeStamp::Now();
   1205    mSendReportLimitCount = 1;
   1206    // Also make sure we warn about omitted messages. (XXX or only do this once
   1207    // per context?)
   1208    mWarnedAboutTooManyReports = false;
   1209    return false;
   1210  }
   1211 
   1212  if (mSendReportLimitCount < kLimitCount) {
   1213    mSendReportLimitCount++;
   1214    return false;
   1215  }
   1216 
   1217  // Rate limit reached
   1218  if (!mWarnedAboutTooManyReports) {
   1219    logToConsole("tooManyReports", {},
   1220                 NS_ConvertUTF16toUTF8(aViolationEventInit.mSourceFile),
   1221                 aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
   1222                 aViolationEventInit.mColumnNumber, nsIScriptError::errorFlag);
   1223    mWarnedAboutTooManyReports = true;
   1224  }
   1225  return true;
   1226 }
   1227 
   1228 nsresult nsCSPContext::SendReports(
   1229    const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit,
   1230    uint32_t aViolatedPolicyIndex) {
   1231  EnsureIPCPoliciesRead();
   1232  NS_ENSURE_ARG_MAX(aViolatedPolicyIndex, mPolicies.Length() - 1);
   1233 
   1234  if (!StaticPrefs::security_csp_reporting_enabled() ||
   1235      ShouldThrottleReport(aViolationEventInit)) {
   1236    return NS_OK;
   1237  }
   1238 
   1239  nsAutoString reportGroup;
   1240  mPolicies[aViolatedPolicyIndex]->getReportGroup(reportGroup);
   1241 
   1242  // CSP Level 3 Reporting
   1243  if (StaticPrefs::dom_reporting_enabled() && !reportGroup.IsEmpty()) {
   1244    return SendReportsToEndpoints(reportGroup, aViolationEventInit);
   1245  }
   1246 
   1247  nsTArray<nsString> reportURIs;
   1248  mPolicies[aViolatedPolicyIndex]->getReportURIs(reportURIs);
   1249 
   1250  // [Deprecated] CSP Level 2 Reporting
   1251  if (!reportURIs.IsEmpty()) {
   1252    return SendReportsToURIs(reportURIs, aViolationEventInit);
   1253  }
   1254 
   1255  return NS_OK;
   1256 }
   1257 
   1258 nsresult nsCSPContext::SendReportsToEndpoints(
   1259    nsAutoString& reportGroup,
   1260    const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
   1261  nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
   1262  if (!doc) {
   1263    return NS_ERROR_FAILURE;
   1264  }
   1265  nsPIDOMWindowInner* window = doc->GetInnerWindow();
   1266  if (NS_WARN_IF(!window)) {
   1267    return NS_ERROR_FAILURE;
   1268  }
   1269 
   1270  RefPtr<CSPViolationReportBody> body =
   1271      new CSPViolationReportBody(window->AsGlobal(), aViolationEventInit);
   1272 
   1273  ReportingUtils::Report(window->AsGlobal(), nsGkAtoms::cspViolation,
   1274                         reportGroup, aViolationEventInit.mDocumentURI, body);
   1275  return NS_OK;
   1276 }
   1277 
   1278 nsresult nsCSPContext::SendReportsToURIs(
   1279    const nsTArray<nsString>& reportURIs,
   1280    const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
   1281  dom::CSPReport report;
   1282 
   1283  // blocked-uri
   1284  report.mCsp_report.mBlocked_uri = aViolationEventInit.mBlockedURI;
   1285 
   1286  // document-uri
   1287  report.mCsp_report.mDocument_uri = aViolationEventInit.mDocumentURI;
   1288 
   1289  // original-policy
   1290  report.mCsp_report.mOriginal_policy = aViolationEventInit.mOriginalPolicy;
   1291 
   1292  // referrer
   1293  report.mCsp_report.mReferrer = aViolationEventInit.mReferrer;
   1294 
   1295  // effective-directive
   1296  report.mCsp_report.mEffective_directive =
   1297      aViolationEventInit.mEffectiveDirective;
   1298 
   1299  // violated-directive
   1300  report.mCsp_report.mViolated_directive =
   1301      aViolationEventInit.mEffectiveDirective;
   1302 
   1303  // disposition
   1304  report.mCsp_report.mDisposition = aViolationEventInit.mDisposition;
   1305 
   1306  // status-code
   1307  report.mCsp_report.mStatus_code = aViolationEventInit.mStatusCode;
   1308 
   1309  // source-file
   1310  if (!aViolationEventInit.mSourceFile.IsEmpty()) {
   1311    report.mCsp_report.mSource_file.Construct();
   1312    CopyUTF16toUTF8(aViolationEventInit.mSourceFile,
   1313                    report.mCsp_report.mSource_file.Value());
   1314  }
   1315 
   1316  // script-sample
   1317  if (!aViolationEventInit.mSample.IsEmpty()) {
   1318    report.mCsp_report.mScript_sample.Construct();
   1319    report.mCsp_report.mScript_sample.Value() = aViolationEventInit.mSample;
   1320  }
   1321 
   1322  // line-number
   1323  if (aViolationEventInit.mLineNumber != 0) {
   1324    report.mCsp_report.mLine_number.Construct();
   1325    report.mCsp_report.mLine_number.Value() = aViolationEventInit.mLineNumber;
   1326  }
   1327 
   1328  if (aViolationEventInit.mColumnNumber != 0) {
   1329    report.mCsp_report.mColumn_number.Construct();
   1330    report.mCsp_report.mColumn_number.Value() =
   1331        aViolationEventInit.mColumnNumber;
   1332  }
   1333 
   1334  nsString csp_report;
   1335  if (!report.ToJSON(csp_report)) {
   1336    return NS_ERROR_FAILURE;
   1337  }
   1338 
   1339  // ---------- Assembled, now send it to all the report URIs ----------- //
   1340  nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
   1341  nsCOMPtr<nsIURI> reportURI;
   1342  nsCOMPtr<nsIChannel> reportChannel;
   1343 
   1344  nsresult rv;
   1345  for (uint32_t r = 0; r < reportURIs.Length(); r++) {
   1346    NS_ConvertUTF16toUTF8 reportURICstring(reportURIs[r]);
   1347    // try to create a new uri from every report-uri string
   1348    rv = NS_NewURI(getter_AddRefs(reportURI), reportURIs[r]);
   1349    if (NS_FAILED(rv)) {
   1350      AutoTArray<nsString, 1> params = {reportURIs[r]};
   1351      CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
   1352                     reportURICstring.get()));
   1353      logToConsole("triedToSendReport", params,
   1354                   NS_ConvertUTF16toUTF8(aViolationEventInit.mSourceFile),
   1355                   aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
   1356                   aViolationEventInit.mColumnNumber,
   1357                   nsIScriptError::errorFlag);
   1358      continue;  // don't return yet, there may be more URIs
   1359    }
   1360 
   1361    // try to create a new channel for every report-uri
   1362    if (doc) {
   1363      rv =
   1364          NS_NewChannel(getter_AddRefs(reportChannel), reportURI, doc,
   1365                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
   1366                        nsIContentPolicy::TYPE_CSP_REPORT);
   1367    } else {
   1368      rv = NS_NewChannel(
   1369          getter_AddRefs(reportChannel), reportURI, mLoadingPrincipal,
   1370          nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
   1371          nsIContentPolicy::TYPE_CSP_REPORT);
   1372    }
   1373 
   1374    if (NS_FAILED(rv)) {
   1375      CSPCONTEXTLOG(("Could not create new channel for report URI %s",
   1376                     reportURICstring.get()));
   1377      continue;  // don't return yet, there may be more URIs
   1378    }
   1379 
   1380    // log a warning to console if scheme is not http or https
   1381    if (!net::SchemeIsHttpOrHttps(reportURI)) {
   1382      AutoTArray<nsString, 1> params = {reportURIs[r]};
   1383      logToConsole("reportURInotHttpsOrHttp2", params,
   1384                   NS_ConvertUTF16toUTF8(aViolationEventInit.mSourceFile),
   1385                   aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
   1386                   aViolationEventInit.mColumnNumber,
   1387                   nsIScriptError::errorFlag);
   1388      continue;
   1389    }
   1390 
   1391    // make sure this is an anonymous request (no cookies) so in case the
   1392    // policy URI is injected, it can't be abused for CSRF.
   1393    nsLoadFlags flags;
   1394    rv = reportChannel->GetLoadFlags(&flags);
   1395    NS_ENSURE_SUCCESS(rv, rv);
   1396    flags |= nsIRequest::LOAD_ANONYMOUS | nsIChannel::LOAD_BACKGROUND |
   1397             nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
   1398    rv = reportChannel->SetLoadFlags(flags);
   1399    NS_ENSURE_SUCCESS(rv, rv);
   1400 
   1401    // we need to set an nsIChannelEventSink on the channel object
   1402    // so we can tell it to not follow redirects when posting the reports
   1403    RefPtr<CSPReportRedirectSink> reportSink = new CSPReportRedirectSink();
   1404    if (doc && doc->GetDocShell()) {
   1405      nsCOMPtr<nsINetworkInterceptController> interceptController =
   1406          do_QueryInterface(doc->GetDocShell());
   1407      reportSink->SetInterceptController(interceptController);
   1408    }
   1409    reportChannel->SetNotificationCallbacks(reportSink);
   1410 
   1411    // apply the loadgroup taken by setRequestContextWithDocument. If there's
   1412    // no loadgroup, AsyncOpen will fail on process-split necko (since the
   1413    // channel cannot query the iBrowserChild).
   1414    rv = reportChannel->SetLoadGroup(mCallingChannelLoadGroup);
   1415    NS_ENSURE_SUCCESS(rv, rv);
   1416 
   1417    // wire in the string input stream to send the report
   1418    nsCOMPtr<nsIStringInputStream> sis(
   1419        do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
   1420    NS_ASSERTION(sis,
   1421                 "nsIStringInputStream is needed but not available to send CSP "
   1422                 "violation reports");
   1423    rv = sis->SetUTF8Data(NS_ConvertUTF16toUTF8(csp_report));
   1424    NS_ENSURE_SUCCESS(rv, rv);
   1425 
   1426    nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(reportChannel));
   1427    if (!uploadChannel) {
   1428      // It's possible the URI provided can't be uploaded to, in which case
   1429      // we skip this one. We'll already have warned about a non-HTTP URI
   1430      // earlier.
   1431      continue;
   1432    }
   1433 
   1434    rv = uploadChannel->SetUploadStream(sis, "application/csp-report"_ns, -1);
   1435    NS_ENSURE_SUCCESS(rv, rv);
   1436 
   1437    // if this is an HTTP channel, set the request method to post
   1438    nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(reportChannel));
   1439    if (httpChannel) {
   1440      rv = httpChannel->SetRequestMethod("POST"_ns);
   1441      MOZ_ASSERT(NS_SUCCEEDED(rv));
   1442    }
   1443 
   1444    RefPtr<CSPViolationReportListener> listener =
   1445        new CSPViolationReportListener();
   1446    rv = reportChannel->AsyncOpen(listener);
   1447 
   1448    // AsyncOpen should not fail, but could if there's no load group (like if
   1449    // SetRequestContextWith{Document,Principal} is not given a channel). This
   1450    // should fail quietly and not return an error since it's really ok if
   1451    // reports don't go out, but it's good to log the error locally.
   1452 
   1453    if (NS_FAILED(rv)) {
   1454      AutoTArray<nsString, 1> params = {reportURIs[r]};
   1455      CSPCONTEXTLOG(("AsyncOpen failed for report URI %s",
   1456                     NS_ConvertUTF16toUTF8(params[0]).get()));
   1457      logToConsole("triedToSendReport", params,
   1458                   NS_ConvertUTF16toUTF8(aViolationEventInit.mSourceFile),
   1459                   aViolationEventInit.mSample, aViolationEventInit.mLineNumber,
   1460                   aViolationEventInit.mColumnNumber,
   1461                   nsIScriptError::errorFlag);
   1462    } else {
   1463      CSPCONTEXTLOG(
   1464          ("Sent violation report to URI %s", reportURICstring.get()));
   1465    }
   1466  }
   1467  return NS_OK;
   1468 }
   1469 
   1470 void nsCSPContext::HandleInternalPageViolation(
   1471    const CSPViolationData& aCSPViolationData,
   1472    const SecurityPolicyViolationEventInit& aInit,
   1473    const nsAString& aViolatedDirectiveNameAndValue) {
   1474  if (!mSelfURI || !mSelfURI->SchemeIs("chrome")) {
   1475    return;
   1476  }
   1477 
   1478  nsAutoCString selfURISpec;
   1479  mSelfURI->GetSpec(selfURISpec);
   1480 
   1481  glean::security::CspViolationInternalPageExtra extra;
   1482  extra.directive = Some(NS_ConvertUTF16toUTF8(aInit.mEffectiveDirective));
   1483 
   1484  FilenameTypeAndDetails self =
   1485      nsContentSecurityUtils::FilenameToFilenameType(selfURISpec, true);
   1486  extra.selftype = Some(self.first);
   1487  extra.selfdetails = self.second;
   1488 
   1489  FilenameTypeAndDetails source =
   1490      nsContentSecurityUtils::FilenameToFilenameType(
   1491          NS_ConvertUTF16toUTF8(aInit.mSourceFile), true);
   1492  extra.sourcetype = Some(source.first);
   1493  extra.sourcedetails = source.second;
   1494 
   1495  extra.linenumber = Some(aInit.mLineNumber);
   1496  extra.columnnumber = Some(aInit.mColumnNumber);
   1497 
   1498  // Don't collect samples for code that is probably not shipped by us.
   1499  if (source.first.EqualsLiteral("chromeuri") ||
   1500      source.first.EqualsLiteral("resourceuri") ||
   1501      source.first.EqualsLiteral("abouturi")) {
   1502    // aInit's sample requires the 'report-sample' keyword.
   1503    extra.sample = Some(NS_ConvertUTF16toUTF8(aCSPViolationData.mSample));
   1504  }
   1505 
   1506  if (aInit.mBlockedURI.EqualsLiteral("inline")) {
   1507    extra.blockeduritype = Some("inline"_ns);
   1508  } else {
   1509    FilenameTypeAndDetails blocked =
   1510        nsContentSecurityUtils::FilenameToFilenameType(
   1511            NS_ConvertUTF16toUTF8(aInit.mBlockedURI), true);
   1512    extra.blockeduritype = Some(blocked.first);
   1513    extra.blockeduridetails = blocked.second;
   1514  }
   1515 
   1516  glean::security::csp_violation_internal_page.Record(Some(extra));
   1517 
   1518 #ifdef DEBUG
   1519  if (!StaticPrefs::security_csp_testing_allow_internal_csp_violation()) {
   1520    NS_ConvertUTF16toUTF8 directive(aViolatedDirectiveNameAndValue);
   1521    nsAutoCString effectiveDirective;
   1522    effectiveDirective.Assign(
   1523        CSP_CSPDirectiveToString(aCSPViolationData.mEffectiveDirective));
   1524    nsFmtCString s(
   1525        FMT_STRING("Unexpected CSP violation on page {} caused by {} (URL: {}, "
   1526                   "Source: {}) violating the directive: \"{}\" (file: {} "
   1527                   "line: {}). For debugging you can set the pref "
   1528                   "security.csp.testing.allow_internal_csp_violation=true."),
   1529        selfURISpec.get(), effectiveDirective.get(),
   1530        NS_ConvertUTF16toUTF8(aInit.mBlockedURI).get(),
   1531        NS_ConvertUTF16toUTF8(aCSPViolationData.mSample).get(), directive.get(),
   1532        aCSPViolationData.mSourceFile.get(), aCSPViolationData.mLineNumber);
   1533    MOZ_CRASH_UNSAFE(s.get());
   1534  }
   1535 #endif
   1536 }
   1537 
   1538 nsresult nsCSPContext::FireViolationEvent(
   1539    Element* aTriggeringElement, nsICSPEventListener* aCSPEventListener,
   1540    const mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit) {
   1541  if (aCSPEventListener) {
   1542    nsAutoString json;
   1543    if (aViolationEventInit.ToJSON(json)) {
   1544      aCSPEventListener->OnCSPViolationEvent(json);
   1545    }
   1546 
   1547    return NS_OK;
   1548  }
   1549 
   1550  // 1. If target is not null, and global is a Window, and target’s
   1551  // shadow-including root is not global’s associated Document, set target to
   1552  // null.
   1553  RefPtr<EventTarget> eventTarget = aTriggeringElement;
   1554 
   1555  nsCOMPtr<Document> doc = do_QueryReferent(mLoadingContext);
   1556  if (doc && aTriggeringElement &&
   1557      aTriggeringElement->GetComposedDoc() != doc) {
   1558    eventTarget = nullptr;
   1559  }
   1560 
   1561  if (!eventTarget) {
   1562    // If target is a Window, set target to target’s associated Document.
   1563    eventTarget = doc;
   1564  }
   1565 
   1566  if (!eventTarget && mInnerWindowID && XRE_IsParentProcess()) {
   1567    if (RefPtr<WindowGlobalParent> parent =
   1568            WindowGlobalParent::GetByInnerWindowId(mInnerWindowID)) {
   1569      nsAutoString json;
   1570      if (aViolationEventInit.ToJSON(json)) {
   1571        (void)parent->SendDispatchSecurityPolicyViolation(json);
   1572      }
   1573    }
   1574    return NS_OK;
   1575  }
   1576 
   1577  if (!eventTarget) {
   1578    // If we are here, we are probably dealing with workers. Those are handled
   1579    // via nsICSPEventListener. Nothing to do here.
   1580    return NS_OK;
   1581  }
   1582 
   1583  RefPtr<mozilla::dom::Event> event =
   1584      mozilla::dom::SecurityPolicyViolationEvent::Constructor(
   1585          eventTarget, u"securitypolicyviolation"_ns, aViolationEventInit);
   1586  event->SetTrusted(true);
   1587 
   1588  ErrorResult rv;
   1589  eventTarget->DispatchEvent(*event, rv);
   1590  return rv.StealNSResult();
   1591 }
   1592 
   1593 /**
   1594 * Dispatched from the main thread to send reports for one CSP violation.
   1595 */
   1596 class CSPReportSenderRunnable final : public Runnable {
   1597 public:
   1598  CSPReportSenderRunnable(nsICSPEventListener* aCSPEventListener,
   1599                          CSPViolationData&& aCSPViolationData,
   1600                          nsIURI* aOriginalURI, bool aReportOnlyFlag,
   1601                          const nsAString& aViolatedDirectiveName,
   1602                          const nsAString& aViolatedDirectiveNameAndValue,
   1603                          const nsAString& aObserverSubject, bool aReportSample,
   1604                          nsCSPContext* aCSPContext)
   1605      : mozilla::Runnable("CSPReportSenderRunnable"),
   1606        mCSPEventListener(aCSPEventListener),
   1607        mCSPViolationData(std::move(aCSPViolationData)),
   1608        mOriginalURI(aOriginalURI),
   1609        mReportOnlyFlag(aReportOnlyFlag),
   1610        mReportSample(aReportSample),
   1611        mViolatedDirectiveName(aViolatedDirectiveName),
   1612        mViolatedDirectiveNameAndValue(aViolatedDirectiveNameAndValue),
   1613        mCSPContext(aCSPContext) {
   1614    NS_ASSERTION(!aViolatedDirectiveName.IsEmpty(),
   1615                 "Can not send reports without a violated directive");
   1616    // the observer subject is an nsISupports: either an nsISupportsCString
   1617    // from the arg passed in directly, or if that's empty, it's the blocked
   1618    // source.
   1619    if (aObserverSubject.IsEmpty() &&
   1620        mCSPViolationData.mResource.is<nsCOMPtr<nsIURI>>()) {
   1621      mObserverSubject = mCSPViolationData.mResource.as<nsCOMPtr<nsIURI>>();
   1622      return;
   1623    }
   1624 
   1625    nsAutoCString subject;
   1626    if (aObserverSubject.IsEmpty()) {
   1627      BlockedContentSourceToString(
   1628          mCSPViolationData.BlockedContentSourceOrUnknown(), subject);
   1629    } else {
   1630      CopyUTF16toUTF8(aObserverSubject, subject);
   1631    }
   1632 
   1633    nsCOMPtr<nsISupportsCString> supportscstr =
   1634        do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
   1635    if (supportscstr) {
   1636      supportscstr->SetData(subject);
   1637      mObserverSubject = do_QueryInterface(supportscstr);
   1638    }
   1639  }
   1640 
   1641  NS_IMETHOD Run() override {
   1642    MOZ_ASSERT(NS_IsMainThread());
   1643 
   1644    // 0) prepare violation data
   1645    mozilla::dom::SecurityPolicyViolationEventInit init;
   1646 
   1647    nsAutoString effectiveDirective;
   1648    effectiveDirective.AssignASCII(
   1649        CSP_CSPDirectiveToString(mCSPViolationData.mEffectiveDirective));
   1650 
   1651    nsresult rv = mCSPContext->GatherSecurityPolicyViolationEventData(
   1652        mOriginalURI, effectiveDirective, mCSPViolationData, mReportSample,
   1653        init);
   1654    NS_ENSURE_SUCCESS(rv, rv);
   1655 
   1656    // 1) notify observers
   1657    nsCOMPtr<nsIObserverService> observerService =
   1658        mozilla::services::GetObserverService();
   1659    if (mObserverSubject && observerService) {
   1660      rv = observerService->NotifyObservers(
   1661          mObserverSubject, CSP_VIOLATION_TOPIC, mViolatedDirectiveName.get());
   1662      NS_ENSURE_SUCCESS(rv, rv);
   1663    }
   1664 
   1665    // 2) send reports for the policy that was violated
   1666    mCSPContext->SendReports(init, mCSPViolationData.mViolatedPolicyIndex);
   1667 
   1668    // 3) log to console (one per policy violation)
   1669    ReportToConsole();
   1670 
   1671    // 4) For internal pages we might send the failure to telemetry or crash.
   1672    mCSPContext->HandleInternalPageViolation(mCSPViolationData, init,
   1673                                             mViolatedDirectiveNameAndValue);
   1674 
   1675    // 5) fire violation event
   1676    // A frame-ancestors violation has occurred, but we should not dispatch
   1677    // the violation event to a potentially cross-origin ancestor.
   1678    if (!mViolatedDirectiveName.EqualsLiteral("frame-ancestors")) {
   1679      mCSPContext->FireViolationEvent(mCSPViolationData.mElement,
   1680                                      mCSPEventListener, init);
   1681    }
   1682 
   1683    return NS_OK;
   1684  }
   1685 
   1686 private:
   1687  void ReportToConsole() const {
   1688    NS_ConvertUTF8toUTF16 effectiveDirective(
   1689        CSP_CSPDirectiveToString(mCSPViolationData.mEffectiveDirective));
   1690 
   1691    const auto blockedContentSource =
   1692        mCSPViolationData.BlockedContentSourceOrUnknown();
   1693 
   1694    switch (blockedContentSource) {
   1695      case CSPViolationData::BlockedContentSource::Inline: {
   1696        const char* errorName = nullptr;
   1697        if (mCSPViolationData.mEffectiveDirective ==
   1698                CSPDirective::STYLE_SRC_ATTR_DIRECTIVE ||
   1699            mCSPViolationData.mEffectiveDirective ==
   1700                CSPDirective::STYLE_SRC_ELEM_DIRECTIVE) {
   1701          errorName = mReportOnlyFlag ? "CSPROInlineStyleViolation2"
   1702                                      : "CSPInlineStyleViolation2";
   1703        } else if (mCSPViolationData.mEffectiveDirective ==
   1704                   CSPDirective::SCRIPT_SRC_ATTR_DIRECTIVE) {
   1705          errorName = mReportOnlyFlag ? "CSPROEventHandlerScriptViolation2"
   1706                                      : "CSPEventHandlerScriptViolation2";
   1707        } else {
   1708          MOZ_ASSERT(mCSPViolationData.mEffectiveDirective ==
   1709                     CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE);
   1710          errorName = mReportOnlyFlag ? "CSPROInlineScriptViolation2"
   1711                                      : "CSPInlineScriptViolation2";
   1712        }
   1713 
   1714        AutoTArray<nsString, 3> params = {
   1715            mViolatedDirectiveNameAndValue, effectiveDirective,
   1716            NS_ConvertUTF8toUTF16(mCSPViolationData.mHashSHA256)};
   1717        mCSPContext->logToConsole(
   1718            errorName, params, mCSPViolationData.mSourceFile,
   1719            mCSPViolationData.mSample, mCSPViolationData.mLineNumber,
   1720            mCSPViolationData.mColumnNumber, nsIScriptError::errorFlag);
   1721        break;
   1722      }
   1723 
   1724      case CSPViolationData::BlockedContentSource::Eval: {
   1725        AutoTArray<nsString, 2> params = {mViolatedDirectiveNameAndValue,
   1726                                          effectiveDirective};
   1727        mCSPContext->logToConsole(
   1728            mReportOnlyFlag ? "CSPROEvalScriptViolation"
   1729                            : "CSPEvalScriptViolation",
   1730            params, mCSPViolationData.mSourceFile, mCSPViolationData.mSample,
   1731            mCSPViolationData.mLineNumber, mCSPViolationData.mColumnNumber,
   1732            nsIScriptError::errorFlag);
   1733        break;
   1734      }
   1735 
   1736      case CSPViolationData::BlockedContentSource::WasmEval: {
   1737        AutoTArray<nsString, 2> params = {mViolatedDirectiveNameAndValue,
   1738                                          effectiveDirective};
   1739        mCSPContext->logToConsole(
   1740            mReportOnlyFlag ? "CSPROWasmEvalScriptViolation"
   1741                            : "CSPWasmEvalScriptViolation",
   1742            params, mCSPViolationData.mSourceFile, mCSPViolationData.mSample,
   1743            mCSPViolationData.mLineNumber, mCSPViolationData.mColumnNumber,
   1744            nsIScriptError::errorFlag);
   1745        break;
   1746      }
   1747 
   1748      case CSPViolationData::BlockedContentSource::TrustedTypesPolicy: {
   1749        AutoTArray<nsString, 1> params = {mViolatedDirectiveNameAndValue};
   1750 
   1751        mCSPContext->logToConsole(
   1752            mReportOnlyFlag ? "CSPROTrustedTypesPolicyViolation"
   1753                            : "CSPTrustedTypesPolicyViolation",
   1754            params, mCSPViolationData.mSourceFile, mCSPViolationData.mSample,
   1755            mCSPViolationData.mLineNumber, mCSPViolationData.mColumnNumber,
   1756            nsIScriptError::errorFlag);
   1757        break;
   1758      }
   1759 
   1760      case CSPViolationData::BlockedContentSource::TrustedTypesSink: {
   1761        mCSPContext->logToConsole(
   1762            mReportOnlyFlag ? "CSPROTrustedTypesSinkViolation"
   1763                            : "CSPTrustedTypesSinkViolation",
   1764            {}, mCSPViolationData.mSourceFile, mCSPViolationData.mSample,
   1765            mCSPViolationData.mLineNumber, mCSPViolationData.mColumnNumber,
   1766            nsIScriptError::errorFlag);
   1767        break;
   1768      }
   1769 
   1770      case CSPViolationData::BlockedContentSource::Self:
   1771      case CSPViolationData::BlockedContentSource::Unknown: {
   1772        nsAutoString source(u"<unknown>"_ns);
   1773        if (mCSPViolationData.mResource.is<nsCOMPtr<nsIURI>>()) {
   1774          nsAutoCString uri;
   1775          auto blockedURI = mCSPViolationData.mResource.as<nsCOMPtr<nsIURI>>();
   1776          blockedURI->GetSpec(uri);
   1777 
   1778          if (blockedURI->SchemeIs("data") &&
   1779              uri.Length() > nsCSPContext::ScriptSampleMaxLength()) {
   1780            uri.Truncate(nsCSPContext::ScriptSampleMaxLength());
   1781            uri.Append(
   1782                NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis()));
   1783          }
   1784 
   1785          if (!uri.IsEmpty()) {
   1786            CopyUTF8toUTF16(uri, source);
   1787          }
   1788        }
   1789 
   1790        const char* errorName = nullptr;
   1791        switch (mCSPViolationData.mEffectiveDirective) {
   1792          case CSPDirective::STYLE_SRC_ELEM_DIRECTIVE:
   1793            errorName =
   1794                mReportOnlyFlag ? "CSPROStyleViolation" : "CSPStyleViolation";
   1795            break;
   1796          case CSPDirective::SCRIPT_SRC_ELEM_DIRECTIVE:
   1797            errorName =
   1798                mReportOnlyFlag ? "CSPROScriptViolation" : "CSPScriptViolation";
   1799            break;
   1800          case CSPDirective::WORKER_SRC_DIRECTIVE:
   1801            errorName =
   1802                mReportOnlyFlag ? "CSPROWorkerViolation" : "CSPWorkerViolation";
   1803            break;
   1804          default:
   1805            errorName = mReportOnlyFlag ? "CSPROGenericViolation"
   1806                                        : "CSPGenericViolation";
   1807        }
   1808 
   1809        AutoTArray<nsString, 3> params = {mViolatedDirectiveNameAndValue,
   1810                                          source, effectiveDirective};
   1811        mCSPContext->logToConsole(
   1812            errorName, params, mCSPViolationData.mSourceFile,
   1813            mCSPViolationData.mSample, mCSPViolationData.mLineNumber,
   1814            mCSPViolationData.mColumnNumber, nsIScriptError::errorFlag);
   1815      }
   1816    }
   1817  }
   1818 
   1819  nsCOMPtr<nsICSPEventListener> mCSPEventListener;
   1820  CSPViolationData mCSPViolationData;
   1821  nsCOMPtr<nsIURI> mOriginalURI;
   1822  bool mReportOnlyFlag;
   1823  bool mReportSample;
   1824  nsString mViolatedDirectiveName;
   1825  nsString mViolatedDirectiveNameAndValue;
   1826  nsCOMPtr<nsISupports> mObserverSubject;
   1827  RefPtr<nsCSPContext> mCSPContext;
   1828 };
   1829 
   1830 nsresult nsCSPContext::AsyncReportViolation(
   1831    nsICSPEventListener* aCSPEventListener,
   1832    mozilla::dom::CSPViolationData&& aCSPViolationData, nsIURI* aOriginalURI,
   1833    const nsAString& aViolatedDirectiveName,
   1834    const nsAString& aViolatedDirectiveNameAndValue,
   1835    const nsAString& aObserverSubject, bool aReportSample) {
   1836  EnsureIPCPoliciesRead();
   1837  NS_ENSURE_ARG_MAX(aCSPViolationData.mViolatedPolicyIndex,
   1838                    mPolicies.Length() - 1);
   1839 
   1840  nsCOMPtr<nsIRunnable> task = new CSPReportSenderRunnable(
   1841      aCSPEventListener, std::move(aCSPViolationData), aOriginalURI,
   1842      mPolicies[aCSPViolationData.mViolatedPolicyIndex]->getReportOnlyFlag(),
   1843      aViolatedDirectiveName, aViolatedDirectiveNameAndValue, aObserverSubject,
   1844      aReportSample, this);
   1845 
   1846  if (XRE_IsContentProcess()) {
   1847    if (mEventTarget) {
   1848      mEventTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
   1849      return NS_OK;
   1850    }
   1851  }
   1852 
   1853  NS_DispatchToMainThread(task.forget());
   1854  return NS_OK;
   1855 }
   1856 
   1857 /**
   1858 * Based on the given loadinfo, determines if this CSP context allows the
   1859 * ancestry.
   1860 *
   1861 * In order to determine the URI of the parent document (one causing the load
   1862 * of this protected document), this function traverses all Browsing Contexts
   1863 * until it reaches the top level browsing context.
   1864 */
   1865 NS_IMETHODIMP
   1866 nsCSPContext::PermitsAncestry(nsILoadInfo* aLoadInfo,
   1867                              bool* outPermitsAncestry) {
   1868  nsresult rv;
   1869 
   1870  *outPermitsAncestry = true;
   1871 
   1872  RefPtr<mozilla::dom::BrowsingContext> ctx;
   1873  aLoadInfo->GetBrowsingContext(getter_AddRefs(ctx));
   1874 
   1875  // extract the ancestry as an array
   1876  nsCOMArray<nsIURI> ancestorsArray;
   1877  nsCOMPtr<nsIURI> uriClone;
   1878 
   1879  while (ctx) {
   1880    nsCOMPtr<nsIPrincipal> currentPrincipal;
   1881    // Generally permitsAncestry is consulted from within the
   1882    // DocumentLoadListener in the parent process. For loads of type object
   1883    // and embed it's called from the Document in the content process.
   1884    // After Bug 1646899 we should be able to remove that branching code for
   1885    // querying the currentURI.
   1886    if (XRE_IsParentProcess()) {
   1887      WindowGlobalParent* window = ctx->Canonical()->GetCurrentWindowGlobal();
   1888      if (window) {
   1889        // Using the URI of the Principal and not the document because e.g.
   1890        // about:blank inherits the principal and hence the URI of the
   1891        // document does not reflect the security context of the document.
   1892        currentPrincipal = window->DocumentPrincipal();
   1893      }
   1894    } else if (nsPIDOMWindowOuter* windowOuter = ctx->GetDOMWindow()) {
   1895      currentPrincipal = nsGlobalWindowOuter::Cast(windowOuter)->GetPrincipal();
   1896    }
   1897 
   1898    if (currentPrincipal) {
   1899      nsCOMPtr<nsIURI> currentURI;
   1900      auto* currentBasePrincipal = BasePrincipal::Cast(currentPrincipal);
   1901      currentBasePrincipal->GetURI(getter_AddRefs(currentURI));
   1902 
   1903      if (currentURI) {
   1904        nsAutoCString spec;
   1905        currentURI->GetSpec(spec);
   1906        // delete the userpass from the URI.
   1907        rv = NS_MutateURI(currentURI)
   1908                 .SetRef(""_ns)
   1909                 .SetUserPass(""_ns)
   1910                 .Finalize(uriClone);
   1911 
   1912        // If setUserPass fails for some reason, just return a clone of the
   1913        // current URI
   1914        if (NS_FAILED(rv)) {
   1915          rv = NS_GetURIWithoutRef(currentURI, getter_AddRefs(uriClone));
   1916          NS_ENSURE_SUCCESS(rv, rv);
   1917        }
   1918        ancestorsArray.AppendElement(uriClone);
   1919      }
   1920    }
   1921    ctx = ctx->GetParent();
   1922  }
   1923 
   1924  nsAutoString violatedDirective;
   1925 
   1926  // Now that we've got the ancestry chain in ancestorsArray, time to check
   1927  // them against any CSP.
   1928  // NOTE:  the ancestors are not allowed to be sent cross origin; this is a
   1929  // restriction not placed on subresource loads.
   1930 
   1931  for (uint32_t a = 0; a < ancestorsArray.Length(); a++) {
   1932    if (CSPCONTEXTLOGENABLED()) {
   1933      CSPCONTEXTLOG(("nsCSPContext::PermitsAncestry, checking ancestor: %s",
   1934                     ancestorsArray[a]->GetSpecOrDefault().get()));
   1935    }
   1936    // omit the ancestor URI in violation reports if cross-origin as per spec
   1937    // (it is a violation of the same-origin policy).
   1938    bool okToSendAncestor =
   1939        NS_SecurityCompareURIs(ancestorsArray[a], mSelfURI, true);
   1940 
   1941    bool permits =
   1942        permitsInternal(nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE,
   1943                        nullptr,  // triggering element
   1944                        nullptr,  // nsICSPEventListener
   1945                        nullptr,  // nsILoadInfo
   1946                        ancestorsArray[a],
   1947                        nullptr,  // no redirect here.
   1948                        true,     // specific, do not use default-src
   1949                        true,     // send violation reports
   1950                        okToSendAncestor);
   1951    if (!permits) {
   1952      *outPermitsAncestry = false;
   1953    }
   1954  }
   1955  return NS_OK;
   1956 }
   1957 
   1958 NS_IMETHODIMP
   1959 nsCSPContext::Permits(Element* aTriggeringElement,
   1960                      nsICSPEventListener* aCSPEventListener, nsIURI* aURI,
   1961                      CSPDirective aDir, bool aSpecific,
   1962                      bool aSendViolationReports, bool* outPermits) {
   1963  // Can't perform check without aURI
   1964  if (aURI == nullptr) {
   1965    return NS_ERROR_FAILURE;
   1966  }
   1967 
   1968  if (aURI->SchemeIs("resource")) {
   1969    // XXX Ideally we would call SubjectToCSP() here but that would also
   1970    // allowlist e.g. javascript: URIs which should not be allowlisted here.
   1971    // As a hotfix we just allowlist pdf.js internals here explicitly.
   1972    nsAutoCString uriSpec;
   1973    aURI->GetSpec(uriSpec);
   1974    if (StringBeginsWith(uriSpec, "resource://pdf.js/"_ns)) {
   1975      *outPermits = true;
   1976      return NS_OK;
   1977    }
   1978  }
   1979 
   1980  *outPermits = permitsInternal(aDir, aTriggeringElement, aCSPEventListener,
   1981                                nullptr,  // no nsILoadInfo
   1982                                aURI,
   1983                                nullptr,  // no original (pre-redirect) URI
   1984                                aSpecific, aSendViolationReports,
   1985                                true);  // send blocked URI in violation reports
   1986 
   1987  if (CSPCONTEXTLOGENABLED()) {
   1988    CSPCONTEXTLOG(("nsCSPContext::Permits, aUri: %s, aDir: %s, isAllowed: %s",
   1989                   aURI->GetSpecOrDefault().get(),
   1990                   CSP_CSPDirectiveToString(aDir),
   1991                   *outPermits ? "allow" : "deny"));
   1992  }
   1993 
   1994  return NS_OK;
   1995 }
   1996 
   1997 NS_IMETHODIMP
   1998 nsCSPContext::ToJSON(nsAString& outCSPinJSON) {
   1999  outCSPinJSON.Truncate();
   2000  dom::CSPPolicies jsonPolicies;
   2001  jsonPolicies.mCsp_policies.Construct();
   2002  EnsureIPCPoliciesRead();
   2003 
   2004  for (uint32_t p = 0; p < mPolicies.Length(); p++) {
   2005    dom::CSP jsonCSP;
   2006    mPolicies[p]->toDomCSPStruct(jsonCSP);
   2007    if (!jsonPolicies.mCsp_policies.Value().AppendElement(jsonCSP, fallible)) {
   2008      return NS_ERROR_OUT_OF_MEMORY;
   2009    }
   2010  }
   2011 
   2012  // convert the gathered information to JSON
   2013  if (!jsonPolicies.ToJSON(outCSPinJSON)) {
   2014    return NS_ERROR_FAILURE;
   2015  }
   2016  return NS_OK;
   2017 }
   2018 
   2019 NS_IMETHODIMP
   2020 nsCSPContext::GetCSPSandboxFlags(uint32_t* aOutSandboxFlags) {
   2021  if (!aOutSandboxFlags) {
   2022    return NS_ERROR_FAILURE;
   2023  }
   2024  *aOutSandboxFlags = SANDBOXED_NONE;
   2025 
   2026  EnsureIPCPoliciesRead();
   2027  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
   2028    uint32_t flags = mPolicies[i]->getSandboxFlags();
   2029 
   2030    // current policy doesn't have sandbox flag, check next policy
   2031    if (!flags) {
   2032      continue;
   2033    }
   2034 
   2035    // current policy has sandbox flags, if the policy is in enforcement-mode
   2036    // (i.e. not report-only) set these flags and check for policies with more
   2037    // restrictions
   2038    if (!mPolicies[i]->getReportOnlyFlag()) {
   2039      *aOutSandboxFlags |= flags;
   2040    } else {
   2041      // sandbox directive is ignored in report-only mode, warn about it and
   2042      // continue the loop checking for an enforcement policy.
   2043      nsAutoString policy;
   2044      mPolicies[i]->toString(policy);
   2045 
   2046      CSPCONTEXTLOG(
   2047          ("nsCSPContext::GetCSPSandboxFlags, report only policy, ignoring "
   2048           "sandbox in: %s",
   2049           NS_ConvertUTF16toUTF8(policy).get()));
   2050 
   2051      AutoTArray<nsString, 1> params = {policy};
   2052      logToConsole("ignoringReportOnlyDirective", params, ""_ns, u""_ns, 0, 1,
   2053                   nsIScriptError::warningFlag);
   2054    }
   2055  }
   2056 
   2057  return NS_OK;
   2058 }
   2059 
   2060 /* ========== CSPViolationReportListener implementation ========== */
   2061 
   2062 NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener,
   2063                  nsIRequestObserver, nsISupports);
   2064 
   2065 CSPViolationReportListener::CSPViolationReportListener() = default;
   2066 
   2067 CSPViolationReportListener::~CSPViolationReportListener() = default;
   2068 
   2069 nsresult AppendSegmentToString(nsIInputStream* aInputStream, void* aClosure,
   2070                               const char* aRawSegment, uint32_t aToOffset,
   2071                               uint32_t aCount, uint32_t* outWrittenCount) {
   2072  nsCString* decodedData = static_cast<nsCString*>(aClosure);
   2073  decodedData->Append(aRawSegment, aCount);
   2074  *outWrittenCount = aCount;
   2075  return NS_OK;
   2076 }
   2077 
   2078 NS_IMETHODIMP
   2079 CSPViolationReportListener::OnDataAvailable(nsIRequest* aRequest,
   2080                                            nsIInputStream* aInputStream,
   2081                                            uint64_t aOffset, uint32_t aCount) {
   2082  uint32_t read;
   2083  nsCString decodedData;
   2084  return aInputStream->ReadSegments(AppendSegmentToString, &decodedData, aCount,
   2085                                    &read);
   2086 }
   2087 
   2088 NS_IMETHODIMP
   2089 CSPViolationReportListener::OnStopRequest(nsIRequest* aRequest,
   2090                                          nsresult aStatus) {
   2091  return NS_OK;
   2092 }
   2093 
   2094 NS_IMETHODIMP
   2095 CSPViolationReportListener::OnStartRequest(nsIRequest* aRequest) {
   2096  return NS_OK;
   2097 }
   2098 
   2099 /* ========== CSPReportRedirectSink implementation ========== */
   2100 
   2101 NS_IMPL_ISUPPORTS(CSPReportRedirectSink, nsIChannelEventSink,
   2102                  nsIInterfaceRequestor);
   2103 
   2104 CSPReportRedirectSink::CSPReportRedirectSink() = default;
   2105 
   2106 CSPReportRedirectSink::~CSPReportRedirectSink() = default;
   2107 
   2108 NS_IMETHODIMP
   2109 CSPReportRedirectSink::AsyncOnChannelRedirect(
   2110    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aRedirFlags,
   2111    nsIAsyncVerifyRedirectCallback* aCallback) {
   2112  if (aRedirFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
   2113    aCallback->OnRedirectVerifyCallback(NS_OK);
   2114    return NS_OK;
   2115  }
   2116 
   2117  // cancel the old channel so XHR failure callback happens
   2118  nsresult rv = aOldChannel->Cancel(NS_ERROR_ABORT);
   2119  NS_ENSURE_SUCCESS(rv, rv);
   2120 
   2121  // notify an observer that we have blocked the report POST due to a
   2122  // redirect, used in testing, do this async since we're in an async call now
   2123  // to begin with
   2124  nsCOMPtr<nsIURI> uri;
   2125  rv = aOldChannel->GetURI(getter_AddRefs(uri));
   2126  NS_ENSURE_SUCCESS(rv, rv);
   2127 
   2128  nsCOMPtr<nsIObserverService> observerService =
   2129      mozilla::services::GetObserverService();
   2130  NS_ASSERTION(observerService,
   2131               "Observer service required to log CSP violations");
   2132  observerService->NotifyObservers(
   2133      uri, CSP_VIOLATION_TOPIC,
   2134      u"denied redirect while sending violation report");
   2135 
   2136  return NS_BINDING_REDIRECTED;
   2137 }
   2138 
   2139 NS_IMETHODIMP
   2140 CSPReportRedirectSink::GetInterface(const nsIID& aIID, void** aResult) {
   2141  if (aIID.Equals(NS_GET_IID(nsINetworkInterceptController)) &&
   2142      mInterceptController) {
   2143    nsCOMPtr<nsINetworkInterceptController> copy(mInterceptController);
   2144    *aResult = copy.forget().take();
   2145 
   2146    return NS_OK;
   2147  }
   2148 
   2149  return QueryInterface(aIID, aResult);
   2150 }
   2151 
   2152 void CSPReportRedirectSink::SetInterceptController(
   2153    nsINetworkInterceptController* aInterceptController) {
   2154  mInterceptController = aInterceptController;
   2155 }
   2156 
   2157 /* ===== nsISerializable implementation ====== */
   2158 
   2159 NS_IMETHODIMP
   2160 nsCSPContext::Read(nsIObjectInputStream* aStream) {
   2161  return ReadImpl(aStream, false);
   2162 }
   2163 
   2164 nsresult nsCSPContext::PolicyContainerRead(nsIObjectInputStream* aInputStream) {
   2165  return ReadImpl(aInputStream, true);
   2166 }
   2167 
   2168 nsresult nsCSPContext::ReadImpl(nsIObjectInputStream* aStream,
   2169                                bool aForPolicyContainer) {
   2170  CSPCONTEXTLOG(("nsCSPContext::Read"));
   2171 
   2172  nsresult rv;
   2173  nsCOMPtr<nsISupports> supports;
   2174 
   2175  rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
   2176  NS_ENSURE_SUCCESS(rv, rv);
   2177 
   2178  mSelfURI = do_QueryInterface(supports);
   2179  MOZ_ASSERT(mSelfURI, "need a self URI to de-serialize");
   2180 
   2181  nsAutoCString JSON;
   2182  rv = aStream->ReadCString(JSON);
   2183  NS_ENSURE_SUCCESS(rv, rv);
   2184 
   2185  nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(JSON);
   2186  mLoadingPrincipal = principal;
   2187  MOZ_ASSERT(mLoadingPrincipal, "need a loadingPrincipal to de-serialize");
   2188 
   2189  uint32_t numPolicies;
   2190  rv = aStream->Read32(&numPolicies);
   2191  NS_ENSURE_SUCCESS(rv, rv);
   2192 
   2193  if (numPolicies == 0) {
   2194    return NS_OK;
   2195  }
   2196 
   2197  if (aForPolicyContainer) {
   2198    return TryReadPolicies(PolicyDataVersion::Post136, aStream, numPolicies,
   2199                           true);
   2200  }
   2201 
   2202  // Note: This assume that there is no other data following the CSP!
   2203  // E10SUtils.deserializeCSP is the only user of this logic.
   2204  nsTArray<uint8_t> data;
   2205  rv = NS_ConsumeStream(aStream, UINT32_MAX, data);
   2206  NS_ENSURE_SUCCESS(rv, rv);
   2207 
   2208  auto createStreamFromData =
   2209      [&data]() -> already_AddRefed<nsIObjectInputStream> {
   2210    nsCOMPtr<nsIInputStream> binaryStream;
   2211    nsresult rv = NS_NewByteInputStream(
   2212        getter_AddRefs(binaryStream),
   2213        Span(reinterpret_cast<const char*>(data.Elements()), data.Length()),
   2214        NS_ASSIGNMENT_DEPEND);
   2215    NS_ENSURE_SUCCESS(rv, nullptr);
   2216 
   2217    nsCOMPtr<nsIObjectInputStream> stream =
   2218        NS_NewObjectInputStream(binaryStream);
   2219 
   2220    return stream.forget();
   2221  };
   2222 
   2223  // Because of accidental backwards incompatible changes we have to try and
   2224  // parse multiple different versions of the CSP data. Starting with the
   2225  // current data format.
   2226 
   2227  nsCOMPtr<nsIObjectInputStream> stream = createStreamFromData();
   2228  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
   2229 
   2230  if (NS_SUCCEEDED(TryReadPolicies(PolicyDataVersion::Post136, stream,
   2231                                   numPolicies, false))) {
   2232    CSPCONTEXTLOG(("nsCSPContext::Read: Data was in version ::Post136."));
   2233    return NS_OK;
   2234  }
   2235 
   2236  stream = createStreamFromData();
   2237  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
   2238  if (NS_SUCCEEDED(TryReadPolicies(PolicyDataVersion::Pre136, stream,
   2239                                   numPolicies, false))) {
   2240    CSPCONTEXTLOG(("nsCSPContext::Read: Data was in version ::Pre136."));
   2241    return NS_OK;
   2242  }
   2243 
   2244  stream = createStreamFromData();
   2245  NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
   2246  if (NS_SUCCEEDED(TryReadPolicies(PolicyDataVersion::V138_9PreRelease, stream,
   2247                                   numPolicies, false))) {
   2248    CSPCONTEXTLOG(
   2249        ("nsCSPContext::Read: Data was in version ::V138_9PreRelease."));
   2250    return NS_OK;
   2251  }
   2252 
   2253  CSPCONTEXTLOG(("nsCSPContext::Read: Failed to read data!"));
   2254  return NS_ERROR_FAILURE;
   2255 }
   2256 
   2257 nsresult nsCSPContext::TryReadPolicies(PolicyDataVersion aVersion,
   2258                                       nsIObjectInputStream* aStream,
   2259                                       uint32_t aNumPolicies,
   2260                                       bool aForPolicyContainer) {
   2261  // Like ReadBoolean, but ensures the byte is actually 0 or 1.
   2262  auto ReadBooleanSafe = [aStream](bool* aBoolean) {
   2263    uint8_t raw = 0;
   2264    nsresult rv = aStream->Read8(&raw);
   2265    NS_ENSURE_SUCCESS(rv, rv);
   2266    if (!(raw == 0 || raw == 1)) {
   2267      CSPCONTEXTLOG(("nsCSPContext::TryReadPolicies: Bad boolean value"));
   2268      return NS_ERROR_FAILURE;
   2269    }
   2270 
   2271    *aBoolean = !!raw;
   2272    return NS_OK;
   2273  };
   2274 
   2275  nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
   2276  nsAutoString policyString;
   2277  while (aNumPolicies > 0) {
   2278    aNumPolicies--;
   2279 
   2280    nsresult rv = aStream->ReadString(policyString);
   2281    NS_ENSURE_SUCCESS(rv, rv);
   2282 
   2283    // nsCSPParser::policy removed all non-ASCII tokens while parsing the CSP
   2284    // that was serialized, so we shouldn't have any in this string. A non-ASCII
   2285    // character is thus a strong indicator for some kind of deserialization
   2286    // error.
   2287    if (!IsAscii(Span(policyString))) {
   2288      CSPCONTEXTLOG(
   2289          ("nsCSPContext::TryReadPolicies: Unexpected non-ASCII policy "
   2290           "string"));
   2291      return NS_ERROR_FAILURE;
   2292    }
   2293 
   2294    bool reportOnly = false;
   2295    rv = ReadBooleanSafe(&reportOnly);
   2296    NS_ENSURE_SUCCESS(rv, rv);
   2297 
   2298    bool deliveredViaMetaTag = false;
   2299    rv = ReadBooleanSafe(&deliveredViaMetaTag);
   2300    NS_ENSURE_SUCCESS(rv, rv);
   2301 
   2302    bool hasRequireTrustedTypesForDirective = false;
   2303    if (aVersion == PolicyDataVersion::Post136 ||
   2304        aVersion == PolicyDataVersion::V138_9PreRelease) {
   2305      // Added in bug 1901492.
   2306      rv = ReadBooleanSafe(&hasRequireTrustedTypesForDirective);
   2307      NS_ENSURE_SUCCESS(rv, rv);
   2308    }
   2309 
   2310    if (aVersion == PolicyDataVersion::V138_9PreRelease) {
   2311      // This was added in bug 1942306, but wasn't really necessary.
   2312      // Removed again in bug 1958259.
   2313      uint32_t numExpressions;
   2314      rv = aStream->Read32(&numExpressions);
   2315      NS_ENSURE_SUCCESS(rv, rv);
   2316      // We assume that because Trusted Types was disabled by default
   2317      // that no "trusted type expressions" were written during that time.
   2318      if (numExpressions != 0) {
   2319        return NS_ERROR_FAILURE;
   2320      }
   2321    }
   2322 
   2323    policies.AppendElement(
   2324        ContentSecurityPolicy(policyString, reportOnly, deliveredViaMetaTag,
   2325                              hasRequireTrustedTypesForDirective));
   2326  }
   2327 
   2328  // PolicyContainer may contain extra stuff.
   2329  if (!aForPolicyContainer) {
   2330    // Make sure all data was consumed.
   2331    uint64_t available = 0;
   2332    nsresult rv = aStream->Available(&available);
   2333    NS_ENSURE_SUCCESS(rv, rv);
   2334    if (available) {
   2335      return NS_ERROR_FAILURE;
   2336    }
   2337  }
   2338 
   2339  // Success! Add the policies now.
   2340  for (auto policy : policies) {
   2341    AddIPCPolicy(policy);
   2342  }
   2343  return NS_OK;
   2344 }
   2345 
   2346 NS_IMETHODIMP
   2347 nsCSPContext::Write(nsIObjectOutputStream* aStream) {
   2348  nsresult rv = NS_WriteOptionalCompoundObject(aStream, mSelfURI,
   2349                                               NS_GET_IID(nsIURI), true);
   2350  NS_ENSURE_SUCCESS(rv, rv);
   2351 
   2352  nsAutoCString JSON;
   2353  BasePrincipal::Cast(mLoadingPrincipal)->ToJSON(JSON);
   2354  rv = aStream->WriteStringZ(JSON.get());
   2355  NS_ENSURE_SUCCESS(rv, rv);
   2356 
   2357  // Serialize all the policies.
   2358  aStream->Write32(mPolicies.Length() + mIPCPolicies.Length());
   2359 
   2360  // WARNING: Any change here needs to be backwards compatible because
   2361  // the serialized CSP data is used across different Firefox versions.
   2362  // Better just don't touch this.
   2363 
   2364  // This writes data in the PolicyDataVersion::Post136 format.
   2365  nsAutoString polStr;
   2366  for (uint32_t p = 0; p < mPolicies.Length(); p++) {
   2367    polStr.Truncate();
   2368    mPolicies[p]->toString(polStr);
   2369    aStream->WriteWStringZ(polStr.get());
   2370    aStream->WriteBoolean(mPolicies[p]->getReportOnlyFlag());
   2371    aStream->WriteBoolean(mPolicies[p]->getDeliveredViaMetaTagFlag());
   2372    aStream->WriteBoolean(mPolicies[p]->hasRequireTrustedTypesForDirective());
   2373  }
   2374  for (auto& policy : mIPCPolicies) {
   2375    aStream->WriteWStringZ(policy.policy().get());
   2376    aStream->WriteBoolean(policy.reportOnlyFlag());
   2377    aStream->WriteBoolean(policy.deliveredViaMetaTagFlag());
   2378    aStream->WriteBoolean(policy.hasRequireTrustedTypesForDirective());
   2379  }
   2380 
   2381  return NS_OK;
   2382 }
   2383 
   2384 void nsCSPContext::AddIPCPolicy(const ContentSecurityPolicy& aPolicy) {
   2385  mIPCPolicies.AppendElement(aPolicy);
   2386  if (aPolicy.hasRequireTrustedTypesForDirective()) {
   2387    if (mRequireTrustedTypesForDirectiveState !=
   2388        RequireTrustedTypesForDirectiveState::ENFORCE) {
   2389      mRequireTrustedTypesForDirectiveState =
   2390          aPolicy.reportOnlyFlag()
   2391              ? RequireTrustedTypesForDirectiveState::REPORT_ONLY
   2392              : RequireTrustedTypesForDirectiveState::ENFORCE;
   2393    }
   2394  }
   2395 }
   2396 
   2397 void nsCSPContext::SerializePolicies(
   2398    nsTArray<ContentSecurityPolicy>& aPolicies) {
   2399  for (auto* policy : mPolicies) {
   2400    nsAutoString policyString;
   2401    policy->toString(policyString);
   2402    aPolicies.AppendElement(
   2403        ContentSecurityPolicy(policyString, policy->getReportOnlyFlag(),
   2404                              policy->getDeliveredViaMetaTagFlag(),
   2405                              policy->hasRequireTrustedTypesForDirective()));
   2406  }
   2407 
   2408  aPolicies.AppendElements(mIPCPolicies);
   2409 }