diff options
Diffstat (limited to 'toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js')
-rw-r--r-- | toolkit/mozapps/extensions/test/xpcshell/test_signed_inject.js | 429 |
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); +}); |