tor-browser

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

nsContentSecurityUtils.cpp (82746B)


      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 /* A namespace class for static content security utilities. */
      8 
      9 #include "nsContentSecurityUtils.h"
     10 
     11 #include "mozilla/Components.h"
     12 #include "mozilla/dom/PolicyContainer.h"
     13 #include "mozilla/dom/ScriptSettings.h"
     14 #include "mozilla/dom/WorkerCommon.h"
     15 #include "mozilla/dom/WorkerPrivate.h"
     16 #include "mozilla/dom/nsMixedContentBlocker.h"
     17 #include "nsComponentManagerUtils.h"
     18 #include "nsIChannel.h"
     19 #include "nsIContentSecurityPolicy.h"
     20 #include "nsIHttpChannel.h"
     21 #include "nsIMultiPartChannel.h"
     22 #include "nsITransfer.h"
     23 #include "nsIURI.h"
     24 #include "nsNetUtil.h"
     25 #include "nsSandboxFlags.h"
     26 #if defined(XP_WIN)
     27 #  include <wininet.h>
     28 
     29 #  include "WinUtils.h"
     30 #  include "mozilla/WinHeaderOnlyUtils.h"
     31 #endif
     32 
     33 #include "FramingChecker.h"
     34 #include "LoadInfo.h"
     35 #include "js/Array.h"  // JS::GetArrayLength
     36 #include "js/ContextOptions.h"
     37 #include "js/PropertyAndElement.h"  // JS_GetElement
     38 #include "js/RegExp.h"
     39 #include "js/RegExpFlags.h"           // JS::RegExpFlags
     40 #include "js/friend/ErrorMessages.h"  // JSMSG_UNSAFE_FILENAME
     41 #include "mozilla/ExtensionPolicyService.h"
     42 #include "mozilla/Logging.h"
     43 #include "mozilla/Preferences.h"
     44 #include "mozilla/StaticPrefs_dom.h"
     45 #include "mozilla/StaticPrefs_extensions.h"
     46 #include "mozilla/StaticPrefs_security.h"
     47 #include "mozilla/dom/Document.h"
     48 #include "mozilla/dom/nsCSPContext.h"
     49 #include "mozilla/glean/DomSecurityMetrics.h"
     50 #include "nsIConsoleService.h"
     51 #include "nsIStringBundle.h"
     52 
     53 using namespace mozilla;
     54 using namespace mozilla::dom;
     55 
     56 extern mozilla::LazyLogModule sCSMLog;
     57 extern Atomic<bool, mozilla::Relaxed> sJSHacksChecked;
     58 extern Atomic<bool, mozilla::Relaxed> sJSHacksPresent;
     59 extern Atomic<bool, mozilla::Relaxed> sCSSHacksChecked;
     60 extern Atomic<bool, mozilla::Relaxed> sCSSHacksPresent;
     61 
     62 // Helper function for IsConsideredSameOriginForUIR which makes
     63 // Principals of scheme 'http' return Principals of scheme 'https'.
     64 static already_AddRefed<nsIPrincipal> MakeHTTPPrincipalHTTPS(
     65    nsIPrincipal* aPrincipal) {
     66  nsCOMPtr<nsIPrincipal> principal = aPrincipal;
     67  // if the principal is not http, then it can also not be upgraded
     68  // to https.
     69  if (!principal->SchemeIs("http")) {
     70    return principal.forget();
     71  }
     72 
     73  nsAutoCString spec;
     74  aPrincipal->GetAsciiSpec(spec);
     75  // replace http with https
     76  spec.ReplaceLiteral(0, 4, "https");
     77 
     78  nsCOMPtr<nsIURI> newURI;
     79  nsresult rv = NS_NewURI(getter_AddRefs(newURI), spec);
     80  if (NS_WARN_IF(NS_FAILED(rv))) {
     81    return nullptr;
     82  }
     83 
     84  mozilla::OriginAttributes OA =
     85      BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
     86 
     87  principal = BasePrincipal::CreateContentPrincipal(newURI, OA);
     88  return principal.forget();
     89 }
     90 
     91 /* static */
     92 bool nsContentSecurityUtils::IsConsideredSameOriginForUIR(
     93    nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aResultPrincipal) {
     94  MOZ_ASSERT(aTriggeringPrincipal);
     95  MOZ_ASSERT(aResultPrincipal);
     96  // we only have to make sure that the following truth table holds:
     97  // aTriggeringPrincipal         | aResultPrincipal             | Result
     98  // ----------------------------------------------------------------
     99  // http://example.com/foo.html  | http://example.com/bar.html  | true
    100  // http://example.com/foo.html  | https://example.com/bar.html | true
    101  // https://example.com/foo.html | https://example.com/bar.html | true
    102  // https://example.com/foo.html | http://example.com/bar.html  | true
    103 
    104  // fast path if both principals are same-origin
    105  if (aTriggeringPrincipal->Equals(aResultPrincipal)) {
    106    return true;
    107  }
    108 
    109  // in case a principal uses a scheme of 'http' then we just upgrade to
    110  // 'https' and use the principal equals comparison operator to check
    111  // for same-origin.
    112  nsCOMPtr<nsIPrincipal> compareTriggeringPrincipal =
    113      MakeHTTPPrincipalHTTPS(aTriggeringPrincipal);
    114 
    115  nsCOMPtr<nsIPrincipal> compareResultPrincipal =
    116      MakeHTTPPrincipalHTTPS(aResultPrincipal);
    117 
    118  return compareTriggeringPrincipal->Equals(compareResultPrincipal);
    119 }
    120 
    121 /* static */
    122 bool nsContentSecurityUtils::IsTrustedScheme(nsIURI* aURI) {
    123  return aURI->SchemeIs("resource") || aURI->SchemeIs("chrome") ||
    124         aURI->SchemeIs("moz-src");
    125 }
    126 
    127 /*
    128 * Performs a Regular Expression match, optionally returning the results.
    129 * This function is not safe to use OMT.
    130 *
    131 * @param aPattern      The regex pattern
    132 * @param aString       The string to compare against
    133 * @param aOnlyMatch    Whether we want match results or only a true/false for
    134 * the match
    135 * @param aMatchResult  Out param for whether or not the pattern matched
    136 * @param aRegexResults Out param for the matches of the regex, if requested
    137 * @returns nsresult indicating correct function operation or error
    138 */
    139 nsresult RegexEval(const nsAString& aPattern, const nsAString& aString,
    140                   bool aOnlyMatch, bool& aMatchResult,
    141                   nsTArray<nsString>* aRegexResults = nullptr) {
    142  MOZ_ASSERT(NS_IsMainThread());
    143  aMatchResult = false;
    144 
    145  mozilla::dom::AutoJSAPI jsapi;
    146  jsapi.Init();
    147 
    148  JSContext* cx = jsapi.cx();
    149  mozilla::AutoDisableJSInterruptCallback disabler(cx);
    150 
    151  // We can use the junk scope here, because we're just using it for regexp
    152  // evaluation, not actual script execution, and we disable statics so that the
    153  // evaluation does not interact with the execution global.
    154  JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
    155 
    156  JS::Rooted<JSObject*> regexp(
    157      cx, JS::NewUCRegExpObject(cx, aPattern.BeginReading(), aPattern.Length(),
    158                                JS::RegExpFlag::Unicode));
    159  if (!regexp) {
    160    return NS_ERROR_ILLEGAL_VALUE;
    161  }
    162 
    163  JS::Rooted<JS::Value> regexResult(cx, JS::NullValue());
    164 
    165  size_t index = 0;
    166  if (!JS::ExecuteRegExpNoStatics(cx, regexp, aString.BeginReading(),
    167                                  aString.Length(), &index, aOnlyMatch,
    168                                  &regexResult)) {
    169    return NS_ERROR_FAILURE;
    170  }
    171 
    172  if (regexResult.isNull()) {
    173    // On no match, ExecuteRegExpNoStatics returns Null
    174    return NS_OK;
    175  }
    176  if (aOnlyMatch) {
    177    // On match, with aOnlyMatch = true, ExecuteRegExpNoStatics returns boolean
    178    // true.
    179    MOZ_ASSERT(regexResult.isBoolean() && regexResult.toBoolean());
    180    aMatchResult = true;
    181    return NS_OK;
    182  }
    183  if (aRegexResults == nullptr) {
    184    return NS_ERROR_INVALID_ARG;
    185  }
    186 
    187  // Now we know we have a result, and we need to extract it so we can read it.
    188  uint32_t length;
    189  JS::Rooted<JSObject*> regexResultObj(cx, &regexResult.toObject());
    190  if (!JS::GetArrayLength(cx, regexResultObj, &length)) {
    191    return NS_ERROR_NOT_AVAILABLE;
    192  }
    193  MOZ_LOG(sCSMLog, LogLevel::Verbose, ("Regex Matched %i strings", length));
    194 
    195  for (uint32_t i = 0; i < length; i++) {
    196    JS::Rooted<JS::Value> element(cx);
    197    if (!JS_GetElement(cx, regexResultObj, i, &element)) {
    198      return NS_ERROR_NO_CONTENT;
    199    }
    200 
    201    nsAutoJSString value;
    202    if (!value.init(cx, element)) {
    203      return NS_ERROR_NO_CONTENT;
    204    }
    205 
    206    MOZ_LOG(sCSMLog, LogLevel::Verbose,
    207            ("Regex Matching: %i: %s", i, NS_ConvertUTF16toUTF8(value).get()));
    208    aRegexResults->AppendElement(value);
    209  }
    210 
    211  aMatchResult = true;
    212  return NS_OK;
    213 }
    214 
    215 /*
    216 * MOZ_CRASH_UNSAFE_PRINTF has a sPrintfCrashReasonSize-sized buffer. We need
    217 * to make sure we don't exceed it.  These functions perform this check and
    218 * munge things for us.
    219 *
    220 */
    221 
    222 /*
    223 * Destructively truncates a string to fit within the limit
    224 */
    225 char* nsContentSecurityUtils::SmartFormatCrashString(const char* str) {
    226  return nsContentSecurityUtils::SmartFormatCrashString(strdup(str));
    227 }
    228 
    229 char* nsContentSecurityUtils::SmartFormatCrashString(char* str) {
    230  auto str_len = strlen(str);
    231 
    232  if (str_len > sPrintfCrashReasonSize) {
    233    str[sPrintfCrashReasonSize - 1] = '\0';
    234    str_len = strlen(str);
    235  }
    236  MOZ_RELEASE_ASSERT(sPrintfCrashReasonSize > str_len);
    237 
    238  return str;
    239 }
    240 
    241 /*
    242 * Destructively truncates two strings to fit within the limit.
    243 * format_string is a format string containing two %s entries
    244 * The second string will be truncated to the _last_ 25 characters
    245 * The first string will be truncated to the remaining limit.
    246 */
    247 nsCString nsContentSecurityUtils::SmartFormatCrashString(
    248    const char* part1, const char* part2, const char* format_string) {
    249  return SmartFormatCrashString(strdup(part1), strdup(part2), format_string);
    250 }
    251 
    252 nsCString nsContentSecurityUtils::SmartFormatCrashString(
    253    char* part1, char* part2, const char* format_string) {
    254  auto part1_len = strlen(part1);
    255  auto part2_len = strlen(part2);
    256 
    257  auto constant_len = strlen(format_string) - 4;
    258 
    259  if (part1_len + part2_len + constant_len > sPrintfCrashReasonSize) {
    260    if (part2_len > 25) {
    261      part2 += (part2_len - 25);
    262    }
    263    part2_len = strlen(part2);
    264 
    265    part1[sPrintfCrashReasonSize - (constant_len + part2_len + 1)] = '\0';
    266    part1_len = strlen(part1);
    267  }
    268  MOZ_RELEASE_ASSERT(sPrintfCrashReasonSize >
    269                     constant_len + part1_len + part2_len);
    270 
    271  auto parts = nsPrintfCString(format_string, part1, part2);
    272  return std::move(parts);
    273 }
    274 
    275 /*
    276 * Telemetry Events extra data only supports 80 characters, so we optimize the
    277 * filename to be smaller and collect more data.
    278 */
    279 nsCString OptimizeFileName(const nsAString& aFileName) {
    280  nsCString optimizedName;
    281  CopyUTF16toUTF8(aFileName, optimizedName);
    282 
    283  MOZ_LOG(sCSMLog, LogLevel::Verbose,
    284          ("Optimizing FileName: %s", optimizedName.get()));
    285 
    286  optimizedName.ReplaceSubstring(".xpi!"_ns, "!"_ns);
    287  optimizedName.ReplaceSubstring("shield.mozilla.org!"_ns, "s!"_ns);
    288  optimizedName.ReplaceSubstring("mozilla.org!"_ns, "m!"_ns);
    289  if (optimizedName.Length() > 80) {
    290    optimizedName.Truncate(80);
    291  }
    292 
    293  MOZ_LOG(sCSMLog, LogLevel::Verbose,
    294          ("Optimized FileName: %s", optimizedName.get()));
    295  return optimizedName;
    296 }
    297 
    298 static nsCString StripQueryRef(const nsACString& aFileName) {
    299  nsCString stripped(aFileName);
    300  int32_t i = stripped.FindCharInSet("#?"_ns);
    301  if (i != kNotFound) {
    302    stripped.Truncate(i);
    303  }
    304  return stripped;
    305 }
    306 
    307 /*
    308 * FilenameToFilenameType takes a fileName and returns a Pair of strings.
    309 * The First entry is a string indicating the type of fileName
    310 * The Second entry is a Maybe<string> that can contain additional details to
    311 * report.
    312 *
    313 * The reason we use strings (instead of an int/enum) is because the Telemetry
    314 * Events API only accepts strings.
    315 *
    316 * Function is a static member of the class to enable gtests.
    317 */
    318 /* static */
    319 FilenameTypeAndDetails nsContentSecurityUtils::FilenameToFilenameType(
    320    const nsACString& fileName, bool collectAdditionalExtensionData) {
    321  // These are strings because the Telemetry Events API only accepts strings
    322  static constexpr auto kChromeURI = "chromeuri"_ns;
    323  static constexpr auto kResourceURI = "resourceuri"_ns;
    324  static constexpr auto kMozSrcURI = "mozsrcuri"_ns;
    325  static constexpr auto kBlobUri = "bloburi"_ns;
    326  static constexpr auto kDataUri = "dataurl"_ns;
    327  static constexpr auto kAboutUri = "abouturi"_ns;
    328  static constexpr auto kDataUriWebExtCStyle =
    329      "dataurl-extension-contentstyle"_ns;
    330  static constexpr auto kSingleString = "singlestring"_ns;
    331  static constexpr auto kMozillaExtensionFile = "mozillaextension_file"_ns;
    332  static constexpr auto kOtherExtensionFile = "otherextension_file"_ns;
    333  static constexpr auto kExtensionURI = "extension_uri"_ns;
    334  static constexpr auto kSuspectedUserChromeJS = "suspectedUserChromeJS"_ns;
    335 #if defined(XP_WIN)
    336  static constexpr auto kSanitizedWindowsURL = "sanitizedWindowsURL"_ns;
    337  static constexpr auto kSanitizedWindowsPath = "sanitizedWindowsPath"_ns;
    338 #endif
    339  static constexpr auto kOther = "other"_ns;
    340  static constexpr auto kOtherWorker = "other-on-worker"_ns;
    341  static constexpr auto kRegexFailure = "regexfailure"_ns;
    342 
    343  static constexpr auto kUCJSRegex = u"(.+).uc.js\\?*[0-9]*$"_ns;
    344  static constexpr auto kExtensionRegex = u"extensions/(.+)@(.+)!(.+)$"_ns;
    345  static constexpr auto kSingleFileRegex = u"^[a-zA-Z0-9.?]+$"_ns;
    346 
    347  if (fileName.IsEmpty()) {
    348    return FilenameTypeAndDetails(kOther, Nothing());
    349  }
    350 
    351  // resource:// and chrome://
    352  // These don't contain any user (profile) paths.
    353  if (StringBeginsWith(fileName, "chrome://"_ns)) {
    354    if (StringBeginsWith(fileName, "chrome://userscripts/"_ns) ||
    355        StringBeginsWith(fileName, "chrome://userchromejs/"_ns) ||
    356        StringBeginsWith(fileName, "chrome://user_chrome_files/"_ns) ||
    357        StringBeginsWith(fileName, "chrome://tabmix"_ns) ||
    358        StringBeginsWith(fileName, "chrome://searchwp/"_ns) ||
    359        StringBeginsWith(fileName, "chrome://custombuttons"_ns) ||
    360        StringBeginsWith(fileName, "chrome://tabgroups-resource/"_ns)) {
    361      return FilenameTypeAndDetails(kSuspectedUserChromeJS,
    362                                    Some(StripQueryRef(fileName)));
    363    }
    364    return FilenameTypeAndDetails(kChromeURI, Some(StripQueryRef(fileName)));
    365  }
    366  if (StringBeginsWith(fileName, "resource://"_ns)) {
    367    if (StringBeginsWith(fileName, "resource://usl-ucjs/"_ns) ||
    368        StringBeginsWith(fileName, "resource://sfm-ucjs/"_ns) ||
    369        StringBeginsWith(fileName, "resource://cpmanager-legacy/"_ns) ||
    370        StringBeginsWith(fileName, "resource://pwa/utils/"_ns)) {
    371      return FilenameTypeAndDetails(kSuspectedUserChromeJS,
    372                                    Some(StripQueryRef(fileName)));
    373    }
    374    return FilenameTypeAndDetails(kResourceURI, Some(StripQueryRef(fileName)));
    375  }
    376  if (StringBeginsWith(fileName, "moz-src://"_ns)) {
    377    return FilenameTypeAndDetails(kMozSrcURI, Some(StripQueryRef(fileName)));
    378  }
    379 
    380  // blob: and data:
    381  if (StringBeginsWith(fileName, "blob:"_ns)) {
    382    return FilenameTypeAndDetails(kBlobUri, Nothing());
    383  }
    384  if (StringBeginsWith(fileName, "data:text/css;extension=style;"_ns)) {
    385    return FilenameTypeAndDetails(kDataUriWebExtCStyle, Nothing());
    386  }
    387  if (StringBeginsWith(fileName, "data:"_ns)) {
    388    return FilenameTypeAndDetails(kDataUri, Nothing());
    389  }
    390 
    391  // Can't do regex matching off-main-thread
    392  if (NS_IsMainThread()) {
    393    NS_ConvertUTF8toUTF16 fileNameA(fileName);
    394    // Extension as loaded via a file://
    395    bool regexMatch;
    396    nsTArray<nsString> regexResults;
    397    nsresult rv =
    398        RegexEval(kExtensionRegex, fileNameA,
    399                  /* aOnlyMatch = */ false, regexMatch, &regexResults);
    400    if (NS_FAILED(rv)) {
    401      return FilenameTypeAndDetails(kRegexFailure, Nothing());
    402    }
    403    if (regexMatch) {
    404      nsCString type = StringEndsWith(regexResults[2], u"mozilla.org.xpi"_ns)
    405                           ? kMozillaExtensionFile
    406                           : kOtherExtensionFile;
    407      const auto& extensionNameAndPath =
    408          Substring(regexResults[0], std::size("extensions/") - 1);
    409      return FilenameTypeAndDetails(
    410          type, Some(OptimizeFileName(extensionNameAndPath)));
    411    }
    412 
    413    // Single File
    414    rv = RegexEval(kSingleFileRegex, fileNameA, /* aOnlyMatch = */ true,
    415                   regexMatch);
    416    if (NS_FAILED(rv)) {
    417      return FilenameTypeAndDetails(kRegexFailure, Nothing());
    418    }
    419    if (regexMatch) {
    420      return FilenameTypeAndDetails(kSingleString, Some(nsCString(fileName)));
    421    }
    422 
    423    // Suspected userChromeJS script
    424    rv = RegexEval(kUCJSRegex, fileNameA, /* aOnlyMatch = */ true, regexMatch);
    425    if (NS_FAILED(rv)) {
    426      return FilenameTypeAndDetails(kRegexFailure, Nothing());
    427    }
    428    if (regexMatch) {
    429      return FilenameTypeAndDetails(kSuspectedUserChromeJS, Nothing());
    430    }
    431  }
    432 
    433  // Something loaded via an about:// URI.
    434  if (StringBeginsWith(fileName, "about:"_ns)) {
    435    return FilenameTypeAndDetails(kAboutUri, Some(StripQueryRef(fileName)));
    436  }
    437 
    438  // Something loaded via a moz-extension:// URI.
    439  if (StringBeginsWith(fileName, "moz-extension://"_ns)) {
    440    if (!collectAdditionalExtensionData) {
    441      return FilenameTypeAndDetails(kExtensionURI, Nothing());
    442    }
    443 
    444    nsAutoCString sanitizedPathAndScheme;
    445    sanitizedPathAndScheme.Append("moz-extension://["_ns);
    446 
    447    nsCOMPtr<nsIURI> uri;
    448    nsresult rv = NS_NewURI(getter_AddRefs(uri), fileName);
    449    if (NS_FAILED(rv)) {
    450      // Return after adding ://[ so we know we failed here.
    451      return FilenameTypeAndDetails(kExtensionURI,
    452                                    Some(sanitizedPathAndScheme));
    453    }
    454 
    455    mozilla::extensions::URLInfo url(uri);
    456    if (NS_IsMainThread()) {
    457      // EPS is only usable on main thread
    458      auto* policy =
    459          ExtensionPolicyService::GetSingleton().GetByHost(url.Host());
    460      if (policy) {
    461        nsString addOnId;
    462        policy->GetId(addOnId);
    463 
    464        sanitizedPathAndScheme.Append(NS_ConvertUTF16toUTF8(addOnId));
    465        sanitizedPathAndScheme.Append(": "_ns);
    466        sanitizedPathAndScheme.Append(NS_ConvertUTF16toUTF8(policy->Name()));
    467        sanitizedPathAndScheme.Append("]"_ns);
    468 
    469        if (policy->IsPrivileged()) {
    470          sanitizedPathAndScheme.Append("P=1"_ns);
    471        } else {
    472          sanitizedPathAndScheme.Append("P=0"_ns);
    473        }
    474      } else {
    475        sanitizedPathAndScheme.Append("failed finding addon by host]"_ns);
    476      }
    477    } else {
    478      sanitizedPathAndScheme.Append("can't get addon off main thread]"_ns);
    479    }
    480 
    481    sanitizedPathAndScheme.Append(url.FilePath());
    482    return FilenameTypeAndDetails(kExtensionURI, Some(sanitizedPathAndScheme));
    483  }
    484 
    485 #if defined(XP_WIN)
    486  auto flags = mozilla::widget::WinUtils::PathTransformFlags::Default |
    487               mozilla::widget::WinUtils::PathTransformFlags::RequireFilePath;
    488  const NS_ConvertUTF8toUTF16 fileNameA(fileName);
    489  nsAutoString strSanitizedPath(fileNameA);
    490  if (widget::WinUtils::PreparePathForTelemetry(strSanitizedPath, flags)) {
    491    DWORD cchDecodedUrl = INTERNET_MAX_URL_LENGTH;
    492    WCHAR szOut[INTERNET_MAX_URL_LENGTH];
    493    HRESULT hr;
    494    SAFECALL_URLMON_FUNC(CoInternetParseUrl, fileNameA.get(), PARSE_SCHEMA, 0,
    495                         szOut, INTERNET_MAX_URL_LENGTH, &cchDecodedUrl, 0);
    496    if (hr == S_OK && cchDecodedUrl) {
    497      nsAutoString sanitizedPathAndScheme;
    498      sanitizedPathAndScheme.Append(szOut);
    499      if (sanitizedPathAndScheme == u"file"_ns) {
    500        sanitizedPathAndScheme.Append(u"://.../"_ns);
    501        sanitizedPathAndScheme.Append(strSanitizedPath);
    502      }
    503      return FilenameTypeAndDetails(
    504          kSanitizedWindowsURL,
    505          Some(NS_ConvertUTF16toUTF8(sanitizedPathAndScheme)));
    506    } else {
    507      return FilenameTypeAndDetails(
    508          kSanitizedWindowsPath, Some(NS_ConvertUTF16toUTF8(strSanitizedPath)));
    509    }
    510  }
    511 #endif
    512 
    513  if (!NS_IsMainThread()) {
    514    return FilenameTypeAndDetails(kOtherWorker, Nothing());
    515  }
    516  return FilenameTypeAndDetails(kOther, Nothing());
    517 }
    518 
    519 #if defined(EARLY_BETA_OR_EARLIER)
    520 // Crash String must be safe from a telemetry point of view.
    521 // This will be ensured when this function is used.
    522 void PossiblyCrash(const char* aPrefSuffix, const char* aUnsafeCrashString,
    523                   const nsCString& aSafeCrashString) {
    524  if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
    525    // We only crash in the parent (unfortunately) because it's
    526    // the only place we can be sure that our only-crash-once
    527    // pref-writing works.
    528    return;
    529  }
    530  if (!NS_IsMainThread()) {
    531    // Setting a pref off the main thread causes ContentParent to observe the
    532    // pref set, resulting in a Release Assertion when it tries to update the
    533    // child off main thread. So don't do any of this off main thread. (Which
    534    // is a bit of a blind spot for this purpose...)
    535    return;
    536  }
    537 
    538  nsCString previous_crashes("security.crash_tracking.");
    539  previous_crashes.Append(aPrefSuffix);
    540  previous_crashes.Append(".prevCrashes");
    541 
    542  nsCString max_crashes("security.crash_tracking.");
    543  max_crashes.Append(aPrefSuffix);
    544  max_crashes.Append(".maxCrashes");
    545 
    546  int32_t numberOfPreviousCrashes = 0;
    547  numberOfPreviousCrashes = Preferences::GetInt(previous_crashes.get(), 0);
    548 
    549  int32_t maxAllowableCrashes = 0;
    550  maxAllowableCrashes = Preferences::GetInt(max_crashes.get(), 0);
    551 
    552  if (numberOfPreviousCrashes >= maxAllowableCrashes) {
    553    return;
    554  }
    555 
    556  nsresult rv =
    557      Preferences::SetInt(previous_crashes.get(), ++numberOfPreviousCrashes);
    558  if (NS_FAILED(rv)) {
    559    return;
    560  }
    561 
    562  nsCOMPtr<nsIPrefService> prefsCom = Preferences::GetService();
    563  Preferences* prefs = static_cast<Preferences*>(prefsCom.get());
    564 
    565  if (!prefs->AllowOffMainThreadSave()) {
    566    // Do not crash if we can't save prefs off the main thread
    567    return;
    568  }
    569 
    570  rv = prefs->SavePrefFileBlocking();
    571  if (!NS_FAILED(rv)) {
    572    // We can only use this in local builds where we don't send stuff up to the
    573    // crash reporter because it has user private data.
    574    // MOZ_CRASH_UNSAFE_PRINTF("%s",
    575    //                        nsContentSecurityUtils::SmartFormatCrashString(aUnsafeCrashString));
    576    MOZ_CRASH_UNSAFE_PRINTF(
    577        "%s",
    578        nsContentSecurityUtils::SmartFormatCrashString(aSafeCrashString.get()));
    579  }
    580 }
    581 #endif
    582 
    583 class EvalUsageNotificationRunnable final : public Runnable {
    584 public:
    585  EvalUsageNotificationRunnable(bool aIsSystemPrincipal,
    586                                const nsACString& aFileName, uint64_t aWindowID,
    587                                uint32_t aLineNumber, uint32_t aColumnNumber)
    588      : mozilla::Runnable("EvalUsageNotificationRunnable"),
    589        mIsSystemPrincipal(aIsSystemPrincipal),
    590        mFileName(aFileName),
    591        mWindowID(aWindowID),
    592        mLineNumber(aLineNumber),
    593        mColumnNumber(aColumnNumber) {}
    594 
    595  NS_IMETHOD Run() override {
    596    nsContentSecurityUtils::NotifyEvalUsage(
    597        mIsSystemPrincipal, mFileName, mWindowID, mLineNumber, mColumnNumber);
    598    return NS_OK;
    599  }
    600 
    601  void Revoke() {}
    602 
    603 private:
    604  bool mIsSystemPrincipal;
    605  nsCString mFileName;
    606  uint64_t mWindowID;
    607  uint32_t mLineNumber;
    608  uint32_t mColumnNumber;
    609 };
    610 
    611 /* static */
    612 bool nsContentSecurityUtils::IsEvalAllowed(JSContext* cx,
    613                                           bool aIsSystemPrincipal,
    614                                           const nsAString& aScript) {
    615  // This allowlist contains files that are permanently allowed to use
    616  // eval()-like functions. It will ideally be restricted to files that are
    617  // exclusively used in testing contexts.
    618  static nsLiteralCString evalAllowlist[] = {
    619      // Test-only third-party library
    620      "resource://testing-common/sinon-7.2.7.js"_ns,
    621      // Test-only utility
    622      "resource://testing-common/content-task.js"_ns,
    623 
    624      // Tracked by Bug 1584605
    625      "resource://gre/modules/translations/cld-worker.js"_ns,
    626 
    627      // require.js implements a script loader for workers. It uses eval
    628      // to load the script; but injection is only possible in situations
    629      // that you could otherwise control script that gets executed, so
    630      // it is okay to allow eval() as it adds no additional attack surface.
    631      // Bug 1584564 tracks requiring safe usage of require.js
    632      "resource://gre/modules/workers/require.js"_ns,
    633 
    634      // The profiler's symbolication code uses a wasm module to extract symbols
    635      // from the binary files result of local builds.
    636      // See bug 1777479
    637      "resource://devtools/shared/performance-new/symbolication.sys.mjs"_ns,
    638 
    639      // The devtools use a WASM module to optimize source-map mapping.
    640      "resource://devtools/client/shared/vendor/source-map/lib/wasm.js"_ns,
    641 
    642      // The Browser Toolbox/Console
    643      "debugger"_ns,
    644 
    645      // Tor Browser's Lox wasm integration
    646      "resource://gre/modules/lox_wasm.jsm"_ns,
    647  };
    648 
    649  // We also permit two specific idioms in eval()-like contexts. We'd like to
    650  // elminate these too; but there are in-the-wild Mozilla privileged extensions
    651  // that use them.
    652  static constexpr auto sAllowedEval1 = u"this"_ns;
    653  static constexpr auto sAllowedEval2 =
    654      u"function anonymous(\n) {\nreturn this\n}"_ns;
    655 
    656  if (MOZ_LIKELY(!aIsSystemPrincipal && !XRE_IsE10sParentProcess())) {
    657    // We restrict eval in the system principal and parent process.
    658    // Other uses (like web content and null principal) are allowed.
    659    return true;
    660  }
    661 
    662  if (JS::ContextOptionsRef(cx).disableEvalSecurityChecks()) {
    663    MOZ_LOG(sCSMLog, LogLevel::Debug,
    664            ("Allowing eval() because this JSContext was set to allow it"));
    665    return true;
    666  }
    667 
    668  if (StaticPrefs::
    669          security_allow_unsafe_dangerous_privileged_evil_eval_AtStartup()) {
    670    MOZ_LOG(
    671        sCSMLog, LogLevel::Debug,
    672        ("Allowing eval() because "
    673         "security.allow_unsafe_dangerous_priviliged_evil_eval is enabled."));
    674    return true;
    675  }
    676 
    677  if (aIsSystemPrincipal &&
    678      StaticPrefs::security_allow_eval_with_system_principal()) {
    679    MOZ_LOG(sCSMLog, LogLevel::Debug,
    680            ("Allowing eval() with System Principal because allowing pref is "
    681             "enabled"));
    682    return true;
    683  }
    684 
    685  if (XRE_IsE10sParentProcess() &&
    686      StaticPrefs::security_allow_eval_in_parent_process()) {
    687    MOZ_LOG(sCSMLog, LogLevel::Debug,
    688            ("Allowing eval() in parent process because allowing pref is "
    689             "enabled"));
    690    return true;
    691  }
    692 
    693  if (XRE_IsE10sParentProcess() &&
    694      !StaticPrefs::extensions_webextensions_remote()) {
    695    MOZ_LOG(sCSMLog, LogLevel::Debug,
    696            ("Allowing eval() in parent process because the web extension "
    697             "process is disabled"));
    698    return true;
    699  }
    700 
    701  // We permit these two common idioms to get access to the global JS object
    702  if (!aScript.IsEmpty() &&
    703      (aScript == sAllowedEval1 || aScript == sAllowedEval2)) {
    704    MOZ_LOG(
    705        sCSMLog, LogLevel::Debug,
    706        ("Allowing eval() %s because a key string is "
    707         "provided",
    708         (aIsSystemPrincipal ? "with System Principal" : "in parent process")));
    709    return true;
    710  }
    711 
    712  // Check the allowlist for the provided filename. getFilename is a helper
    713  // function
    714  auto location = JSCallingLocation::Get(cx);
    715  const nsCString& fileName = location.FileName();
    716  for (const nsLiteralCString& allowlistEntry : evalAllowlist) {
    717    // checking if current filename begins with entry, because JS Engine
    718    // gives us additional stuff for code inside eval or Function ctor
    719    // e.g., "require.js > Function"
    720    if (StringBeginsWith(fileName, allowlistEntry)) {
    721      MOZ_LOG(sCSMLog, LogLevel::Debug,
    722              ("Allowing eval() %s because the containing "
    723               "file is in the allowlist",
    724               (aIsSystemPrincipal ? "with System Principal"
    725                                   : "in parent process")));
    726      return true;
    727    }
    728  }
    729 
    730  // Send Telemetry and Log to the Console
    731  uint64_t windowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
    732  if (NS_IsMainThread()) {
    733    nsContentSecurityUtils::NotifyEvalUsage(aIsSystemPrincipal, fileName,
    734                                            windowID, location.mLine,
    735                                            location.mColumn);
    736  } else {
    737    auto runnable = new EvalUsageNotificationRunnable(
    738        aIsSystemPrincipal, fileName, windowID, location.mLine,
    739        location.mColumn);
    740    NS_DispatchToMainThread(runnable);
    741  }
    742 
    743  // Log to MOZ_LOG
    744  MOZ_LOG(sCSMLog, LogLevel::Error,
    745          ("Blocking eval() %s from file %s and script "
    746           "provided %s",
    747           (aIsSystemPrincipal ? "with System Principal" : "in parent process"),
    748           fileName.get(), NS_ConvertUTF16toUTF8(aScript).get()));
    749 
    750  // Maybe Crash
    751 #if defined(DEBUG) || defined(FUZZING)
    752  auto crashString = nsContentSecurityUtils::SmartFormatCrashString(
    753      NS_ConvertUTF16toUTF8(aScript).get(), fileName.get(),
    754      (aIsSystemPrincipal
    755           ? "Blocking eval() with System Principal with script %s from file %s"
    756           : "Blocking eval() in parent process with script %s from file %s"));
    757  MOZ_CRASH_UNSAFE_PRINTF("%s", crashString.get());
    758 #endif
    759 
    760 #if defined(ANDROID) && !defined(NIGHTLY_BUILD)
    761  return true;
    762 #else
    763  return false;
    764 #endif
    765 }
    766 
    767 /* static */
    768 void nsContentSecurityUtils::NotifyEvalUsage(bool aIsSystemPrincipal,
    769                                             const nsACString& aFileName,
    770                                             uint64_t aWindowID,
    771                                             uint32_t aLineNumber,
    772                                             uint32_t aColumnNumber) {
    773  FilenameTypeAndDetails fileNameTypeAndDetails =
    774      FilenameToFilenameType(aFileName, false);
    775  auto fileinfo = fileNameTypeAndDetails.second;
    776  auto value = Some(fileNameTypeAndDetails.first);
    777  if (aIsSystemPrincipal) {
    778    glean::security::EvalUsageSystemContextExtra extra = {
    779        .fileinfo = fileinfo,
    780        .value = value,
    781    };
    782    glean::security::eval_usage_system_context.Record(Some(extra));
    783  } else {
    784    glean::security::EvalUsageParentProcessExtra extra = {
    785        .fileinfo = fileinfo,
    786        .value = value,
    787    };
    788    glean::security::eval_usage_parent_process.Record(Some(extra));
    789  }
    790 
    791  // Report an error to console
    792  nsCOMPtr<nsIConsoleService> console(
    793      do_GetService(NS_CONSOLESERVICE_CONTRACTID));
    794  if (!console) {
    795    return;
    796  }
    797  nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
    798  if (!error) {
    799    return;
    800  }
    801  nsCOMPtr<nsIStringBundle> bundle;
    802  nsCOMPtr<nsIStringBundleService> stringService =
    803      mozilla::components::StringBundle::Service();
    804  if (!stringService) {
    805    return;
    806  }
    807  stringService->CreateBundle(
    808      "chrome://global/locale/security/security.properties",
    809      getter_AddRefs(bundle));
    810  if (!bundle) {
    811    return;
    812  }
    813  nsAutoString message;
    814  NS_ConvertUTF8toUTF16 fileNameA(aFileName);
    815  AutoTArray<nsString, 1> formatStrings = {fileNameA};
    816  nsresult rv = bundle->FormatStringFromName("RestrictBrowserEvalUsage",
    817                                             formatStrings, message);
    818  if (NS_FAILED(rv)) {
    819    return;
    820  }
    821 
    822  rv = error->InitWithWindowID(message, aFileName, aLineNumber, aColumnNumber,
    823                               nsIScriptError::errorFlag, "BrowserEvalUsage",
    824                               aWindowID, true /* From chrome context */);
    825  if (NS_FAILED(rv)) {
    826    return;
    827  }
    828  console->LogMessage(error);
    829 }
    830 
    831 // If we detect that one of the relevant prefs has been changed, reset
    832 // sJSHacksChecked to cause us to re-evaluate all the pref values.
    833 // This will stop us from crashing because a user enabled one of these
    834 // prefs during a session and then triggered the JavaScript load mitigation
    835 // (which can cause a crash).
    836 class JSHackPrefObserver final {
    837 public:
    838  JSHackPrefObserver() = default;
    839  static void PrefChanged(const char* aPref, void* aData);
    840 
    841 protected:
    842  ~JSHackPrefObserver() = default;
    843 };
    844 
    845 // static
    846 void JSHackPrefObserver::PrefChanged(const char* aPref, void* aData) {
    847  sJSHacksChecked = false;
    848 }
    849 
    850 static bool sJSHackObserverAdded = false;
    851 
    852 /* static */
    853 void nsContentSecurityUtils::DetectJsHacks() {
    854  // We can only perform the check of this preference on the Main Thread
    855  // (because a String-based preference check is only safe on Main Thread.)
    856  // In theory, it would be possible that a separate thread could get here
    857  // before the main thread, resulting in the other thread not being able to
    858  // perform this check, but the odds of that are small (and probably zero.)
    859  if (!NS_IsMainThread()) {
    860    return;
    861  }
    862 
    863  // If the pref service isn't available, do nothing and re-do this later.
    864  if (!Preferences::IsServiceAvailable()) {
    865    return;
    866  }
    867 
    868  // No need to check again.
    869  if (MOZ_LIKELY(sJSHacksChecked || sJSHacksPresent)) {
    870    return;
    871  }
    872 
    873  static const char* kObservedPrefs[] = {
    874      "xpinstall.signatures.required", "general.config.filename",
    875      "autoadmin.global_config_url", "autoadmin.failover_to_cached", nullptr};
    876  if (MOZ_UNLIKELY(!sJSHackObserverAdded)) {
    877    Preferences::RegisterCallbacks(JSHackPrefObserver::PrefChanged,
    878                                   kObservedPrefs);
    879    sJSHackObserverAdded = true;
    880  }
    881 
    882  nsresult rv;
    883  sJSHacksChecked = true;
    884 
    885  // This preference is required by bootstrapLoader.xpi, which is an
    886  // alternate way to load legacy-style extensions. It only works on
    887  // DevEdition/Nightly.
    888  bool xpinstallSignatures;
    889  rv = Preferences::GetBool("xpinstall.signatures.required",
    890                            &xpinstallSignatures, PrefValueKind::Default);
    891  if (!NS_FAILED(rv) && !xpinstallSignatures) {
    892    sJSHacksPresent = true;
    893    return;
    894  }
    895  rv = Preferences::GetBool("xpinstall.signatures.required",
    896                            &xpinstallSignatures, PrefValueKind::User);
    897  if (!NS_FAILED(rv) && !xpinstallSignatures) {
    898    sJSHacksPresent = true;
    899    return;
    900  }
    901 
    902  if (Preferences::HasDefaultValue("general.config.filename")) {
    903    sJSHacksPresent = true;
    904    return;
    905  }
    906  if (Preferences::HasUserValue("general.config.filename")) {
    907    sJSHacksPresent = true;
    908    return;
    909  }
    910  if (Preferences::HasDefaultValue("autoadmin.global_config_url")) {
    911    sJSHacksPresent = true;
    912    return;
    913  }
    914  if (Preferences::HasUserValue("autoadmin.global_config_url")) {
    915    sJSHacksPresent = true;
    916    return;
    917  }
    918 
    919  bool failOverToCache;
    920  rv = Preferences::GetBool("autoadmin.failover_to_cached", &failOverToCache,
    921                            PrefValueKind::Default);
    922  if (!NS_FAILED(rv) && failOverToCache) {
    923    sJSHacksPresent = true;
    924    return;
    925  }
    926  rv = Preferences::GetBool("autoadmin.failover_to_cached", &failOverToCache,
    927                            PrefValueKind::User);
    928  if (!NS_FAILED(rv) && failOverToCache) {
    929    sJSHacksPresent = true;
    930  }
    931 }
    932 
    933 /* static */
    934 void nsContentSecurityUtils::DetectCssHacks() {
    935  // We can only perform the check of this preference on the Main Thread
    936  // It's possible that this function may therefore race and we expect the
    937  // caller to ensure that the checks have actually happened.
    938  if (!NS_IsMainThread()) {
    939    return;
    940  }
    941 
    942  // If the pref service isn't available, do nothing and re-do this later.
    943  if (!Preferences::IsServiceAvailable()) {
    944    return;
    945  }
    946 
    947  // No need to check again.
    948  if (MOZ_LIKELY(sCSSHacksChecked || sCSSHacksPresent)) {
    949    return;
    950  }
    951 
    952  // This preference is a bool to see if userChrome css is loaded
    953  bool customStylesPresent = Preferences::GetBool(
    954      "toolkit.legacyUserProfileCustomizations.stylesheets", false);
    955  if (customStylesPresent) {
    956    sCSSHacksPresent = true;
    957  }
    958 
    959  sCSSHacksChecked = true;
    960 }
    961 
    962 /* static */
    963 nsresult nsContentSecurityUtils::GetHttpChannelFromPotentialMultiPart(
    964    nsIChannel* aChannel, nsIHttpChannel** aHttpChannel) {
    965  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
    966  if (httpChannel) {
    967    httpChannel.forget(aHttpChannel);
    968    return NS_OK;
    969  }
    970 
    971  nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
    972  if (!multipart) {
    973    *aHttpChannel = nullptr;
    974    return NS_OK;
    975  }
    976 
    977  nsCOMPtr<nsIChannel> baseChannel;
    978  nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
    979  if (NS_WARN_IF(NS_FAILED(rv))) {
    980    return rv;
    981  }
    982 
    983  httpChannel = do_QueryInterface(baseChannel);
    984  httpChannel.forget(aHttpChannel);
    985 
    986  return NS_OK;
    987 }
    988 
    989 nsresult CheckCSPFrameAncestorPolicy(nsIChannel* aChannel,
    990                                     nsIContentSecurityPolicy** aOutCSP) {
    991  MOZ_ASSERT(aChannel);
    992 
    993  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    994  ExtContentPolicyType contentType = loadInfo->GetExternalContentPolicyType();
    995  // frame-ancestor check only makes sense for subdocument and object loads,
    996  // if this is not a load of such type, there is nothing to do here.
    997  if (contentType != ExtContentPolicy::TYPE_SUBDOCUMENT &&
    998      contentType != ExtContentPolicy::TYPE_OBJECT) {
    999    return NS_OK;
   1000  }
   1001 
   1002  // CSP can only hang off an http channel, if this channel is not
   1003  // an http channel then there is nothing to do here,
   1004  // except with add-ons, where the CSP is stored in a WebExtensionPolicy.
   1005  nsCOMPtr<nsIHttpChannel> httpChannel;
   1006  nsresult rv = nsContentSecurityUtils::GetHttpChannelFromPotentialMultiPart(
   1007      aChannel, getter_AddRefs(httpChannel));
   1008  if (NS_WARN_IF(NS_FAILED(rv))) {
   1009    return rv;
   1010  }
   1011 
   1012  nsAutoCString tCspHeaderValue, tCspROHeaderValue;
   1013  if (httpChannel) {
   1014    (void)httpChannel->GetResponseHeader("content-security-policy"_ns,
   1015                                         tCspHeaderValue);
   1016 
   1017    (void)httpChannel->GetResponseHeader(
   1018        "content-security-policy-report-only"_ns, tCspROHeaderValue);
   1019 
   1020    // if there are no CSP values, then there is nothing to do here.
   1021    if (tCspHeaderValue.IsEmpty() && tCspROHeaderValue.IsEmpty()) {
   1022      return NS_OK;
   1023    }
   1024  }
   1025 
   1026  nsCOMPtr<nsIPrincipal> resultPrincipal;
   1027  rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   1028      aChannel, getter_AddRefs(resultPrincipal));
   1029  NS_ENSURE_SUCCESS(rv, rv);
   1030 
   1031  RefPtr<extensions::WebExtensionPolicy> addonPolicy;
   1032  if (!httpChannel) {
   1033    addonPolicy = BasePrincipal::Cast(resultPrincipal)->AddonPolicy();
   1034    if (!addonPolicy) {
   1035      // Neither a HTTP channel, nor a moz-extension:-resource.
   1036      // CSP is not supported.
   1037      return NS_OK;
   1038    }
   1039  }
   1040 
   1041  RefPtr<nsCSPContext> csp = new nsCSPContext();
   1042  // This CSPContext is only used for checking frame-ancestors, we
   1043  // will parse the CSP again anyway. (Unless this blocks the load, but
   1044  // parser warnings aren't really important in that case)
   1045  csp->SuppressParserLogMessages();
   1046 
   1047  nsCOMPtr<nsIURI> selfURI;
   1048  nsAutoCString referrerSpec;
   1049  if (httpChannel) {
   1050    aChannel->GetURI(getter_AddRefs(selfURI));
   1051    nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
   1052    if (referrerInfo) {
   1053      referrerInfo->GetComputedReferrerSpec(referrerSpec);
   1054    }
   1055  } else {
   1056    // aChannel::GetURI would return the jar: or file:-URI for extensions.
   1057    // Use the "final" URI to get the actual moz-extension:-URL.
   1058    NS_GetFinalChannelURI(aChannel, getter_AddRefs(selfURI));
   1059  }
   1060 
   1061  uint64_t innerWindowID = loadInfo->GetInnerWindowID();
   1062 
   1063  rv = csp->SetRequestContextWithPrincipal(resultPrincipal, selfURI,
   1064                                           referrerSpec, innerWindowID);
   1065  if (NS_WARN_IF(NS_FAILED(rv))) {
   1066    return rv;
   1067  }
   1068 
   1069  if (addonPolicy) {
   1070    csp->AppendPolicy(addonPolicy->BaseCSP(), false, false);
   1071    csp->AppendPolicy(addonPolicy->ExtensionPageCSP(), false, false);
   1072  } else {
   1073    NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
   1074    NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
   1075 
   1076    // ----- if there's a full-strength CSP header, apply it.
   1077    if (!cspHeaderValue.IsEmpty()) {
   1078      rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
   1079      NS_ENSURE_SUCCESS(rv, rv);
   1080    }
   1081 
   1082    // ----- if there's a report-only CSP header, apply it.
   1083    if (!cspROHeaderValue.IsEmpty()) {
   1084      rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
   1085      NS_ENSURE_SUCCESS(rv, rv);
   1086    }
   1087  }
   1088 
   1089  // ----- Enforce frame-ancestor policy on any applied policies
   1090  bool safeAncestry = false;
   1091  // PermitsAncestry sends violation reports when necessary
   1092  rv = csp->PermitsAncestry(loadInfo, &safeAncestry);
   1093 
   1094  if (NS_FAILED(rv) || !safeAncestry) {
   1095    // stop!  ERROR page!
   1096    return NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION;
   1097  }
   1098 
   1099  // return the CSP for x-frame-options check
   1100  csp.forget(aOutCSP);
   1101 
   1102  return NS_OK;
   1103 }
   1104 
   1105 void EnforceCSPFrameAncestorPolicy(nsIChannel* aChannel,
   1106                                   const nsresult& aError) {
   1107  if (aError == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION) {
   1108    aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
   1109  }
   1110 }
   1111 
   1112 void EnforceXFrameOptionsCheck(nsIChannel* aChannel,
   1113                               nsIContentSecurityPolicy* aCsp) {
   1114  MOZ_ASSERT(aChannel);
   1115  bool isFrameOptionsIgnored = false;
   1116  // check for XFO options
   1117  // XFO checks can be skipped if there are frame ancestors
   1118  if (!FramingChecker::CheckFrameOptions(aChannel, aCsp,
   1119                                         isFrameOptionsIgnored)) {
   1120    // stop!  ERROR page!
   1121    aChannel->Cancel(NS_ERROR_XFO_VIOLATION);
   1122  }
   1123 
   1124  if (isFrameOptionsIgnored) {
   1125    // log warning to console that xfo is ignored because of CSP
   1126    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   1127    uint64_t innerWindowID = loadInfo->GetInnerWindowID();
   1128    bool privateWindow = loadInfo->GetOriginAttributes().IsPrivateBrowsing();
   1129    AutoTArray<nsString, 2> params = {u"x-frame-options"_ns,
   1130                                      u"frame-ancestors"_ns};
   1131    CSP_LogLocalizedStr("IgnoringSrcBecauseOfDirective", params,
   1132                        ""_ns,   // no sourcefile
   1133                        u""_ns,  // no scriptsample
   1134                        0,       // no linenumber
   1135                        1,       // no columnnumber
   1136                        nsIScriptError::warningFlag,
   1137                        "IgnoringSrcBecauseOfDirective"_ns, innerWindowID,
   1138                        privateWindow);
   1139  }
   1140 }
   1141 
   1142 /* static */
   1143 void nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(
   1144    nsIChannel* aChannel) {
   1145  nsCOMPtr<nsIContentSecurityPolicy> csp;
   1146  nsresult rv = CheckCSPFrameAncestorPolicy(aChannel, getter_AddRefs(csp));
   1147 
   1148  if (NS_FAILED(rv)) {
   1149    EnforceCSPFrameAncestorPolicy(aChannel, rv);
   1150    return;
   1151  }
   1152 
   1153  // X-Frame-Options needs to be enforced after CSP frame-ancestors
   1154  // checks because if frame-ancestors is present, then x-frame-options
   1155  // will be discarded
   1156  EnforceXFrameOptionsCheck(aChannel, csp);
   1157 }
   1158 /* static */
   1159 bool nsContentSecurityUtils::CheckCSPFrameAncestorAndXFO(nsIChannel* aChannel) {
   1160  nsCOMPtr<nsIContentSecurityPolicy> csp;
   1161  nsresult rv = CheckCSPFrameAncestorPolicy(aChannel, getter_AddRefs(csp));
   1162 
   1163  if (NS_FAILED(rv)) {
   1164    return false;
   1165  }
   1166 
   1167  bool isFrameOptionsIgnored = false;
   1168 
   1169  return FramingChecker::CheckFrameOptions(aChannel, csp,
   1170                                           isFrameOptionsIgnored);
   1171 }
   1172 
   1173 // https://w3c.github.io/webappsec-csp/#is-element-nonceable
   1174 /* static */
   1175 nsString nsContentSecurityUtils::GetIsElementNonceableNonce(
   1176    const Element& aElement) {
   1177  // Step 1. If element does not have an attribute named "nonce", return "Not
   1178  // Nonceable".
   1179  nsString nonce;
   1180  if (nsString* cspNonce =
   1181          static_cast<nsString*>(aElement.GetProperty(nsGkAtoms::nonce))) {
   1182    nonce = *cspNonce;
   1183  }
   1184  if (nonce.IsEmpty()) {
   1185    return nonce;
   1186  }
   1187 
   1188  // Step 2. If element is a script element, then for each attribute of
   1189  // element’s attribute list:
   1190  if (nsCOMPtr<nsIScriptElement> script =
   1191          do_QueryInterface(const_cast<Element*>(&aElement))) {
   1192    auto containsScriptOrStyle = [](const nsAString& aStr) {
   1193      return aStr.LowerCaseFindASCII("<script") != kNotFound ||
   1194             aStr.LowerCaseFindASCII("<style") != kNotFound;
   1195    };
   1196 
   1197    nsString value;
   1198    uint32_t i = 0;
   1199    while (BorrowedAttrInfo info = aElement.GetAttrInfoAt(i++)) {
   1200      // Step 2.1. If attribute’s name contains an ASCII case-insensitive match
   1201      // for "<script" or "<style", return "Not Nonceable".
   1202      const nsAttrName* name = info.mName;
   1203      if (nsAtom* prefix = name->GetPrefix()) {
   1204        if (containsScriptOrStyle(nsDependentAtomString(prefix))) {
   1205          return EmptyString();
   1206        }
   1207      }
   1208      if (containsScriptOrStyle(nsDependentAtomString(name->LocalName()))) {
   1209        return EmptyString();
   1210      }
   1211 
   1212      // Step 2.2. If attribute’s value contains an ASCII case-insensitive match
   1213      // for "<script" or "<style", return "Not Nonceable".
   1214      info.mValue->ToString(value);
   1215      if (containsScriptOrStyle(value)) {
   1216        return EmptyString();
   1217      }
   1218    }
   1219  }
   1220 
   1221  // Step 3. If element had a duplicate-attribute parse error during
   1222  // tokenization, return "Not Nonceable".
   1223  if (aElement.HasFlag(ELEMENT_PARSER_HAD_DUPLICATE_ATTR_ERROR)) {
   1224    return EmptyString();
   1225  }
   1226 
   1227  // Step 4. Return "Nonceable".
   1228  return nonce;
   1229 }
   1230 
   1231 #if defined(DEBUG)
   1232 
   1233 #  include "mozilla/dom/nsCSPContext.h"
   1234 
   1235 // The follow lists define the exceptions to the usual default list
   1236 // of allowed CSP sources for internal pages. The default list
   1237 // allows chrome: and resource: URLs for everything, with the exception
   1238 // of object-src.
   1239 //
   1240 // Generally adding something to these lists should be seen as a bad
   1241 // sign, but it is obviously impossible for some pages, e.g.
   1242 // those that are meant to include content from the web.
   1243 //
   1244 // Do note: We will _never_ allow any additional source for scripts
   1245 // (script-src, script-src-elem, script-src-attr, worker-src)
   1246 
   1247 // style-src data:
   1248 //  This is more or less the same as allowing arbitrary inline styles.
   1249 static nsLiteralCString sStyleSrcDataAllowList[] = {
   1250    "about:preferences"_ns, "about:settings"_ns,
   1251    // STOP! Do not add anything to this list.
   1252 };
   1253 // style-src 'unsafe-inline'
   1254 static nsLiteralCString sStyleSrcUnsafeInlineAllowList[] = {
   1255    // Bug 1579160: Remove 'unsafe-inline' from style-src within
   1256    // about:preferences
   1257    "about:preferences"_ns,
   1258    "about:settings"_ns,
   1259    // Bug 1571346: Remove 'unsafe-inline' from style-src within about:addons
   1260    "about:addons"_ns,
   1261    // Bug 1584485: Remove 'unsafe-inline' from style-src within:
   1262    // * about:newtab
   1263    // * about:welcome
   1264    // * about:home
   1265    "about:newtab"_ns,
   1266    "about:welcome"_ns,
   1267    "about:home"_ns,
   1268    "chrome://browser/content/pageinfo/pageInfo.xhtml"_ns,
   1269    "chrome://browser/content/places/bookmarkProperties.xhtml"_ns,
   1270    "chrome://browser/content/places/bookmarksSidebar.xhtml"_ns,
   1271    "chrome://browser/content/places/historySidebar.xhtml"_ns,
   1272    "chrome://browser/content/places/interactionsViewer.html"_ns,
   1273    "chrome://browser/content/places/places.xhtml"_ns,
   1274    "chrome://browser/content/preferences/dialogs/applicationManager.xhtml"_ns,
   1275    "chrome://browser/content/preferences/dialogs/browserLanguages.xhtml"_ns,
   1276    "chrome://browser/content/preferences/dialogs/clearSiteData.xhtml"_ns,
   1277    "chrome://browser/content/preferences/dialogs/colors.xhtml"_ns,
   1278    "chrome://browser/content/preferences/dialogs/connection.xhtml"_ns,
   1279    "chrome://browser/content/preferences/dialogs/containers.xhtml"_ns,
   1280    "chrome://browser/content/preferences/dialogs/dohExceptions.xhtml"_ns,
   1281    "chrome://browser/content/preferences/dialogs/fonts.xhtml"_ns,
   1282    "chrome://browser/content/preferences/dialogs/languages.xhtml"_ns,
   1283    "chrome://browser/content/preferences/dialogs/permissions.xhtml"_ns,
   1284    "chrome://browser/content/preferences/dialogs/selectBookmark.xhtml"_ns,
   1285    "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml"_ns,
   1286    "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml"_ns,
   1287    "chrome://browser/content/preferences/dialogs/syncChooseWhatToSync.xhtml"_ns,
   1288    "chrome://browser/content/preferences/dialogs/translations.xhtml"_ns,
   1289    "chrome://browser/content/preferences/fxaPairDevice.xhtml"_ns,
   1290    "chrome://browser/content/safeMode.xhtml"_ns,
   1291    "chrome://browser/content/sanitize.xhtml"_ns,
   1292    "chrome://browser/content/sanitize_v2.xhtml"_ns,
   1293    "chrome://browser/content/search/addEngine.xhtml"_ns,
   1294    "chrome://browser/content/setDesktopBackground.xhtml"_ns,
   1295    "chrome://browser/content/spotlight.html"_ns,
   1296    "chrome://devtools/content/debugger/index.html"_ns,
   1297    "chrome://devtools/content/framework/browser-toolbox/window.html"_ns,
   1298    "chrome://devtools/content/framework/toolbox-options.html"_ns,
   1299    "chrome://devtools/content/framework/toolbox-window.xhtml"_ns,
   1300    "chrome://devtools/content/inspector/index.xhtml"_ns,
   1301    "chrome://devtools/content/inspector/markup/markup.xhtml"_ns,
   1302    "chrome://devtools/content/netmonitor/index.html"_ns,
   1303    "chrome://devtools/content/memory/index.xhtml"_ns,
   1304    "chrome://devtools/content/shared/sourceeditor/codemirror/cmiframe.html"_ns,
   1305    "chrome://devtools/content/webconsole/index.html"_ns,
   1306    "chrome://formautofill/content/manageAddresses.xhtml"_ns,
   1307    "chrome://formautofill/content/manageCreditCards.xhtml"_ns,
   1308    "chrome://gfxsanity/content/sanityparent.html"_ns,
   1309    "chrome://gfxsanity/content/sanitytest.html"_ns,
   1310    "chrome://global/content/commonDialog.xhtml"_ns,
   1311    "chrome://global/content/resetProfileProgress.xhtml"_ns,
   1312    "chrome://layoutdebug/content/layoutdebug.xhtml"_ns,
   1313    "chrome://mozapps/content/downloads/unknownContentType.xhtml"_ns,
   1314    "chrome://mozapps/content/handling/appChooser.xhtml"_ns,
   1315    "chrome://mozapps/content/preferences/changemp.xhtml"_ns,
   1316    "chrome://mozapps/content/preferences/removemp.xhtml"_ns,
   1317    "chrome://mozapps/content/profile/profileDowngrade.xhtml"_ns,
   1318    "chrome://mozapps/content/profile/profileSelection.xhtml"_ns,
   1319    "chrome://mozapps/content/profile/createProfileWizard.xhtml"_ns,
   1320    "chrome://mozapps/content/update/history.xhtml"_ns,
   1321    "chrome://mozapps/content/update/updateElevation.xhtml"_ns,
   1322    "chrome://pippki/content/certManager.xhtml"_ns,
   1323    "chrome://pippki/content/changepassword.xhtml"_ns,
   1324    "chrome://pippki/content/deletecert.xhtml"_ns,
   1325    "chrome://pippki/content/device_manager.xhtml"_ns,
   1326    "chrome://pippki/content/downloadcert.xhtml"_ns,
   1327    "chrome://pippki/content/editcacert.xhtml"_ns,
   1328    "chrome://pippki/content/load_device.xhtml"_ns,
   1329    "chrome://pippki/content/setp12password.xhtml"_ns,
   1330 };
   1331 // img-src moz-remote-image:
   1332 static nsLiteralCString sImgSrcMozRemoteImageAllowList[] = {
   1333    "about:preferences"_ns,
   1334    "about:settings"_ns,
   1335    "chrome://browser/content/preferences/dialogs/applicationManager.xhtml"_ns,
   1336    "chrome://mozapps/content/handling/appChooser.xhtml"_ns,
   1337 };
   1338 // img-src data: blob:
   1339 static nsLiteralCString sImgSrcDataBlobAllowList[] = {
   1340    "about:addons"_ns,
   1341    "about:debugging"_ns,
   1342    "about:deleteprofile"_ns,
   1343    "about:devtools-toolbox"_ns,
   1344    "about:editprofile"_ns,
   1345    "about:firefoxview"_ns,
   1346    "about:home"_ns,
   1347    "about:inference"_ns,
   1348    "about:logins"_ns,
   1349    "about:newprofile"_ns,
   1350    "about:newtab"_ns,
   1351    "about:opentabs"_ns,
   1352    "about:preferences"_ns,
   1353    "about:privatebrowsing"_ns,
   1354    "about:processes"_ns,
   1355    "about:profilemanager"_ns,
   1356    "about:protections"_ns,
   1357    "about:reader"_ns,
   1358    "about:sessionrestore"_ns,
   1359    "about:settings"_ns,
   1360    "about:test-about-content-search-ui"_ns,
   1361    "about:welcome"_ns,
   1362    "chrome://browser/content/aboutDialog.xhtml"_ns,
   1363    "chrome://browser/content/aboutlogins/aboutLogins.html"_ns,
   1364    "chrome://browser/content/genai/chat.html"_ns,
   1365    "chrome://browser/content/places/bookmarksSidebar.xhtml"_ns,
   1366    "chrome://browser/content/places/places.xhtml"_ns,
   1367    "chrome://browser/content/preferences/dialogs/permissions.xhtml"_ns,
   1368    "chrome://browser/content/preferences/fxaPairDevice.xhtml"_ns,
   1369    "chrome://browser/content/screenshots/screenshots-preview.html"_ns,
   1370    "chrome://browser/content/sidebar/sidebar-customize.html"_ns,
   1371    "chrome://browser/content/sidebar/sidebar-history.html"_ns,
   1372    "chrome://browser/content/sidebar/sidebar-syncedtabs.html"_ns,
   1373    "chrome://browser/content/spotlight.html"_ns,
   1374    "chrome://browser/content/syncedtabs/sidebar.xhtml"_ns,
   1375    "chrome://browser/content/webext-panels.xhtml"_ns,
   1376    "chrome://devtools/content/application/index.html"_ns,
   1377    "chrome://devtools/content/framework/browser-toolbox/window.html"_ns,
   1378    "chrome://devtools/content/framework/toolbox-window.xhtml"_ns,
   1379    "chrome://devtools/content/inspector/index.xhtml"_ns,
   1380    "chrome://devtools/content/inspector/markup/markup.xhtml"_ns,
   1381    "chrome://devtools/content/netmonitor/index.html"_ns,
   1382    "chrome://devtools/content/responsive/toolbar.xhtml"_ns,
   1383    "chrome://devtools/content/shared/sourceeditor/codemirror/cmiframe.html"_ns,
   1384    "chrome://devtools/content/webconsole/index.html"_ns,
   1385    "chrome://global/content/alerts/alert.xhtml"_ns,
   1386    "chrome://global/content/print.html"_ns,
   1387    "chrome://browser/content/torpreferences/requestBridgeDialog.xhtml"_ns,
   1388 };
   1389 // img-src https:
   1390 static nsLiteralCString sImgSrcHttpsAllowList[] = {
   1391    "about:addons"_ns,
   1392    "about:debugging"_ns,
   1393    "about:home"_ns,
   1394    "about:newtab"_ns,
   1395    "about:preferences"_ns,
   1396    "about:settings"_ns,
   1397    "about:welcome"_ns,
   1398    "chrome://devtools/content/application/index.html"_ns,
   1399    "chrome://devtools/content/framework/browser-toolbox/window.html"_ns,
   1400    "chrome://devtools/content/framework/toolbox-window.xhtml"_ns,
   1401    "chrome://global/content/alerts/alert.xhtml"_ns,
   1402 };
   1403 // img-src http:
   1404 //  UNSAFE! Do not use.
   1405 static nsLiteralCString sImgSrcHttpAllowList[] = {
   1406    "about:addons"_ns,
   1407    "chrome://devtools/content/application/index.html"_ns,
   1408    "chrome://devtools/content/framework/browser-toolbox/window.html"_ns,
   1409    "chrome://devtools/content/framework/toolbox-window.xhtml"_ns,
   1410    "chrome://global/content/alerts/alert.xhtml"_ns,
   1411    // STOP! Do not add anything to this list.
   1412 };
   1413 // img-src jar: file:
   1414 //  UNSAFE! Do not use.
   1415 static nsLiteralCString sImgSrcAddonsAllowList[] = {
   1416    "about:addons"_ns,
   1417    // STOP! Do not add anything to this list.
   1418 };
   1419 // img-src *
   1420 //  UNSAFE! Allows loading everything.
   1421 static nsLiteralCString sImgSrcWildcardAllowList[] = {
   1422    "about:reader"_ns, "chrome://browser/content/syncedtabs/sidebar.xhtml"_ns,
   1423    // STOP! Do not add anything to this list.
   1424 };
   1425 // img-src https://example.org
   1426 //  Any https host source.
   1427 static nsLiteralCString sImgSrcHttpsHostAllowList[] = {
   1428    "about:logins"_ns,
   1429    "chrome://browser/content/aboutlogins/aboutLogins.html"_ns,
   1430    "chrome://browser/content/spotlight.html"_ns,
   1431 };
   1432 // media-src *
   1433 //  UNSAFE! Allows loading everything.
   1434 static nsLiteralCString sMediaSrcWildcardAllowList[] = {
   1435    "about:reader"_ns,
   1436    // STOP! Do not add anything to this list.
   1437 };
   1438 // media-src https://example.org
   1439 //  Any https host source.
   1440 static nsLiteralCString sMediaSrcHttpsHostAllowList[] = {"about:welcome"_ns};
   1441 // connect-src https:
   1442 static nsLiteralCString sConnectSrcHttpsAllowList[] = {
   1443    "about:addons"_ns,
   1444    "about:home"_ns,
   1445    "about:newtab"_ns,
   1446    "about:welcome"_ns,
   1447 };
   1448 // connect-src data: http:
   1449 //  UNSAFE! Do not use.
   1450 static nsLiteralCString sConnectSrcAddonsAllowList[] = {
   1451    "about:addons"_ns,
   1452    // STOP! Do not add anything to this list.
   1453 };
   1454 // connect-src https://example.org
   1455 //  Any https host source.
   1456 static nsLiteralCString sConnectSrcHttpsHostAllowList[] = {"about:logging"_ns};
   1457 
   1458 class DisallowingVisitor : public nsCSPSrcVisitor {
   1459 public:
   1460  DisallowingVisitor(CSPDirective aDirective, nsACString& aURL)
   1461      : mDirective(aDirective), mURL(aURL) {}
   1462 
   1463  bool visit(const nsCSPPolicy* aPolicy) {
   1464    return aPolicy->visitDirectiveSrcs(mDirective, this);
   1465  }
   1466 
   1467  bool visitSchemeSrc(const nsCSPSchemeSrc& src) override {
   1468    Assert(src);
   1469    return false;
   1470  };
   1471 
   1472  bool visitHostSrc(const nsCSPHostSrc& src) override {
   1473    Assert(src);
   1474    return false;
   1475  };
   1476 
   1477  bool visitKeywordSrc(const nsCSPKeywordSrc& src) override {
   1478    // Using the 'none' keyword doesn't allow anything.
   1479    if (src.isKeyword(CSPKeyword::CSP_NONE)) {
   1480      return true;
   1481    }
   1482 
   1483    Assert(src);
   1484    return false;
   1485  }
   1486 
   1487  bool visitNonceSrc(const nsCSPNonceSrc& src) override {
   1488    Assert(src);
   1489    return false;
   1490  };
   1491 
   1492  bool visitHashSrc(const nsCSPHashSrc& src) override {
   1493    Assert(src);
   1494    return false;
   1495  };
   1496 
   1497 protected:
   1498  bool CheckAllowList(Span<nsLiteralCString> aList) {
   1499    for (const nsLiteralCString& entry : aList) {
   1500      // please note that we perform a substring match here on purpose,
   1501      // so we don't have to deal and parse out all the query arguments
   1502      // the various about pages rely on.
   1503      if (StringBeginsWith(mURL, entry)) {
   1504        return true;
   1505      }
   1506    }
   1507 
   1508    return false;
   1509  }
   1510 
   1511  void Assert(const nsCSPBaseSrc& aSrc) {
   1512    nsAutoString srcStr;
   1513    aSrc.toString(srcStr);
   1514    NS_ConvertUTF16toUTF8 srcStrUtf8(srcStr);
   1515 
   1516    MOZ_CRASH_UNSAFE_PRINTF(
   1517        "Page %s must not contain a CSP with the "
   1518        "directive %s that includes %s",
   1519        mURL.get(), CSP_CSPDirectiveToString(mDirective), srcStrUtf8.get());
   1520  }
   1521 
   1522  CSPDirective mDirective;
   1523  nsCString mURL;
   1524 };
   1525 
   1526 // Only allows loads from chrome:, moz-src: and resource: URLs:
   1527 class AllowBuiltinSrcVisitor : public DisallowingVisitor {
   1528 public:
   1529  AllowBuiltinSrcVisitor(CSPDirective aDirective, nsACString& aURL)
   1530      : DisallowingVisitor(aDirective, aURL) {}
   1531 
   1532  bool visitSchemeSrc(const nsCSPSchemeSrc& src) override {
   1533    nsAutoString scheme;
   1534    src.getScheme(scheme);
   1535    if (scheme == u"chrome"_ns || scheme == u"moz-src" ||
   1536        scheme == u"resource"_ns) {
   1537      return true;
   1538    }
   1539 
   1540    return DisallowingVisitor::visitSchemeSrc(src);
   1541  }
   1542 
   1543 protected:
   1544  bool VisitHostSrcWithWildcardAndHttpsHostAllowLists(
   1545      const nsCSPHostSrc& aSrc, const Span<nsLiteralCString> aWildcard,
   1546      const Span<nsLiteralCString> aHttpsHost) {
   1547    nsAutoString str;
   1548    aSrc.toString(str);
   1549 
   1550    if (str.EqualsLiteral("*")) {
   1551      if (CheckAllowList(aWildcard)) {
   1552        return true;
   1553      }
   1554    } else {
   1555      MOZ_ASSERT(StringBeginsWith(str, u"https://"_ns),
   1556                 "Must use https: for host sources!");
   1557      MOZ_ASSERT(!FindInReadable(u"*"_ns, str),
   1558                 "Can not include wildcard in host sources!");
   1559      if (CheckAllowList(aHttpsHost)) {
   1560        return true;
   1561      }
   1562    }
   1563 
   1564    return DisallowingVisitor::visitHostSrc(aSrc);
   1565  }
   1566 };
   1567 
   1568 class StyleSrcVisitor : public AllowBuiltinSrcVisitor {
   1569 public:
   1570  StyleSrcVisitor(CSPDirective aDirective, nsACString& aURL)
   1571      : AllowBuiltinSrcVisitor(aDirective, aURL) {
   1572    MOZ_ASSERT(aDirective == CSPDirective::STYLE_SRC_DIRECTIVE);
   1573  }
   1574 
   1575  bool visitSchemeSrc(const nsCSPSchemeSrc& src) override {
   1576    nsAutoString scheme;
   1577    src.getScheme(scheme);
   1578 
   1579    if (scheme == u"data"_ns) {
   1580      if (CheckAllowList(Span(sStyleSrcDataAllowList))) {
   1581        return true;
   1582      }
   1583    }
   1584 
   1585    return AllowBuiltinSrcVisitor::visitSchemeSrc(src);
   1586  }
   1587 
   1588  bool visitKeywordSrc(const nsCSPKeywordSrc& src) override {
   1589    if (src.isKeyword(CSPKeyword::CSP_UNSAFE_INLINE)) {
   1590      if (CheckAllowList(Span(sStyleSrcUnsafeInlineAllowList))) {
   1591        return true;
   1592      }
   1593    }
   1594 
   1595    return AllowBuiltinSrcVisitor::visitKeywordSrc(src);
   1596  }
   1597 };
   1598 
   1599 class ImgSrcVisitor : public AllowBuiltinSrcVisitor {
   1600 public:
   1601  ImgSrcVisitor(CSPDirective aDirective, nsACString& aURL)
   1602      : AllowBuiltinSrcVisitor(aDirective, aURL) {
   1603    MOZ_ASSERT(aDirective == CSPDirective::IMG_SRC_DIRECTIVE);
   1604  }
   1605 
   1606  bool visitSchemeSrc(const nsCSPSchemeSrc& src) override {
   1607    nsAutoString scheme;
   1608    src.getScheme(scheme);
   1609 
   1610    // moz-icon is used for loading known favicons.
   1611    if (scheme == u"moz-icon"_ns) {
   1612      return true;
   1613    }
   1614 
   1615    // moz-remote-image: safely re-encodes the image, but can still be used for
   1616    // arbitrary network requests.
   1617    if (scheme == u"moz-remote-image"_ns) {
   1618      if (CheckAllowList(sImgSrcMozRemoteImageAllowList)) {
   1619        return true;
   1620      }
   1621    }
   1622 
   1623    // data: and blob: can be used to decode arbitrary images.
   1624    if (scheme == u"data"_ns || scheme == u"blob") {
   1625      if (CheckAllowList(sImgSrcDataBlobAllowList)) {
   1626        return true;
   1627      }
   1628    }
   1629 
   1630    if (scheme == u"https"_ns) {
   1631      if (CheckAllowList(Span(sImgSrcHttpsAllowList))) {
   1632        return true;
   1633      }
   1634    }
   1635 
   1636    if (scheme == u"http"_ns) {
   1637      if (CheckAllowList(Span(sImgSrcHttpAllowList))) {
   1638        return true;
   1639      }
   1640    }
   1641 
   1642    if (scheme == u"jar"_ns || scheme == u"file"_ns) {
   1643      if (CheckAllowList(Span(sImgSrcAddonsAllowList))) {
   1644        return true;
   1645      }
   1646    }
   1647 
   1648    return AllowBuiltinSrcVisitor::visitSchemeSrc(src);
   1649  }
   1650 
   1651  bool visitHostSrc(const nsCSPHostSrc& src) override {
   1652    return VisitHostSrcWithWildcardAndHttpsHostAllowLists(
   1653        src, sImgSrcWildcardAllowList, sImgSrcHttpsHostAllowList);
   1654  }
   1655 };
   1656 
   1657 class MediaSrcVisitor : public AllowBuiltinSrcVisitor {
   1658 public:
   1659  MediaSrcVisitor(CSPDirective aDirective, nsACString& aURL)
   1660      : AllowBuiltinSrcVisitor(aDirective, aURL) {
   1661    MOZ_ASSERT(aDirective == CSPDirective::MEDIA_SRC_DIRECTIVE);
   1662  }
   1663 
   1664  bool visitHostSrc(const nsCSPHostSrc& src) override {
   1665    return VisitHostSrcWithWildcardAndHttpsHostAllowLists(
   1666        src, sMediaSrcWildcardAllowList, sMediaSrcHttpsHostAllowList);
   1667  }
   1668 };
   1669 
   1670 class ConnectSrcVisitor : public AllowBuiltinSrcVisitor {
   1671 public:
   1672  ConnectSrcVisitor(CSPDirective aDirective, nsACString& aURL)
   1673      : AllowBuiltinSrcVisitor(aDirective, aURL) {
   1674    MOZ_ASSERT(aDirective == CSPDirective::CONNECT_SRC_DIRECTIVE);
   1675  }
   1676 
   1677  bool visitSchemeSrc(const nsCSPSchemeSrc& src) override {
   1678    nsAutoString scheme;
   1679    src.getScheme(scheme);
   1680 
   1681    if (scheme == u"https"_ns) {
   1682      if (CheckAllowList(Span(sConnectSrcHttpsAllowList))) {
   1683        return true;
   1684      }
   1685    }
   1686 
   1687    if (scheme == u"data"_ns || scheme == u"http") {
   1688      if (CheckAllowList(Span(sConnectSrcAddonsAllowList))) {
   1689        return true;
   1690      }
   1691    }
   1692 
   1693    return AllowBuiltinSrcVisitor::visitSchemeSrc(src);
   1694  }
   1695 
   1696  bool visitHostSrc(const nsCSPHostSrc& src) override {
   1697    return VisitHostSrcWithWildcardAndHttpsHostAllowLists(
   1698        src, nullptr, sConnectSrcHttpsHostAllowList);
   1699  }
   1700 };
   1701 
   1702 class AddonSrcVisitor : public AllowBuiltinSrcVisitor {
   1703 public:
   1704  AddonSrcVisitor(CSPDirective aDirective, nsACString& aURL)
   1705      : AllowBuiltinSrcVisitor(aDirective, aURL) {
   1706    MOZ_ASSERT(aDirective == CSPDirective::DEFAULT_SRC_DIRECTIVE ||
   1707               aDirective == CSPDirective::SCRIPT_SRC_DIRECTIVE);
   1708  }
   1709 
   1710  bool visitHostSrc(const nsCSPHostSrc& src) override {
   1711    nsAutoString str;
   1712    src.toString(str);
   1713    if (str == u"'self'"_ns) {
   1714      return true;
   1715    }
   1716    return AllowBuiltinSrcVisitor::visitHostSrc(src);
   1717  }
   1718 
   1719  bool visitHashSrc(const nsCSPHashSrc& src) override {
   1720    if (mDirective == CSPDirective::SCRIPT_SRC_DIRECTIVE) {
   1721      return true;
   1722    }
   1723    return AllowBuiltinSrcVisitor::visitHashSrc(src);
   1724  }
   1725 };
   1726 
   1727 #  define CHECK_DIR(DIR, VISITOR)                                           \
   1728    do {                                                                    \
   1729      VISITOR visitor(CSPDirective::DIR, spec);                             \
   1730      /* We don't assert here, because we know that the default fallback is \
   1731       * secure. */                                                         \
   1732      visitor.visit(policy);                                                \
   1733    } while (false)
   1734 
   1735 /* static */
   1736 void nsContentSecurityUtils::AssertAboutPageHasCSP(Document* aDocument) {
   1737  // We want to get to a point where all about: pages ship with a CSP. This
   1738  // assertion ensures that we can not deploy new about: pages without a CSP.
   1739  // Please note that any about: page should not use inline JS or inline CSS,
   1740  // and instead should load JS and CSS from an external file (*.js, *.css)
   1741  // which allows us to apply a strong CSP omitting 'unsafe-inline'. Ideally,
   1742  // the CSP allows precisely the resources that need to be loaded; but it
   1743  // should at least be as strong as:
   1744  // <meta http-equiv="Content-Security-Policy" content="default-src chrome:;
   1745  // object-src 'none'"/>
   1746 
   1747  // This is a data document, created using DOMParser or
   1748  // document.implementation.createDocument() or such, not an about: page which
   1749  // is loaded as a web page.
   1750  if (aDocument->IsLoadedAsData()) {
   1751    return;
   1752  }
   1753 
   1754  // Check if we should skip the assertion
   1755  if (StaticPrefs::dom_security_skip_about_page_has_csp_assert()) {
   1756    return;
   1757  }
   1758 
   1759  // Check if we are loading an about: URI at all
   1760  nsCOMPtr<nsIURI> documentURI = aDocument->GetDocumentURI();
   1761  if (!documentURI->SchemeIs("about")) {
   1762    return;
   1763  }
   1764 
   1765  nsCSPContext* csp = nsCSPContext::Cast(
   1766      PolicyContainer::GetCSP(aDocument->GetPolicyContainer()));
   1767  bool foundDefaultSrc = false;
   1768  uint32_t policyCount = 0;
   1769  if (csp) {
   1770    csp->GetPolicyCount(&policyCount);
   1771    for (uint32_t i = 0; i < policyCount; i++) {
   1772      const nsCSPPolicy* policy = csp->GetPolicy(i);
   1773 
   1774      foundDefaultSrc =
   1775          policy->hasDirective(CSPDirective::DEFAULT_SRC_DIRECTIVE);
   1776      if (foundDefaultSrc) {
   1777        break;
   1778      }
   1779    }
   1780  }
   1781 
   1782  // Check if we should skip the allowlist and assert right away. Please note
   1783  // that this pref can and should only be set for automated testing.
   1784  if (StaticPrefs::dom_security_skip_about_page_csp_allowlist_and_assert()) {
   1785    NS_ASSERTION(foundDefaultSrc, "about: page must have a CSP");
   1786    return;
   1787  }
   1788 
   1789  nsAutoCString spec;
   1790  documentURI->GetSpec(spec);
   1791  ToLowerCase(spec);
   1792 
   1793  // This allowlist contains about: pages that are permanently allowed to
   1794  // render without a CSP applied.
   1795  static nsLiteralCString sAllowedAboutPagesWithNoCSP[] = {
   1796      // about:blank is a special about page -> no CSP
   1797      "about:blank"_ns,
   1798      // about:srcdoc is a special about page -> no CSP
   1799      "about:srcdoc"_ns,
   1800      // about:sync-log displays plain text only -> no CSP
   1801      "about:sync-log"_ns,
   1802      // about:logo just displays the firefox logo -> no CSP
   1803      "about:logo"_ns,
   1804      // about:sync is a special mozilla-signed developer addon with low usage
   1805      // ->
   1806      // no CSP
   1807      "about:sync"_ns,
   1808 #  if defined(ANDROID)
   1809      "about:config"_ns,
   1810 #  endif
   1811  };
   1812 
   1813  for (const nsLiteralCString& allowlistEntry : sAllowedAboutPagesWithNoCSP) {
   1814    // please note that we perform a substring match here on purpose,
   1815    // so we don't have to deal and parse out all the query arguments
   1816    // the various about pages rely on.
   1817    if (StringBeginsWith(spec, allowlistEntry)) {
   1818      return;
   1819    }
   1820  }
   1821 
   1822  if (aDocument->IsExtensionPage()) {
   1823    // Extensions have two CSP policies applied where the baseline CSP
   1824    // includes 'unsafe-eval' and 'unsafe-inline', hence we only
   1825    // make sure the second CSP is more restrictive.
   1826    //
   1827    // Extension CSPs look quite different to other pages, so for now we just
   1828    // assert some basic security properties.
   1829    MOZ_ASSERT(policyCount == 2,
   1830               "about: page from extension should have two CSP");
   1831    const nsCSPPolicy* policy = csp->GetPolicy(1);
   1832 
   1833    {
   1834      AddonSrcVisitor visitor(CSPDirective::DEFAULT_SRC_DIRECTIVE, spec);
   1835      if (!visitor.visit(policy)) {
   1836        MOZ_ASSERT(false, "about: page must contain a secure default-src");
   1837      }
   1838    }
   1839 
   1840    {
   1841      DisallowingVisitor visitor(CSPDirective::OBJECT_SRC_DIRECTIVE, spec);
   1842      if (!visitor.visit(policy)) {
   1843        MOZ_ASSERT(
   1844            false,
   1845            "about: page must contain a secure object-src 'none'; directive");
   1846      }
   1847    }
   1848 
   1849    CHECK_DIR(SCRIPT_SRC_DIRECTIVE, AddonSrcVisitor);
   1850 
   1851    nsTArray<nsString> directiveNames;
   1852    policy->getDirectiveNames(directiveNames);
   1853    for (nsString dir : directiveNames) {
   1854      MOZ_ASSERT(!dir.EqualsLiteral("script-src-elem") &&
   1855                 !dir.EqualsLiteral("script-src-attr"));
   1856    }
   1857 
   1858    return;
   1859  }
   1860 
   1861  MOZ_ASSERT(policyCount == 1, "about: page should have exactly one CSP");
   1862 
   1863  const nsCSPPolicy* policy = csp->GetPolicy(0);
   1864  {
   1865    AllowBuiltinSrcVisitor visitor(CSPDirective::DEFAULT_SRC_DIRECTIVE, spec);
   1866    if (!visitor.visit(policy)) {
   1867      MOZ_ASSERT(false, "about: page must contain a secure default-src");
   1868    }
   1869  }
   1870 
   1871  {
   1872    DisallowingVisitor visitor(CSPDirective::OBJECT_SRC_DIRECTIVE, spec);
   1873    if (!visitor.visit(policy)) {
   1874      MOZ_ASSERT(
   1875          false,
   1876          "about: page must contain a secure object-src 'none'; directive");
   1877    }
   1878  }
   1879 
   1880  CHECK_DIR(SCRIPT_SRC_DIRECTIVE, AllowBuiltinSrcVisitor);
   1881  CHECK_DIR(STYLE_SRC_DIRECTIVE, StyleSrcVisitor);
   1882  CHECK_DIR(IMG_SRC_DIRECTIVE, ImgSrcVisitor);
   1883  CHECK_DIR(MEDIA_SRC_DIRECTIVE, MediaSrcVisitor);
   1884  CHECK_DIR(CONNECT_SRC_DIRECTIVE, ConnectSrcVisitor);
   1885 
   1886  // Make sure we have a checker for all the directives that are being used.
   1887  nsTArray<nsString> directiveNames;
   1888  policy->getDirectiveNames(directiveNames);
   1889  for (nsString dir : directiveNames) {
   1890    if (dir.EqualsLiteral("default-src") || dir.EqualsLiteral("object-src") ||
   1891        dir.EqualsLiteral("script-src") || dir.EqualsLiteral("style-src") ||
   1892        dir.EqualsLiteral("img-src") || dir.EqualsLiteral("media-src") ||
   1893        dir.EqualsLiteral("connect-src")) {
   1894      continue;
   1895    }
   1896 
   1897    NS_WARNING(
   1898        nsPrintfCString(
   1899            "Page %s must not contain a CSP with the unchecked directive %s",
   1900            spec.get(), NS_ConvertUTF16toUTF8(dir).get())
   1901            .get());
   1902    MOZ_ASSERT(false, "Unchecked CSP directive found on internal page.");
   1903  }
   1904 }
   1905 
   1906 /* static */
   1907 void nsContentSecurityUtils::AssertChromePageHasCSP(Document* aDocument) {
   1908  nsCOMPtr<nsIURI> documentURI = aDocument->GetDocumentURI();
   1909  if (!documentURI->SchemeIs("chrome")) {
   1910    return;
   1911  }
   1912 
   1913  // We load a lot of SVG images from chrome:.
   1914  if (aDocument->IsBeingUsedAsImage() || aDocument->IsLoadedAsData()) {
   1915    return;
   1916  }
   1917 
   1918  nsAutoCString spec;
   1919  documentURI->GetSpec(spec);
   1920 
   1921  nsCOMPtr<nsIContentSecurityPolicy> csp =
   1922      PolicyContainer::GetCSP(aDocument->GetPolicyContainer());
   1923  uint32_t count = 0;
   1924  if (csp) {
   1925    static_cast<nsCSPContext*>(csp.get())->GetPolicyCount(&count);
   1926  }
   1927  if (count != 0) {
   1928    MOZ_ASSERT(count == 1, "chrome: pages should have exactly one CSP");
   1929 
   1930    // Both of these have a known weaker policy that differs
   1931    // from all other chrome: pages.
   1932    if (StringBeginsWith(spec, "chrome://browser/content/browser.xhtml"_ns) ||
   1933        StringBeginsWith(spec,
   1934                         "chrome://browser/content/hiddenWindowMac.xhtml"_ns)) {
   1935      return;
   1936    }
   1937 
   1938    // Thunderbird's CSP does not pass these checks.
   1939 #  ifndef MOZ_THUNDERBIRD
   1940    const nsCSPPolicy* policy =
   1941        static_cast<nsCSPContext*>(csp.get())->GetPolicy(0);
   1942    {
   1943      AllowBuiltinSrcVisitor visitor(CSPDirective::DEFAULT_SRC_DIRECTIVE, spec);
   1944      if (!visitor.visit(policy)) {
   1945        MOZ_CRASH_UNSAFE_PRINTF(
   1946            "Document (%s) CSP does not have a default-src!", spec.get());
   1947      }
   1948    }
   1949 
   1950    CHECK_DIR(SCRIPT_SRC_DIRECTIVE, AllowBuiltinSrcVisitor);
   1951    // If the policy being checked does not have an explicit |script-src-attr|
   1952    // directive, nsCSPPolicy::visitDirectiveSrcs will fallback to using the
   1953    // |script-src| directive, but not default-src.
   1954    // This means we can't use DisallowingVisitor here, because the script-src
   1955    // fallback will usually contain at least a chrome: source.
   1956    // This is not a problem from a security perspective, because inline scripts
   1957    // are not loaded from an URL and thus still disallowed.
   1958    CHECK_DIR(SCRIPT_SRC_ATTR_DIRECTIVE, AllowBuiltinSrcVisitor);
   1959    CHECK_DIR(STYLE_SRC_DIRECTIVE, StyleSrcVisitor);
   1960    CHECK_DIR(IMG_SRC_DIRECTIVE, ImgSrcVisitor);
   1961    CHECK_DIR(MEDIA_SRC_DIRECTIVE, MediaSrcVisitor);
   1962    // For now we don't require chrome: pages to have a `object-src 'none'`
   1963    // directive.
   1964    CHECK_DIR(OBJECT_SRC_DIRECTIVE, DisallowingVisitor);
   1965 
   1966    nsTArray<nsString> directiveNames;
   1967    policy->getDirectiveNames(directiveNames);
   1968    for (nsString dir : directiveNames) {
   1969      if (dir.EqualsLiteral("default-src") || dir.EqualsLiteral("script-src") ||
   1970          dir.EqualsLiteral("script-src-attr") ||
   1971          dir.EqualsLiteral("style-src") || dir.EqualsLiteral("img-src") ||
   1972          dir.EqualsLiteral("media-src") || dir.EqualsLiteral("object-src")) {
   1973        continue;
   1974      }
   1975 
   1976      MOZ_CRASH_UNSAFE_PRINTF(
   1977          "Document (%s) must not contain a CSP with the unchecked directive "
   1978          "%s",
   1979          spec.get(), NS_ConvertUTF16toUTF8(dir).get());
   1980    }
   1981 #  endif
   1982    return;
   1983  }
   1984 
   1985  if (xpc::IsInAutomation()) {
   1986    // Test files
   1987    static nsLiteralCString sAllowedTestPathsWithNoCSP[] = {
   1988        "chrome://mochikit/"_ns,
   1989        "chrome://mochitests/"_ns,
   1990        "chrome://pageloader/content/pageloader.xhtml"_ns,
   1991        "chrome://reftest/"_ns,
   1992    };
   1993 
   1994    for (const nsLiteralCString& entry : sAllowedTestPathsWithNoCSP) {
   1995      if (StringBeginsWith(spec, entry)) {
   1996        return;
   1997      }
   1998    }
   1999  }
   2000 
   2001  // CSP for browser.xhtml has been disabled
   2002  if (spec.EqualsLiteral("chrome://browser/content/browser.xhtml") &&
   2003      !StaticPrefs::security_browser_xhtml_csp_enabled()) {
   2004    return;
   2005  }
   2006 
   2007  MOZ_CRASH_UNSAFE_PRINTF("Document (%s) does not have a CSP!", spec.get());
   2008 }
   2009 
   2010 #  undef CHECK_DIR
   2011 
   2012 #endif
   2013 
   2014 // Add a lock for the gVeryFirstUnexpectedJavascriptLoadFilename variable
   2015 static StaticMutex gVeryFirstUnexpectedJavascriptLoadFilenameMutex;
   2016 static StaticAutoPtr<nsCString> gVeryFirstUnexpectedJavascriptLoadFilename
   2017    MOZ_GUARDED_BY(gVeryFirstUnexpectedJavascriptLoadFilenameMutex);
   2018 
   2019 /* static */
   2020 nsresult nsContentSecurityUtils::GetVeryFirstUnexpectedScriptFilename(
   2021    nsACString& aFilename) {
   2022  StaticMutexAutoLock lock(gVeryFirstUnexpectedJavascriptLoadFilenameMutex);
   2023  if (gVeryFirstUnexpectedJavascriptLoadFilename) {
   2024    aFilename = *gVeryFirstUnexpectedJavascriptLoadFilename;
   2025  }
   2026  return NS_OK;
   2027 }
   2028 
   2029 /* static */
   2030 bool nsContentSecurityUtils::ValidateScriptFilename(JSContext* cx,
   2031                                                    const char* aFilename) {
   2032  // If the pref is permissive, allow everything
   2033  if (StaticPrefs::security_allow_parent_unrestricted_js_loads()) {
   2034    return true;
   2035  }
   2036 
   2037  // If we're not in the parent process allow everything (presently)
   2038  if (!XRE_IsE10sParentProcess()) {
   2039    return true;
   2040  }
   2041 
   2042  // If we have allowed eval (because of a user configuration or more
   2043  // likely a test has requested it), and the script is an eval, allow it.
   2044  nsDependentCString filename(aFilename);
   2045  if (StaticPrefs::security_allow_eval_with_system_principal() ||
   2046      StaticPrefs::security_allow_eval_in_parent_process()) {
   2047    if (StringEndsWith(filename, "> eval"_ns)) {
   2048      return true;
   2049    }
   2050  }
   2051 
   2052  DetectJsHacks();
   2053 
   2054  if (!StaticPrefs::security_parent_unrestricted_js_loads_skip_jshacks() &&
   2055      MOZ_UNLIKELY(!sJSHacksChecked)) {
   2056    MOZ_LOG(
   2057        sCSMLog, LogLevel::Debug,
   2058        ("Allowing a javascript load of %s because "
   2059         "we have not yet been able to determine if JS hacks may be present",
   2060         aFilename));
   2061    return true;
   2062  }
   2063 
   2064  if (!StaticPrefs::security_parent_unrestricted_js_loads_skip_jshacks() &&
   2065      MOZ_UNLIKELY(sJSHacksPresent)) {
   2066    MOZ_LOG(sCSMLog, LogLevel::Debug,
   2067            ("Allowing a javascript load of %s because "
   2068             "some JS hacks may be present",
   2069             aFilename));
   2070    return true;
   2071  }
   2072 
   2073  if (XRE_IsE10sParentProcess() &&
   2074      !StaticPrefs::extensions_webextensions_remote()) {
   2075    MOZ_LOG(sCSMLog, LogLevel::Debug,
   2076            ("Allowing a javascript load of %s because the web extension "
   2077             "process is disabled.",
   2078             aFilename));
   2079    return true;
   2080  }
   2081 
   2082  if (StringBeginsWith(filename, "chrome://"_ns)) {
   2083    // If it's a chrome:// url, allow it
   2084    return true;
   2085  }
   2086  if (StringBeginsWith(filename, "resource://"_ns)) {
   2087    // If it's a resource:// url, allow it
   2088    return true;
   2089  }
   2090  if (StringBeginsWith(filename, "moz-src://"_ns)) {
   2091    // If it's a moz-src:// url, allow it
   2092    return true;
   2093  }
   2094  if (StringBeginsWith(filename, "file://"_ns)) {
   2095    // We will temporarily allow all file:// URIs through for now
   2096    return true;
   2097  }
   2098  if (StringBeginsWith(filename, "jar:file://"_ns)) {
   2099    // We will temporarily allow all jar URIs through for now
   2100    return true;
   2101  }
   2102  if (filename.Equals("about:sync-log"_ns)) {
   2103    // about:sync-log runs in the parent process and displays a directory
   2104    // listing. The listing has inline javascript that executes on load.
   2105    return true;
   2106  }
   2107 
   2108  if (StringBeginsWith(filename, "moz-extension://"_ns)) {
   2109    nsCOMPtr<nsIURI> uri;
   2110    nsresult rv = NS_NewURI(getter_AddRefs(uri), aFilename);
   2111    if (!NS_FAILED(rv) && NS_IsMainThread()) {
   2112      mozilla::extensions::URLInfo url(uri);
   2113      auto* policy =
   2114          ExtensionPolicyService::GetSingleton().GetByHost(url.Host());
   2115 
   2116      if (policy && policy->IsPrivileged()) {
   2117        MOZ_LOG(sCSMLog, LogLevel::Debug,
   2118                ("Allowing a javascript load of %s because the web extension "
   2119                 "it is associated with is privileged.",
   2120                 aFilename));
   2121        return true;
   2122      }
   2123    }
   2124  } else if (!NS_IsMainThread()) {
   2125    WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
   2126    if (workerPrivate && workerPrivate->IsPrivilegedAddonGlobal()) {
   2127      MOZ_LOG(sCSMLog, LogLevel::Debug,
   2128              ("Allowing a javascript load of %s because the web extension "
   2129               "it is associated with is privileged.",
   2130               aFilename));
   2131      return true;
   2132    }
   2133  }
   2134 
   2135  auto kAllowedFilenamesPrefix = {
   2136      // Until 371900 is fixed, we need to do something about about:downloads
   2137      // and this is the most reasonable. See 1727770
   2138      "about:downloads"_ns,
   2139      // We think this is the same problem as about:downloads
   2140      "about:preferences"_ns, "about:settings"_ns,
   2141      // Browser console will give a filename of 'debugger' See 1763943
   2142      // Sometimes it's 'debugger eager eval code', other times just 'debugger
   2143      // eval code'
   2144      "debugger"_ns};
   2145 
   2146  for (auto allowedFilenamePrefix : kAllowedFilenamesPrefix) {
   2147    if (StringBeginsWith(filename, allowedFilenamePrefix)) {
   2148      return true;
   2149    }
   2150  }
   2151 
   2152  FilenameTypeAndDetails fileNameTypeAndDetails =
   2153      FilenameToFilenameType(filename, true);
   2154  glean::security::JavascriptLoadParentProcessExtra extra = {
   2155      .fileinfo = fileNameTypeAndDetails.second,
   2156      .value = Some(fileNameTypeAndDetails.first)};
   2157 
   2158  if (StaticPrefs::security_block_parent_unrestricted_js_loads_temporary()) {
   2159    // Log to MOZ_LOG
   2160    MOZ_LOG(sCSMLog, LogLevel::Error,
   2161            ("ValidateScriptFilename Failed, But Blocking: %s\n", aFilename));
   2162 
   2163    extra.blocked = Some(true);
   2164    glean::security::javascript_load_parent_process.Record(Some(extra));
   2165 
   2166    return false;
   2167  }
   2168  MOZ_LOG(sCSMLog, LogLevel::Error,
   2169          ("ValidateScriptFilename Failed: %s\n", aFilename));
   2170 
   2171  glean::security::javascript_load_parent_process.Record(Some(extra));
   2172 
   2173 #if defined(DEBUG) || defined(FUZZING)
   2174  auto crashString = nsContentSecurityUtils::SmartFormatCrashString(
   2175      aFilename,
   2176      fileNameTypeAndDetails.second.isSome()
   2177          ? fileNameTypeAndDetails.second.value().get()
   2178          : "(None)",
   2179      "Blocking a script load %s from file %s");
   2180  MOZ_CRASH_UNSAFE_PRINTF("%s", crashString.get());
   2181 #elif defined(EARLY_BETA_OR_EARLIER)
   2182  // Cause a crash (if we've never crashed before and we can ensure we won't do
   2183  // it again.)
   2184  // The details in the second arg, passed to UNSAFE_PRINTF, are also included
   2185  // in Event Telemetry and have received data review.
   2186  if (fileNameTypeAndDetails.second.isSome()) {
   2187    PossiblyCrash("js_load_1", aFilename,
   2188                  fileNameTypeAndDetails.second.value());
   2189  } else {
   2190    PossiblyCrash("js_load_1", aFilename, "(None)"_ns);
   2191  }
   2192 #endif
   2193 
   2194  {
   2195    StaticMutexAutoLock lock(gVeryFirstUnexpectedJavascriptLoadFilenameMutex);
   2196    if (gVeryFirstUnexpectedJavascriptLoadFilename == nullptr) {
   2197      gVeryFirstUnexpectedJavascriptLoadFilename = new nsCString(aFilename);
   2198    }
   2199  }
   2200 
   2201  if (NS_IsMainThread()) {
   2202    nsCOMPtr<nsIObserverService> observerService =
   2203        mozilla::services::GetObserverService();
   2204    if (observerService) {
   2205      observerService->NotifyObservers(nullptr, "UnexpectedJavaScriptLoad-Live",
   2206                                       NS_ConvertUTF8toUTF16(filename).get());
   2207    }
   2208  } else {
   2209    NS_DispatchToMainThread(
   2210        NS_NewRunnableFunction("NotifyObserversRunnable", [filename]() {
   2211          nsCOMPtr<nsIObserverService> observerService =
   2212              mozilla::services::GetObserverService();
   2213          if (observerService) {
   2214            observerService->NotifyObservers(
   2215                nullptr, "UnexpectedJavaScriptLoad-Live",
   2216                NS_ConvertUTF8toUTF16(filename).get());
   2217          }
   2218        }));
   2219  }
   2220 
   2221  // Presently we are only enforcing restrictions for the script filename
   2222  // on Nightly.  On all channels we are reporting Telemetry. In the future we
   2223  // will assert in debug builds and return false to prevent execution in
   2224  // non-debug builds.
   2225 #ifdef NIGHTLY_BUILD
   2226  return false;
   2227 #else
   2228  return true;
   2229 #endif
   2230 }
   2231 
   2232 /* static */
   2233 void nsContentSecurityUtils::LogMessageToConsole(nsIHttpChannel* aChannel,
   2234                                                 const char* aMsg) {
   2235  nsCOMPtr<nsIURI> uri;
   2236  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
   2237  if (NS_FAILED(rv)) {
   2238    return;
   2239  }
   2240 
   2241  uint64_t windowID = 0;
   2242  rv = aChannel->GetTopLevelContentWindowId(&windowID);
   2243  if (NS_WARN_IF(NS_FAILED(rv))) {
   2244    return;
   2245  }
   2246  if (!windowID) {
   2247    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   2248    loadInfo->GetInnerWindowID(&windowID);
   2249  }
   2250 
   2251  nsAutoString localizedMsg;
   2252  nsAutoCString spec;
   2253  uri->GetSpec(spec);
   2254  AutoTArray<nsString, 1> params = {NS_ConvertUTF8toUTF16(spec)};
   2255  rv = nsContentUtils::FormatLocalizedString(
   2256      nsContentUtils::eSECURITY_PROPERTIES, aMsg, params, localizedMsg);
   2257  if (NS_WARN_IF(NS_FAILED(rv))) {
   2258    return;
   2259  }
   2260 
   2261  nsContentUtils::ReportToConsoleByWindowID(
   2262      localizedMsg, nsIScriptError::warningFlag, "Security"_ns, windowID,
   2263      SourceLocation{uri.get()});
   2264 }
   2265 
   2266 /* static */
   2267 long nsContentSecurityUtils::ClassifyDownload(nsIChannel* aChannel) {
   2268  MOZ_ASSERT(aChannel, "IsDownloadAllowed without channel?");
   2269 
   2270  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   2271  if ((loadInfo->GetTriggeringSandboxFlags() & SANDBOXED_DOWNLOADS) ||
   2272      (loadInfo->GetSandboxFlags() & SANDBOXED_DOWNLOADS)) {
   2273    if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
   2274      LogMessageToConsole(httpChannel, "IframeSandboxBlockedDownload");
   2275    }
   2276    return nsITransfer::DOWNLOAD_FORBIDDEN;
   2277  }
   2278 
   2279  nsCOMPtr<nsIURI> contentLocation;
   2280  aChannel->GetURI(getter_AddRefs(contentLocation));
   2281 
   2282  nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal();
   2283  if (!loadingPrincipal) {
   2284    loadingPrincipal = loadInfo->TriggeringPrincipal();
   2285  }
   2286  // Creating a fake Loadinfo that is just used for the MCB check.
   2287  Result<RefPtr<net::LoadInfo>, nsresult> maybeLoadInfo = net::LoadInfo::Create(
   2288      loadingPrincipal, loadInfo->TriggeringPrincipal(), nullptr,
   2289      nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
   2290      nsIContentPolicy::TYPE_FETCH);
   2291  if (maybeLoadInfo.isErr()) {
   2292    return nsITransfer::DOWNLOAD_FORBIDDEN;
   2293  }
   2294  RefPtr<net::LoadInfo> secCheckLoadInfo = maybeLoadInfo.unwrap();
   2295  // Disable HTTPS-Only checks for that loadinfo. This is required because
   2296  // otherwise nsMixedContentBlocker::ShouldLoad would assume that the request
   2297  // is safe, because HTTPS-Only is handling it.
   2298  secCheckLoadInfo->SetHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_EXEMPT);
   2299 
   2300  int16_t decission = nsIContentPolicy::ACCEPT;
   2301  nsMixedContentBlocker::ShouldLoad(false,  //  aHadInsecureImageRedirect
   2302                                    contentLocation,   //  aContentLocation,
   2303                                    secCheckLoadInfo,  //  aLoadinfo
   2304                                    false,             //  aReportError
   2305                                    &decission         // aDecision
   2306  );
   2307 
   2308  if (StaticPrefs::dom_block_download_insecure() &&
   2309      decission != nsIContentPolicy::ACCEPT) {
   2310    if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel)) {
   2311      LogMessageToConsole(httpChannel, "MixedContentBlockedDownload");
   2312    }
   2313    return nsITransfer::DOWNLOAD_POTENTIALLY_UNSAFE;
   2314  }
   2315 
   2316  return nsITransfer::DOWNLOAD_ACCEPTABLE;
   2317 }