commit f1a3613f4487c3fd4dc5b1e2add94c5a560af8eb parent f8ea0f226284bb76ac8fb52806c4cc3a05eb7803 Author: Andrea Marchesini <amarchesini@mozilla.com> Date: Wed, 3 Dec 2025 15:45:12 +0000 Bug 1986320 - Implement an add-on block URL-Classifier feature - part 1, r=dimi,geckoview-reviewers,extension-reviewers,tcampbell,robwu Differential Revision: https://phabricator.services.mozilla.com/D263290 Diffstat:
17 files changed, 445 insertions(+), 4 deletions(-)
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js @@ -1755,6 +1755,7 @@ pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true); pref("services.sync.prefs.sync.privacy.globalprivacycontrol.enabled", true); pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true); pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true); +pref("services.sync.prefs.sync.privacy.trackingprotection.harmfuladdon.enabled", true); pref("services.sync.prefs.sync.privacy.trackingprotection.cryptomining.enabled", true); pref("services.sync.prefs.sync.privacy.trackingprotection.fingerprinting.enabled", true); pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true); @@ -2402,6 +2403,9 @@ pref("network.cookie.cookieBehavior", 5 /* BEHAVIOR_REJECT_TRACKER_AND_PARTITION // Enable Dynamic First-Party Isolation in the private browsing mode. pref("network.cookie.cookieBehavior.pbmode", 5 /* BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN */); +// Enable harmful addon URL blocking by default for all channels, only on desktop. +pref("privacy.trackingprotection.harmfuladdon.enabled", true); + // Enable fingerprinting blocking by default for all channels, only on desktop. pref("privacy.trackingprotection.fingerprinting.enabled", true); diff --git a/browser/components/enterprisepolicies/Policies.sys.mjs b/browser/components/enterprisepolicies/Policies.sys.mjs @@ -1313,6 +1313,13 @@ export var Policies = { param.Locked ); } + if ("HarmfulAddon" in param) { + PoliciesUtils.setDefaultPref( + "privacy.trackingprotection.harmfuladdon.enabled", + param.HarmfulAddon, + param.Locked + ); + } if ("Fingerprinting" in param) { PoliciesUtils.setDefaultPref( "privacy.trackingprotection.fingerprinting.enabled", diff --git a/js/xpconnect/src/xpc.msg b/js/xpconnect/src/xpc.msg @@ -259,6 +259,7 @@ XPC_MSG_DEF(NS_ERROR_FINGERPRINTING_URI , "The URI is fingerprinti XPC_MSG_DEF(NS_ERROR_CRYPTOMINING_URI , "The URI is cryptomining") XPC_MSG_DEF(NS_ERROR_SOCIALTRACKING_URI , "The URI is social tracking") XPC_MSG_DEF(NS_ERROR_EMAILTRACKING_URI , "The URI is email tracking") +XPC_MSG_DEF(NS_ERROR_HARMFULADDON_URI , "The URI is not available for add-ons") /* Profile manager error codes */ XPC_MSG_DEF(NS_ERROR_DATABASE_CHANGED , "Flushing the profiles to disk would have overwritten changes made elsewhere.") diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml @@ -16345,6 +16345,12 @@ value: true mirror: always +# Block harmful addon URLs. +- name: privacy.trackingprotection.harmfuladdon.enabled + type: bool + value: false + mirror: always + # Block 3rd party fingerprinting resources. - name: privacy.trackingprotection.fingerprinting.enabled type: bool diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js @@ -3311,6 +3311,8 @@ pref("urlclassifier.trackingAnnotationWhitelistTable", "moztest-trackwhite-simpl pref("urlclassifier.trackingTable", "moztest-track-simple,ads-track-digest256,social-track-digest256,analytics-track-digest256"); pref("urlclassifier.trackingWhitelistTable", "moztest-trackwhite-simple,mozstd-trackwhite-digest256,google-trackwhite-digest256"); +pref("urlclassifier.features.harmfuladdon.blocklistTables", "harmfuladdon-block-digest256"); +pref("urlclassifier.features.harmfuladdon.entitylistTables", "harmfuladdon-entitylist-digest256"); pref("urlclassifier.features.fingerprinting.blacklistTables", "base-fingerprinting-track-digest256"); pref("urlclassifier.features.fingerprinting.whitelistTables", "mozstd-trackwhite-digest256,google-trackwhite-digest256"); pref("urlclassifier.features.fingerprinting.annotate.blacklistTables", "base-fingerprinting-track-digest256"); @@ -3428,7 +3430,7 @@ pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozil // Mozilla Safe Browsing provider (for tracking protection and plugin blocking) pref("browser.safebrowsing.provider.mozilla.pver", "2.2"); -pref("browser.safebrowsing.provider.mozilla.lists", "base-track-digest256,mozstd-trackwhite-digest256,google-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,ads-track-digest256,social-track-digest256,analytics-track-digest256,base-fingerprinting-track-digest256,content-fingerprinting-track-digest256,base-cryptomining-track-digest256,content-cryptomining-track-digest256,fanboyannoyance-ads-digest256,fanboysocial-ads-digest256,easylist-ads-digest256,easyprivacy-ads-digest256,adguard-ads-digest256,social-tracking-protection-digest256,social-tracking-protection-facebook-digest256,social-tracking-protection-linkedin-digest256,social-tracking-protection-twitter-digest256,base-email-track-digest256,content-email-track-digest256,consent-manager-track-digest256,anti-fraud-track-digest256"); +pref("browser.safebrowsing.provider.mozilla.lists", "base-track-digest256,mozstd-trackwhite-digest256,google-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,ads-track-digest256,social-track-digest256,analytics-track-digest256,base-fingerprinting-track-digest256,content-fingerprinting-track-digest256,base-cryptomining-track-digest256,content-cryptomining-track-digest256,fanboyannoyance-ads-digest256,fanboysocial-ads-digest256,easylist-ads-digest256,easyprivacy-ads-digest256,adguard-ads-digest256,social-tracking-protection-digest256,social-tracking-protection-facebook-digest256,social-tracking-protection-linkedin-digest256,social-tracking-protection-twitter-digest256,base-email-track-digest256,content-email-track-digest256,consent-manager-track-digest256,anti-fraud-track-digest256,harmfuladdon-block-digest256,harmfuladdon-entitylist-digest256"); pref("browser.safebrowsing.provider.mozilla.updateURL", "moz-sbrs:://antitracking"); pref("browser.safebrowsing.provider.mozilla.gethashURL", "https://shavar.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2"); // Set to a date in the past to force immediate download in new profiles. diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp @@ -1198,6 +1198,7 @@ void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest, aChannelStatus == NS_ERROR_UNWANTED_URI || aChannelStatus == NS_ERROR_BLOCKED_URI || aChannelStatus == NS_ERROR_HARMFUL_URI || + aChannelStatus == NS_ERROR_HARMFULADDON_URI || aChannelStatus == NS_ERROR_PHISHING_URI) { nsCString list, provider, fullhash; diff --git a/netwerk/url-classifier/UrlClassifierCommon.cpp b/netwerk/url-classifier/UrlClassifierCommon.cpp @@ -58,7 +58,7 @@ bool UrlClassifierCommon::AddonMayLoad(nsIChannel* aChannel, nsIURI* aURI) { /* static */ bool UrlClassifierCommon::ShouldEnableProtectionForChannel( - nsIChannel* aChannel) { + nsIChannel* aChannel, bool aShouldAllowAddons) { MOZ_ASSERT(aChannel); nsCOMPtr<nsIURI> chanURI; @@ -67,7 +67,8 @@ bool UrlClassifierCommon::ShouldEnableProtectionForChannel( return false; } - if (UrlClassifierCommon::AddonMayLoad(aChannel, chanURI)) { + if (aShouldAllowAddons && + UrlClassifierCommon::AddonMayLoad(aChannel, chanURI)) { return false; } @@ -139,6 +140,10 @@ nsresult UrlClassifierCommon::SetBlockedContent(nsIChannel* channel, NS_ENSURE_ARG(!aList.IsEmpty()); switch (aErrorCode) { + case NS_ERROR_HARMFULADDON_URI: + NS_SetRequestBlockingReason( + channel, nsILoadInfo::BLOCKING_REASON_CLASSIFY_BLOCKED_URI); + break; case NS_ERROR_MALWARE_URI: NS_SetRequestBlockingReason( channel, nsILoadInfo::BLOCKING_REASON_CLASSIFY_MALWARE_URI); diff --git a/netwerk/url-classifier/UrlClassifierCommon.h b/netwerk/url-classifier/UrlClassifierCommon.h @@ -39,7 +39,8 @@ class UrlClassifierCommon final { static bool AddonMayLoad(nsIChannel* aChannel, nsIURI* aURI); - static bool ShouldEnableProtectionForChannel(nsIChannel* aChannel); + static bool ShouldEnableProtectionForChannel(nsIChannel* aChannel, + bool aShouldAllowAddons = true); static nsresult SetBlockedContent(nsIChannel* channel, nsresult aErrorCode, const nsACString& aList, diff --git a/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp b/netwerk/url-classifier/UrlClassifierFeatureFactory.cpp @@ -15,6 +15,7 @@ #include "UrlClassifierFeatureEmailTrackingProtection.h" #include "UrlClassifierFeatureFingerprintingAnnotation.h" #include "UrlClassifierFeatureFingerprintingProtection.h" +#include "UrlClassifierFeatureHarmfulAddonProtection.h" #include "UrlClassifierFeaturePhishingProtection.h" #include "UrlClassifierFeatureSocialTrackingAnnotation.h" #include "UrlClassifierFeatureSocialTrackingProtection.h" @@ -48,6 +49,7 @@ void UrlClassifierFeatureFactory::Shutdown() { UrlClassifierFeatureSocialTrackingProtection::MaybeShutdown(); UrlClassifierFeatureTrackingAnnotation::MaybeShutdown(); UrlClassifierFeatureTrackingProtection::MaybeShutdown(); + UrlClassifierFeatureHarmfulAddonProtection::MaybeShutdown(); } /* static */ @@ -114,6 +116,12 @@ void UrlClassifierFeatureFactory::GetFeaturesFromChannel( aFeatures.AppendElement(feature); } + // Addon Protection + feature = UrlClassifierFeatureHarmfulAddonProtection::MaybeCreate(aChannel); + if (feature) { + aFeatures.AppendElement(feature); + } + // Tracking Protection feature = UrlClassifierFeatureTrackingProtection::MaybeCreate(aChannel); if (feature) { @@ -245,6 +253,13 @@ UrlClassifierFeatureFactory::GetFeatureByName(const nsACString& aName) { return feature.forget(); } + // Addon Protection + feature = + UrlClassifierFeatureHarmfulAddonProtection::GetIfNameMatches(aName); + if (feature) { + return feature.forget(); + } + return nullptr; } @@ -328,6 +343,12 @@ void UrlClassifierFeatureFactory::GetFeatureNames(nsTArray<nsCString>& aArray) { aArray.AppendElement(name); } + // Addon Protection + name.Assign(UrlClassifierFeatureHarmfulAddonProtection::Name()); + if (!name.IsEmpty()) { + aArray.AppendElement(name); + } + // PhishingProtection features { nsTArray<nsCString> features; diff --git a/netwerk/url-classifier/UrlClassifierFeatureHarmfulAddonProtection.cpp b/netwerk/url-classifier/UrlClassifierFeatureHarmfulAddonProtection.cpp @@ -0,0 +1,204 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "UrlClassifierFeatureHarmfulAddonProtection.h" + +#include "mozilla/AntiTrackingUtils.h" +#include "mozilla/net/UrlClassifierCommon.h" +#include "ChannelClassifierService.h" +#include "mozilla/StaticPrefs_privacy.h" +#include "nsNetUtil.h" +#include "mozilla/StaticPtr.h" +#include "nsIWebProgressListener.h" +#include "nsIHttpChannelInternal.h" +#include "nsIChannel.h" + +namespace mozilla { +namespace net { + +namespace { + +#define HARMFULADDON_FEATURE_NAME "harmfuladdon-protection" + +#define URLCLASSIFIER_HARMFULADDON_BLOCKLIST \ + "urlclassifier.features.harmfuladdon.blocklistTables" +#define URLCLASSIFIER_HARMFULADDON_BLOCKLIST_TEST_ENTRIES \ + "urlclassifier.features.harmfuladdon.blocklistHosts" +#define URLCLASSIFIER_HARMFULADDON_ENTITYLIST \ + "urlclassifier.features.harmfuladdon.entitylistTables" +#define URLCLASSIFIER_HARMFULADDON_ENTITYLIST_TEST_ENTRIES \ + "urlclassifier.features.harmfuladdon.entitylistHosts" +#define URLCLASSIFIER_HARMFULADDON_EXCEPTION_URLS \ + "urlclassifier.features.harmfuladdon.skipURLs" +#define TABLE_HARMFULADDON_BLOCKLIST_PREF "harmfuladdon-blocklist-pref" +#define TABLE_HARMFULADDON_ENTITYLIST_PREF "harmfuladdon-entitylist-pref" + +StaticRefPtr<UrlClassifierFeatureHarmfulAddonProtection> + gFeatureHarmfulAddonProtection; + +} // namespace + +UrlClassifierFeatureHarmfulAddonProtection:: + UrlClassifierFeatureHarmfulAddonProtection() + : UrlClassifierFeatureAntiTrackingBase( + nsLiteralCString(HARMFULADDON_FEATURE_NAME), + nsLiteralCString(URLCLASSIFIER_HARMFULADDON_BLOCKLIST), + nsLiteralCString(URLCLASSIFIER_HARMFULADDON_ENTITYLIST), + nsLiteralCString(URLCLASSIFIER_HARMFULADDON_BLOCKLIST_TEST_ENTRIES), + nsLiteralCString(URLCLASSIFIER_HARMFULADDON_ENTITYLIST_TEST_ENTRIES), + nsLiteralCString(TABLE_HARMFULADDON_BLOCKLIST_PREF), + nsLiteralCString(TABLE_HARMFULADDON_ENTITYLIST_PREF), + nsLiteralCString(URLCLASSIFIER_HARMFULADDON_EXCEPTION_URLS)) {} + +/* static */ const char* UrlClassifierFeatureHarmfulAddonProtection::Name() { + return HARMFULADDON_FEATURE_NAME; +} + +/* static */ +void UrlClassifierFeatureHarmfulAddonProtection::MaybeInitialize() { + UC_LOG_LEAK(("UrlClassifierFeatureHarmfulAddonProtection::MaybeInitialize")); + + if (!gFeatureHarmfulAddonProtection) { + gFeatureHarmfulAddonProtection = + new UrlClassifierFeatureHarmfulAddonProtection(); + gFeatureHarmfulAddonProtection->InitializePreferences(); + } +} + +/* static */ +void UrlClassifierFeatureHarmfulAddonProtection::MaybeShutdown() { + UC_LOG_LEAK(("UrlClassifierFeatureHarmfulAddonProtection::MaybeShutdown")); + + if (gFeatureHarmfulAddonProtection) { + gFeatureHarmfulAddonProtection->ShutdownPreferences(); + gFeatureHarmfulAddonProtection = nullptr; + } +} + +/* static */ +already_AddRefed<UrlClassifierFeatureHarmfulAddonProtection> +UrlClassifierFeatureHarmfulAddonProtection::MaybeCreate(nsIChannel* aChannel) { + MOZ_ASSERT(aChannel); + + UC_LOG_LEAK( + ("UrlClassifierFeatureHarmfulAddonProtection::MaybeCreate - channel %p", + aChannel)); + + if (!StaticPrefs::privacy_trackingprotection_harmfuladdon_enabled()) { + return nullptr; + } + + RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); + nsIPrincipal* triggeringPrincipal = loadInfo->TriggeringPrincipal(); + bool addonTriggeringPrincipal = + triggeringPrincipal && + triggeringPrincipal->GetIsAddonOrExpandedAddonPrincipal(); + + nsIPrincipal* loadingPrincipal = loadInfo->GetLoadingPrincipal(); + bool addonLoadingPrincipal = + loadingPrincipal && + loadingPrincipal->GetIsAddonOrExpandedAddonPrincipal(); + + if (!addonTriggeringPrincipal && !addonLoadingPrincipal) { + return nullptr; + } + + if (!UrlClassifierCommon::ShouldEnableProtectionForChannel(aChannel, false)) { + return nullptr; + } + + MaybeInitialize(); + MOZ_ASSERT(gFeatureHarmfulAddonProtection); + + RefPtr<UrlClassifierFeatureHarmfulAddonProtection> self = + gFeatureHarmfulAddonProtection; + return self.forget(); +} + +/* static */ +already_AddRefed<nsIUrlClassifierFeature> +UrlClassifierFeatureHarmfulAddonProtection::GetIfNameMatches( + const nsACString& aName) { + if (!aName.EqualsLiteral(HARMFULADDON_FEATURE_NAME)) { + return nullptr; + } + + MaybeInitialize(); + MOZ_ASSERT(gFeatureHarmfulAddonProtection); + + RefPtr<UrlClassifierFeatureHarmfulAddonProtection> self = + gFeatureHarmfulAddonProtection; + return self.forget(); +} + +NS_IMETHODIMP +UrlClassifierFeatureHarmfulAddonProtection::ProcessChannel( + nsIChannel* aChannel, const nsTArray<nsCString>& aList, + const nsTArray<nsCString>& aHashes, bool* aShouldContinue) { + NS_ENSURE_ARG_POINTER(aChannel); + NS_ENSURE_ARG_POINTER(aShouldContinue); + + bool isAllowListed = UrlClassifierCommon::IsAllowListed(aChannel); + + // This is a blocking feature. + *aShouldContinue = isAllowListed; + + if (isAllowListed) { + return NS_OK; + } + + bool ShouldProcessByProtectionFeature = + UrlClassifierCommon::ShouldProcessWithProtectionFeature(aChannel); + + *aShouldContinue = !ShouldProcessByProtectionFeature; + + if (!ShouldProcessByProtectionFeature) { + return NS_OK; + } + + nsAutoCString list; + UrlClassifierCommon::TablesToString(aList, list); + + ChannelBlockDecision decision = + ChannelClassifierService::OnBeforeBlockChannel(aChannel, mName, list); + if (decision != ChannelBlockDecision::Blocked) { + *aShouldContinue = true; + return NS_OK; + } + + UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_HARMFULADDON_URI, + list, ""_ns, ""_ns); + + UC_LOG( + ("UrlClassifierFeatureHarmfulAddonProtection::ProcessChannel - " + "cancelling channel %p", + aChannel)); + + (void)aChannel->Cancel(NS_ERROR_HARMFULADDON_URI); + return NS_OK; +} + +NS_IMETHODIMP +UrlClassifierFeatureHarmfulAddonProtection::GetURIByListType( + nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType, + nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) { + NS_ENSURE_ARG_POINTER(aChannel); + NS_ENSURE_ARG_POINTER(aURIType); + NS_ENSURE_ARG_POINTER(aURI); + + if (aListType == nsIUrlClassifierFeature::blocklist) { + *aURIType = nsIUrlClassifierFeature::blocklistURI; + return aChannel->GetURI(aURI); + } + + MOZ_ASSERT(aListType == nsIUrlClassifierFeature::entitylist); + + *aURIType = nsIUrlClassifierFeature::pairwiseEntitylistURI; + return aChannel->GetURI(aURI); +} + +} // namespace net +} // namespace mozilla diff --git a/netwerk/url-classifier/UrlClassifierFeatureHarmfulAddonProtection.h b/netwerk/url-classifier/UrlClassifierFeatureHarmfulAddonProtection.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_net_UrlClassifierFeatureHarmfulAddonProtection_h +#define mozilla_net_UrlClassifierFeatureHarmfulAddonProtection_h + +#include "UrlClassifierFeatureBase.h" + +class nsIChannel; + +namespace mozilla { +namespace net { + +class UrlClassifierFeatureHarmfulAddonProtection final + : public UrlClassifierFeatureAntiTrackingBase { + public: + static const char* Name(); + + static void MaybeShutdown(); + + static already_AddRefed<UrlClassifierFeatureHarmfulAddonProtection> + MaybeCreate(nsIChannel* aChannel); + + static already_AddRefed<nsIUrlClassifierFeature> GetIfNameMatches( + const nsACString& aName); + + NS_IMETHOD ProcessChannel(nsIChannel* aChannel, + const nsTArray<nsCString>& aList, + const nsTArray<nsCString>& aHashes, + bool* aShouldContinue) override; + + NS_IMETHOD GetURIByListType(nsIChannel* aChannel, + nsIUrlClassifierFeature::listType aListType, + nsIUrlClassifierFeature::URIType* aURIType, + nsIURI** aURI) override; + + private: + UrlClassifierFeatureHarmfulAddonProtection(); + + static void MaybeInitialize(); +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_UrlClassifierFeatureHarmfulAddonProtection_h diff --git a/netwerk/url-classifier/moz.build b/netwerk/url-classifier/moz.build @@ -47,6 +47,7 @@ UNIFIED_SOURCES += [ "UrlClassifierFeatureFactory.cpp", "UrlClassifierFeatureFingerprintingAnnotation.cpp", "UrlClassifierFeatureFingerprintingProtection.cpp", + "UrlClassifierFeatureHarmfulAddonProtection.cpp", "UrlClassifierFeaturePhishingProtection.cpp", "UrlClassifierFeatureResult.cpp", "UrlClassifierFeatureSocialTrackingAnnotation.cpp", diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_urlclassifier.js b/toolkit/components/extensions/test/xpcshell/test_ext_urlclassifier.js @@ -0,0 +1,116 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +add_setup(async () => { + Services.prefs.setCharPref( + "urlclassifier.features.harmfuladdon.blocklistHosts", + "example.org" + ); + Services.prefs.setCharPref( + "urlclassifier.features.harmfuladdon.entitylistHosts", + "" + ); + Services.prefs.setCharPref( + "urlclassifier.features.harmfuladdon.skipURLs", + "" + ); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref( + "urlclassifier.features.harmfuladdon.blocklistHosts" + ); + Services.prefs.clearUserPref( + "urlclassifier.features.harmfuladdon.entitylistHosts" + ); + Services.prefs.clearUserPref( + "urlclassifier.features.harmfuladdon.skipURLs" + ); + }); +}); + +const server = AddonTestUtils.createHttpServer({ + hosts: ["example.com", "example.org"], +}); + +server.registerPathHandler("/dummy", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + response.write("<!DOCTYPE html><html></html>"); +}); + +server.registerPathHandler("/contentScript", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + response.write("<h1>Content Script</h1>"); +}); + +server.registerPathHandler("/backgroundScript", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html", false); + response.write("<h1>Background Script</h1>"); +}); + +add_task( + { pref_set: [["privacy.trackingprotection.harmfuladdon.enabled", true]] }, + async function test_addon_blocked_by_url_classifier() { + await runTest("backgroundScript_failed", "contentScript_failed"); + } +); + +add_task( + { pref_set: [["privacy.trackingprotection.harmfuladdon.enabled", false]] }, + async function test_addon_blocked_by_url_classifier() { + await runTest("backgroundScript_loaded", "contentScript_loaded"); + } +); + +async function runTest(message1, message2) { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + host_permissions: ["http://example.org/"], + + content_scripts: [ + { + matches: ["http://example.com/*"], + run_at: "document_end", + js: ["contentscript.js"], + }, + ], + }, + + background: async () => { + try { + await fetch("http://example.org/backgroundScript").then(r => r.text()); + browser.test.sendMessage("backgroundScript_loaded"); + } catch (e) { + browser.test.sendMessage("backgroundScript_failed"); + } + }, + + files: { + "contentscript.js": async () => { + try { + await fetch("http://example.org/contentScript").then(r => r.text()); + browser.test.sendMessage("contentScript_loaded"); + } catch (e) { + browser.test.sendMessage("contentScript_failed"); + } + }, + }, + }); + + await extension.startup(); + + const contentPage = await ExtensionTestUtils.loadContentPage( + "http://example.com/dummy" + ); + + await extension.awaitMessage(message1); + await extension.awaitMessage(message2); + + await contentPage.close(); + await extension.unload(); +} diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell.toml b/toolkit/components/extensions/test/xpcshell/xpcshell.toml @@ -140,6 +140,8 @@ skip-if = [ ["test_ext_unknown_permissions.js"] +["test_ext_urlclassifier.js"] + ["test_ext_webRequest_handlerBehaviorChanged.js"] ["test_ext_webRequest_urlclassification.js"] diff --git a/toolkit/components/url-classifier/SafeBrowsing.sys.mjs b/toolkit/components/url-classifier/SafeBrowsing.sys.mjs @@ -314,6 +314,25 @@ const FEATURES = [ ); }, }, + { + name: "harmfuladdon-protection", + list: [ + "urlclassifier.features.harmfuladdon.blocklistTables", + "urlclassifier.features.harmfuladdon.entitylistTables", + ], + enabled() { + return Services.prefs.getBoolPref( + "privacy.trackingprotection.harmfuladdon.enabled", + false + ); + }, + update() { + return Services.prefs.getBoolPref( + "browser.safebrowsing.features.harmfuladdon.update", + this.enabled() + ); + }, + }, ]; export var SafeBrowsing = { diff --git a/toolkit/modules/RemotePageAccessManager.sys.mjs b/toolkit/modules/RemotePageAccessManager.sys.mjs @@ -213,6 +213,7 @@ export let RemotePageAccessManager = { "browser.contentblocking.report.proxy.enabled", "privacy.trackingprotection.cryptomining.enabled", "privacy.trackingprotection.fingerprinting.enabled", + "privacy.trackingprotection.harmfuladdon.enabled", "privacy.trackingprotection.enabled", "privacy.trackingprotection.socialtracking.enabled", "browser.contentblocking.report.show_mobile_app", diff --git a/xpcom/base/ErrorList.py b/xpcom/base/ErrorList.py @@ -953,6 +953,7 @@ with modules["URILOADER"]: errors["NS_ERROR_SOCIALTRACKING_URI"] = FAILURE(43) errors["NS_ERROR_EMAILTRACKING_URI"] = FAILURE(44) errors["NS_ERROR_RESTRICTED_CONTENT"] = FAILURE(45) + errors["NS_ERROR_HARMFULADDON_URI"] = FAILURE(46) # Used when "Save Link As..." doesn't see the headers quickly enough to # choose a filename. See nsContextMenu.js. errors["NS_ERROR_SAVE_LINK_AS_TIMEOUT"] = FAILURE(32)