Bug 1954188: bFPP Part 2 - Implement granular overrides. r=tjr

Differential Revision: https://phabricator.services.mozilla.com/D241852
This commit is contained in:
Fatih Kilic
2025-04-03 17:30:18 +00:00
parent b0162ee293
commit aba2cdec5d
5 changed files with 116 additions and 56 deletions

View File

@@ -570,6 +570,10 @@ nsresult ServiceWorkerPrivate::Initialize() {
nsCOMPtr<nsIURI> firstPartyURI; nsCOMPtr<nsIURI> firstPartyURI;
bool foreignByAncestorContext = false; bool foreignByAncestorContext = false;
bool isOn3PCBExceptionList = false; bool isOn3PCBExceptionList = false;
// Firefox doesn't support service workers in PBM,
// but we add this just so that when we do,
// we can handle it correctly.
bool isPBM = principal->GetIsInPrivateBrowsing();
if (!principal->OriginAttributesRef().mPartitionKey.IsEmpty()) { if (!principal->OriginAttributesRef().mPartitionKey.IsEmpty()) {
net::CookieJarSettings::Cast(cookieJarSettings) net::CookieJarSettings::Cast(cookieJarSettings)
->SetPartitionKey(principal->OriginAttributesRef().mPartitionKey); ->SetPartitionKey(principal->OriginAttributesRef().mPartitionKey);
@@ -592,7 +596,7 @@ nsresult ServiceWorkerPrivate::Initialize() {
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
overriddenFingerprintingSettings = overriddenFingerprintingSettings =
nsRFPService::GetOverriddenFingerprintingSettingsForURI( nsRFPService::GetOverriddenFingerprintingSettingsForURI(
firstPartyURI, uri); firstPartyURI, uri, isPBM);
if (overriddenFingerprintingSettings.isSome()) { if (overriddenFingerprintingSettings.isSome()) {
overriddenFingerprintingSettingsArg.emplace( overriddenFingerprintingSettingsArg.emplace(
overriddenFingerprintingSettings.ref()); overriddenFingerprintingSettings.ref());
@@ -625,9 +629,9 @@ nsresult ServiceWorkerPrivate::Initialize() {
overriddenFingerprintingSettings = overriddenFingerprintingSettings =
isThirdParty isThirdParty
? nsRFPService::GetOverriddenFingerprintingSettingsForURI( ? nsRFPService::GetOverriddenFingerprintingSettingsForURI(
firstPartyURI, uri) firstPartyURI, uri, isPBM)
: nsRFPService::GetOverriddenFingerprintingSettingsForURI( : nsRFPService::GetOverriddenFingerprintingSettingsForURI(
uri, nullptr); uri, nullptr, isPBM);
RefPtr<net::CookieService> csSingleton = RefPtr<net::CookieService> csSingleton =
net::CookieService::GetSingleton(); net::CookieService::GetSingleton();
@@ -650,7 +654,8 @@ nsresult ServiceWorkerPrivate::Initialize() {
// the service worker as the first-party domain to get the fingerprinting // the service worker as the first-party domain to get the fingerprinting
// protection overrides. // protection overrides.
overriddenFingerprintingSettings = overriddenFingerprintingSettings =
nsRFPService::GetOverriddenFingerprintingSettingsForURI(uri, nullptr); nsRFPService::GetOverriddenFingerprintingSettingsForURI(uri, nullptr,
isPBM);
if (overriddenFingerprintingSettings.isSome()) { if (overriddenFingerprintingSettings.isSome()) {
overriddenFingerprintingSettingsArg.emplace( overriddenFingerprintingSettingsArg.emplace(
@@ -658,7 +663,6 @@ nsresult ServiceWorkerPrivate::Initialize() {
} }
} }
bool isPBM = principal->GetIsInPrivateBrowsing();
if (ContentBlockingAllowList::Check(principal, isPBM)) { if (ContentBlockingAllowList::Check(principal, isPBM)) {
net::CookieJarSettings::Cast(cookieJarSettings) net::CookieJarSettings::Cast(cookieJarSettings)
->SetIsOnContentBlockingAllowList(true); ->SetIsOnContentBlockingAllowList(true);

View File

@@ -49,6 +49,8 @@ const SCHEMA = `{
const COLLECTION_NAME = "fingerprinting-protection-overrides"; const COLLECTION_NAME = "fingerprinting-protection-overrides";
const PREF_GRANULAR_OVERRIDES = const PREF_GRANULAR_OVERRIDES =
"privacy.fingerprintingProtection.granularOverrides"; "privacy.fingerprintingProtection.granularOverrides";
const PREF_GRANULAR_OVERRIDES_BASELINE =
"privacy.baselineFingerprintingProtection.granularOverrides";
const PREF_REMOTE_OVERRIDES_ENABLED = const PREF_REMOTE_OVERRIDES_ENABLED =
"privacy.fingerprintingProtection.remoteOverrides.enabled"; "privacy.fingerprintingProtection.remoteOverrides.enabled";
@@ -58,6 +60,12 @@ XPCOMUtils.defineLazyPreferenceGetter(
PREF_GRANULAR_OVERRIDES PREF_GRANULAR_OVERRIDES
); );
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"baselineGranularOverridesPref",
PREF_GRANULAR_OVERRIDES_BASELINE
);
XPCOMUtils.defineLazyPreferenceGetter( XPCOMUtils.defineLazyPreferenceGetter(
lazy, lazy,
"remoteOverridesEnabled", "remoteOverridesEnabled",
@@ -71,10 +79,11 @@ export class FingerprintingOverride {
classID = Components.ID("{07f45442-1806-44be-9230-12eb79de9bac}"); classID = Components.ID("{07f45442-1806-44be-9230-12eb79de9bac}");
QueryInterface = ChromeUtils.generateQI(["nsIFingerprintingOverride"]); QueryInterface = ChromeUtils.generateQI(["nsIFingerprintingOverride"]);
constructor(firstPartyDomain, thirdPartyDomain, overrides) { constructor(firstPartyDomain, thirdPartyDomain, overrides, isBaseline) {
this.firstPartyDomain = firstPartyDomain; this.firstPartyDomain = firstPartyDomain;
this.thirdPartyDomain = thirdPartyDomain; this.thirdPartyDomain = thirdPartyDomain;
this.overrides = overrides; this.overrides = overrides;
this.isBaseline = isBaseline;
} }
} }
@@ -125,6 +134,7 @@ export class FingerprintingWebCompatService {
// Register listener to import overrides when the overrides pref changes. // Register listener to import overrides when the overrides pref changes.
Services.prefs.addObserver(PREF_GRANULAR_OVERRIDES, this); Services.prefs.addObserver(PREF_GRANULAR_OVERRIDES, this);
Services.prefs.addObserver(PREF_GRANULAR_OVERRIDES_BASELINE, this);
// Register the sync event for the remote settings updates. // Register the sync event for the remote settings updates.
this.#rs.on("sync", event => { this.#rs.on("sync", event => {
@@ -155,37 +165,42 @@ export class FingerprintingWebCompatService {
// Clear overrides before we update. // Clear overrides before we update.
this.#granularOverrides.clear(); this.#granularOverrides.clear();
let overrides; for (const [pref, isBaseline] of [
try { [lazy.baselineGranularOverridesPref, true],
overrides = JSON.parse(lazy.granularOverridesPref || "[]"); [lazy.granularOverridesPref, false],
} catch (error) { ]) {
lazy.logConsole.error( let overrides;
`Failed to parse granular override JSON string: Not a valid JSON.`, try {
error overrides = JSON.parse(pref || "[]");
); } catch (error) {
return; lazy.logConsole.error(
} `Failed to parse granular override JSON string: Not a valid JSON.`,
error
// Ensure we have an array we can iterate over and not an object. );
if (!Array.isArray(overrides)) { return;
lazy.logConsole.error(
"Failed to parse granular overrides JSON String: Not an array."
);
return;
}
for (let override of overrides) {
// Validate the override.
let { valid, errors } = this.#validator.validate(override);
if (!valid) {
lazy.logConsole.debug("Override validation error", override, errors);
continue;
} }
this.#granularOverrides.add( // Ensure we have an array we can iterate over and not an object.
this.#createFingerprintingOverrideFrom(override) if (!Array.isArray(overrides)) {
); lazy.logConsole.error(
"Failed to parse granular overrides JSON String: Not an array."
);
return;
}
for (let override of overrides) {
// Validate the override.
let { valid, errors } = this.#validator.validate(override);
if (!valid) {
lazy.logConsole.debug("Override validation error", override, errors);
continue;
}
this.#granularOverrides.add(
this.#createFingerprintingOverrideFrom(override, isBaseline)
);
}
} }
} }
@@ -217,11 +232,18 @@ export class FingerprintingWebCompatService {
} }
} }
#createFingerprintingOverrideFrom(entry) { #createFingerprintingOverrideFrom(entry, isBaselineOverride = null) {
// If the isBaselineOverride is not provided, we will use the value from the
// entry. If the entry doesn't have the isBaseline field, we will default to
// false.
// We default to false because we didn't have the baseline mode in the past,
// and the existing entries don't have the isBaseline field.
const isBaseline = isBaselineOverride ?? entry.isBaseline ?? false;
return new FingerprintingOverride( return new FingerprintingOverride(
entry.firstPartyDomain, entry.firstPartyDomain,
entry.thirdPartyDomain, entry.thirdPartyDomain,
entry.overrides entry.overrides,
isBaseline
); );
} }
@@ -240,7 +262,10 @@ export class FingerprintingWebCompatService {
} }
observe(subject, topic, prefName) { observe(subject, topic, prefName) {
if (prefName != PREF_GRANULAR_OVERRIDES) { if (
prefName != PREF_GRANULAR_OVERRIDES &&
prefName != PREF_GRANULAR_OVERRIDES_BASELINE
) {
return; return;
} }
@@ -252,5 +277,6 @@ export class FingerprintingWebCompatService {
lazy.logConsole.debug("shutdown"); lazy.logConsole.debug("shutdown");
Services.prefs.removeObserver(PREF_GRANULAR_OVERRIDES, this); Services.prefs.removeObserver(PREF_GRANULAR_OVERRIDES, this);
Services.prefs.removeObserver(PREF_GRANULAR_OVERRIDES_BASELINE, this);
} }
} }

View File

@@ -21,6 +21,11 @@ interface nsIFingerprintingOverride : nsISupports
* fingerprinting override pref "privacy.fingerprintingProtection.overrides". * fingerprinting override pref "privacy.fingerprintingProtection.overrides".
*/ */
readonly attribute ACString overrides; readonly attribute ACString overrides;
/**
* Whether the override is baseline or FPP.
*/
readonly attribute boolean isBaseline;
}; };
/** /**

View File

@@ -2072,6 +2072,10 @@ nsresult nsRFPService::CreateOverrideDomainKey(
nsresult rv = aOverride->GetFirstPartyDomain(firstPartyDomain); nsresult rv = aOverride->GetFirstPartyDomain(firstPartyDomain);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
bool isBaseline = false;
rv = aOverride->GetIsBaseline(&isBaseline);
NS_ENSURE_SUCCESS(rv, rv);
// The first party domain shouldn't be empty. And it shouldn't contain a comma // The first party domain shouldn't be empty. And it shouldn't contain a comma
// because we use a comma as a delimiter. // because we use a comma as a delimiter.
if (firstPartyDomain.IsEmpty() || if (firstPartyDomain.IsEmpty() ||
@@ -2102,6 +2106,9 @@ nsresult nsRFPService::CreateOverrideDomainKey(
aDomainKey.Append(thirdPartyDomain); aDomainKey.Append(thirdPartyDomain);
} }
aDomainKey.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER);
aDomainKey.Append(isBaseline ? "1" : "0");
return NS_OK; return NS_OK;
} }
@@ -2162,8 +2169,9 @@ nsRFPService::SetFingerprintingOverrides(
const nsTArray<RefPtr<nsIFingerprintingOverride>>& aOverrides) { const nsTArray<RefPtr<nsIFingerprintingOverride>>& aOverrides) {
MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(XRE_IsParentProcess());
// Clear all overrides before importing. // Clear all overrides before importing.
mFingerprintingOverrides.Clear(); CleanAllOverrides();
StaticMutexAutoLock lock(sEnabledFingerprintingProtectionsMutex);
for (const auto& fpOverride : aOverrides) { for (const auto& fpOverride : aOverrides) {
nsAutoCString domainKey; nsAutoCString domainKey;
@@ -2177,14 +2185,18 @@ nsRFPService::SetFingerprintingOverrides(
rv = fpOverride->GetOverrides(overridesText); rv = fpOverride->GetOverrides(overridesText);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
StaticMutexAutoLock lock(sEnabledFingerprintingProtectionsMutex); bool isBaseline = false;
rv = fpOverride->GetIsBaseline(&isBaseline);
NS_ENSURE_SUCCESS(rv, rv);
RFPTargetSet baseOverrides = isBaseline
? sEnabledFingerprintingProtectionsBase
: sEnabledFingerprintingProtections;
RFPTargetSet targets = nsRFPService::CreateOverridesFromText( RFPTargetSet targets = nsRFPService::CreateOverridesFromText(
NS_ConvertUTF8toUTF16(overridesText), NS_ConvertUTF8toUTF16(overridesText),
mFingerprintingOverrides.Contains(domainKey) mFingerprintingOverrides.Contains(domainKey)
? mFingerprintingOverrides.Get(domainKey) ? mFingerprintingOverrides.Get(domainKey)
// TODO(fkilic): We will handle granular overrides in the following : baseOverrides);
// patches.
: sEnabledFingerprintingProtections);
// The newly added one will replace the existing one for the given domain // The newly added one will replace the existing one for the given domain
// key. // key.
@@ -2273,9 +2285,11 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForChannel(
return Nothing(); return Nothing();
} }
bool isPrivate = loadInfo->GetOriginAttributes().IsPrivateBrowsing();
// The channel is for the first-party load. // The channel is for the first-party load.
if (!AntiTrackingUtils::IsThirdPartyChannel(aChannel)) { if (!AntiTrackingUtils::IsThirdPartyChannel(aChannel)) {
return GetOverriddenFingerprintingSettingsForURI(uri, nullptr); return GetOverriddenFingerprintingSettingsForURI(uri, nullptr, isPrivate);
} }
// The channel is for the third-party load. We get the first-party URI from // The channel is for the third-party load. We get the first-party URI from
@@ -2328,7 +2342,7 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForChannel(
rv = NS_NewURI(getter_AddRefs(topURI), scheme + u"://"_ns + domain); rv = NS_NewURI(getter_AddRefs(topURI), scheme + u"://"_ns + domain);
MOZ_ASSERT(NS_SUCCEEDED(rv)); MOZ_ASSERT(NS_SUCCEEDED(rv));
return GetOverriddenFingerprintingSettingsForURI(topURI, uri); return GetOverriddenFingerprintingSettingsForURI(topURI, uri, isPrivate);
} }
nsCOMPtr<nsIPrincipal> topPrincipal = topWGP->DocumentPrincipal(); nsCOMPtr<nsIPrincipal> topPrincipal = topWGP->DocumentPrincipal();
@@ -2380,12 +2394,12 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForChannel(
attrsForeignByAncestor.mPartitionKey.Equals(partitionKey)); attrsForeignByAncestor.mPartitionKey.Equals(partitionKey));
#endif #endif
return GetOverriddenFingerprintingSettingsForURI(topURI, uri); return GetOverriddenFingerprintingSettingsForURI(topURI, uri, isPrivate);
} }
/* static */ /* static */
Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI( Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI(
nsIURI* aFirstPartyURI, nsIURI* aThirdPartyURI) { nsIURI* aFirstPartyURI, nsIURI* aThirdPartyURI, bool aIsPrivate) {
MOZ_ASSERT(aFirstPartyURI); MOZ_ASSERT(aFirstPartyURI);
MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(XRE_IsParentProcess());
@@ -2399,9 +2413,17 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI(
// will take over {first-party domain, *} because the latter one has a smaller // will take over {first-party domain, *} because the latter one has a smaller
// scope. // scope.
bool isBaseline = !IsFPPEnabled(aIsPrivate);
auto addIsBaseline = [](nsAutoCString& aKey, bool aIsBaseline) {
aKey.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER);
aKey.Append(aIsBaseline ? "1" : "0");
};
// First, we get the overrides that applies to every context. // First, we get the overrides that applies to every context.
Maybe<RFPTargetSet> result = nsAutoCString key;
service->mFingerprintingOverrides.MaybeGet("*"_ns); key.Assign("*"_ns);
addIsBaseline(key, isBaseline);
Maybe<RFPTargetSet> result = service->mFingerprintingOverrides.MaybeGet(key);
nsCOMPtr<nsIEffectiveTLDService> eTLDService = nsCOMPtr<nsIEffectiveTLDService> eTLDService =
mozilla::components::EffectiveTLD::Service(); mozilla::components::EffectiveTLD::Service();
@@ -2424,11 +2446,10 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI(
// first-party domain. // first-party domain.
if (!aThirdPartyURI) { if (!aThirdPartyURI) {
// Test the {first-party domain, *} scope. // Test the {first-party domain, *} scope.
nsAutoCString key;
key.Assign(firstPartyDomain); key.Assign(firstPartyDomain);
key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER); key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER);
key.Append("*"); key.Append("*"_ns);
addIsBaseline(key, isBaseline);
Maybe<RFPTargetSet> fpOverrides = Maybe<RFPTargetSet> fpOverrides =
service->mFingerprintingOverrides.MaybeGet(key); service->mFingerprintingOverrides.MaybeGet(key);
if (fpOverrides) { if (fpOverrides) {
@@ -2436,7 +2457,9 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI(
} }
// Test the {first-party domain} scope. // Test the {first-party domain} scope.
fpOverrides = service->mFingerprintingOverrides.MaybeGet(firstPartyDomain); key.Assign(firstPartyDomain);
addIsBaseline(key, isBaseline);
fpOverrides = service->mFingerprintingOverrides.MaybeGet(key);
if (fpOverrides) { if (fpOverrides) {
result = fpOverrides; result = fpOverrides;
} }
@@ -2460,10 +2483,10 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI(
} }
// Test {first-party domain, *} scope. // Test {first-party domain, *} scope.
nsAutoCString key;
key.Assign(firstPartyDomain); key.Assign(firstPartyDomain);
key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER); key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER);
key.Append("*"); key.Append("*"_ns);
addIsBaseline(key, isBaseline);
Maybe<RFPTargetSet> fpOverrides = Maybe<RFPTargetSet> fpOverrides =
service->mFingerprintingOverrides.MaybeGet(key); service->mFingerprintingOverrides.MaybeGet(key);
if (fpOverrides) { if (fpOverrides) {
@@ -2474,6 +2497,7 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI(
key.Assign("*"); key.Assign("*");
key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER); key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER);
key.Append(thirdPartyDomain); key.Append(thirdPartyDomain);
addIsBaseline(key, isBaseline);
fpOverrides = service->mFingerprintingOverrides.MaybeGet(key); fpOverrides = service->mFingerprintingOverrides.MaybeGet(key);
if (fpOverrides) { if (fpOverrides) {
result = fpOverrides; result = fpOverrides;
@@ -2483,6 +2507,7 @@ Maybe<RFPTargetSet> nsRFPService::GetOverriddenFingerprintingSettingsForURI(
key.Assign(firstPartyDomain); key.Assign(firstPartyDomain);
key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER); key.Append(FP_OVERRIDES_DOMAIN_KEY_DELIMITER);
key.Append(thirdPartyDomain); key.Append(thirdPartyDomain);
addIsBaseline(key, isBaseline);
fpOverrides = service->mFingerprintingOverrides.MaybeGet(key); fpOverrides = service->mFingerprintingOverrides.MaybeGet(key);
if (fpOverrides) { if (fpOverrides) {
result = fpOverrides; result = fpOverrides;

View File

@@ -355,7 +355,7 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
// and third-party URI. Otherwise, it will return Nothing() to indicate using // and third-party URI. Otherwise, it will return Nothing() to indicate using
// the default RFPTargets. // the default RFPTargets.
static Maybe<RFPTargetSet> GetOverriddenFingerprintingSettingsForURI( static Maybe<RFPTargetSet> GetOverriddenFingerprintingSettingsForURI(
nsIURI* aFirstPartyURI, nsIURI* aThirdPartyURI); nsIURI* aFirstPartyURI, nsIURI* aThirdPartyURI, bool aIsPrivate);
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------