summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js')
-rw-r--r--toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js429
1 files changed, 429 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js b/toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js
new file mode 100644
index 0000000000..5a5e73acb4
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js
@@ -0,0 +1,429 @@
+// Enable signature checks for these tests
+gUseRealCertChecks = true;
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+const DATA = "data/signing_checks/";
+const ADDONS = {
+ bootstrap: {
+ unsigned: "unsigned_bootstrap_2.xpi",
+ badid: "signed_bootstrap_badid_2.xpi",
+ signed: "signed_bootstrap_2.xpi",
+ preliminary: "preliminary_bootstrap_2.xpi",
+ },
+ nonbootstrap: {
+ unsigned: "unsigned_nonbootstrap_2.xpi",
+ badid: "signed_nonbootstrap_badid_2.xpi",
+ signed: "signed_nonbootstrap_2.xpi",
+ },
+};
+const ID = "test@tests.mozilla.org";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Deletes a file from the test add-on in the profile
+function breakAddon(file) {
+ if (TEST_UNPACKED) {
+ let f = file.clone();
+ f.append("test.txt");
+ f.remove(true);
+
+ f = file.clone();
+ f.append("install.rdf");
+ f.lastModifiedTime = Date.now();
+ } else {
+ var zipW = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
+ zipW.open(file, FileUtils.MODE_RDWR | FileUtils.MODE_APPEND);
+ zipW.removeEntry("test.txt", false);
+ zipW.close();
+ }
+}
+
+function resetPrefs() {
+ Services.prefs.setIntPref("bootstraptest.active_version", -1);
+ Services.prefs.setIntPref("bootstraptest.installed_version", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.install_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", -1);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", -1);
+}
+
+function clearCache(file) {
+ if (TEST_UNPACKED) {
+ return;
+ }
+
+ Services.obs.notifyObservers(file, "flush-cache-entry");
+}
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+add_task(async function setup() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");
+
+ // Start and stop the manager to initialise everything in the profile before
+ // actual testing
+ await promiseStartupManager();
+ await promiseShutdownManager();
+ resetPrefs();
+});
+
+// Injecting into profile (bootstrap)
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.bootstrap.unsigned),
+ profileDir,
+ ID
+ );
+
+ await promiseStartupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+ Assert.equal(getActiveVersion(), -1);
+
+ await addon.uninstall();
+ await promiseShutdownManager();
+ resetPrefs();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.bootstrap.signed),
+ profileDir,
+ ID
+ );
+ breakAddon(file);
+
+ await promiseStartupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ Assert.equal(getActiveVersion(), -1);
+
+ await addon.uninstall();
+ await promiseShutdownManager();
+ resetPrefs();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.bootstrap.badid),
+ profileDir,
+ ID
+ );
+
+ await promiseStartupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ Assert.equal(getActiveVersion(), -1);
+
+ await addon.uninstall();
+ await promiseShutdownManager();
+ resetPrefs();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+// Installs a signed add-on then modifies it in place breaking its signing
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.bootstrap.signed),
+ profileDir,
+ ID
+ );
+
+ // Make it appear to come from the past so when we modify it later it is
+ // detected during startup. Obviously malware can bypass this method of
+ // detection but the periodic scan will catch that
+ await promiseSetExtensionModifiedTime(file.path, Date.now() - 600000);
+
+ await promiseStartupManager();
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED);
+ Assert.equal(getActiveVersion(), 2);
+
+ await promiseShutdownManager();
+ Assert.equal(getActiveVersion(), 0);
+
+ clearCache(file);
+ breakAddon(file);
+ resetPrefs();
+
+ await promiseStartupManager();
+
+ addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ Assert.equal(getActiveVersion(), -1);
+
+ let ids = AddonManager.getStartupChanges(
+ AddonManager.STARTUP_CHANGE_DISABLED
+ );
+ Assert.equal(ids.length, 1);
+ Assert.equal(ids[0], ID);
+
+ await addon.uninstall();
+ await promiseShutdownManager();
+ resetPrefs();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+// Injecting into profile (non-bootstrap)
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.nonbootstrap.unsigned),
+ profileDir,
+ ID
+ );
+
+ await promiseStartupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+ await addon.uninstall();
+ await promiseRestartManager();
+ await promiseShutdownManager();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.nonbootstrap.signed),
+ profileDir,
+ ID
+ );
+ breakAddon(file);
+
+ await promiseStartupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+
+ await addon.uninstall();
+ await promiseRestartManager();
+ await promiseShutdownManager();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.nonbootstrap.badid),
+ profileDir,
+ ID
+ );
+
+ await promiseStartupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+
+ await addon.uninstall();
+ await promiseRestartManager();
+ await promiseShutdownManager();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+// Installs a signed add-on then modifies it in place breaking its signing
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.nonbootstrap.signed),
+ profileDir,
+ ID
+ );
+
+ // Make it appear to come from the past so when we modify it later it is
+ // detected during startup. Obviously malware can bypass this method of
+ // detection but the periodic scan will catch that
+ await promiseSetExtensionModifiedTime(file.path, Date.now() - 60000);
+
+ await promiseStartupManager();
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED);
+
+ await promiseShutdownManager();
+
+ clearCache(file);
+ breakAddon(file);
+
+ await promiseStartupManager();
+
+ addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+
+ let ids = AddonManager.getStartupChanges(
+ AddonManager.STARTUP_CHANGE_DISABLED
+ );
+ Assert.equal(ids.length, 1);
+ Assert.equal(ids[0], ID);
+
+ await addon.uninstall();
+ await promiseRestartManager();
+ await promiseShutdownManager();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+// Stage install then modify before startup (non-bootstrap)
+add_task(async function() {
+ await promiseStartupManager();
+ await promiseInstallAllFiles([
+ do_get_file(DATA + ADDONS.nonbootstrap.signed),
+ ]);
+ await promiseShutdownManager();
+
+ let staged = profileDir.clone();
+ staged.append("staged");
+ staged.append(do_get_expected_addon_name(ID));
+ Assert.ok(staged.exists());
+
+ breakAddon(staged);
+ await promiseStartupManager();
+
+ // Should have refused to install the broken staged version
+ let addon = await promiseAddonByID(ID);
+ Assert.equal(addon, null);
+
+ clearCache(staged);
+
+ await promiseShutdownManager();
+});
+
+// Manufacture staged install (bootstrap)
+add_task(async function() {
+ let stage = profileDir.clone();
+ stage.append("staged");
+
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.bootstrap.signed),
+ stage,
+ ID
+ );
+ breakAddon(file);
+
+ await promiseStartupManager();
+
+ // Should have refused to install the broken staged version
+ let addon = await promiseAddonByID(ID);
+ Assert.equal(addon, null);
+ Assert.equal(getActiveVersion(), -1);
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+
+ await promiseShutdownManager();
+ resetPrefs();
+});
+
+// Preliminarily-signed sideloaded add-ons should work
+add_task(async function() {
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.bootstrap.preliminary),
+ profileDir,
+ ID
+ );
+
+ await promiseStartupManager();
+
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_PRELIMINARY);
+ Assert.equal(getActiveVersion(), 2);
+
+ await addon.uninstall();
+ await promiseShutdownManager();
+ resetPrefs();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});
+
+// Preliminarily-signed sideloaded add-ons should work via staged install
+add_task(async function() {
+ let stage = profileDir.clone();
+ stage.append("staged");
+
+ let file = await manuallyInstall(
+ do_get_file(DATA + ADDONS.bootstrap.preliminary),
+ stage,
+ ID
+ );
+
+ await promiseStartupManager();
+
+ let addon = await promiseAddonByID(ID);
+ Assert.notEqual(addon, null);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.isActive);
+ Assert.equal(addon.signedState, AddonManager.SIGNEDSTATE_PRELIMINARY);
+ Assert.equal(getActiveVersion(), 2);
+
+ await addon.uninstall();
+ await promiseShutdownManager();
+ resetPrefs();
+
+ Assert.ok(!file.exists());
+ clearCache(file);
+});