Bug 1284407 Generate addon id from the path for temporarily loaded xpis r?rhelmer
MozReview-Commit-ID: 4XChacM8xE0
--- 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",