Bug 1273229 - auto-generate IDs for temp installs from dir path. r=kmag
authorKumar McMillan <kumar.mcmillan@gmail.com>
Fri, 03 Jun 2016 16:43:45 -0500
changeset 301153 2011b521c19712830278bf7af43dae1034a14a7f
parent 301152 54354d5ade58598ba75e4fcbe29f2cae3d3cbfa1
child 301154 35e71e69db49f4672512db7b26dc2ad517d7cf81
push id78241
push userkwierso@gmail.com
push dateThu, 09 Jun 2016 00:09:10 +0000
treeherdermozilla-inbound@2c66b75bbb7f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1273229
milestone50.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 1273229 - auto-generate IDs for temp installs from dir path. r=kmag MozReview-Commit-ID: JFuEVUZs2Ci
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -239,16 +239,23 @@ const RESTARTLESS_TYPES = new Set([
 ]);
 
 const SIGNED_TYPES = new Set([
   "webextension",
   "extension",
   "experiment",
 ]);
 
+// This is a random number array that can be used as "salt" when generating
+// an automatic ID based on the directory path of an add-on. It will prevent
+// someone from creating an ID for a permanent add-on that could be replaced
+// by a temporary add-on (because that would be confusing, I guess).
+const TEMP_INSTALL_ID_GEN_SESSION =
+  new Uint8Array(Float64Array.of(Math.random()).buffer);
+
 // Whether add-on signing is required.
 function mustSign(aType) {
   if (!SIGNED_TYPES.has(aType))
     return false;
   return REQUIRE_SIGNING || Preferences.get(PREF_XPI_SIGNATURES_REQUIRED, false);
 }
 
 // Keep track of where we are in startup for telemetry
@@ -1324,21 +1331,29 @@ var loadManifestFromDir = Task.async(fun
 
   let uri = Services.io.newFileURI(file).QueryInterface(Ci.nsIFileURL);
 
   let addon;
   if (file.leafName == FILE_WEB_MANIFEST) {
     addon = yield loadManifestFromWebManifest(uri);
     if (!addon.id) {
       if (aInstallLocation == TemporaryInstallLocation) {
-        let id = Cc["@mozilla.org/uuid-generator;1"]
-            .getService(Ci.nsIUUIDGenerator)
-            .generateUUID().toString();
-        logger.info(`Generated temporary id ${id} for ${aDir.path}`);
-        addon.id = id;
+        // Generate a unique ID based on the directory path of
+        // this temporary add-on location.
+        const hasher = Cc["@mozilla.org/security/hash;1"]
+          .createInstance(Ci.nsICryptoHash);
+        hasher.init(hasher.SHA1);
+        const data = new TextEncoder().encode(aDir.path);
+        // Make it so this ID cannot be guessed.
+        const sess = TEMP_INSTALL_ID_GEN_SESSION;
+        hasher.update(sess, sess.length);
+        hasher.update(data, data.length);
+        addon.id = `${getHashStringForCrypto(hasher)}@temporary-addon`;
+        logger.info(
+          `Generated temp id ${addon.id} (${sess.join("")}) for ${aDir.path}`);
       } else {
         addon.id = aDir.leafName;
       }
     }
   } else {
     addon = loadFromRDF(uri);
   }
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
@@ -41,16 +41,81 @@ add_task(function* test_implicit_id_temp
   yield AddonManager.installTemporaryAddon(xpifile);
 
   addon = yield promiseAddonByID(IMPLICIT_ID_ID);
   do_check_neq(addon, null);
 
   addon.uninstall();
 });
 
+// We should be able to temporarily install an unsigned web extension
+// that does not have an ID in its manifest.
+add_task(function* test_unsigned_no_id_temp_install() {
+  if (!TEST_UNPACKED) {
+    do_print("This test does not apply when using packed extensions");
+    return;
+  }
+  const manifest = {
+    name: "no ID",
+    description: "extension without an ID",
+    manifest_version: 2,
+    version: "1.0"
+  };
+
+  const addonDir = writeWebManifestForExtension(manifest, gTmpD,
+                                                "the-addon-sub-dir");
+  const addon = yield AddonManager.installTemporaryAddon(addonDir);
+  ok(addon.id, "ID should have been auto-generated");
+
+  // Install the same directory again, as if re-installing or reloading.
+  const secondAddon = yield AddonManager.installTemporaryAddon(addonDir);
+  // The IDs should be the same.
+  equal(secondAddon.id, addon.id);
+
+  secondAddon.uninstall();
+  addonDir.remove(true);
+});
+
+// We should be able to install two extensions from manifests without IDs
+// at different locations and get two unique extensions.
+add_task(function* test_multiple_no_id_extensions() {
+  if (!TEST_UNPACKED) {
+    do_print("This test does not apply when using packed extensions");
+    return;
+  }
+  const manifest = {
+    name: "no ID",
+    description: "extension without an ID",
+    manifest_version: 2,
+    version: "1.0"
+  };
+
+  const firstAddonDir = writeWebManifestForExtension(manifest, gTmpD,
+                                                     "addon-sub-dir-one");
+  const secondAddonDir = writeWebManifestForExtension(manifest, gTmpD,
+                                                      "addon-sub-dir-two");
+  const [firstAddon, secondAddon] = yield Promise.all([
+    AddonManager.installTemporaryAddon(firstAddonDir),
+    AddonManager.installTemporaryAddon(secondAddonDir)
+  ]);
+
+  const allAddons = yield new Promise(resolve => {
+    AddonManager.getAllAddons(addons => resolve(addons));
+  });
+  do_print(`Found these add-ons: ${allAddons.map(a => a.name).join(", ")}`);
+  const filtered = allAddons.filter(addon => addon.name === manifest.name);
+  // Make sure we have two add-ons by the same name.
+  equal(filtered.length, 2);
+
+  firstAddon.uninstall();
+  firstAddonDir.remove(true);
+  secondAddon.uninstall();
+  secondAddonDir.remove(true);
+});
+
 // Test that we can get the ID from browser_specific_settings
 add_task(function* test_bss_id() {
   const ID = "webext_bss_id@tests.mozilla.org";
 
   let manifest = {
     name: "bss test",
     description: "test that ID may be in browser_specific_settings",
     manifest_version: 2,