tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 1e7b1753d4f32ed3f4ad7a523a41c455b672ce43
parent 928a253e9e58046a59d1a300a62f94f34da67a0b
Author: Andrea Marchesini <amarchesini@mozilla.com>
Date:   Wed,  3 Dec 2025 09:45:35 +0000

Bug 1986320 - Implement an add-on block URL-Classifier feature - part 4 - Expose the URLClassifier addon feature to non-recommended addons only, r=zombie

Differential Revision: https://phabricator.services.mozilla.com/D263546

Diffstat:
Mdom/chrome-webidl/WebExtensionPolicy.webidl | 8++++++++
Mnetwerk/url-classifier/UrlClassifierFeatureHarmfulAddonProtection.cpp | 6++++++
Mtoolkit/components/extensions/Extension.sys.mjs | 5+++++
Mtoolkit/components/extensions/ExtensionProcessScript.sys.mjs | 1+
Mtoolkit/components/extensions/WebExtensionPolicy.cpp | 5+++++
Mtoolkit/components/extensions/WebExtensionPolicy.h | 14++++++++++++++
Mtoolkit/mozapps/extensions/test/xpcshell/test_recommendations.js | 15+++++++++++++++
7 files changed, 54 insertions(+), 0 deletions(-)

diff --git a/dom/chrome-webidl/WebExtensionPolicy.webidl b/dom/chrome-webidl/WebExtensionPolicy.webidl @@ -126,6 +126,12 @@ interface WebExtensionPolicy { attribute boolean ignoreQuarantine; /** + * True if this extension has recommended state. + */ + [Cached, Pure] + readonly attribute boolean hasRecommendedState; + + /** * True if both e10s and webextensions.remote are enabled. This must be * used instead of checking the remote pref directly since remote extensions * require both to be enabled. @@ -324,6 +330,8 @@ dictionary WebExtensionInit { boolean ignoreQuarantine = false; + boolean hasRecommendedState = false; + boolean temporarilyInstalled = false; required WebExtensionLocalizeCallback localizeCallback; diff --git a/netwerk/url-classifier/UrlClassifierFeatureHarmfulAddonProtection.cpp b/netwerk/url-classifier/UrlClassifierFeatureHarmfulAddonProtection.cpp @@ -211,6 +211,12 @@ UrlClassifierFeatureHarmfulAddonProtection::MaybeCreate(nsIChannel* aChannel) { return nullptr; } + // Recommended add-ons are exempt. + extensions::WebExtensionPolicy* policy = GetAddonPolicy(aChannel); + if (policy && policy->HasRecommendedState()) { + return nullptr; + } + MaybeInitialize(); MOZ_ASSERT(gFeatureHarmfulAddonProtection); diff --git a/toolkit/components/extensions/Extension.sys.mjs b/toolkit/components/extensions/Extension.sys.mjs @@ -3469,6 +3469,8 @@ export class Extension extends ExtensionData { !!addonData.recommendationState?.states?.length || lazy.QuarantinedDomains.isUserAllowedAddonId(this.id); + this.hasRecommendedState = !!addonData.recommendationState?.states?.length; + this.views = new Set(); this._backgroundPageFrameLoader = null; @@ -3799,6 +3801,7 @@ export class Extension extends ExtensionData { optionalPermissions: this.optionalPermissions, isPrivileged: this.isPrivileged, ignoreQuarantine: this.ignoreQuarantine, + hasRecommendedState: this.hasRecommendedState, temporarilyInstalled: this.temporarilyInstalled, }; } @@ -4073,6 +4076,7 @@ export class Extension extends ExtensionData { baseURL: this.resourceURL, isPrivileged: this.isPrivileged, ignoreQuarantine: this.ignoreQuarantine, + hasRecommendedState: this.hasRecommendedState, temporarilyInstalled: this.temporarilyInstalled, allowedOrigins: new MatchPatternSet([]), localizeCallback: () => "", @@ -4089,6 +4093,7 @@ export class Extension extends ExtensionData { baseURL: this.resourceURL, isPrivileged: this.isPrivileged, ignoreQuarantine: this.ignoreQuarantine, + hasRecommendedState: this.hasRecommendedState, }); sharedData.set("extensions/pending", pendingExtensions); diff --git a/toolkit/components/extensions/ExtensionProcessScript.sys.mjs b/toolkit/components/extensions/ExtensionProcessScript.sys.mjs @@ -145,6 +145,7 @@ ExtensionManager = { isPrivileged: extension.isPrivileged, ignoreQuarantine: extension.ignoreQuarantine, + hasRecommendedState: extension.hasRecommendedState, temporarilyInstalled: extension.temporarilyInstalled, permissions: extension.permissions, allowedOrigins: extension.allowedOrigins, diff --git a/toolkit/components/extensions/WebExtensionPolicy.cpp b/toolkit/components/extensions/WebExtensionPolicy.cpp @@ -197,6 +197,7 @@ WebExtensionPolicyCore::WebExtensionPolicyCore(GlobalObject& aGlobal, mTemporarilyInstalled(aInit.mTemporarilyInstalled), mBackgroundWorkerScript(aInit.mBackgroundWorkerScript), mIgnoreQuarantine(aInit.mIsPrivileged || aInit.mIgnoreQuarantine), + mHasRecommendedState(aInit.mHasRecommendedState), mPermissions(new AtomSet(aInit.mPermissions)) { // In practice this is not necessary, but in tests where the uuid // passed in is not lowercased various tests can fail. @@ -468,6 +469,10 @@ void WebExtensionPolicy::SetIgnoreQuarantine(bool aIgnore) { mCore->SetIgnoreQuarantine(aIgnore); } +void WebExtensionPolicy::SetHasRecommendedState(bool aHasRecommendedState) { + mCore->SetHasRecommendedState(aHasRecommendedState); +} + void WebExtensionPolicy::RegisterContentScript( WebExtensionContentScript& script, ErrorResult& aRv) { // Raise an "invalid argument" error if the script is not related to diff --git a/toolkit/components/extensions/WebExtensionPolicy.h b/toolkit/components/extensions/WebExtensionPolicy.h @@ -144,6 +144,16 @@ class WebExtensionPolicyCore final { bool QuarantinedFromDoc(const DocInfo& aDoc) const; bool QuarantinedFromURI(const URLInfo& aURI) const MOZ_EXCLUDES(mLock); + bool HasRecommendedState() const MOZ_EXCLUDES(mLock) { + AutoReadLock lock(mLock); + return mHasRecommendedState; + } + + void SetHasRecommendedState(bool aHasRecommendedState) MOZ_EXCLUDES(mLock) { + AutoWriteLock lock(mLock); + mHasRecommendedState = aHasRecommendedState; + } + bool PrivateBrowsingAllowed() const; // Try to get a reference to the cycle-collected main-thread-only @@ -194,6 +204,7 @@ class WebExtensionPolicyCore final { mutable RWLock mLock{"WebExtensionPolicyCore"}; bool mIgnoreQuarantine MOZ_GUARDED_BY(mLock); + bool mHasRecommendedState MOZ_GUARDED_BY(mLock); RefPtr<AtomSet> mPermissions MOZ_GUARDED_BY(mLock); RefPtr<MatchPatternSetCore> mHostPermissions MOZ_GUARDED_BY(mLock); }; @@ -315,6 +326,9 @@ class WebExtensionPolicy final : public nsISupports, public nsWrapperCache { bool IgnoreQuarantine() const { return mCore->IgnoreQuarantine(); } void SetIgnoreQuarantine(bool aIgnore); + bool HasRecommendedState() const { return mCore->HasRecommendedState(); } + void SetHasRecommendedState(bool aHasRecommendedState); + void GetContentScripts(ScriptArray& aScripts) const; const ScriptArray& ContentScripts() const { return mContentScripts; } diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_recommendations.js b/toolkit/mozapps/extensions/test/xpcshell/test_recommendations.js @@ -700,6 +700,21 @@ add_task( extensionLine.awaitStartup(), ]); + Assert.equal( + WebExtensionPolicy.getByID(extensionInvalidRecommended.id) + .hasRecommendedState, + false + ); + Assert.equal( + WebExtensionPolicy.getByID(extensionValidRecommended.id) + .hasRecommendedState, + true + ); + Assert.equal( + WebExtensionPolicy.getByID(extensionLine.id).hasRecommendedState, + true + ); + // Uninstall all test extensions. await Promise.all( Object.keys(recommendationStatesPerId).map(async addonId => {