bug 1341734 - remove the disableSHA1rollout addon r?jcj,felipe draft
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 22 Feb 2017 13:37:12 -0800
changeset 488246 16196c566a11a286ea5fe3ce5f736c1882117223
parent 488125 7abeac2f2d668554f0093fc0bdb1488f9a77d16e
child 546686 058f2d16427e3bec6ea51f0c14524d261ef837f2
push id46475
push userdkeeler@mozilla.com
push dateWed, 22 Feb 2017 21:49:07 +0000
reviewersjcj, felipe
bugs1341734
milestone54.0a1
bug 1341734 - remove the disableSHA1rollout addon r?jcj,felipe MozReview-Commit-ID: 5PUT6csFK5H
browser/extensions/disableSHA1rollout/README.md
browser/extensions/disableSHA1rollout/bootstrap.js
browser/extensions/disableSHA1rollout/install.rdf.in
browser/extensions/disableSHA1rollout/moz.build
browser/extensions/moz.build
testing/talos/talos/xtalos/xperf_whitelist.json
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/README.md
+++ /dev/null
@@ -1,99 +0,0 @@
-This system add-on is a follow-up to the MITM prevalence experiment. The purpose
-is to facilitate rolling out the disabling of SHA-1 in signatures on
-certificates issued by publicly-trusted roots. When installed, this add-on will
-perform a number of checks to determine if it should change the preference that
-controls the SHA-1 policy. First, this should only apply to users on the beta
-update channel. It should also only apply to users who have not otherwise
-changed the policy to always allow or always forbid SHA-1. Additionally, it
-must double-check that the user is not affected by a TLS intercepting proxy
-using a publicly-trusted root. If these checks pass, the add-on will divide the
-population into a test group and a control group (starting on a 10%/90% split).
-The test group will have the policy changed. After doing this, a telemetry
-payload is reported with the following values:
-
-* cohortName -- the name of the group this user is in:
-  1. "notSafeToDisableSHA1" if the user is behind a MITM proxy using a
-     publicly-trusted root
-  2. "optedOut" if the user already set the SHA-1 policy to always allow or
-     always forbid
-  3. "optedIn" if the user already set the SHA-1 policy to only allow for
-     non-built-in roots
-  4. "test" if the user is in the test cohort (and SHA-1 will be disabled)
-  5. "control" if the user is not in the test cohort
-* errorCode -- 0 for successful connections, some PR error code otherwise
-* error -- a short description of one of four error conditions encountered, if
-  applicable, and an empty string otherwise:
-  1. "timeout" if the connection to telemetry.mozilla.org timed out
-  2. "user override" if the user has stored a permanent certificate exception
-     override for telemetry.mozilla.org (due to technical limitations, we can't
-     gather much information in this situation)
-  3. "certificate reverification" if re-building the certificate chain after
-     connecting failed for some reason (unfortunately this step is necessary
-     due to technical limitations)
-  4. "connection error" if the connection to telemetry.mozilla.org failed for
-     another reason
-* chain -- a list of dictionaries each corresponding to a certificate in the
-  verified certificate chain, if it was successfully constructed. The first
-  entry is the end-entity certificate. The last entry is the root certificate.
-  This will be empty if the connection failed or if reverification failed. Each
-  element in the list contains the following values:
-  * sha256Fingerprint -- a hex string representing the SHA-256 hash of the
-    certificate
-  * isBuiltInRoot -- true if the certificate is a trust anchor in the web PKI,
-    false otherwise
-  * signatureAlgorithm -- a description of the algorithm used to sign the
-    certificate. Will be one of "md2WithRSAEncryption", "md5WithRSAEncryption",
-    "sha1WithRSAEncryption", "sha256WithRSAEncryption",
-    "sha384WithRSAEncryption", "sha512WithRSAEncryption", "ecdsaWithSHA1",
-    "ecdsaWithSHA224", "ecdsaWithSHA256", "ecdsaWithSHA384", "ecdsaWithSHA512",
-    or "unknown".
-* disabledSHA1 -- true if SHA-1 was disabled, false otherwise
-* didNotDisableSHA1Because -- a short string describing why SHA-1 could not be
-    disabled, if applicable. Reasons are limited to:
-    1. "MITM" if the user is behind a TLS intercepting proxy using a
-       publicly-trusted root
-    2. "connection error" if there was an error connecting to
-       telemetry.mozilla.org
-    3. "code error" if some inconsistent state was detected, and it was
-       determined that the experiment should not attempt to change the
-       preference
-    4. "preference:userReset" if the user reset the SHA-1 policy after it had
-       been changed by this add-on
-    5. "preference:allow" if the user had already configured Firefox to always
-       accept SHA-1 signatures
-    6. "preference:forbid" if the user had already configured Firefox to always
-       forbid SHA-1 signatures
-
-For a connection not intercepted by a TLS proxy and where the user is in the
-test cohort, the expected result will be:
-
-    { "cohortName": "test",
-      "errorCode": 0,
-      "error": "",
-      "chain": [
-        { "sha256Fingerprint": "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c",
-          "isBuiltInRoot": false,
-          "signatureAlgorithm": "sha256WithRSAEncryption"
-        },
-        { "sha256Fingerprint": "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
-          "isBuiltInRoot": false,
-          "signatureAlgorithm": "sha256WithRSAEncryption"
-        },
-        { "sha256Fingerprint": "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161",
-          "isBuiltInRoot": true,
-          "signatureAlgorithm": "sha1WithRSAEncryption"
-        }
-      ],
-      "disabledSHA1": true,
-      "didNotDisableSHA1Because": ""
-    }
-
-When this result is encountered, the user's preferences are updated to disable
-SHA-1 in signatures on certificates issued by publicly-trusted roots.
-Similarly, if the user is behind a TLS intercepting proxy but the root
-certificate is not part of the public web PKI, we can also disable SHA-1 in
-signatures on certificates issued by publicly-trusted roots.
-
-If the user has already indicated in their preferences that they will always
-accept SHA-1 in signatures or that they will never accept SHA-1 in signatures,
-then the preference is not changed.
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/bootstrap.js
+++ /dev/null
@@ -1,306 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
-Cu.import("resource://gre/modules/TelemetryController.jsm");
-
- // Percentage of the population to attempt to disable SHA-1 for, by channel.
-const TEST_THRESHOLD = {
-  beta: 0.1, // 10%
-};
-
-const PREF_COHORT_SAMPLE = "disableSHA1.rollout.cohortSample";
-const PREF_COHORT_NAME = "disableSHA1.rollout.cohort";
-const PREF_SHA1_POLICY = "security.pki.sha1_enforcement_level";
-const PREF_SHA1_POLICY_SET_BY_ADDON = "disableSHA1.rollout.policySetByAddOn";
-const PREF_SHA1_POLICY_RESET_BY_USER = "disableSHA1.rollout.userResetPref";
-
-const SHA1_MODE_ALLOW = 0;
-const SHA1_MODE_FORBID = 1;
-const SHA1_MODE_IMPORTED_ROOTS_ONLY = 3;
-const SHA1_MODE_CURRENT_DEFAULT = 4;
-
-function startup() {
-  Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged);
-}
-
-function install() {
-  let updateChannel = UpdateUtils.getUpdateChannel(false);
-  if (updateChannel in TEST_THRESHOLD) {
-    makeRequest().then(defineCohort).catch((e) => console.error(e));
-  }
-}
-
-function policyPreferenceChanged() {
-  let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                         SHA1_MODE_CURRENT_DEFAULT);
-  Preferences.reset(PREF_SHA1_POLICY_RESET_BY_USER);
-  if (currentPrefValue == SHA1_MODE_CURRENT_DEFAULT) {
-    Preferences.set(PREF_SHA1_POLICY_RESET_BY_USER, true);
-  }
-}
-
-function defineCohort(result) {
-  let userOptedOut = optedOut();
-  let userOptedIn = optedIn();
-  let shouldNotDisableSHA1Because = reasonToNotDisableSHA1(result);
-  let safeToDisableSHA1 = shouldNotDisableSHA1Because.length == 0;
-  let updateChannel = UpdateUtils.getUpdateChannel(false);
-  let testGroup = getUserSample() < TEST_THRESHOLD[updateChannel];
-
-  let cohortName;
-  if (!safeToDisableSHA1) {
-    cohortName = "notSafeToDisableSHA1";
-  } else if (userOptedOut) {
-    cohortName = "optedOut";
-  } else if (userOptedIn) {
-    cohortName = "optedIn";
-  } else if (testGroup) {
-    cohortName = "test";
-    Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged);
-    Preferences.set(PREF_SHA1_POLICY, SHA1_MODE_IMPORTED_ROOTS_ONLY);
-    Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged);
-    Preferences.set(PREF_SHA1_POLICY_SET_BY_ADDON, true);
-  } else {
-    cohortName = "control";
-  }
-  Preferences.set(PREF_COHORT_NAME, cohortName);
-  reportTelemetry(result, cohortName, shouldNotDisableSHA1Because,
-                  cohortName == "test");
-}
-
-function shutdown(data, reason) {
-  Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged);
-}
-
-function uninstall() {
-}
-
-function getUserSample() {
-  let prefValue = Preferences.get(PREF_COHORT_SAMPLE, undefined);
-  let value = 0.0;
-
-  if (typeof(prefValue) == "string") {
-    value = parseFloat(prefValue, 10);
-    return value;
-  }
-
-  value = Math.random();
-
-  Preferences.set(PREF_COHORT_SAMPLE, value.toString().substr(0, 8));
-  return value;
-}
-
-function reportTelemetry(result, cohortName, didNotDisableSHA1Because,
-                         disabledSHA1) {
-  result.cohortName = cohortName;
-  result.disabledSHA1 = disabledSHA1;
-  if (cohortName == "optedOut") {
-    let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false);
-    let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                           SHA1_MODE_CURRENT_DEFAULT);
-    if (userResetPref) {
-      didNotDisableSHA1Because = "preference:userReset";
-    } else if (currentPrefValue == SHA1_MODE_ALLOW) {
-      didNotDisableSHA1Because = "preference:allow";
-    } else {
-      didNotDisableSHA1Because = "preference:forbid";
-    }
-  }
-  result.didNotDisableSHA1Because = didNotDisableSHA1Because;
-  return TelemetryController.submitExternalPing("disableSHA1rollout", result,
-                                                {});
-}
-
-function optedIn() {
-  let policySetByAddOn = Preferences.get(PREF_SHA1_POLICY_SET_BY_ADDON, false);
-  let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                         SHA1_MODE_CURRENT_DEFAULT);
-  return currentPrefValue == SHA1_MODE_IMPORTED_ROOTS_ONLY && !policySetByAddOn;
-}
-
-function optedOut() {
-  // Users can also opt-out by setting the policy to always allow or always
-  // forbid SHA-1, or by resetting the preference after this add-on has changed
-  // it (in that case, this will be reported the next time this add-on is
-  // updated).
-  let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                         SHA1_MODE_CURRENT_DEFAULT);
-  let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false);
-  return currentPrefValue == SHA1_MODE_ALLOW ||
-         currentPrefValue == SHA1_MODE_FORBID ||
-         userResetPref;
-}
-
-function delocalizeAlgorithm(localizedString) {
-  let bundle = Services.strings.createBundle(
-    "chrome://pipnss/locale/pipnss.properties");
-  let algorithmStringIdsToOIDDescriptionMap = {
-    "CertDumpMD2WithRSA":                       "md2WithRSAEncryption",
-    "CertDumpMD5WithRSA":                       "md5WithRSAEncryption",
-    "CertDumpSHA1WithRSA":                      "sha1WithRSAEncryption",
-    "CertDumpSHA256WithRSA":                    "sha256WithRSAEncryption",
-    "CertDumpSHA384WithRSA":                    "sha384WithRSAEncryption",
-    "CertDumpSHA512WithRSA":                    "sha512WithRSAEncryption",
-    "CertDumpAnsiX962ECDsaSignatureWithSha1":   "ecdsaWithSHA1",
-    "CertDumpAnsiX962ECDsaSignatureWithSha224": "ecdsaWithSHA224",
-    "CertDumpAnsiX962ECDsaSignatureWithSha256": "ecdsaWithSHA256",
-    "CertDumpAnsiX962ECDsaSignatureWithSha384": "ecdsaWithSHA384",
-    "CertDumpAnsiX962ECDsaSignatureWithSha512": "ecdsaWithSHA512",
-  };
-
-  let description;
-  Object.keys(algorithmStringIdsToOIDDescriptionMap).forEach((l10nID) => {
-    let candidateLocalizedString = bundle.GetStringFromName(l10nID);
-    if (localizedString == candidateLocalizedString) {
-      description = algorithmStringIdsToOIDDescriptionMap[l10nID];
-    }
-  });
-  if (!description) {
-    return "unknown";
-  }
-  return description;
-}
-
-function getSignatureAlgorithm(cert) {
-  // Certificate  ::=  SEQUENCE  {
-  //      tbsCertificate       TBSCertificate,
-  //      signatureAlgorithm   AlgorithmIdentifier,
-  //      signatureValue       BIT STRING  }
-  let certificate = cert.ASN1Structure.QueryInterface(Ci.nsIASN1Sequence);
-  let signatureAlgorithm = certificate.ASN1Objects
-                                      .queryElementAt(1, Ci.nsIASN1Sequence);
-  // AlgorithmIdentifier  ::=  SEQUENCE  {
-  //      algorithm               OBJECT IDENTIFIER,
-  //      parameters              ANY DEFINED BY algorithm OPTIONAL  }
-
-  // If parameters is NULL (or empty), signatureAlgorithm won't be a container
-  // under this implementation. Just get its displayValue.
-  if (!signatureAlgorithm.isValidContainer) {
-    return signatureAlgorithm.displayValue;
-  }
-  let oid = signatureAlgorithm.ASN1Objects.queryElementAt(0, Ci.nsIASN1Object);
-  return oid.displayValue;
-}
-
-function processCertChain(chain) {
-  let output = [];
-  let enumerator = chain.getEnumerator();
-  while (enumerator.hasMoreElements()) {
-    let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
-    output.push({
-      sha256Fingerprint: cert.sha256Fingerprint.replace(/:/g, "").toLowerCase(),
-      isBuiltInRoot: cert.isBuiltInRoot,
-      signatureAlgorithm: delocalizeAlgorithm(getSignatureAlgorithm(cert)),
-    });
-  }
-  return output;
-}
-
-class CertificateVerificationResult {
-  constructor(resolve) {
-    this.resolve = resolve;
-  }
-
-  verifyCertFinished(aPRErrorCode, aVerifiedChain, aEVStatus) {
-    let result = { errorCode: aPRErrorCode, error: "", chain: [] };
-    if (aPRErrorCode == 0) {
-      result.chain = processCertChain(aVerifiedChain);
-    } else {
-      result.error = "certificate reverification";
-    }
-    this.resolve(result);
-  }
-}
-
-function makeRequest() {
-  return new Promise((resolve) => {
-    let hostname = "telemetry.mozilla.org";
-    let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
-                .createInstance(Ci.nsIXMLHttpRequest);
-    req.open("GET", "https://" + hostname);
-    req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
-    req.timeout = 30000;
-    req.addEventListener("error", (evt) => {
-      // If we can't connect to telemetry.mozilla.org, then how did we even
-      // download the experiment? In any case, we may still be able to get some
-      // information.
-      let result = { error: "connection error" };
-      if (evt.target.channel && evt.target.channel.securityInfo) {
-        let securityInfo = evt.target.channel.securityInfo
-                             .QueryInterface(Ci.nsITransportSecurityInfo);
-        if (securityInfo) {
-          result.errorCode = securityInfo.errorCode;
-        }
-        if (securityInfo && securityInfo.failedCertChain) {
-          result.chain = processCertChain(securityInfo.failedCertChain);
-        }
-      }
-      resolve(result);
-    });
-    req.addEventListener("timeout", (evt) => {
-      resolve({ error: "timeout" });
-    });
-    req.addEventListener("load", (evt) => {
-      let securityInfo = evt.target.channel.securityInfo
-                           .QueryInterface(Ci.nsITransportSecurityInfo);
-      if (securityInfo.securityState &
-          Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN) {
-        resolve({ error: "user override" });
-        return;
-      }
-      let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                        .SSLStatus;
-      let certdb = Cc["@mozilla.org/security/x509certdb;1"]
-                     .getService(Ci.nsIX509CertDB);
-      let result = new CertificateVerificationResult(resolve);
-      // Unfortunately, we don't have direct access to the verified certificate
-      // chain as built by the AuthCertificate hook, so we have to re-build it
-      // here. In theory we are likely to get the same result.
-      certdb.asyncVerifyCertAtTime(sslStatus.serverCert,
-                                   2, // certificateUsageSSLServer
-                                   0, // flags
-                                   hostname,
-                                   Date.now() / 1000,
-                                   result);
-    });
-    req.send();
-  });
-}
-
-// As best we know, it is safe to disable SHA1 if the connection was successful
-// and either the connection was MITM'd by a root not in the public PKI or the
-// chain is part of the public PKI and is the one served by the real
-// telemetry.mozilla.org.
-// This will return a short string description of why it might not be safe to
-// disable SHA1 or an empty string if it is safe to disable SHA1.
-function reasonToNotDisableSHA1(result) {
-  if (!("errorCode" in result) || result.errorCode != 0) {
-    return "connection error";
-  }
-  if (!("chain" in result)) {
-    return "code error";
-  }
-  if (!result.chain[result.chain.length - 1].isBuiltInRoot) {
-    return "";
-  }
-  if (result.chain.length != 3) {
-    return "MITM";
-  }
-  const kEndEntityFingerprint = "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c";
-  const kIntermediateFingerprint = "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f";
-  const kRootFingerprint = "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161";
-  if (result.chain[0].sha256Fingerprint != kEndEntityFingerprint ||
-      result.chain[1].sha256Fingerprint != kIntermediateFingerprint ||
-      result.chain[2].sha256Fingerprint != kRootFingerprint) {
-    return "MITM";
-  }
-  return "";
-}
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/install.rdf.in
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-#filter substitution
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>disableSHA1rollout@mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:type>2</em:type>
-    <em:bootstrap>true</em:bootstrap>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-
-    <!-- Target Application this theme can install into,
-        with minimum and maximum supported versions. -->
-    <em:targetApplication>
-      <Description>
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
-        <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Front End MetaData -->
-    <em:name>SHA-1 deprecation staged rollout</em:name>
-    <em:description>Staged rollout deprecating SHA-1 in certificate signatures.</em:description>
-  </Description>
-</RDF>
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/moz.build
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
-DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
-
-FINAL_TARGET_FILES.features['disableSHA1rollout@mozilla.org'] += [
-  'bootstrap.js'
-]
-
-FINAL_TARGET_PP_FILES.features['disableSHA1rollout@mozilla.org'] += [
-  'install.rdf.in'
-]
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,17 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'aushelper',
-    'disableSHA1rollout',
     'e10srollout',
     'pdfjs',
     'pocket',
     'webcompat',
     'shield-recipe-client',
 ]
 
 # Only include the following system add-ons if building Aurora or Nightly
--- a/testing/talos/talos/xtalos/xperf_whitelist.json
+++ b/testing/talos/talos/xtalos/xperf_whitelist.json
@@ -2,17 +2,16 @@
  "C:\\$Mft": {"ignore": true},
  "C:\\$Extend\\$UsnJrnl:$J": {"ignore": true},
  "C:\\Windows\\Prefetch\\{prefetch}.pf": {"ignore": true},
  "C:\\$Secure": {"ignore": true},
  "C:\\$logfile": {"ignore": true},
  "{firefox}\\omni.ja": {"mincount": 0, "maxcount": 46, "minbytes": 0, "maxbytes": 3014656},
  "{firefox}\\browser\\omni.ja": {"mincount": 0, "maxcount": 28, "minbytes": 0, "maxbytes": 1835008},
  "{firefox}\\browser\\features\\aushelper@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
- "{firefox}\\browser\\features\\disableSHA1rollout@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\e10srollout@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\flyweb@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\formautofill@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\loop@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\firefox@getpocket.com.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\presentation@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\webcompat@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},
  "{firefox}\\browser\\features\\webcompat-reporter@mozilla.org.xpi": {"mincount": 0, "maxcount": 100, "minbytes": 0, "maxbytes": 10000000},