Bug 1461308: Fix tests that rely on fake privileged signatures for unpacked extensions. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Mon, 14 May 2018 18:13:21 -0700
changeset 795114 2e7f00802c8a0d467adde858335b817c41869bc0
parent 795113 fe2b8fcd7a475b65fd5cf85921a58ff22868ac53
push id109861
push usermaglione.k@gmail.com
push dateTue, 15 May 2018 01:13:42 +0000
reviewersaswan
bugs1461308
milestone62.0a1
Bug 1461308: Fix tests that rely on fake privileged signatures for unpacked extensions. r?aswan MozReview-Commit-ID: DFN6w5qLIKN
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
toolkit/mozapps/extensions/test/xpcshell/data/test_proxy/bootstrap.js
toolkit/mozapps/extensions/test/xpcshell/test_cache_certdb.js
toolkit/mozapps/extensions/test/xpcshell/test_proxy.js
toolkit/mozapps/extensions/test/xpcshell/test_signed_verify.js
toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -2466,36 +2466,44 @@ this.XPIDatabaseReconcile = {
     //    directory scan.
     let isNewInstall = !!aNewAddon || !XPIDatabase.rebuildingDatabase;
 
     // If it's a new install and we haven't yet loaded the manifest then it
     // must be something dropped directly into the install location
     let isDetectedInstall = isNewInstall && !aNewAddon;
 
     // Load the manifest if necessary and sanity check the add-on ID
+    let unsigned;
     try {
       if (!aNewAddon) {
         // Load the manifest from the add-on.
         let file = new nsIFile(aAddonState.path);
         aNewAddon = XPIInstall.syncLoadManifestFromFile(file, aInstallLocation);
       }
       // The add-on in the manifest should match the add-on ID.
       if (aNewAddon.id != aId) {
         throw new Error("Invalid addon ID: expected addon ID " + aId +
                         ", found " + aNewAddon.id + " in manifest");
       }
+
+      unsigned = XPIDatabase.mustSign(aNewAddon.type) && !aNewAddon.isCorrectlySigned;
+      if (unsigned) {
+          throw Error(`Extension ${aNewAddon.id} is not correctly signed`);
+      }
     } catch (e) {
       logger.warn("addMetadata: Add-on " + aId + " is invalid", e);
 
       // Remove the invalid add-on from the install location if the install
       // location isn't locked
       if (aInstallLocation.isLinkedAddon(aId))
         logger.warn("Not uninstalling invalid item because it is a proxy file");
       else if (aInstallLocation.locked)
         logger.warn("Could not uninstall invalid item from locked install location");
+      else if (unsigned && !isNewInstall)
+        logger.warn("Not uninstalling existing unsigned add-on");
       else
         aInstallLocation.uninstallAddon(aId);
       return null;
     }
 
     // Update the AddonInternal properties.
     aNewAddon.installDate = aAddonState.mtime;
     aNewAddon.updateDate = aAddonState.mtime;
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_proxy/bootstrap.js
+++ /dev/null
@@ -1,1 +0,0 @@
-ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_cache_certdb.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_cache_certdb.js
@@ -18,12 +18,10 @@ add_task(async function() {
 
   await promiseStartupManager();
 
   // Force a rescan of signatures
   const { XPIProvider } = ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
   await XPIProvider.verifySignatures();
 
   let addon = await AddonManager.getAddonByID(ID);
-  Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
-  Assert.ok(!addon.isActive);
-  Assert.ok(addon.appDisabled);
+  Assert.equal(addon, null, "Unsigned extensions should not be installed at startup");
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_proxy.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_proxy.js
@@ -3,94 +3,97 @@
  */
 
 const ID = "proxy1@tests.mozilla.org";
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
 BootstrapMonitor.init();
 
+const BOOTSTRAP_JS = `ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);`;
+
 // Ensure that a proxy file to an add-on with a valid manifest works.
 add_task(async function() {
   Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
 
   await promiseStartupManager();
 
   let tempdir = gTmpD.clone();
-  await promiseWriteInstallRDFToDir({
+  let unpackedAddon = await promiseWriteInstallRDFToDir({
     id: ID,
     version: "1.0",
     bootstrap: true,
     unpack: true,
     targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
+      id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
-        }],
+    }],
     name: "Test Bootstrap 1 (proxy)",
-  }, tempdir, ID, "bootstrap.js");
-
-  let unpackedAddon = tempdir.clone();
-  unpackedAddon.append(ID);
-  do_get_file("data/test_proxy/bootstrap.js")
-    .copyTo(unpackedAddon, "bootstrap.js");
+  }, tempdir, ID, {
+    "bootstrap.js": BOOTSTRAP_JS,
+  });
 
   // create proxy file in profile/extensions dir
   let extensionsDir = gProfD.clone();
   extensionsDir.append("extensions");
   let proxyFile = await promiseWriteProxyFileToDir(extensionsDir, unpackedAddon, ID);
 
   await promiseRestartManager();
 
-  BootstrapMonitor.checkAddonInstalled(ID, "1.0");
-  BootstrapMonitor.checkAddonStarted(ID, "1.0");
-
   let addon = await promiseAddonByID(ID);
 
-  Assert.notEqual(addon, null);
-  Assert.equal(addon.version, "1.0");
-  Assert.equal(addon.name, "Test Bootstrap 1 (proxy)");
-  Assert.ok(addon.isCompatible);
-  Assert.ok(!addon.appDisabled);
-  Assert.ok(addon.isActive);
-  Assert.equal(addon.type, "extension");
-  Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+  if (AppConstants.MOZ_REQUIRE_SIGNING) {
+    BootstrapMonitor.checkAddonNotInstalled(ID, "1.0");
+    BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+
+    Assert.equal(addon, null);
+  } else {
+    BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+    BootstrapMonitor.checkAddonStarted(ID, "1.0");
 
-  Assert.ok(proxyFile.exists());
+    Assert.notEqual(addon, null);
+    Assert.equal(addon.version, "1.0");
+    Assert.equal(addon.name, "Test Bootstrap 1 (proxy)");
+    Assert.ok(addon.isCompatible);
+    Assert.ok(!addon.appDisabled);
+    Assert.ok(addon.isActive);
+    Assert.equal(addon.type, "extension");
+    Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_UNKNOWN : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
-  addon.uninstall();
+    Assert.ok(proxyFile.exists());
+
+    addon.uninstall();
+  }
   unpackedAddon.remove(true);
 
   await promiseRestartManager();
 });
 
 
 // Ensure that a proxy file to an add-on is not removed even
 // if the manifest file is invalid. See bug 1195353.
 add_task(async function() {
   let tempdir = gTmpD.clone();
 
   // use a mismatched ID to make this install.rdf invalid
-  await promiseWriteInstallRDFToDir({
+  let unpackedAddon = await promiseWriteInstallRDFToDir({
     id: "bad-proxy1@tests.mozilla.org",
     version: "1.0",
     bootstrap: true,
     unpack: true,
     targetApplications: [{
-          id: "xpcshell@tests.mozilla.org",
+      id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
-        }],
+    }],
     name: "Test Bootstrap 1 (proxy)",
-  }, tempdir, ID, "bootstrap.js");
-
-  let unpackedAddon = tempdir.clone();
-  unpackedAddon.append(ID);
-  do_get_file("data/test_proxy/bootstrap.js")
-    .copyTo(unpackedAddon, "bootstrap.js");
+  }, tempdir, ID, {
+    "bootstrap.js": BOOTSTRAP_JS,
+  });
 
   // create proxy file in profile/extensions dir
   let extensionsDir = gProfD.clone();
   extensionsDir.append("extensions");
   let proxyFile = await promiseWriteProxyFileToDir(extensionsDir, unpackedAddon, ID);
 
   await promiseRestartManager();
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_signed_verify.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_signed_verify.js
@@ -85,78 +85,16 @@ function verify_no_change([startFile, st
 
     // Remove the add-on and restart to let it go away
     manuallyUninstall(profileDir, ID);
     await promiseRestartManager();
     await promiseShutdownManager();
   });
 }
 
-function verify_enables([startFile, startState], [endFile, endState]) {
-  add_task(async function() {
-    info("A switch from " + startFile + " to " + endFile + " should enable the add-on.");
-
-    // Install the first add-on
-    await manuallyInstall(do_get_file(DATA + startFile), profileDir, ID);
-    await promiseStartupManager();
-
-    let addon = await promiseAddonByID(ID);
-    Assert.notEqual(addon, null);
-    Assert.ok(!addon.isActive);
-    Assert.equal(addon.pendingOperations, AddonManager.PENDING_NONE);
-    Assert.equal(addon.signedState, startState);
-
-    // Swap in the files from the next add-on
-    manuallyUninstall(profileDir, ID);
-    await manuallyInstall(do_get_file(DATA + endFile), profileDir, ID);
-
-    let needsRestart = hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
-    info(needsRestart);
-
-    let events = {};
-    if (!needsRestart) {
-      events[ID] = [
-        ["onPropertyChanged", ["appDisabled"]],
-        ["onEnabling", false],
-        "onEnabled"
-      ];
-    } else {
-      events[ID] = [
-        ["onPropertyChanged", ["appDisabled"]],
-        "onEnabling"
-      ];
-    }
-
-    if (startState != endState)
-      events[ID].unshift(["onPropertyChanged", ["signedState"]]);
-
-    prepare_test(events);
-
-    // Trigger the check
-    let changes = await verifySignatures();
-    Assert.equal(changes.enabled.length, 1);
-    Assert.equal(changes.enabled[0], ID);
-    Assert.equal(changes.disabled.length, 0);
-
-    Assert.ok(!addon.appDisabled);
-    if (needsRestart)
-      Assert.notEqual(addon.pendingOperations, AddonManager.PENDING_NONE);
-    else
-      Assert.ok(addon.isActive);
-    Assert.equal(addon.signedState, endState);
-
-    ensure_test_completed();
-
-    // Remove the add-on and restart to let it go away
-    manuallyUninstall(profileDir, ID);
-    await promiseRestartManager();
-    await promiseShutdownManager();
-  });
-}
-
 function verify_disables([startFile, startState], [endFile, endState]) {
   add_task(async function() {
     info("A switch from " + startFile + " to " + endFile + " should disable the add-on.");
 
     // Install the first add-on
     await manuallyInstall(do_get_file(DATA + startFile), profileDir, ID);
     await promiseStartupManager();
 
@@ -214,25 +152,13 @@ function verify_disables([startFile, sta
 }
 
 for (let start of GOOD) {
   for (let end of BAD) {
     verify_disables(start, end);
   }
 }
 
-for (let start of BAD) {
-  for (let end of GOOD) {
-    verify_enables(start, end);
-  }
-}
-
 for (let start of GOOD) {
   for (let end of GOOD.filter(f => f != start)) {
     verify_no_change(start, end);
   }
 }
-
-for (let start of BAD) {
-  for (let end of BAD.filter(f => f != start)) {
-    verify_no_change(start, end);
-  }
-}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_temporary.js
@@ -167,16 +167,24 @@ add_task(async function() {
           }],
           name: "Test Bootstrap 1 (temporary)",
         }),
         "bootstrap.js": bootstrapJS,
       };
 
       let target;
       if (!packed) {
+        // Unpacked extensions don't support signing, which means that
+        // our mock signing service is not able to give them a
+        // privileged signed state, and we can't install them on release
+        // builds.
+        if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
+          continue;
+        }
+
         target = tempdir.clone();
         target.append(ID);
 
         await AddonTestUtils.promiseWriteFilesToDir(target.path, files);
       } else {
         target = tempdir.clone();
         target.append(`${ID}.xpi`);
 
@@ -357,16 +365,21 @@ add_task(async function test_samefile() 
   Assert.equal(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_PRIVILEGED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
 
   addon.uninstall();
 });
 
 // Install a temporary add-on over the top of an existing add-on.
 // Uninstall it and make sure the existing add-on comes back.
 add_task(async function() {
+  // We can't install unpacked add-ons on release builds. See above.
+  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
+    return;
+  }
+
   await promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
 
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
 
   let tempdir = gTmpD.clone();
   await promiseWriteInstallRDFToDir({
     id: ID,
@@ -459,16 +472,21 @@ add_task(async function() {
   BootstrapMonitor.checkAddonNotStarted(ID);
 
   await promiseRestartManager();
 });
 
 // Install a temporary add-on as a version upgrade over the top of an
 // existing temporary add-on.
 add_task(async function() {
+  // We can't install unpacked add-ons on release builds. See above.
+  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
+    return;
+  }
+
   const tempdir = gTmpD.clone();
 
   await promiseWriteInstallRDFToDir(sampleRDFManifest, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
 
   const unpackedAddon = tempdir.clone();
   unpackedAddon.append(ID);
   do_get_file("data/test_temporary/bootstrap.js")
     .copyTo(unpackedAddon, "bootstrap.js");
@@ -510,16 +528,21 @@ add_task(async function() {
 
   unpackedAddon.remove(true);
   await promiseRestartManager();
 });
 
 // Install a temporary add-on as a version downgrade over the top of an
 // existing temporary add-on.
 add_task(async function() {
+  // We can't install unpacked add-ons on release builds. See above.
+  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
+    return;
+  }
+
   const tempdir = gTmpD.clone();
 
   await promiseWriteInstallRDFToDir(sampleRDFManifest, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
 
   const unpackedAddon = tempdir.clone();
   unpackedAddon.append(ID);
   do_get_file("data/test_temporary/bootstrap.js")
     .copyTo(unpackedAddon, "bootstrap.js");
@@ -559,16 +582,21 @@ add_task(async function() {
 
   unpackedAddon.remove(true);
   await promiseRestartManager();
 });
 
 // Installing a temporary add-on over an existing add-on with the same
 // version number should be installed as an upgrade.
 add_task(async function() {
+  // We can't install unpacked add-ons on release builds. See above.
+  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
+    return;
+  }
+
   const tempdir = gTmpD.clone();
 
   await promiseWriteInstallRDFToDir(sampleRDFManifest, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
 
   const unpackedAddon = tempdir.clone();
   unpackedAddon.append(ID);
   do_get_file("data/test_temporary/bootstrap.js")
     .copyTo(unpackedAddon, "bootstrap.js");
@@ -616,16 +644,21 @@ add_task(async function() {
 
   unpackedAddon.remove(true);
   await promiseRestartManager();
 });
 
 // Install a temporary add-on over the top of an existing disabled add-on.
 // After restart, the existing add-on should continue to be installed and disabled.
 add_task(async function() {
+  // We can't install unpacked add-ons on release builds. See above.
+  if (!AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS) {
+    return;
+  }
+
   await promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
 
   BootstrapMonitor.checkAddonInstalled(ID, "1.0");
   BootstrapMonitor.checkAddonStarted(ID, "1.0");
 
   let addon = await promiseAddonByID(ID);
 
   addon.userDisabled = true;