bug 1346017 - develop system add-on to check the deployments of various mozilla properties data-review=bsmedberg r=bsmedberg,Felipe,jcj
authorDavid Keeler <dkeeler@mozilla.com>
Thu, 09 Mar 2017 14:05:27 -0800
changeset 347368 8245e6ca72d4384218ea51c5392547c7ffece51e
parent 347367 5ca7be935164ebedf565def12bdc8b3e6d9c9939
child 347369 b52e66f507df2594305adb41f812fea422019065
push id31496
push usercbook@mozilla.com
push dateTue, 14 Mar 2017 13:21:57 +0000
treeherdermozilla-central@9a26ed658fdc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, Felipe, jcj
bugs1346017
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1346017 - develop system add-on to check the deployments of various mozilla properties data-review=bsmedberg r=bsmedberg,Felipe,jcj 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},