Bug 1357205 - only load system add-ons from a built-in list r?aswan
MozReview-Commit-ID: A6c5kaLmNPP
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -53,17 +53,17 @@ XPCOMUtils.defineLazyServiceGetters(this
Blocklist: ["@mozilla.org/extensions/blocklist;1", "nsIBlocklistService"],
ChromeRegistry: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
ResProtocolHandler: ["@mozilla.org/network/protocol;1?name=resource", "nsIResProtocolHandler"],
AddonPolicyService: ["@mozilla.org/addons/policy-service;1", "nsIAddonPolicyService"],
AddonPathService: ["@mozilla.org/addon-path-service;1", "amIAddonPathService"],
aomStartup: ["@mozilla.org/addons/addon-manager-startup;1", "amIAddonManagerStartup"],
});
-Cu.importGlobalProperties(["URL"]);
+Cu.importGlobalProperties(["URL", "fetch"]);
const nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
"initWithPath");
const PREF_DB_SCHEMA = "extensions.databaseSchema";
const PREF_XPI_STATE = "extensions.xpiState";
const PREF_BLOCKLIST_ITEM_URL = "extensions.blocklist.itemURL";
const PREF_BOOTSTRAP_ADDONS = "extensions.bootstrappedAddons";
@@ -97,16 +97,21 @@ const PREF_ALLOW_LEGACY =
const PREF_ALLOW_NON_MPC = "extensions.allow-non-mpc-extensions";
const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
const PREF_EM_LAST_APP_BUILD_ID = "extensions.lastAppBuildId";
+// Specify a list of valid built-in add-ons to load.
+// Only works in automation; otherwise comes from BUILT_IN_ADDONS_URI.
+const PREF_BUILT_IN_ADDONS = "extensions.builtInAddons";
+const BUILT_IN_ADDONS_URI = "resource:///chrome/browser/content/browser/built_in_addons.js";
+
const OBSOLETE_PREFERENCES = [
"extensions.bootstrappedAddons",
"extensions.enabledAddons",
"extensions.xpiState",
"extensions.installCache",
];
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
@@ -2034,16 +2039,28 @@ this.XPIProvider = {
logger.warn("Failed to add directory install location " + aName, e);
return;
}
XPIProvider.installLocations.push(location);
XPIProvider.installLocationsByName[location.name] = location;
}
+ function addBuiltInInstallLocation(name, key, paths, scope) {
+ try {
+ let dir = FileUtils.getDir(key, paths);
+ let location = new BuiltInInstallLocation(name, dir, scope);
+
+ XPIProvider.installLocations.push(location);
+ XPIProvider.installLocationsByName[location.name] = location;
+ } catch(e) {
+ logger.warn(`Failed to add built-in install location ${name}`, e);
+ }
+ }
+
function addSystemAddonInstallLocation(aName, aKey, aPaths, aScope) {
try {
var dir = FileUtils.getDir(aKey, aPaths);
} catch (e) {
// Some directories aren't defined on some platforms, ignore them
logger.debug("Skipping unavailable install location " + aName);
return;
}
@@ -2102,18 +2119,18 @@ this.XPIProvider = {
addDirectoryInstallLocation(KEY_APP_PROFILE, KEY_PROFILEDIR,
[DIR_EXTENSIONS],
AddonManager.SCOPE_PROFILE, false);
addSystemAddonInstallLocation(KEY_APP_SYSTEM_ADDONS, KEY_PROFILEDIR,
[DIR_SYSTEM_ADDONS],
AddonManager.SCOPE_PROFILE);
- addDirectoryInstallLocation(KEY_APP_SYSTEM_DEFAULTS, KEY_APP_FEATURES,
- [], AddonManager.SCOPE_PROFILE, true);
+ addBuiltInInstallLocation(KEY_APP_SYSTEM_DEFAULTS, KEY_APP_FEATURES,
+ [], AddonManager.SCOPE_PROFILE);
if (enabledScopes & AddonManager.SCOPE_USER) {
addDirectoryInstallLocation(KEY_APP_SYSTEM_USER, "XREUSysExt",
[Services.appinfo.ID],
AddonManager.SCOPE_USER, true);
if (hasRegistry) {
addRegistryInstallLocation("winreg-app-user",
Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
@@ -6485,16 +6502,79 @@ class MutableDirectoryInstallLocation ex
XPIStates.removeAddon(this.name, aId);
delete this._IDToFileMap[aId];
}
}
/**
+ * An object which identifies a built-in install location for add-ons.
+ * Generally this is either the omni jar or a directory in a local build.
+ */
+class BuiltInInstallLocation extends DirectoryInstallLocation {
+ /**
+ * @param {string} name - The string identifier of this location.
+ * @param {nsIFile} directory - The directory containing files for this location.
+ * @param {string} scope - The scope of add-ons installed in this location.
+ */
+ constructor(name, directory, scope) {
+ super(name, directory, scope);
+
+ this.locked = true;
+ this._IDToFileMap = {};
+ }
+
+ /**
+ * Read the index and build a mapping between ID and URI for each
+ * installed add-on.
+ */
+ async _readAddons() {
+ let index;
+ try {
+ if (Cu.isInAutomation) {
+ index = JSON.parse(Services.prefs.getStringPref(PREF_BUILT_IN_ADDONS, ""));
+ } else {
+ dump(`rhelmer debug NOT in automation\n`);
+ let url = Services.io.newURI(BUILT_IN_ADDONS_URI);
+ let response = await fetch(url.spec);
+ index = await response.json();
+ }
+ } catch (e) {
+ logger.warn("List of valid built-in add-ons could not be parsed.", e);
+ return;
+ }
+
+ if (!("system" in index)) {
+ logger.warn("No list of valid system add-ons found.");
+ return;
+ }
+
+ for (let id of index.system) {
+ let file = new nsIFile(this._directory.path);
+ file.append(`${id}.xpi`);
+
+ if (!file.exists()) {
+ // if the XPI file can't be loaded, try the directory.
+ file = new nsIFile(this._directory.path);
+ file.append(`${id}`);
+ }
+
+ if (!file.exists()) {
+ logger.warn("Ignoring missing add-on in " + file.path);
+ continue;
+ }
+
+ this._IDToFileMap[id] = file;
+ XPIProvider._addURIMapping(id, file);
+ }
+ }
+}
+
+/**
* An object which identifies a directory install location for system add-ons
* updates.
*/
class SystemAddonInstallLocation extends MutableDirectoryInstallLocation {
/**
* The location consists of a directory which contains the add-ons installed.
*
* @param aName
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -19,16 +19,17 @@ const PREF_EM_MIN_COMPAT_APP_VERSION
const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
const PREF_GETADDONS_BYIDS_PERFORMANCE = "extensions.getAddons.getWithPerformance.url";
const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
const PREF_SYSTEM_ADDON_UPDATE_URL = "extensions.systemAddon.update.url";
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
const PREF_ALLOW_NON_MPC = "extensions.allow-non-mpc-extensions";
+const PREF_BUILT_IN_ADDONS = "extensions.builtInAddons";
// Forcibly end the test if it runs longer than 15 minutes
const TIMEOUT_MS = 900000;
// Maximum error in file modification times. Some file systems don't store
// modification times exactly. As long as we are closer than this then it
// still passes.
const MAX_TIME_DIFFERENCE = 3000;
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_allowed.js
@@ -0,0 +1,58 @@
+// Tests that AddonRepository doesn't download results for system add-ons
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+BootstrapMonitor.init();
+
+// Build the test set
+var distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
+do_get_file("data/system_addons/system1_1.xpi").copyTo(distroDir, "system1@tests.mozilla.org.xpi");
+do_get_file("data/system_addons/system2_1.xpi").copyTo(distroDir, "system2@tests.mozilla.org.xpi");
+do_get_file("data/system_addons/system3_1.xpi").copyTo(distroDir, "system3@tests.mozilla.org.xpi");
+registerDirectory("XREAppFeat", distroDir);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "0");
+
+// Built-in add-ons can only be overridden via a pref if in automation.
+const PREF_DISABLE_SECURITY = ("security.turn_off_all_security_so_that_" +
+ "viruses_can_take_over_this_computer");
+Services.prefs.setBoolPref(PREF_DISABLE_SECURITY, true);
+
+let validAddons = { "system": ["system1@tests.mozilla.org", "system2@tests.mozilla.org"]};
+Services.prefs.setCharPref(PREF_BUILT_IN_ADDONS, JSON.stringify(validAddons));
+
+function getCachedAddon(id) {
+ return new Promise(resolve => AddonRepository.getCachedAddonByID(id, resolve));
+}
+
+// Ensure that only allowed add-ons are loaded.
+add_task(async function test_allowed_addons() {
+ startupManager();
+
+ // 1 and 2 are allowed, 3 is not.
+ let addon = await AddonManager.getAddonByID("system1@tests.mozilla.org");
+ do_check_neq(addon, null);
+
+ addon = await AddonManager.getAddonByID("system2@tests.mozilla.org");
+ do_check_neq(addon, null);
+
+ addon = await AddonManager.getAddonByID("system3@tests.mozilla.org");
+ do_check_eq(addon, null);
+
+ // 3 is now allowed, 1 and 2 are not.
+ validAddons = { "system": ["system3@tests.mozilla.org"]};
+ Services.prefs.setCharPref(PREF_BUILT_IN_ADDONS, JSON.stringify(validAddons));
+
+ await promiseRestartManager();
+
+ addon = await AddonManager.getAddonByID("system1@tests.mozilla.org");
+ do_check_eq(addon, null);
+
+ addon = await AddonManager.getAddonByID("system2@tests.mozilla.org");
+ do_check_eq(addon, null);
+
+ addon = await AddonManager.getAddonByID("system3@tests.mozilla.org");
+ do_check_neq(addon, null);
+
+ await promiseShutdownManager();
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_delay_update.js
@@ -9,16 +9,23 @@ const profileDir = gProfD.clone();
profileDir.append("extensions");
const IGNORE_ID = "system_delay_ignore@tests.mozilla.org";
const COMPLETE_ID = "system_delay_complete@tests.mozilla.org";
const DEFER_ID = "system_delay_defer@tests.mozilla.org";
const DEFER_ALSO_ID = "system_delay_defer_also@tests.mozilla.org";
const NORMAL_ID = "system1@tests.mozilla.org";
+// Built-in add-ons can only be overridden via a pref if in automation.
+const PREF_DISABLE_SECURITY = ("security.turn_off_all_security_so_that_" +
+ "viruses_can_take_over_this_computer");
+Services.prefs.setBoolPref(PREF_DISABLE_SECURITY, true);
+
+let validAddons = { "system": [IGNORE_ID, COMPLETE_ID, DEFER_ID, DEFER_ALSO_ID, NORMAL_ID]};
+Services.prefs.setCharPref(PREF_BUILT_IN_ADDONS, JSON.stringify(validAddons));
const TEST_IGNORE_PREF = "delaytest.ignore";
const distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
registerDirectory("XREAppFeat", distroDir);
let testserver = new HttpServer();
testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_repository.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_repository.js
@@ -12,16 +12,25 @@ gServer.start(-1);
var distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
do_get_file("data/system_addons/system1_1.xpi").copyTo(distroDir, "system1@tests.mozilla.org.xpi");
do_get_file("data/system_addons/system2_1.xpi").copyTo(distroDir, "system2@tests.mozilla.org.xpi");
do_get_file("data/system_addons/system3_1.xpi").copyTo(distroDir, "system3@tests.mozilla.org.xpi");
registerDirectory("XREAppFeat", distroDir);
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "0");
+// Built-in add-ons can only be overridden via a pref if in automation.
+const PREF_DISABLE_SECURITY = ("security.turn_off_all_security_so_that_" +
+ "viruses_can_take_over_this_computer");
+Services.prefs.setBoolPref(PREF_DISABLE_SECURITY, true);
+
+let validAddons = { "system": ["system3@tests.mozilla.org", "system2@tests.mozilla.org",
+ "system1@tests.mozilla.org"]};
+Services.prefs.setCharPref(PREF_BUILT_IN_ADDONS, JSON.stringify(validAddons));
+
function getCachedAddon(id) {
return new Promise(resolve => AddonRepository.getCachedAddonByID(id, resolve));
}
// Test with a missing features directory
add_task(async function test_app_addons() {
Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, `http://localhost:${gServer.identity.primaryPort}/get?%IDS%`);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
@@ -1,13 +1,22 @@
// Tests that we reset to the default system add-ons correctly when switching
// application versions
BootstrapMonitor.init();
+// Built-in add-ons can only be overridden via a pref if in automation.
+const PREF_DISABLE_SECURITY = ("security.turn_off_all_security_so_that_" +
+ "viruses_can_take_over_this_computer");
+Services.prefs.setBoolPref(PREF_DISABLE_SECURITY, true);
+
+let validAddons = { "system": ["system3@tests.mozilla.org", "system2@tests.mozilla.org",
+ "system1@tests.mozilla.org"]};
+Services.prefs.setCharPref(PREF_BUILT_IN_ADDONS, JSON.stringify(validAddons));
+
const updatesDir = FileUtils.getDir("ProfD", ["features"]);
// Build the test sets
var dir = FileUtils.getDir("ProfD", ["sysfeatures", "app1"], true);
do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
dir = FileUtils.getDir("ProfD", ["sysfeatures", "app2"], true);
@@ -412,9 +421,8 @@ add_task(async function test_updated_bad
let conditions = [
{ isUpgrade: false, version: "1.0" },
];
await check_installed(conditions);
await promiseShutdownManager();
});
-
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
@@ -9,16 +9,25 @@ createAppInfo("xpcshell@tests.mozilla.or
var testserver = new HttpServer();
testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
testserver.start();
var root = testserver.identity.primaryScheme + "://" +
testserver.identity.primaryHost + ":" +
testserver.identity.primaryPort + "/data/"
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml");
+// Built-in add-ons can only be overridden via a pref if in automation.
+const PREF_DISABLE_SECURITY = ("security.turn_off_all_security_so_that_" +
+ "viruses_can_take_over_this_computer");
+Services.prefs.setBoolPref(PREF_DISABLE_SECURITY, true);
+
+let validAddons = { "system": ["system3@tests.mozilla.org", "system2@tests.mozilla.org",
+ "system1@tests.mozilla.org"]};
+Services.prefs.setCharPref(PREF_BUILT_IN_ADDONS, JSON.stringify(validAddons));
+
let distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true);
registerDirectory("XREAppFeat", distroDir);
initSystemAddonDirs();
/**
* Defines the set of initial conditions to run each test against. Each should
* define the following properties:
*
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_update_fail.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_update_fail.js
@@ -9,16 +9,25 @@ createAppInfo("xpcshell@tests.mozilla.or
var testserver = new HttpServer();
testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
testserver.start();
var root = testserver.identity.primaryScheme + "://" +
testserver.identity.primaryHost + ":" +
testserver.identity.primaryPort + "/data/"
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml");
+// Built-in add-ons can only be overridden via a pref if in automation.
+const PREF_DISABLE_SECURITY = ("security.turn_off_all_security_so_that_" +
+ "viruses_can_take_over_this_computer");
+Services.prefs.setBoolPref(PREF_DISABLE_SECURITY, true);
+
+let validAddons = { "system": ["system3@tests.mozilla.org", "system2@tests.mozilla.org",
+ "system1@tests.mozilla.org"]};
+Services.prefs.setCharPref(PREF_BUILT_IN_ADDONS, JSON.stringify(validAddons));
+
let distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true);
registerDirectory("XREAppFeat", distroDir);
initSystemAddonDirs();
/**
* Defines the set of initial conditions to run each test against. Each should
* define the following properties:
*
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -46,10 +46,11 @@ tags = webextensions
[test_nodisable_hidden.js]
[test_delay_update_webextension.js]
skip-if = appname == "thunderbird"
tags = webextensions
[test_dependencies.js]
[test_system_delay_update.js]
[test_schema_change.js]
[test_registerchrome.js]
+[test_system_allowed.js]
[include:xpcshell-shared.ini]