bug 1346017 - develop system add-on to check the deployments of various mozilla properties data-review=bsmedberg r?jcj,felipe draft
authorDavid Keeler <dkeeler@mozilla.com>
Thu, 09 Mar 2017 14:05:27 -0800
changeset 496847 02cbcd7b7e35755ab09e0a929af76a2df5eb970b
parent 496843 7f6c7d78dad3e92ea69577d0e4ecd17c3ab45004
child 548713 9e90e313142f944a84ca92364a351dae95414a00
push id48706
push userbmo:dkeeler@mozilla.com
push dateFri, 10 Mar 2017 18:51:07 +0000
reviewersjcj, felipe
bugs1346017
milestone55.0a1
bug 1346017 - develop system add-on to check the deployments of various mozilla properties data-review=bsmedberg r?jcj,felipe MozReview-Commit-ID: Jv8nGCqiTdU
browser/extensions/deployment-checker/README.md
browser/extensions/deployment-checker/bootstrap.js
browser/extensions/deployment-checker/install.rdf.in
browser/extensions/deployment-checker/moz.build
browser/extensions/moz.build
testing/talos/talos/xtalos/xperf_whitelist.json
new file mode 100644
--- /dev/null
+++ b/browser/extensions/deployment-checker/README.md
@@ -0,0 +1,61 @@
+This system add-on attempts to confirm that users encounter Mozilla sites as
+deployed by Mozilla. The add-on has a list of Mozilla properties (see after
+this paragraph) and a list of expected certificate hashes. For each host, if
+the add-on connects successfully to that host and determines that the
+certificates sent are part of the web PKI (as in, the root is a built-in) yet
+do not match the expected certificate chain, it will include in a telemetry
+ping the host and the certificates in the chain (base64-encoded). The name of
+the telemetry ping is "deployment-checker". The ping does not include the client
+ID.
+
+The Mozilla properties queried are:
+
+* incoming.telemetry.mozilla.org
+* telemetry.mozilla.org
+* addons.mozilla.org
+* services.addons.mozilla.org
+* aus5.mozilla.org
+* versioncheck.addons.mozilla.org
+* support.mozilla.org
+* ftp.mozilla.org
+* mozilla.org
+* bugzilla.mozilla.org
+* crash-reports.mozilla.com
+* releases.mozilla.com
+* download-installer.cdn.mozilla.net
+* firefox.settings.services.mozilla.com
+* push.services.mozilla.com
+* token.services.mozilla.com
+* shavar.services.mozilla.com
+* search.services.mozilla.com
+
+The report payload is a JSON dictionary containing two values:
+
+* version -- a version string to differentiate iterations of this add-on, if
+  necessary
+* mismatches -- a list of objects with the properties:
+  * hostname -- the host for which a mismatch was detected
+  * chain -- a list of base64-encoded strings representing the bytes of the
+    certificates in the chain
+
+For example, if the add-on determined that the hosts "example1.mozilla.org" and
+"example2.mozilla.org" were not sending the expected certificates (and yet they
+verified correctly and were issued by a root in the web PKI), the payload may
+look like:
+
+    { "version": "1.0",
+      "mismatches": [
+        { "hostname": "example1.mozilla.org",
+          "chain": [
+            "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",
+            "MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3VyYW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC24C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMICKq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0Xsh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcftbZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwdaOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNHE+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zuxICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0AecPUeybQ="
+          ]
+        },
+        { "hostname": "example2.mozilla.org",
+          "chain": [
+            "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",
+            "MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3VyYW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC24C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMICKq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0Xsh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcftbZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwdaOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNHE+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly/D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zuxICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0AecPUeybQ="
+          ]
+        }
+      ]
+    }
new file mode 100644
--- /dev/null
+++ b/browser/extensions/deployment-checker/bootstrap.js
@@ -0,0 +1,258 @@
+"use strict";
+
+var { 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/TelemetryController.jsm");
+var btoa = Cu.import("resource://gre/modules/Log.jsm").btoa;
+
+function certToBase64(cert) {
+  let derString = "";
+  for (let rawByte of cert.getRawDER({})) {
+    derString += String.fromCharCode(rawByte);
+  }
+  return btoa(derString);
+}
+
+function certArrayToBase64(certs) {
+  let result = [];
+  for (let cert of certs) {
+    result.push(certToBase64(cert));
+  }
+  return result;
+}
+
+function certListToJSArray(certList) {
+  let result = [];
+  let enumerator = certList.getEnumerator();
+  while (enumerator.hasMoreElements()) {
+    let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+    result.push(cert);
+  }
+  return result;
+}
+
+class CertificateVerificationResult {
+  constructor(hostname, resolve) {
+    this.hostname = hostname;
+    this.resolve = resolve;
+  }
+
+  verifyCertFinished(aPRErrorCode, aVerifiedChain, aEVStatus) {
+    let result = { hostname: this.hostname };
+    if (aPRErrorCode == 0) {
+      result.chain = certListToJSArray(aVerifiedChain);
+    } else {
+      result.error = "certificate reverification";
+      console.log(`${this.hostname}: ${aPRErrorCode}`);
+    }
+    this.resolve(result);
+  }
+}
+
+function makeRequest(hostname) {
+  return new Promise((resolve) => {
+    let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+                .createInstance(Ci.nsIXMLHttpRequest);
+    req.open("GET", "https://" + hostname);
+    req.timeout = 30000;
+    req.addEventListener("error", (evt) => {
+      resolve({ hostname, error: "connection error" });
+    });
+    req.addEventListener("timeout", (evt) => {
+      resolve({ hostname, 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({ hostname, 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(hostname, 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();
+  });
+}
+
+var sites = {
+  "incoming.telemetry.mozilla.org": [
+    "63eb34876cbd2ebbc3b254961d96cdafb00f28719229f61e27b19a2510929012",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "telemetry.mozilla.org": [
+    "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "addons.mozilla.org": [
+    "51646c662bb3fd3a3bac9d976803f4e6869183bb483b7d30dcdfc5c4d0487b41",
+    "403e062a2653059113285baf80a0d4ae422c848c9f78fad01fc94bc5b87fef1a",
+    "7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf"
+  ],
+  "services.addons.mozilla.org": [
+    "51646c662bb3fd3a3bac9d976803f4e6869183bb483b7d30dcdfc5c4d0487b41",
+    "403e062a2653059113285baf80a0d4ae422c848c9f78fad01fc94bc5b87fef1a",
+    "7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf"
+  ],
+  "aus5.mozilla.org": [
+    "60e8e2e092bdc3b69ce260d6a52f90fd6368768600f911a22ee9f1b8833abeea",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "versioncheck.addons.mozilla.org": [
+    "f7ac5873798f0322c206744901a8df1e944966be772e3a8bea2a4a9969fdfb38",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "support.mozilla.org": [
+    "1751e120f14ddbd5306d037aaa0dd753e2989cc4f6e5560b6821a6f807525147",
+    "19400be5b7a31fb733917700789d2f0a2471c0c9d506c0e504c06c16d7cb17c0",
+    "7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf"
+  ],
+  "ftp.mozilla.org": [
+    "3b9ff6dc11f896b162603d29360be64e69f834e9b37a057a5b84cd54e58e7c8b",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "mozilla.org": [
+    "8a43602dc67d8c5934fa638c2b066d385918a1c3f5fd5307d13a7b363cd526d3",
+    "403e062a2653059113285baf80a0d4ae422c848c9f78fad01fc94bc5b87fef1a",
+    "7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf"
+  ],
+  "bugzilla.mozilla.org": [
+    "1095a8c1e1c318fae495409911076de379abe5b02950ff40e8e863c4fdf39fcb",
+    "403e062a2653059113285baf80a0d4ae422c848c9f78fad01fc94bc5b87fef1a",
+    "7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf"
+  ],
+  "crash-reports.mozilla.com": [
+    "58fe74d89c13624f79c9c97bcf9f2da14d22eb1e8d1caeeaee0735f8e68ef4a5",
+    "403e062a2653059113285baf80a0d4ae422c848c9f78fad01fc94bc5b87fef1a",
+    "7431e5f4c3c1ce4690774f0b61e05440883ba9a01ed00ba6abd7806ed3b118cf"
+  ],
+  "releases.mozilla.com": [
+    "3b9ff6dc11f896b162603d29360be64e69f834e9b37a057a5b84cd54e58e7c8b",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "download-installer.cdn.mozilla.net": [
+    "6442cb8d30d303bc67c685ba319e9497aa39aeffc3caca9a707f151071ab3ca8",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "firefox.settings.services.mozilla.com": [
+    "ee6ddb1ac9614695a2c37579edb7844fa19fde18a490d1738e19cf0a49541918",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "push.services.mozilla.com": [
+    "ad3ef2e8244aa2d3575189a34311b274ceb8e9be323fe48c843e1f66bb62f6fe",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "token.services.mozilla.com": [
+    "dd123bd00f11e08d2995d907b80777edbff6169d2569d5d34f4fe10983d8901d",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "shavar.services.mozilla.com": [
+    "ab0cab1d1d1157eb5dff0ea41cd6d1eeebf59d1f123042954c61ea78003457d0",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ],
+  "search.services.mozilla.com": [
+    "e5bd9cc4248f835d9e8d359bcac7b3e5073890b67b8e1e070a322e3e09ab0754",
+    "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
+    "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161"
+  ]
+};
+
+function makeRequests() {
+  let promises = [];
+  for (let hostname of Object.keys(sites)) {
+    promises.push(makeRequest(hostname));
+  }
+  return Promise.all(promises);
+}
+
+function analyzeAndReport(results) {
+  let payload = { version: "1.0", mismatches: [] };
+  for (let result of results) {
+    // Skip if the connection resulted in any kind of error.
+    if ("error" in result) {
+      console.log(`${result.hostname}: ${result.error} - skipping`);
+      continue;
+    }
+    // Skip imported roots.
+    if (!result.chain[result.chain.length - 1].isBuiltInRoot) {
+      console.log(`${result.hostname}: imported root - skipping`);
+      continue;
+    }
+
+    let report = false;
+    let expectedHashes = sites[result.hostname];
+    // If we have chains of different length, obviously we'll have different
+    // chains, so report this chain.
+    if (expectedHashes.length != result.chain.length) {
+      report = true;
+    } else {
+      // Otherwise, compare each hash. If we encounter an unexpected one, report
+      // this chain.
+      for (let i = 0; i < expectedHashes.length; i++) {
+        let actualHash = result.chain[i].sha256Fingerprint.replace(/:/g, "")
+                           .toLowerCase();
+        if (actualHash != expectedHashes[i]) {
+          report = true;
+          break;
+        }
+      }
+    }
+    if (report) {
+      payload.mismatches.push({ hostname: result.hostname,
+                                chain: certArrayToBase64(result.chain) });
+    }
+  }
+  console.log("deployment-checker results:");
+  console.log(results);
+  console.log("deployment-checker payload:");
+  console.log(payload);
+  return TelemetryController.submitExternalPing("deployment-checker", payload,
+                                                {});
+}
+
+// We only run once - when installed.
+function install() {
+  // Only run if we have a good indication that we're not in a testing
+  // environment (in which case attempting to connect to telemetry.mozilla.org
+  // will result in a test failure).
+  let telemetryServerURL = Preferences.get("toolkit.telemetry.server",
+                                           undefined);
+  // Also only run if the user has unified telemetry enabled (because we don't
+  // want to submit a telemetry ping if they've opted out).
+  let unifiedTelemetryEnabled = Preferences.get("toolkit.telemetry.unified",
+                                                undefined);
+  if (telemetryServerURL == "https://incoming.telemetry.mozilla.org" &&
+      unifiedTelemetryEnabled === true) {
+    makeRequests().then(analyzeAndReport).catch(Cu.reportError);
+  }
+}
+
+function startup() {}
+function shutdown() {}
+function uninstall() {}
copy from browser/extensions/e10srollout/install.rdf.in
copy to browser/extensions/deployment-checker/install.rdf.in
--- a/browser/extensions/e10srollout/install.rdf.in
+++ b/browser/extensions/deployment-checker/install.rdf.in
@@ -4,29 +4,29 @@
    - 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>e10srollout@mozilla.org</em:id>
-    <em:version>1.11</em:version>
+    <em:id>deployment-checker@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>Multi-process staged rollout</em:name>
-    <em:description>Staged rollout of Firefox multi-process feature.</em:description>
+    <em:name>Site Deployment Checker</em:name>
+    <em:description>Check that Users Encounter Mozilla Sites as Deployed by Mozilla</em:description>
   </Description>
 </RDF>
copy from browser/extensions/e10srollout/moz.build
copy to browser/extensions/deployment-checker/moz.build
--- a/browser/extensions/e10srollout/moz.build
+++ b/browser/extensions/deployment-checker/moz.build
@@ -2,15 +2,15 @@
 # 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['e10srollout@mozilla.org'] += [
+FINAL_TARGET_FILES.features['deployment-checker@mozilla.org'] += [
   'bootstrap.js'
 ]
 
-FINAL_TARGET_PP_FILES.features['e10srollout@mozilla.org'] += [
+FINAL_TARGET_PP_FILES.features['deployment-checker@mozilla.org'] += [
   'install.rdf.in'
 ]
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,16 +1,17 @@
 # -*- 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',
+    'deployment-checker',
     'e10srollout',
     'pdfjs',
     'pocket',
     'webcompat',
 ]
 
 # Only include the following system add-ons if building Aurora or Nightly
 if not CONFIG['RELEASE_OR_BETA']:
--- a/testing/talos/talos/xtalos/xperf_whitelist.json
+++ b/testing/talos/talos/xtalos/xperf_whitelist.json
@@ -2,16 +2,17 @@
  "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\\deployment-checker@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},