Bug 1284407 Generate addon id from the path for temporarily loaded xpis r?rhelmer draft
authorAndrew Swan <aswan@mozilla.com>
Mon, 12 Sep 2016 16:13:42 -0700
changeset 413290 0c9a7615e924cfb194f8ccaa09c205f73958d4ba
parent 412167 feff79e5b1374439f17c5ea10a559acf1380a8d5
child 531193 585925df0b71f865e8ad83c54dff0e6d22e1333f
push id29394
push useraswan@mozilla.com
push dateTue, 13 Sep 2016 23:05:13 +0000
reviewersrhelmer
bugs1284407
milestone51.0a1
Bug 1284407 Generate addon id from the path for temporarily loaded xpis r?rhelmer MozReview-Commit-ID: 4XChacM8xE0
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
@@ -1295,16 +1295,31 @@ function defineSyncGUID(aAddon) {
       delete aAddon.syncGUID;
       aAddon.syncGUID = val;
     },
     configurable: true,
     enumerable: true,
   });
 }
 
+// Generate a unique ID based on the path to this temporary add-on location.
+function generateTemporaryInstallID(aFile) {
+  const hasher = Cc["@mozilla.org/security/hash;1"]
+        .createInstance(Ci.nsICryptoHash);
+  hasher.init(hasher.SHA1);
+  const data = new TextEncoder().encode(aFile.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);
+  let id = `${getHashStringForCrypto(hasher)}@temporary-addon`;
+  logger.info(`Generated temp id ${id} (${sess.join("")}) for ${aFile.path}`);
+  return id;
+}
+
 /**
  * Loads an AddonInternal object from an add-on extracted in a directory.
  *
  * @param  aDir
  *         The nsIFile directory holding the add-on
  * @return an AddonInternal object
  * @throws if the directory does not contain a valid install manifest
  */
@@ -1370,29 +1385,17 @@ 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) {
-        // 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}`);
+        addon.id = generateTemporaryInstallID(aDir);
       } else {
         addon.id = aDir.leafName;
       }
     }
   } else {
     addon = loadFromRDF(uri);
   }
 
@@ -1470,20 +1473,25 @@ var loadManifestFromZipReader = Task.asy
 
   addon.size = 0;
   let entries = aZipReader.findEntries(null);
   while (entries.hasMore())
     addon.size += aZipReader.getEntry(entries.getNext()).realSize;
 
   let {signedState, cert} = yield verifyZipSignedState(aZipReader.file, addon);
   addon.signedState = signedState;
-  if (isWebExtension && !addon.id && cert) {
-    addon.id = cert.commonName;
-    if (!gIDTest.test(addon.id)) {
-      throw new Error(`Webextension is signed with an invalid id (${addon.id})`);
+  if (isWebExtension && !addon.id) {
+    if (cert) {
+      addon.id = cert.commonName;
+      if (!gIDTest.test(addon.id)) {
+        throw new Error(`Webextension is signed with an invalid id (${addon.id})`);
+      }
+    }
+    if (!addon.id && aInstallLocation == TemporaryInstallLocation) {
+      addon.id = generateTemporaryInstallID(aZipReader.file);
     }
   }
   addon.appDisabled = !isUsableAddon(addon);
 
   defineSyncGUID(addon);
 
   return addon;
 });
@@ -4814,18 +4822,17 @@ this.XPIProvider = {
    * @param  aReason
    *         The reason flag to pass to the bootstrap's startup method
    * @param  aExtraParams
    *         An object of additional key/value pairs to pass to the method in
    *         the params argument
    */
   callBootstrapMethod: function(aAddon, aFile, aMethod, aReason, aExtraParams) {
     if (!aAddon.id || !aAddon.version || !aAddon.type) {
-      logger.error(new Error("aAddon must include an id, version, and type"));
-      return;
+      throw new Error("aAddon must include an id, version, and type");
     }
 
     // Only run in safe mode if allowed to
     let runInSafeMode = "runInSafeMode" in aAddon ? aAddon.runInSafeMode : canRunInSafeMode(aAddon);
     if (Services.appinfo.inSafeMode && !runInSafeMode)
       return;
 
     let timeStart = new Date();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
@@ -64,20 +64,17 @@ add_task(function* test_implicit_id_temp
         "SourceURI of the add-on has the expected value");
 
   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;
-  }
+  AddonTestUtils.useRealCertChecks = true;
   const manifest = {
     name: "no ID",
     description: "extension without an ID",
     manifest_version: 2,
     version: "1.0"
   };
 
   const addonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
@@ -92,26 +89,25 @@ add_task(function* test_unsigned_no_id_t
         "SourceURI of the add-on has the expected value");
 
   // 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, "Reinstalled add-on has the expected ID");
 
   secondAddon.uninstall();
+  Services.obs.notifyObservers(addonDir, "flush-cache-entry", null);
   addonDir.remove(true);
+  AddonTestUtils.useRealCertChecks = false;
 });
 
 // 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;
-  }
+  AddonTestUtils.useRealCertChecks = true;
   const manifest = {
     name: "no ID",
     description: "extension without an ID",
     manifest_version: 2,
     version: "1.0"
   };
 
   const firstAddonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
@@ -127,19 +123,22 @@ add_task(function* test_multiple_no_id_e
     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, "Two add-ons are installed with the same name");
 
   firstAddon.uninstall();
+  Services.obs.notifyObservers(firstAddonDir, "flush-cache-entry", null);
   firstAddonDir.remove(true);
   secondAddon.uninstall();
+  Services.obs.notifyObservers(secondAddonDir, "flush-cache-entry", null);
   secondAddonDir.remove(true);
+  AddonTestUtils.useRealCertChecks = false;
 });
 
 // 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",