From 00ecde85e271f3bef3da4da0a10a708ef0ec151e Mon Sep 17 00:00:00 2001 From: celenity Date: Wed, 4 Jun 2025 01:11:56 -0400 Subject: [PATCH] feat: Allow installing add-ons from `addons.mozilla.org` without the privileged mozAddonManager API (Like Desktop), and disable mozAddonManager by default Signed-off-by: celenity --- README.md | 2 +- ...ons-from-amo-without-mozaddonmanager.patch | 91 +++++++++++++++++++ patches/preferences/ironfox.js | 8 ++ patches/restrict-mozaddonmanager.patch | 46 ---------- scripts/patches.yaml | 8 +- 5 files changed, 104 insertions(+), 51 deletions(-) create mode 100644 patches/install-addons-from-amo-without-mozaddonmanager.patch delete mode 100644 patches/restrict-mozaddonmanager.patch diff --git a/README.md b/README.md index 8a85314b..43e965eb 100644 --- a/README.md +++ b/README.md @@ -330,7 +330,7 @@ Changes to patches are licensed according to the header in the files this patch Parts of `branding.patch`, `librewolf-rs-blocker.patch`, and `ublock-assets.patch`, are adapted from [LibreWolf](https://librewolf.net/). See [LibreWolf License and Disclaimers](https://librewolf.net/license-disclaimers/). -`tor-spoof-english.patch` is adapted from the [Tor Project](hhttps://www.torproject.org/). See [LICENSE](https://gitlab.torproject.org/tpo/core/tor/-/raw/HEAD/LICENSE). +`install-addons-from-amo-without-mozaddonmanager.patch` and `tor-spoof-english.patch` are adapted from the [Tor Project](hhttps://www.torproject.org/). See [LICENSE](https://gitlab.torproject.org/tpo/core/tor/-/raw/HEAD/LICENSE). ## Notices diff --git a/patches/install-addons-from-amo-without-mozaddonmanager.patch b/patches/install-addons-from-amo-without-mozaddonmanager.patch new file mode 100644 index 00000000..e1e11e53 --- /dev/null +++ b/patches/install-addons-from-amo-without-mozaddonmanager.patch @@ -0,0 +1,91 @@ +diff --git a/toolkit/mozapps/extensions/AddonManager.sys.mjs b/toolkit/mozapps/extensions/AddonManager.sys.mjs +index e09ea87de8..9c230f27db 100644 +--- a/toolkit/mozapps/extensions/AddonManager.sys.mjs ++++ b/toolkit/mozapps/extensions/AddonManager.sys.mjs +@@ -84,6 +84,8 @@ const lazy = {}; + ChromeUtils.defineESModuleGetters(lazy, { + AbuseReporter: "resource://gre/modules/AbuseReporter.sys.mjs", + AddonRepository: "resource://gre/modules/addons/AddonRepository.sys.mjs", ++ GeckoViewWebExtension: "resource://gre/modules/GeckoViewWebExtension.sys.mjs", ++ EventDispatcher: "resource://gre/modules/Messaging.sys.mjs", + Extension: "resource://gre/modules/Extension.sys.mjs", + RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", + TelemetryTimestamps: "resource://gre/modules/TelemetryTimestamps.sys.mjs", +@@ -2346,6 +2348,22 @@ var AddonManagerInternal = { + return promiseInstall; + }, + ++ async installGeckoViewWebExtension(extensionUri) { ++ const installId = Services.uuid.generateUUID().toString(); ++ let { extension } = await lazy.GeckoViewWebExtension.installWebExtension( ++ installId, ++ extensionUri ++ ); ++ if (extension) { ++ await lazy.EventDispatcher.instance.sendRequest({ ++ type: "GeckoView:WebExtension:OnInstalled", ++ extension, ++ }); ++ } else { ++ console.error("Failed to install the extension: extension is null."); ++ } ++ }, ++ + /** + * Starts installation of an AddonInstall notifying the registered + * web install listener of a blocked or started install. +@@ -2518,6 +2536,11 @@ var AddonManagerInternal = { + ); + + if (installAllowed) { ++ if (AppConstants.platform == "android") { ++ aInstall.cancel(); ++ this.installGeckoViewWebExtension(aInstall.sourceURI); ++ return; ++ } + startInstall("AMO"); + } else if (installPerm === Ci.nsIPermissionManager.DENY_ACTION) { + // Block without prompt +diff --git a/toolkit/mozapps/extensions/components.conf b/toolkit/mozapps/extensions/components.conf +index 680aa57c07..1d0ce25519 100644 +--- a/toolkit/mozapps/extensions/components.conf ++++ b/toolkit/mozapps/extensions/components.conf +@@ -32,14 +32,10 @@ Classes = [ + 'esModule': 'resource://gre/modules/amWebAPI.sys.mjs', + 'constructor': 'WebAPI', + }, ++ { ++ 'cid': '{7beb3ba8-6ec3-41b4-b67c-da89b8518922}', ++ 'contract_ids': ['@mozilla.org/uriloader/content-handler;1?type=application/x-xpinstall'], ++ 'esModule': 'resource://gre/modules/amContentHandler.sys.mjs', ++ 'constructor': 'amContentHandler', ++ }, + ] +- +-if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] != 'android': +- Classes += [ +- { +- 'cid': '{7beb3ba8-6ec3-41b4-b67c-da89b8518922}', +- 'contract_ids': ['@mozilla.org/uriloader/content-handler;1?type=application/x-xpinstall'], +- 'esModule': 'resource://gre/modules/amContentHandler.sys.mjs', +- 'constructor': 'amContentHandler', +- }, +- ] +diff --git a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs +index 03c4f77791..34891b361c 100644 +--- a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs ++++ b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs +@@ -4477,6 +4477,13 @@ export var XPIInstall = { + return false; + } + ++ if ( ++ AppConstants.platform == "android" && ++ uri.prePath == "https://addons.mozilla.org" ++ ) { ++ return true; ++ } ++ + let requireWhitelist = Services.prefs.getBoolPref( + PREF_XPI_WHITELIST_REQUIRED, + true diff --git a/patches/preferences/ironfox.js b/patches/preferences/ironfox.js index 84e80ff5..da09dd9b 100644 --- a/patches/preferences/ironfox.js +++ b/patches/preferences/ironfox.js @@ -11,6 +11,14 @@ pref("privacy.fingerprintingProtection.overrides", ""); // [DEFAULT] // We're including these internally with a custom Remote Settings dump instead of setting them here, which makes it far easier for users to add their own overrides if desired (by using this preference). pref("privacy.fingerprintingProtection.granularOverrides", ''); // [DEFAULT] +/// Disable mozAddonManager +// mozAddonManager prevents extensions from working on `addons.mozilla.org`, and this API also exposes a list of the user's installed add-ons to `addons.mozilla.org` +// Disabling the following preferences typically breaks installation of extensions from `addons.mozilla.org` on Android, but we fix this with our `install-addons-from-amo-without-mozaddonmanager` patch. +// https://bugzilla.mozilla.org/show_bug.cgi?id=1952390#c4 +// https://bugzilla.mozilla.org/show_bug.cgi?id=1384330 +pref("extensions.webapi.enabled", false); +pref("privacy.resistFingerprinting.block_mozAddonManager", true); + /// Re-enable the use of Cookie Banner Reduction rules from Remote Settings // We disable this functionality in Phoenix and instead set the rules locally via the "cookiebanners.listService.testRules" pref // We include the Cookie Banner Reduction rules local dump though, so we can just leave this on, but block remotely fetching the rules with the "browser.ironfox.services.settings.allowedCollections" pref instead diff --git a/patches/restrict-mozaddonmanager.patch b/patches/restrict-mozaddonmanager.patch deleted file mode 100644 index f4919e3b..00000000 --- a/patches/restrict-mozaddonmanager.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff --git a/toolkit/mozapps/extensions/amManager.sys.mjs b/toolkit/mozapps/extensions/amManager.sys.mjs -index 3d9f3e6b40..aa1ed91690 100644 ---- a/toolkit/mozapps/extensions/amManager.sys.mjs -+++ b/toolkit/mozapps/extensions/amManager.sys.mjs -@@ -179,16 +179,6 @@ amManager.prototype = { - target.sendAsyncMessage(MSG_ADDON_EVENT, { event, id }); - }; - let listener = { -- onEnabling: addon => handler("onEnabling", addon.id), -- onEnabled: addon => handler("onEnabled", addon.id), -- onDisabling: addon => handler("onDisabling", addon.id), -- onDisabled: addon => handler("onDisabled", addon.id), -- onInstalling: addon => handler("onInstalling", addon.id), -- onInstalled: addon => handler("onInstalled", addon.id), -- onUninstalling: addon => handler("onUninstalling", addon.id), -- onUninstalled: addon => handler("onUninstalled", addon.id), -- onOperationCancelled: addon => -- handler("onOperationCancelled", addon.id), - }; - this.addonListeners.set(target, listener); - AddonManager.addAddonListener(listener); -diff --git a/toolkit/mozapps/extensions/amWebAPI.sys.mjs b/toolkit/mozapps/extensions/amWebAPI.sys.mjs -index 59d093603a..0c9d9c6176 100644 ---- a/toolkit/mozapps/extensions/amWebAPI.sys.mjs -+++ b/toolkit/mozapps/extensions/amWebAPI.sys.mjs -@@ -154,11 +154,9 @@ class Addon extends APIObject { - } - - uninstall() { -- return this._apiTask("addonUninstall", [this.id]); - } - - setEnabled(value) { -- return this._apiTask("addonSetEnabled", [this.id, value]); - } - } - -@@ -210,7 +208,7 @@ export class WebAPI extends APIObject { - - getAddonByID(id) { - return this._apiTask("getAddonByID", [id], addonInfo => { -- if (!addonInfo) { -+ if (!addonInfo || addonInfo) { - return null; - } - let addon = new Addon(this.window, this.broker, addonInfo); diff --git a/scripts/patches.yaml b/scripts/patches.yaml index 2325594b..bc361e15 100644 --- a/scripts/patches.yaml +++ b/scripts/patches.yaml @@ -229,11 +229,11 @@ patches: effect: "Clean new tab page without predetermined site suggestions." category: "Privacy" - - file: "restrict-mozaddonmanager.patch" - name: "Restrict mozAddonManager" - description: "Prevents addons.mozilla.org from being able to query a list of the user's installed add-ons, and removes its ability to enable and uninstall add-ons." + - file: "install-addons-from-amo-without-mozaddonmanager.patch" + name: "Allow installation of add-ons from the AMO (addons.mozilla.org) without the mozAddonManager API" + description: "Allows users to install add-ons from addons.mozilla.org, without allowing Mozilla to enable, uninstall, and query a list of the user's installed add-ons (via the mozAddonManager API)." reason: "To prevent fingerprinting and reduce attack surface." - effect: "Allows users to install add-ons from addons.mozilla.org (via the mozAddonManager API), while reducing the amount of information shared with Mozilla and limiting the API's capabilities." + effect: "Users can safely install add-ons from addons.mozilla.org, without compromising privacy and security." category: "Privacy" - file: "sanitize-on-exit.patch"