tor-browser

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

interface_exposure_checker.js (5281B)


      1 function entryDisabled(
      2  entry,
      3  {
      4    isNightly,
      5    isEarlyBetaOrEarlier,
      6    isRelease,
      7    isDesktop,
      8    isWindows,
      9    isMac,
     10    isLinux,
     11    isAndroid,
     12    isAarch64,
     13    isInsecureContext,
     14    isFennec,
     15    isCrossOriginIsolated,
     16    isSessionHistoryInParent,
     17  }
     18 ) {
     19  return (
     20    entry.nightly === !isNightly ||
     21    (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
     22    entry.desktop === !isDesktop ||
     23    entry.windows === !isWindows ||
     24    entry.mac === !isMac ||
     25    entry.linux === !isLinux ||
     26    (entry.android === !isAndroid && !entry.nightlyAndroid) ||
     27    entry.aarch64 === !isAarch64 ||
     28    entry.fennecOrDesktop === (isAndroid && !isFennec) ||
     29    entry.fennec === !isFennec ||
     30    entry.release === !isRelease ||
     31    // The insecureContext test is very purposefully converting
     32    // entry.insecureContext to boolean, so undefined will convert to
     33    // false.  That way entries without an insecureContext annotation
     34    // will get treated as "insecureContext: false", which means exposed
     35    // only in secure contexts.
     36    (isInsecureContext && !entry.insecureContext) ||
     37    entry.earlyBetaOrEarlier === !isEarlyBetaOrEarlier ||
     38    entry.crossOriginIsolated === !isCrossOriginIsolated ||
     39    entry.sessionHistoryInParent === !isSessionHistoryInParent ||
     40    entry.disabled
     41  );
     42 }
     43 
     44 function createInterfaceMap(data, interfaceGroups) {
     45  var interfaceMap = {};
     46 
     47  /** @param {any[]} interfaceGroup */
     48  function checkSorted(interfaceGroup) {
     49    /** @type {(entry) => string} */
     50    let getName = entry => (typeof entry === "string" ? entry : entry.name);
     51 
     52    // slice(1) to start from index 1 (index 0 has nothing to compare with)
     53    for (let [index, entry] of interfaceGroup.slice(1).entries()) {
     54      let x = getName(interfaceGroup[index]);
     55      let y = getName(entry);
     56      ok(
     57        x <= y,
     58        `The interface group is not sorted! ${y} must come before ${x}!`
     59      );
     60    }
     61  }
     62 
     63  function addInterfaces(interfaces) {
     64    for (var entry of interfaces) {
     65      if (typeof entry === "string") {
     66        interfaceMap[entry] = !data.isInsecureContext;
     67      } else {
     68        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
     69        interfaceMap[entry.name] ||= !entryDisabled(entry, data);
     70      }
     71    }
     72  }
     73 
     74  for (let interfaceGroup of interfaceGroups) {
     75    checkSorted(interfaceGroup);
     76    addInterfaces(interfaceGroup);
     77  }
     78 
     79  return interfaceMap;
     80 }
     81 
     82 function runTest(
     83  parentName,
     84  parent,
     85  { data, interfaceGroups, testFunctions = [] }
     86 ) {
     87  var interfaceMap = createInterfaceMap(data, interfaceGroups);
     88  for (var name of Object.getOwnPropertyNames(parent)) {
     89    // Ignore functions on the global that are part of the test (harness).
     90    if (parent === self && testFunctions.includes(name)) {
     91      continue;
     92    }
     93    ok(
     94      interfaceMap[name],
     95      "If this is failing: DANGER, are you sure you want to expose the new interface " +
     96        name +
     97        " to all webpages as a property on '" +
     98        parentName +
     99        "'? Do not make a change to this file without a " +
    100        " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)"
    101    );
    102 
    103    ok(
    104      name in parent,
    105      `${name} is exposed as an own property on '${parentName}' but tests false for "in" in the global scope`
    106    );
    107    ok(
    108      Object.getOwnPropertyDescriptor(parent, name),
    109      `${name} is exposed as an own property on '${parentName}' but has no property descriptor in the global scope`
    110    );
    111 
    112    delete interfaceMap[name];
    113  }
    114  for (var name of Object.keys(interfaceMap)) {
    115    const not = interfaceMap[name] ? "" : " NOT";
    116    ok(
    117      name in parent === interfaceMap[name],
    118      `${name} should${not} be defined on ${parentName}`
    119    );
    120    if (!interfaceMap[name]) {
    121      delete interfaceMap[name];
    122    }
    123  }
    124  is(
    125    Object.keys(interfaceMap).length,
    126    0,
    127    "The following interface(s) are not enumerated: " +
    128      Object.keys(interfaceMap).join(", ")
    129  );
    130 }
    131 
    132 if (typeof window !== "undefined") {
    133  window.getHelperData = () => {
    134    const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
    135      "resource://gre/modules/AppConstants.sys.mjs"
    136    );
    137    const sysinfo = SpecialPowers.Services.sysinfo;
    138    const appinfo = SpecialPowers.Services.appinfo;
    139 
    140    return {
    141      isNightly: AppConstants.NIGHTLY_BUILD,
    142      isEarlyBetaOrEarlier: AppConstants.EARLY_BETA_OR_EARLIER,
    143      isRelease: AppConstants.RELEASE_OR_BETA,
    144      isDesktop: !/Mobile|Tablet/.test(navigator.userAgent),
    145      isMac: AppConstants.platform == "macosx",
    146      isWindows: AppConstants.platform == "win",
    147      isAndroid: AppConstants.platform == "android",
    148      isLinux: AppConstants.platform == "linux",
    149      isAarch64: sysinfo.get("arch") === "aarch64",
    150      isInsecureContext: !window.isSecureContext,
    151      // Currently, MOZ_APP_NAME is always "fennec" for all mobile builds, so we can't use AppConstants for this
    152      isFennec:
    153        AppConstants.platform == "android" &&
    154        SpecialPowers.Cc["@mozilla.org/android/bridge;1"].getService(
    155          SpecialPowers.Ci.nsIGeckoViewBridge
    156        ).isFennec,
    157      isCrossOriginIsolated: window.crossOriginIsolated,
    158      isSessionHistoryInParent: appinfo.sessionHistoryInParent,
    159    };
    160  };
    161 }