diff options
Diffstat (limited to 'toolkit/components/antitracking')
26 files changed, 1233 insertions, 210 deletions
diff --git a/toolkit/components/antitracking/AntiTrackingUtils.cpp b/toolkit/components/antitracking/AntiTrackingUtils.cpp index 2c530dc3da..d9624237de 100644 --- a/toolkit/components/antitracking/AntiTrackingUtils.cpp +++ b/toolkit/components/antitracking/AntiTrackingUtils.cpp @@ -514,6 +514,10 @@ AntiTrackingUtils::GetStoragePermissionStateInParent(nsIChannel* aChannel) { return nsILoadInfo::NoStoragePermission; } + if (targetPrincipal->IsSystemPrincipal()) { + return nsILoadInfo::HasStoragePermission; + } + nsCOMPtr<nsIURI> trackingURI; rv = aChannel->GetURI(getter_AddRefs(trackingURI)); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -821,7 +825,8 @@ void AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(nsIChannel* aChannel) { // whether the page is third-party, so we use channel result principal // instead. By doing this, an the resource inherits the principal from // its parent is considered not a third-party. - if (NS_IsAboutBlank(uri) || NS_IsAboutSrcdoc(uri)) { + if (NS_IsAboutBlank(uri) || NS_IsAboutSrcdoc(uri) || + uri->SchemeIs("blob")) { nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); if (NS_WARN_IF(!ssm)) { return; @@ -847,10 +852,36 @@ void AntiTrackingUtils::ComputeIsThirdPartyToTopWindow(nsIChannel* aChannel) { bool AntiTrackingUtils::IsThirdPartyChannel(nsIChannel* aChannel) { MOZ_ASSERT(aChannel); - // We only care whether the channel is 3rd-party with respect to - // the top-level. - nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); - return loadInfo->GetIsThirdPartyContextToTopWindow(); + // We have to handle blob URLs here because they always fail + // IsThirdPartyChannel because of how blob URLs are constructed. We just + // recompare to their ancestor chain from the loadInfo, bailing if any is + // third party. + nsAutoCString scheme; + nsCOMPtr<nsIURI> channelURI; + nsresult rv = aChannel->GetURI(getter_AddRefs(channelURI)); + if (NS_SUCCEEDED(rv) && channelURI->SchemeIs("blob")) { + nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); + for (const nsCOMPtr<nsIPrincipal>& principal : + loadInfo->AncestorPrincipals()) { + bool thirdParty = true; + rv = loadInfo->PrincipalToInherit()->IsThirdPartyPrincipal(principal, + &thirdParty); + if (NS_SUCCEEDED(rv) && thirdParty) { + return true; + } + } + return false; + } + + nsCOMPtr<mozIThirdPartyUtil> tpuService = + mozilla::components::ThirdPartyUtil::Service(); + if (!tpuService) { + return true; + } + bool thirdParty = true; + rv = tpuService->IsThirdPartyChannel(aChannel, nullptr, &thirdParty); + NS_ENSURE_SUCCESS(rv, true); + return thirdParty; } /* static */ @@ -903,19 +934,29 @@ bool AntiTrackingUtils::IsThirdPartyWindow(nsPIDOMWindowInner* aWindow, /* static */ bool AntiTrackingUtils::IsThirdPartyDocument(Document* aDocument) { MOZ_ASSERT(aDocument); - if (!aDocument->GetChannel()) { + nsCOMPtr<mozIThirdPartyUtil> tpuService = + mozilla::components::ThirdPartyUtil::Service(); + if (!tpuService) { + return true; + } + bool thirdParty = true; + if (!aDocument->GetChannel() || + aDocument->GetDocumentURI()->SchemeIs("blob")) { // If we can't get the channel from the document, i.e. initial about:blank // page, we use the browsingContext of the document to check if it's in the // third-party context. If the browsing context is still not available, we // will treat the window as third-party. + // We also rely on IsThirdPartyContext for blob documents because the + // IsThirdPartyChannel check relies on getting the BaseDomain, + // which correctly fails for blobs URIs. RefPtr<BrowsingContext> bc = aDocument->GetBrowsingContext(); return bc ? IsThirdPartyContext(bc) : true; } - // We only care whether the channel is 3rd-party with respect to - // the top-level. - nsCOMPtr<nsILoadInfo> loadInfo = aDocument->GetChannel()->LoadInfo(); - return loadInfo->GetIsThirdPartyContextToTopWindow(); + nsresult rv = tpuService->IsThirdPartyChannel(aDocument->GetChannel(), + nullptr, &thirdParty); + NS_ENSURE_SUCCESS(rv, true); + return thirdParty; } /* static */ @@ -923,41 +964,47 @@ bool AntiTrackingUtils::IsThirdPartyContext(BrowsingContext* aBrowsingContext) { MOZ_ASSERT(aBrowsingContext); MOZ_ASSERT(aBrowsingContext->IsInProcess()); - if (aBrowsingContext->IsTopContent()) { - return false; - } - - // If the top browsing context is not in the same process, it's cross-origin. - if (!aBrowsingContext->Top()->IsInProcess()) { - return true; - } - + // iframes with SANDBOX_ORIGIN are always third-party contexts + // because they are a unique origin nsIDocShell* docShell = aBrowsingContext->GetDocShell(); if (!docShell) { return true; } Document* doc = docShell->GetExtantDocument(); - if (!doc) { + if (!doc || doc->GetSandboxFlags() & SANDBOXED_ORIGIN) { return true; } nsIPrincipal* principal = doc->NodePrincipal(); - nsIDocShell* topDocShell = aBrowsingContext->Top()->GetDocShell(); - if (!topDocShell) { - return true; - } - Document* topDoc = topDocShell->GetDocument(); - if (!topDoc) { - return true; - } - nsIPrincipal* topPrincipal = topDoc->NodePrincipal(); + BrowsingContext* traversingParent = aBrowsingContext->GetParent(); + while (traversingParent) { + // If the parent browsing context is not in the same process, it's + // cross-origin. + if (!traversingParent->IsInProcess()) { + return true; + } - auto* topBasePrin = BasePrincipal::Cast(topPrincipal); - bool isThirdParty = true; + nsIDocShell* parentDocShell = traversingParent->GetDocShell(); + if (!parentDocShell) { + return true; + } + Document* parentDoc = parentDocShell->GetDocument(); + if (!parentDoc || parentDoc->GetSandboxFlags() & SANDBOXED_ORIGIN) { + return true; + } + nsIPrincipal* parentPrincipal = parentDoc->NodePrincipal(); + + auto* parentBasePrin = BasePrincipal::Cast(parentPrincipal); + bool isThirdParty = true; - topBasePrin->IsThirdPartyPrincipal(principal, &isThirdParty); + parentBasePrin->IsThirdPartyPrincipal(principal, &isThirdParty); + if (isThirdParty) { + return true; + } - return isThirdParty; + traversingParent = traversingParent->GetParent(); + } + return false; } /* static */ @@ -1005,6 +1052,18 @@ void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) { ->MarkOverriddenFingerprintingSettingsAsSet(); #endif + ExtContentPolicyType contentType = loadInfo->GetExternalContentPolicyType(); + if (contentType == ExtContentPolicy::TYPE_DOCUMENT || + contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) { + nsCOMPtr<nsICookieJarSettings> cookieJarSettings; + Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); + // For subdocuments, the channel's partition key is that of the parent + // document. This document may have a different partition key, particularly + // one without the same-site bit. + net::CookieJarSettings::Cast(cookieJarSettings) + ->UpdatePartitionKeyForDocumentLoadedByChannel(aChannel); + } + // We only update the IsOnContentBlockingAllowList flag and the partition key // for the top-level http channel. // @@ -1015,17 +1074,15 @@ void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) { // The partition key is computed based on the site, so it's no point to set it // for channels other than http channels. nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); - if (!httpChannel || loadInfo->GetExternalContentPolicyType() != - ExtContentPolicy::TYPE_DOCUMENT) { + if (!httpChannel || contentType != ExtContentPolicy::TYPE_DOCUMENT) { return; } - nsCOMPtr<nsICookieJarSettings> cookieJarSettings; - Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); - // Update the IsOnContentBlockingAllowList flag in the CookieJarSettings // if this is a top level loading. For sub-document loading, this flag // would inherit from the parent. + nsCOMPtr<nsICookieJarSettings> cookieJarSettings; + Unused << loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); net::CookieJarSettings::Cast(cookieJarSettings) ->UpdateIsOnContentBlockingAllowList(aChannel); @@ -1033,7 +1090,7 @@ void AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(nsIChannel* aChannel) { // propagated to non-top level loads via CookieJarSetting. nsCOMPtr<nsIURI> uri; Unused << aChannel->GetURI(getter_AddRefs(uri)); - net::CookieJarSettings::Cast(cookieJarSettings)->SetPartitionKey(uri); + net::CookieJarSettings::Cast(cookieJarSettings)->SetPartitionKey(uri, false); // Generate the fingerprinting randomization key for top-level loads. The key // will automatically be propagated to sub loads. diff --git a/toolkit/components/antitracking/StorageAccess.h b/toolkit/components/antitracking/StorageAccess.h index 4dbd5355c0..bca26057f3 100644 --- a/toolkit/components/antitracking/StorageAccess.h +++ b/toolkit/components/antitracking/StorageAccess.h @@ -40,8 +40,6 @@ enum class StorageAccess { // Allow access to the storage, but only if it is secure to do so in a // private browsing context. ePrivateBrowsing = 1, - // Allow access to the storage, but only persist it for the current session - eSessionScoped = 2, // Allow access to the storage eAllow = 3, // Keep this at the end. Used for serialization, but not a valid value. diff --git a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp b/toolkit/components/antitracking/StorageAccessAPIHelper.cpp index 5baa2c2557..b4fcf2f6e9 100644 --- a/toolkit/components/antitracking/StorageAccessAPIHelper.cpp +++ b/toolkit/components/antitracking/StorageAccessAPIHelper.cpp @@ -475,7 +475,6 @@ StorageAccessAPIHelper::CompleteAllowAccessForOnParentProcess( [aParentContext, aTopLevelWindowId, trackingOrigin, trackingPrincipal, aCookieBehavior, aReason](int aAllowMode) -> RefPtr<StorageAccessPermissionGrantPromise> { - MOZ_ASSERT(!aParentContext->IsInProcess()); // We don't have the window, send an IPC to the content process that // owns the parent window. But there is a special case, for window.open, // we'll return to the content process we need to inform when this @@ -1060,12 +1059,7 @@ StorageAccessAPIHelper::CheckSameSiteCallingContextDecidesStorageAccessAPI( } } - nsIChannel* chan = aDocument->GetChannel(); - if (!chan) { - return Some(false); - } - nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); - if (loadInfo->GetIsThirdPartyContextToTopWindow()) { + if (AntiTrackingUtils::IsThirdPartyDocument(aDocument)) { return Some(false); } diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.cpp b/toolkit/components/antitracking/StoragePrincipalHelper.cpp index 10be1112ca..79bafead2c 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp +++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp @@ -86,8 +86,10 @@ bool ChooseOriginAttributes(nsIChannel* aChannel, OriginAttributes& aAttrs, if (NS_WARN_IF(NS_FAILED(rv))) { return false; } - - aAttrs.SetPartitionKey(principalURI); + bool foreignByAncestorContext = + AntiTrackingUtils::IsThirdPartyChannel(aChannel) && + !loadInfo->GetIsThirdPartyContextToTopWindow(); + aAttrs.SetPartitionKey(principalURI, foreignByAncestorContext); return true; } @@ -313,7 +315,7 @@ nsresult StoragePrincipalHelper::GetPrincipal(nsIChannel* aChannel, // We only support foreign partitioned principal when dFPI is enabled. if (cjs->GetCookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN && - loadInfo->GetIsThirdPartyContextToTopWindow()) { + AntiTrackingUtils::IsThirdPartyChannel(aChannel)) { outPrincipal = partitionedPrincipal; } break; @@ -435,7 +437,7 @@ bool StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker( return false; } - return aWorkerPrivate->IsThirdPartyContextToTopWindow(); + return aWorkerPrivate->IsThirdPartyContext(); } // static @@ -479,7 +481,7 @@ bool StoragePrincipalHelper::GetOriginAttributes( // Otherwise, we will use the regular principal. if (cjs->GetCookieBehavior() == nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN && - loadInfo->GetIsThirdPartyContextToTopWindow()) { + AntiTrackingUtils::IsThirdPartyChannel(aChannel)) { ChooseOriginAttributes(aChannel, aAttributes, true); } break; @@ -560,7 +562,7 @@ void StoragePrincipalHelper::UpdateOriginAttributesForNetworkState( return; } - aAttributes.SetPartitionKey(aFirstPartyURI); + aAttributes.SetPartitionKey(aFirstPartyURI, false); } enum SupportedScheme { HTTP, HTTPS }; @@ -664,8 +666,9 @@ bool StoragePrincipalHelper::PartitionKeyHasBaseDomain( nsString scheme; nsString pkBaseDomain; int32_t port; - bool success = OriginAttributes::ParsePartitionKey(aPartitionKey, scheme, - pkBaseDomain, port); + bool foreign; + bool success = OriginAttributes::ParsePartitionKey( + aPartitionKey, scheme, pkBaseDomain, port, foreign); if (!success) { return false; @@ -674,4 +677,26 @@ bool StoragePrincipalHelper::PartitionKeyHasBaseDomain( return aBaseDomain.Equals(pkBaseDomain); } +// static +void StoragePrincipalHelper::UpdatePartitionKeyWithForeignAncestorBit( + nsAString& aKey, bool aForeignByAncestorContext) { + bool site = 0 == aKey.Find(u"("); + if (!site) { + return; + } + if (aForeignByAncestorContext) { + int32_t index = aKey.Find(u",f)"); + if (index == -1) { + uint32_t cutStart = aKey.Length() - 1; + aKey.ReplaceLiteral(cutStart, 1, u",f)"); + } + } else { + int32_t index = aKey.Find(u",f)"); + if (index != -1) { + uint32_t cutLength = aKey.Length() - index; + aKey.ReplaceLiteral(index, cutLength, u")"); + } + } +} + } // namespace mozilla diff --git a/toolkit/components/antitracking/StoragePrincipalHelper.h b/toolkit/components/antitracking/StoragePrincipalHelper.h index f813417eb6..0fe362d8c1 100644 --- a/toolkit/components/antitracking/StoragePrincipalHelper.h +++ b/toolkit/components/antitracking/StoragePrincipalHelper.h @@ -351,6 +351,14 @@ class StoragePrincipalHelper final { static bool PartitionKeyHasBaseDomain(const nsAString& aPartitionKey, const nsAString& aBaseDomain); + + // Partition keys can have the same-site bit added or removed from them. + // "(https,foo.com)", false -> "(https,foo.com)" + // "(https,foo.com,f)", false -> "(https,foo.com)" + // "(https,foo.com,f)", true -> "(https,foo.com,f)" + // "(https,foo.com)", true -> "(https,foo.com,f)" + static void UpdatePartitionKeyWithForeignAncestorBit( + nsAString& aKey, bool aForeignByAncestorContext); }; } // namespace mozilla diff --git a/toolkit/components/antitracking/StripOnShareLists/LGPL/LICENSE b/toolkit/components/antitracking/StripOnShareLists/LGPL/LICENSE new file mode 100644 index 0000000000..6600f1c98d --- /dev/null +++ b/toolkit/components/antitracking/StripOnShareLists/LGPL/LICENSE @@ -0,0 +1,165 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/toolkit/components/antitracking/StripOnShareLists/LGPL/StripOnShareLGPL.json b/toolkit/components/antitracking/StripOnShareLists/LGPL/StripOnShareLGPL.json new file mode 100644 index 0000000000..b34fbfff6b --- /dev/null +++ b/toolkit/components/antitracking/StripOnShareLists/LGPL/StripOnShareLGPL.json @@ -0,0 +1,556 @@ +{ + "global": { + "queryParams": [ + "at_campaign", + "at_send_date", + "at_campaign_type", + "at_recipient_list", + "at_creation", + "at_recipient_id", + "at_emailtype", + "at_ptr_name", + "at_link", + "at_link_id", + "at_medium", + "at_link_type", + "at_link_origin", + "utm_email", + "utm_email_id", + "utm_campaign_id", + "utm_newsletter_id" + ], + "topLevelSites": ["*"] + }, + "twitter": { + "queryParams": ["refsrc", "cxt", "s"], + "topLevelSites": ["www.twitter.com", "twitter.com", "x.com"] + }, + "amazon": { + "queryParams": [ + "pd_rd_i", + "pf_rd_i", + "pf_rd_m", + "pf_rd_s", + "pf_rd_t", + "pf_rd_w", + "adgrpid", + "ascsubtag", + "creative", + "creativeASIN", + "dchild", + "field-lbr_brands_browse-bin", + "hvadid", + "hvbmt", + "hvdev", + "hvlocphy", + "hvnetw", + "hvrand", + "hvtargid", + "hydadcr", + "initialIssue", + "ingress", + "linkCode", + "linkId", + "plattr", + "qid", + "rdc", + "refRID", + "th", + "ts_id", + "visitId", + "vtr", + "spIA", + "_encoding", + "qualifier" + ], + "topLevelSites": [ + "www.amazon.com", + "www.amazon.de", + "www.amazon.nl", + "www.amazon.fr", + "www.amazon.co.jp", + "www.amazon.in", + "www.amazon.es", + "www.amazon.ac", + "www.amazon.cn", + "www.amazon.eg", + "www.amazon.in", + "www.amazon.co.uk", + "www.amazon.it", + "www.amazon.pl", + "www.amazon.sg", + "www.amazon.ca" + ] + }, + "youtube": { + "queryParams": ["si", "feature", "kw"], + "topLevelSites": ["www.youtube.com", "www.youtu.be"] + }, + "carousell": { + "queryParams": [ + "referrer_search_query", + "referrer_search_query_source", + "referrer_request_id", + "referrer_sort_by", + "referrer_source", + "referrer_page_type", + "referrer_category_id", + "t-source", + "tap_index", + "t-referrer_browse_type", + "t-id" + ], + "topLevelSites": [ + "ca.carousell.com", + "www.carousell.sg", + "www.carousell.com.hk", + "us.carousell.com", + "au.carousell.com" + ] + }, + "etsy": { + "queryParams": ["click_sum", "ref", "click_key", "organic_search_click"], + "topLevelSites": ["www.etsy.com"] + }, + "wikipedia": { + "queryParams": ["wprov"], + "topLevelSites": ["www.wikipedia.org"] + }, + "wallstreetjournal": { + "queryParams": ["reflink"], + "topLevelSites": ["www.wsj.com"] + }, + "theathletic": { + "queryParams": ["source"], + "topLevelSites": ["theathletic.com"] + }, + "forbes": { + "queryParams": ["sh"], + "topLevelSites": ["www.forbes.com"] + }, + "bloomberg": { + "queryParams": ["leadSource", "sref", "srnd"], + "topLevelSites": ["www.bloomberg.com"] + }, + "tiktok": { + "queryParams": [ + "share_app_id", + "share_author_id", + "tt_from", + "embed_source", + "referer_url", + "refer", + "is_from_webapp", + "sender_device", + "sec_uid", + "web_id", + "enter_method", + "t", + "q" + ], + "topLevelSites": ["www.tiktok.com"] + }, + "bbc": { + "queryParams": [ + "facebook_page", + "at_bbc_team", + "ocid", + "ns_mchannel", + "ns_source", + "ns_campaign", + "ns_linkname", + "ns_fee", + "xtor" + ], + "topLevelSites": ["www.bbc.com"] + }, + "cnn": { + "queryParams": ["ref"], + "topLevelSites": ["www.cnn.co.jp", "www.cnn.com"] + }, + "imdb": { + "queryParams": [ + "pf_rd_p", + "pf_rd_i", + "pf_rd_m", + "pf_rd_s", + "pf_rd_t", + "pf_rd_r" + ], + "topLevelSites": ["www.imdb.com"] + }, + "mirror": { + "queryParams": ["int_source"], + "topLevelSites": ["www.mirror.co.uk"] + }, + "wise": { + "queryParams": ["partnerizecampaignID", "clickref", "adref", "partnerID"], + "topLevelSites": ["wise.com"] + }, + "facebook": { + "queryParams": ["tracking", "extid", "mibextid"], + "topLevelSites": ["www.facebook.com"] + }, + "lazada": { + "queryParams": [ + "mkttid", + "laz_trackid", + "spm", + "clickTrackInfo", + "trafficFrom", + "scm", + "acm", + "ad_src", + "did", + "mp", + "cid", + "pos" + ], + "topLevelSites": [ + "www.lazada.com.ph", + "www.lazada.vn", + "www.lazada.sg", + "www.lazada.com.my" + ] + }, + "msn": { + "queryParams": ["cvid", "pc", "ei"], + "topLevelSites": ["www.msn.com"] + }, + "aliexpress": { + "queryParams": [ + "sk", + "dp", + "spm", + "scm", + "pvid", + "scm_id", + "scm-url", + "utparam", + "aff_fsk", + "aff_fcid", + "terminal_id", + "aff_trace_key", + "pdp_npi", + "aff_short_key", + "aff_platform", + "algo_pvid", + "curPageLogUid", + "mall_affr", + "algo_expid", + "gps-id" + ], + "topLevelSites": ["www.tiktok.com"] + }, + "reddit": { + "queryParams": [ + "ref", + "ref_source", + "ref_campaign", + "correlation_id", + "share_id" + ], + "topLevelSites": ["www.reddit.com"] + }, + "wired": { + "queryParams": ["mbid"], + "topLevelSites": ["www.wired.co.uk", "www.wired.com"] + }, + "yandex": { + "queryParams": [ + "utm-term", + "did", + "from", + "msid", + "stid", + "mlid", + "persistent_id", + "source-serpid", + "suggest_reqid", + "clid" + ], + "topLevelSites": ["yandex.com"] + }, + "ebay": { + "queryParams": [ + "_trkparms", + "mkcid", + "_trksid", + "mkevt", + "amdata", + "ssuid", + "mkrid", + "campid", + "sssrc", + "ssspo", + "_from", + "hash" + ], + "topLevelSites": ["www.ebay.com"] + }, + "flipkart": { + "queryParams": [ + "_refId", + "store", + "ssid", + "affid", + "cid", + "iid", + "pwsvid", + "pageUID", + "lid" + ], + "topLevelSites": ["www.flipkart.com"] + }, + "google": { + "queryParams": [ + "sca_esv", + "ved", + "pcampaignid", + "rlz", + "ei", + "sxsrf", + "sourceid", + "aqs", + "cad", + "usg", + "dpr", + "dcr", + "sei", + "cd", + "vet", + "esrc", + "site", + "gs_l", + "gs_lp", + "sclient", + "oe", + "visit_id", + "biw", + "bih", + "gs_lcp", + "gs_lcrp" + ], + "topLevelSites": [ + "www.google.com", + "www.google.ad", + "www.google.ae", + "www.google.com.af", + "www.google.com.ag", + "www.google.al", + "www.google.am", + "www.google.co.ao", + "www.google.com.ar", + "www.google.as", + "www.google.at", + "www.google.com.au", + "www.google.az", + "www.google.ba", + "www.google.com.bd", + "www.google.be", + "www.google.bf", + "www.google.bg", + "www.google.com.bh", + "www.google.bi", + "www.google.bj", + "www.google.com.bn", + "www.google.com.bo", + "www.google.com.br", + "www.google.bs", + "www.google.bt", + "www.google.co.bw", + "www.google.by", + "www.google.com.bz", + "www.google.ca", + "www.google.cd", + "www.google.cf", + "www.google.cg", + "www.google.ch", + "www.google.ci", + "www.google.co.ck", + "www.google.cl", + "www.google.cm", + "www.google.cn", + "www.google.com.co", + "www.google.co.cr", + "www.google.com.cu", + "www.google.cv", + "www.google.com.cy", + "www.google.cz", + "www.google.de", + "www.google.dj", + "www.google.dk", + "www.google.dm", + "www.google.com.do", + "www.google.dz", + "www.google.com.ec", + "www.google.ee", + "www.google.com.eg", + "www.google.es", + "www.google.com.et", + "www.google.fi", + "www.google.com.fj", + "www.google.fm", + "www.google.fr", + "www.google.ga", + "www.google.ge", + "www.google.gg", + "www.google.com.gh", + "www.google.com.gi", + "www.google.gl", + "www.google.gm", + "www.google.gr", + "www.google.com.gt", + "www.google.gy", + "www.google.com.hk", + "www.google.hn", + "www.google.hr", + "www.google.ht", + "www.google.hu", + "www.google.co.id", + "www.google.ie", + "www.google.co.il", + "www.google.im", + "www.google.co.in", + "www.google.iq", + "www.google.is", + "www.google.it", + "www.google.je", + "www.google.com.jm", + "www.google.jo", + "www.google.co.jp", + "www.google.co.ke", + "www.google.com.kh", + "www.google.ki", + "www.google.kg", + "www.google.co.kr", + "www.google.com.kw", + "www.google.kz", + "www.google.la", + "www.google.com.lb", + "www.google.li", + "www.google.lk", + "www.google.co.ls", + "www.google.lt", + "www.google.lu", + "www.google.lv", + "www.google.com.ly", + "www.google.co.ma", + "www.google.md", + "www.google.me", + "www.google.mg", + "www.google.mk", + "www.google.ml", + "www.google.com.mm", + "www.google.mn", + "www.google.com.mt", + "www.google.mu", + "www.google.mv", + "www.google.mw", + "www.google.com.mx", + "www.google.com.my", + "www.google.co.mz", + "www.google.com.na", + "www.google.com.ng", + "www.google.com.ni", + "www.google.ne", + "www.google.nl", + "www.google.no", + "www.google.com.np", + "www.google.nr", + "www.google.nu", + "www.google.co.nz", + "www.google.com.om", + "www.google.com.pa", + "www.google.com.pe", + "www.google.com.pg", + "www.google.com.ph", + "www.google.com.pk", + "www.google.pl", + "www.google.pn", + "www.google.com.pr", + "www.google.ps", + "www.google.pt", + "www.google.com.py", + "www.google.com.qa", + "www.google.ro", + "www.google.ru", + "www.google.rw", + "www.google.com.sa", + "www.google.com.sb", + "www.google.sc", + "www.google.se", + "www.google.com.sg", + "www.google.sh", + "www.google.si", + "www.google.sk", + "www.google.com.sl", + "www.google.sn", + "www.google.so", + "www.google.sm", + "www.google.sr", + "www.google.st", + "www.google.com.sv", + "www.google.td", + "www.google.tg", + "www.google.co.th", + "www.google.com.tj", + "www.google.tl", + "www.google.tm", + "www.google.tn", + "www.google.to", + "www.google.com.tr", + "www.google.tt", + "www.google.com.tw", + "www.google.co.tz", + "www.google.com.ua", + "www.google.co.ug", + "www.google.co.uk", + "www.google.com.uy", + "www.google.co.uz", + "www.google.com.vc", + "www.google.co.ve", + "www.google.co.vi", + "www.google.com.vn", + "www.google.vu", + "www.google.ws", + "www.google.rs", + "www.google.co.za", + "www.google.co.zm", + "www.google.co.zw", + "www.google.cat" + ] + }, + "bing": { + "queryParams": ["qp", "cvid", "qs", "form", "sk", "sc", "sp"], + "topLevelSites": ["www.bing.com"] + }, + "twitch": { + "queryParams": ["tt_content", "tt_medium"], + "topLevelSites": ["www.twitch.tv"] + }, + "cnet": { + "queryParams": ["ftag"], + "topLevelSites": ["www.cnet.com"] + }, + "nytimes": { + "queryParams": ["smid"], + "topLevelSites": ["www.nytimes.com"] + }, + "github": { + "queryParams": ["email_token", "email_source"], + "topLevelSites": ["github.com"] + }, + "linkedin": { + "queryParams": ["refId", "trk", "trackingId"], + "topLevelSites": ["ca.linkedin.com"] + }, + "newyorker": { + "queryParams": ["esrc", "bxid", "cndid", "source", "mbid"], + "topLevelSites": ["www.newyorker.com"] + }, + "bestbuy": { + "queryParams": ["acampID", "irclickid", "irgwc", "loc", "mpid", "intl"], + "topLevelSites": ["www.bestbuy.com"] + } +} diff --git a/toolkit/components/antitracking/data/StripOnShare.json b/toolkit/components/antitracking/StripOnShareLists/MPL2/StripOnShare.json index 394723fa00..00cc973af6 100644 --- a/toolkit/components/antitracking/data/StripOnShare.json +++ b/toolkit/components/antitracking/StripOnShareLists/MPL2/StripOnShare.json @@ -55,7 +55,7 @@ }, "twitter": { "queryParams": ["ref_src", "ref_url"], - "topLevelSites": ["www.twitter.com", "twitter.com"] + "topLevelSites": ["www.twitter.com", "twitter.com", "x.com"] }, "instagram": { "queryParams": ["igshid", "ig_rid"], diff --git a/toolkit/components/antitracking/URLDecorationStripper.cpp b/toolkit/components/antitracking/URLDecorationStripper.cpp index 38af391945..fc94fe8ed5 100644 --- a/toolkit/components/antitracking/URLDecorationStripper.cpp +++ b/toolkit/components/antitracking/URLDecorationStripper.cpp @@ -22,8 +22,8 @@ namespace mozilla { nsresult URLDecorationStripper::StripTrackingIdentifiers(nsIURI* aURI, nsACString& aOutSpec) { - nsAutoString tokenList; - nsresult rv = Preferences::GetString(kPrefName, tokenList); + nsAutoCString tokenList; + nsresult rv = Preferences::GetCString(kPrefName, tokenList); ToLowerCase(tokenList); nsAutoCString path; @@ -34,12 +34,12 @@ nsresult URLDecorationStripper::StripTrackingIdentifiers(nsIURI* aURI, int32_t queryBegins = path.FindChar('?'); // Only positive values are valid since the path must begin with a '/'. if (queryBegins > 0) { - for (const nsAString& token : tokenList.Split(' ')) { + for (const nsACString& token : tokenList.Split(' ')) { if (token.IsEmpty()) { continue; } - nsAutoString value; + nsAutoCString value; if (URLParams::Extract(Substring(path, queryBegins + 1), token, value) && !value.IsVoid()) { // Tracking identifier found in the URL! diff --git a/toolkit/components/antitracking/URLQueryStringStripper.cpp b/toolkit/components/antitracking/URLQueryStringStripper.cpp index 2e154b9103..5eb513472b 100644 --- a/toolkit/components/antitracking/URLQueryStringStripper.cpp +++ b/toolkit/components/antitracking/URLQueryStringStripper.cpp @@ -17,6 +17,7 @@ #include "nsIURIMutator.h" #include "nsUnicharUtils.h" #include "nsURLHelper.h" +#include "nsNetUtil.h" #include "mozilla/dom/StripOnShareRuleBinding.h" namespace { @@ -84,64 +85,16 @@ URLQueryStringStripper::StripForCopyOrShare(nsIURI* aURI, NS_ENSURE_ARG_POINTER(strippedURI); int aStripCount = 0; - nsAutoCString query; - nsresult rv = aURI->GetQuery(query); - NS_ENSURE_SUCCESS(rv, rv); - // We don't need to do anything if there is no query string. - if (query.IsEmpty()) { - Telemetry::Accumulate(Telemetry::STRIP_ON_SHARE_PARAMS_REMOVED, 0); - return NS_OK; - } - nsAutoCString host; - rv = aURI->GetHost(host); + nsresult rv = + StripForCopyOrShareInternal(aURI, strippedURI, aStripCount, false); NS_ENSURE_SUCCESS(rv, rv); - URLParams params; - - URLParams::Parse(query, true, [&](nsString&& name, nsString&& value) { - nsAutoString lowerCaseName; - ToLowerCase(name, lowerCaseName); - // Look through the global rules. - dom::StripRule globalRule; - bool keyExists = mStripOnShareMap.Get("*"_ns, &globalRule); - // There should always be a global rule. - MOZ_ASSERT(keyExists); - for (const auto& param : globalRule.mQueryParams) { - if (param == lowerCaseName) { - aStripCount++; - return true; - } - } - - // Check for site specific rules. - dom::StripRule siteSpecificRule; - keyExists = mStripOnShareMap.Get(host, &siteSpecificRule); - if (keyExists) { - for (const auto& param : siteSpecificRule.mQueryParams) { - if (param == lowerCaseName) { - aStripCount++; - return true; - } - } - } - - params.Append(name, value); - return true; - }); - Telemetry::Accumulate(Telemetry::STRIP_ON_SHARE_PARAMS_REMOVED, aStripCount); if (!aStripCount) { return NS_OK; } - nsAutoString newQuery; - params.Serialize(newQuery, false); - - Unused << NS_MutateURI(aURI) - .SetQuery(NS_ConvertUTF16toUTF8(newQuery)) - .Finalize(strippedURI); - // To calculate difference in length of the URL // after stripping occurs for Telemetry nsAutoCString specOriginalURI; @@ -308,9 +261,8 @@ nsresult URLQueryStringStripper::StripQueryString(nsIURI* aURI, URLParams params; - URLParams::Parse(query, false, [&](nsString&& name, nsString&& value) { - nsAutoString lowerCaseName; - + URLParams::Parse(query, false, [&](nsCString&& name, nsCString&& value) { + nsAutoCString lowerCaseName; ToLowerCase(name, lowerCaseName); if (mList.Contains(lowerCaseName)) { @@ -320,7 +272,7 @@ nsresult URLQueryStringStripper::StripQueryString(nsIURI* aURI, // this will only count query params listed in the Histogram definition. // Calls for any other query params will be discarded. nsAutoCString telemetryLabel("param_"); - AppendUTF16toUTF8(lowerCaseName, telemetryLabel); + telemetryLabel.Append(lowerCaseName); Telemetry::AccumulateCategorical( Telemetry::QUERY_STRIPPING_COUNT_BY_PARAM, telemetryLabel); @@ -336,13 +288,10 @@ nsresult URLQueryStringStripper::StripQueryString(nsIURI* aURI, return NS_OK; } - nsAutoString newQuery; + nsAutoCString newQuery; params.Serialize(newQuery, false); - Unused << NS_MutateURI(uri) - .SetQuery(NS_ConvertUTF16toUTF8(newQuery)) - .Finalize(aOutput); - + Unused << NS_MutateURI(uri).SetQuery(newQuery).Finalize(aOutput); return NS_OK; } @@ -362,10 +311,10 @@ bool URLQueryStringStripper::CheckAllowList(nsIURI* aURI) { return mAllowList.Contains(baseDomain); } -void URLQueryStringStripper::PopulateStripList(const nsAString& aList) { +void URLQueryStringStripper::PopulateStripList(const nsACString& aList) { mList.Clear(); - for (const nsAString& item : aList.Split(' ')) { + for (const nsACString& item : aList.Split(' ')) { mList.Insert(item); } } @@ -380,7 +329,7 @@ void URLQueryStringStripper::PopulateAllowList(const nsACString& aList) { NS_IMETHODIMP URLQueryStringStripper::OnQueryStrippingListUpdate( - const nsAString& aStripList, const nsACString& aAllowList) { + const nsACString& aStripList, const nsACString& aAllowList) { PopulateStripList(aStripList); PopulateAllowList(aAllowList); return NS_OK; @@ -396,8 +345,7 @@ URLQueryStringStripper::OnStripOnShareUpdate(const nsTArray<nsString>& aArgs, continue; } for (const auto& topLevelSite : rule.mTopLevelSites) { - mStripOnShareMap.InsertOrUpdate(NS_ConvertUTF16toUTF8(topLevelSite), - rule); + mStripOnShareMap.InsertOrUpdate(topLevelSite, rule); } } return NS_OK; @@ -407,10 +355,9 @@ NS_IMETHODIMP URLQueryStringStripper::TestGetStripList(nsACString& aStripList) { aStripList.Truncate(); - StringJoinAppend(aStripList, " "_ns, mList, - [](auto& aResult, const auto& aValue) { - aResult.Append(NS_ConvertUTF16toUTF8(aValue)); - }); + StringJoinAppend( + aStripList, " "_ns, mList, + [](auto& aResult, const auto& aValue) { aResult.Append(aValue); }); return NS_OK; } @@ -426,4 +373,110 @@ URLQueryStringStripper::Observe(nsISupports*, const char* aTopic, return NS_OK; } +nsresult URLQueryStringStripper::StripForCopyOrShareInternal( + nsIURI* aURI, nsIURI** strippedURI, int& aStripCount, + bool aStripNestedURIs) { + nsAutoCString query; + nsresult rv = aURI->GetQuery(query); + NS_ENSURE_SUCCESS(rv, rv); + + // We don't need to do anything if there is no query string. + if (query.IsEmpty()) { + Telemetry::Accumulate(Telemetry::STRIP_ON_SHARE_PARAMS_REMOVED, 0); + return NS_OK; + } + + nsAutoCString host; + rv = aURI->GetHost(host); + NS_ENSURE_SUCCESS(rv, rv); + + URLParams params; + + URLParams::Parse(query, false, [&](nsCString&& name, nsCString&& value) { + nsAutoCString lowerCaseName; + ToLowerCase(name, lowerCaseName); + + // Look through the global rules. + dom::StripRule globalRule; + bool keyExists = mStripOnShareMap.Get("*"_ns, &globalRule); + // There should always be a global rule. + MOZ_ASSERT(keyExists); + + // Look through the global rules. + for (const auto& param : globalRule.mQueryParams) { + if (param == lowerCaseName) { + aStripCount++; + return true; + } + } + + // Check for site specific rules. + dom::StripRule siteSpecificRule; + keyExists = mStripOnShareMap.Get(host, &siteSpecificRule); + if (keyExists) { + for (const auto& param : siteSpecificRule.mQueryParams) { + if (param == lowerCaseName) { + aStripCount++; + return true; + } + } + } + + // Only if it is top layer of the recursion then it + // checks if the value of the query parameter is a valid URI + // if not then it gets added back to the query, if it is then + // it gets passed back into this method but with the recursive + // stripping flag set to true + if (!aStripNestedURIs) { + nsAutoCString decodeValue; + URLParams::DecodeString(value, decodeValue); + + nsCOMPtr<nsIURI> nestedURI; + rv = NS_NewURI(getter_AddRefs(nestedURI), decodeValue); + + if (NS_WARN_IF(NS_FAILED(rv))) { + params.Append(name, value); + return true; + } + + nsCOMPtr<nsIURI> strippedNestedURI; + rv = StripForCopyOrShareInternal( + nestedURI, getter_AddRefs(strippedNestedURI), aStripCount, true); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (!strippedNestedURI) { + params.Append(name, value); + return true; + } + + nsAutoCString nestedURIString; + rv = strippedNestedURI->GetSpec(nestedURIString); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + // Encodes URI + nsAutoCString encodedURI; + URLParams::SerializeString(nestedURIString, encodedURI); + + params.Append(name, encodedURI); + return true; + } + + params.Append(name, value); + return true; + }); + + // Returns null for strippedURI if no query params have been stripped. + if (!aStripCount) { + return NS_OK; + } + + nsAutoCString newQuery; + params.Serialize(newQuery, false); + return NS_MutateURI(aURI).SetQuery(newQuery).Finalize(strippedURI); +} + } // namespace mozilla diff --git a/toolkit/components/antitracking/URLQueryStringStripper.h b/toolkit/components/antitracking/URLQueryStringStripper.h index 89466cfd12..3e8e8203a9 100644 --- a/toolkit/components/antitracking/URLQueryStringStripper.h +++ b/toolkit/components/antitracking/URLQueryStringStripper.h @@ -46,10 +46,16 @@ class URLQueryStringStripper final : public nsIObserver, bool CheckAllowList(nsIURI* aURI); - void PopulateStripList(const nsAString& aList); + void PopulateStripList(const nsACString& aList); void PopulateAllowList(const nsACString& aList); - nsTHashSet<nsString> mList; + // Recursive helper function that helps strip URIs of tracking parameters + // and enables the stripping of tracking paramerters that are in a URI which + // is nested in a query parameter + nsresult StripForCopyOrShareInternal(nsIURI* aURI, nsIURI** strippedURI, + int& aStripCount, bool aStripNestedURIs); + + nsTHashSet<nsCString> mList; nsTHashSet<nsCString> mAllowList; nsCOMPtr<nsIURLQueryStrippingListService> mListService; nsTHashMap<nsCString, dom::StripRule> mStripOnShareMap; diff --git a/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs b/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs index baaa9f3824..d9c1995821 100644 --- a/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs +++ b/toolkit/components/antitracking/URLQueryStrippingListService.sys.mjs @@ -31,10 +31,9 @@ XPCOMUtils.defineLazyPreferenceGetter( PREF_STRIP_IS_TEST ); -// Lazy getter for the strip-on-share strip list. -ChromeUtils.defineLazyGetter(lazy, "StripOnShareList", async () => { +async function fetchAndParseList(fileName) { let response = await fetch( - "chrome://global/content/antitracking/StripOnShare.json" + "chrome://global/content/antitracking/" + fileName ); if (!response.ok) { lazy.logger.error( @@ -44,7 +43,48 @@ ChromeUtils.defineLazyGetter(lazy, "StripOnShareList", async () => { "Error fetching strip-on-share strip list" + response.status ); } - return response.json(); + return await response.json(); +} + +// Lazy getter for the strip-on-share strip list. +ChromeUtils.defineLazyGetter(lazy, "StripOnShareList", async () => { + let [stripOnShareList, stripOnShareLGPLParams] = await Promise.all([ + fetchAndParseList("StripOnShare.json"), + fetchAndParseList("StripOnShareLGPL.json"), + ]); + + if (!stripOnShareList || !stripOnShareLGPLParams) { + lazy.logger.error("Error strip-on-share strip list were not loaded"); + throw new Error("Error fetching strip-on-share strip list were not loaded"); + } + + // Combines the mozilla licensed strip on share param + // list and the LGPL licensed strip on share param list + for (const key in stripOnShareLGPLParams) { + if (Object.hasOwn(stripOnShareList, key)) { + stripOnShareList[key].queryParams.push( + ...stripOnShareLGPLParams[key].queryParams + ); + + stripOnShareList[key].topLevelSites.push( + ...stripOnShareLGPLParams[key].topLevelSites + ); + + // Removes duplicates topLevelSitres + stripOnShareList[key].topLevelSites = [ + ...new Set(stripOnShareList[key].topLevelSites), + ]; + + // Removes duplicates queryParams + stripOnShareList[key].queryParams = [ + ...new Set(stripOnShareList[key].queryParams), + ]; + } else { + stripOnShareList[key] = stripOnShareLGPLParams[key]; + } + } + + return stripOnShareList; }); export class URLQueryStrippingListService { @@ -142,11 +182,11 @@ export class URLQueryStrippingListService { } // Get the list from pref. - this._onPrefUpdate( + await this._onPrefUpdate( PREF_STRIP_LIST_NAME, Services.prefs.getStringPref(PREF_STRIP_LIST_NAME, "") ); - this._onPrefUpdate( + await this._onPrefUpdate( PREF_ALLOW_LIST_NAME, Services.prefs.getStringPref(PREF_ALLOW_LIST_NAME, "") ); @@ -219,7 +259,7 @@ export class URLQueryStrippingListService { this._notifyObservers(); } - _onPrefUpdate(pref, value) { + async _onPrefUpdate(pref, value) { switch (pref) { case PREF_STRIP_LIST_NAME: this.prefStripList = new Set(value ? value.split(" ") : []); @@ -235,7 +275,7 @@ export class URLQueryStrippingListService { } this._notifyObservers(); - this._notifyStripOnShareObservers(); + await this._notifyStripOnShareObservers(); } _getListFromSharedData() { diff --git a/toolkit/components/antitracking/jar.mn b/toolkit/components/antitracking/jar.mn index e45b37c5b6..2f8c4d7ee4 100644 --- a/toolkit/components/antitracking/jar.mn +++ b/toolkit/components/antitracking/jar.mn @@ -9,4 +9,7 @@ toolkit.jar: # domain1: {queryParams: [param1, param2, ..], topLevelSites: [www.site.de, www.site.com,...]}, domain2: {...} # This list will be consumed from the nsIQueryStrippingListService and # later be dispatched to the nsIURLQueryStringStripper in a further processed form. - content/global/antitracking/StripOnShare.json (data/StripOnShare.json) + content/global/antitracking/StripOnShare.json (StripOnShareLists/MPL2/StripOnShare.json) + + # Separate StripOnShare list for parameters that are covered in the LGPL License + content/global/antitracking/StripOnShareLGPL.json (StripOnShareLists/LGPL/StripOnShareLGPL.json) diff --git a/toolkit/components/antitracking/nsIURLQueryStringStripper.idl b/toolkit/components/antitracking/nsIURLQueryStringStripper.idl index 372cc0f94a..b3e939094b 100644 --- a/toolkit/components/antitracking/nsIURLQueryStringStripper.idl +++ b/toolkit/components/antitracking/nsIURLQueryStringStripper.idl @@ -23,7 +23,7 @@ interface nsIURLQueryStringStripper : nsISupports { // Strip the query parameters that are in the strip list. Return the amount of // query parameters that have been stripped. Returns 0 if no query parameters // have been stripped or the feature is disabled. - uint32_t strip(in nsIURI aURI, in bool aIsPBM, out nsIURI aOutput); + uint32_t strip(in nsIURI aURI, in boolean aIsPBM, out nsIURI aOutput); // Strip the query parameters that are in the stripForCopy/Share strip list. // Returns ether the stripped URI or null if no query parameters have been stripped diff --git a/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl b/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl index f746126c13..1af2c4727f 100644 --- a/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl +++ b/toolkit/components/antitracking/nsIURLQueryStrippingListService.idl @@ -22,7 +22,7 @@ interface nsIURLQueryStrippingListObserver : nsISupports * A comma-separated list of hosts (eTLD+1) that are exempt from query * stripping. */ - void onQueryStrippingListUpdate(in AString aStripList, in ACString aAllowList); + void onQueryStrippingListUpdate(in ACString aStripList, in ACString aAllowList); /** * Called by nsIQueryStrippingListService when the list of query stripping diff --git a/toolkit/components/antitracking/test/browser/browser.toml b/toolkit/components/antitracking/test/browser/browser.toml index 9d0874ed3a..edb429b49e 100644 --- a/toolkit/components/antitracking/test/browser/browser.toml +++ b/toolkit/components/antitracking/test/browser/browser.toml @@ -1,5 +1,9 @@ [DEFAULT] -skip-if = ["os == 'linux' && (asan || tsan)"] # bug 1662229 - task exception +skip-if = [ + "os == 'linux' && os_version == '18.04' && asan", # bug 1662229 - task exception + "os == 'linux' && os_version == '18.04' && tsan", # bug 1662229 - task exception + "debug", # bug 1884982 - takes 20+ minutes to run on debug +] prefs = [ "dom.storage_access.prompt.testing=true", # Disable the Storage Access API prompts for all of the tests in this directory "dom.storage_access.prompt.testing.allow=true", @@ -108,6 +112,8 @@ skip-if = ["os == 'mac' && !debug"] # Bug 1503778, 1577362 ["browser_onModifyRequestNotificationForTrackingResources.js"] +["browser_partitionedABA.js"] + ["browser_partitionedClearSiteDataHeader.js"] support-files = ["clearSiteData.sjs"] @@ -194,7 +200,7 @@ support-files = [ "!/browser/components/originattributes/test/browser/file_thirdPartyChild.request.html", "!/browser/components/originattributes/test/browser/file_thirdPartyChild.script.js", "!/browser/components/originattributes/test/browser/file_thirdPartyChild.sharedworker.js", - "!/browser/components/originattributes/test/browser/file_thirdPartyChild.video.ogv", + "!/browser/components/originattributes/test/browser/file_thirdPartyChild.video.webm", "!/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.fetch.html", "!/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.js", "!/browser/components/originattributes/test/browser/file_thirdPartyChild.worker.request.html", @@ -210,7 +216,7 @@ support-files = [ "file_saveAsImage.sjs", "file_saveAsVideo.sjs", "file_saveAsPageInfo.html", - "file_video.ogv", + "file_video.webm", ] ["browser_staticPartition_tls_session.js"] diff --git a/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js b/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js index 4f38b29a7d..c3b6342ce1 100644 --- a/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js +++ b/toolkit/components/antitracking/test/browser/browser_doublyNestedTracker.js @@ -36,7 +36,7 @@ add_task(async function () { async function runChecks() { is(document.cookie, "", "No cookies for me"); document.cookie = "name=value"; - is(document.cookie, "name=value", "I have the cookies!"); + is(document.cookie, "", "I don't have the cookies!"); } await new Promise(resolve => { diff --git a/toolkit/components/antitracking/test/browser/browser_partitionedABA.js b/toolkit/components/antitracking/test/browser/browser_partitionedABA.js new file mode 100644 index 0000000000..b6acc82d48 --- /dev/null +++ b/toolkit/components/antitracking/test/browser/browser_partitionedABA.js @@ -0,0 +1,86 @@ +/* vim: set ts=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/. */ + +/* + * A test to verify that ABA iframes partition at least localStorage and document.cookie + */ + +"use strict"; + +add_setup(async function () { + await setCookieBehaviorPref( + BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, + false + ); +}); + +add_task(async function runTest() { + info("Creating the tab"); + let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE); + gBrowser.selectedTab = tab; + + let browser = tab.linkedBrowser; + await BrowserTestUtils.browserLoaded(browser); + + info("Creating the third-party iframe"); + let ifrBC = await SpecialPowers.spawn( + browser, + [TEST_TOP_PAGE_7], + async page => { + let ifr = content.document.createElement("iframe"); + + let loading = ContentTaskUtils.waitForEvent(ifr, "load"); + content.document.body.appendChild(ifr); + ifr.src = page; + await loading; + + return ifr.browsingContext; + } + ); + + info("Creating the ABA iframe"); + let ifrABABC = await SpecialPowers.spawn( + ifrBC, + [TEST_TOP_PAGE], + async page => { + let ifr = content.document.createElement("iframe"); + + let loading = ContentTaskUtils.waitForEvent(ifr, "load"); + content.document.body.appendChild(ifr); + ifr.src = page; + await loading; + + return ifr.browsingContext; + } + ); + + info("Write cookie to the ABA third-party iframe"); + await SpecialPowers.spawn(ifrABABC, [], async _ => { + content.document.cookie = "foo; SameSite=None; Secure; Partitioned"; + }); + + let cookie = await SpecialPowers.spawn(browser, [], async () => { + return content.document.cookie; + }); + is(cookie, "", "Cookie is not in the top level"); + + info("Write localstorage to the ABA third-party iframe"); + await SpecialPowers.spawn(ifrABABC, [], async _ => { + content.localStorage.setItem("foo", "bar"); + }); + + let storage = await SpecialPowers.spawn(browser, [], async () => { + return content.localStorage.getItem("foo"); + }); + is(storage, null, "LocalStorage update is not in the top level"); + + info("Clean up"); + BrowserTestUtils.removeTab(tab); + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, () => + resolve() + ); + }); +}); diff --git a/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js b/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js index 9afe3180fb..9383e5a6f9 100644 --- a/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js +++ b/toolkit/components/antitracking/test/browser/browser_staticPartition_cache.js @@ -105,7 +105,7 @@ add_task(async function () { // The CSS cache needs to be cleared in-process. content.windowUtils.clearSharedStyleSheetCache(); - let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.ogv"; + let videoURL = arg.urlPrefix + "file_thirdPartyChild.video.webm"; let audioURL = arg.urlPrefix + "file_thirdPartyChild.audio.ogg"; let URLSuffix = "?r=" + arg.randomSuffix; @@ -176,7 +176,7 @@ add_task(async function () { "xhr.html", "worker.xhr.html", "audio.ogg", - "video.ogv", + "video.webm", "fetch.html", "worker.fetch.html", "request.html", diff --git a/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js b/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js index b9984829eb..9da751cadb 100644 --- a/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js +++ b/toolkit/components/antitracking/test/browser/browser_staticPartition_saveAs.js @@ -401,7 +401,7 @@ add_task(async function testPageInfoMediaSaveAs() { ); info("Open the media panel of the pageinfo."); - let pageInfo = BrowserPageInfo( + let pageInfo = BrowserCommands.pageInfo( gBrowser.selectedBrowser.currentURI.spec, "mediaTab" ); @@ -478,7 +478,7 @@ add_task(async function testPageInfoMediaMultipleSelectedSaveAs() { ); info("Open the media panel of the pageinfo."); - let pageInfo = BrowserPageInfo( + let pageInfo = BrowserCommands.pageInfo( gBrowser.selectedBrowser.currentURI.spec, "mediaTab" ); diff --git a/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js b/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js index f31c7893d0..8ea97dac68 100644 --- a/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js +++ b/toolkit/components/antitracking/test/browser/browser_storageAccessThirdPartyChecks_alwaysPartition.js @@ -53,6 +53,7 @@ AntiTracking._createTask({ "https://tracking.example.org", "https://tracking.example.org", "https://tracking.example.org", + "https://another-tracking.example.net", "https://itisatracker.org", ], }); diff --git a/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html b/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html index aa3de2a555..c8c409d130 100644 --- a/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html +++ b/toolkit/components/antitracking/test/browser/file_saveAsPageInfo.html @@ -1,6 +1,6 @@ <html> <body> <img src="http://example.net/browser/toolkit/components/antitracking/test/browser/raptor.jpg" id="image1"> - <video src="http://example.net/browser/toolkit/components/antitracking/test/browser/file_video.ogv" id="video1"> </video> + <video src="http://example.net/browser/toolkit/components/antitracking/test/browser/file_video.webm" id="video1"> </video> </body> </html> diff --git a/toolkit/components/antitracking/test/browser/file_video.ogv b/toolkit/components/antitracking/test/browser/file_video.ogv Binary files differdeleted file mode 100644 index 68dee3cf2b..0000000000 --- a/toolkit/components/antitracking/test/browser/file_video.ogv +++ /dev/null diff --git a/toolkit/components/antitracking/test/browser/file_video.webm b/toolkit/components/antitracking/test/browser/file_video.webm Binary files differnew file mode 100644 index 0000000000..2cee5a9e51 --- /dev/null +++ b/toolkit/components/antitracking/test/browser/file_video.webm diff --git a/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp b/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp index 44959e4dc8..666757c382 100644 --- a/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp +++ b/toolkit/components/antitracking/test/gtest/TestStoragePrincipalHelper.cpp @@ -45,7 +45,7 @@ nsresult CreateMockChannel(nsIPrincipal* aPrincipal, bool isThirdParty, NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsILoadInfo> mockLoadInfo = mockChannel->LoadInfo(); - rv = mockLoadInfo->SetIsThirdPartyContextToTopWindow(isThirdParty); + rv = mockLoadInfo->SetIsInThirdPartyContext(isThirdParty); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsICookieJarSettings> cjs; diff --git a/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js b/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js index 48a0bd4682..f8be5fccad 100644 --- a/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js +++ b/toolkit/components/antitracking/test/xpcshell/test_validate_strip_on_share_list.js @@ -7,67 +7,19 @@ const { JsonSchema } = ChromeUtils.importESModule( "resource://gre/modules/JsonSchema.sys.mjs" ); -let stripOnShareList; +let stripOnShareMergedList, stripOnShareLGPLParams, stripOnShareList; -// Fetching strip on share list -add_setup(async function () { - /* globals fetch */ - let response = await fetch( - "chrome://global/content/antitracking/StripOnShare.json" - ); +async function fetchAndParseList(fileName) { + let response = await fetch("chrome://global/content/" + fileName); if (!response.ok) { throw new Error( "Error fetching strip-on-share strip list" + response.status ); } - stripOnShareList = await response.json(); -}); - -// Check if the Strip on Share list contains any duplicate params -add_task(async function test_check_duplicates() { - let stripOnShareParams = stripOnShareList; - - const allQueryParams = []; - - for (const domain in stripOnShareParams) { - for (let param in stripOnShareParams[domain].queryParams) { - allQueryParams.push(stripOnShareParams[domain].queryParams[param]); - } - } - - let setOfParams = new Set(allQueryParams); - - if (setOfParams.size != allQueryParams.length) { - let setToCheckDupes = new Set(); - let dupeList = new Set(); - for (const domain in stripOnShareParams) { - for (let param in stripOnShareParams[domain].queryParams) { - let tempParam = stripOnShareParams[domain].queryParams[param]; - - if (setToCheckDupes.has(tempParam)) { - dupeList.add(tempParam); - } else { - setToCheckDupes.add(tempParam); - } - } - } - - Assert.equal( - setOfParams.size, - allQueryParams.length, - "There are duplicates rules. The duplicate rules are " + [...dupeList] - ); - } + return await response.json(); +} - Assert.equal( - setOfParams.size, - allQueryParams.length, - "There are no duplicates rules." - ); -}); - -// Validate the format of Strip on Share list with Schema -add_task(async function test_check_schema() { +async function validateSchema(paramList, nameOfList) { let schema = { $schema: "http://json-schema.org/draft-07/schema#", type: "object", @@ -88,13 +40,86 @@ add_task(async function test_check_schema() { required: ["global"], }; - let stripOnShareParams = stripOnShareList; let validator = new JsonSchema.Validator(schema); - let { valid, errors } = validator.validate(stripOnShareParams); + let { valid, errors } = validator.validate(paramList); if (!valid) { - info("validation errors: " + JSON.stringify(errors, null, 2)); + info( + nameOfList + + " JSON contains these validation errors: " + + JSON.stringify(errors, null, 2) + ); + } + + Assert.ok(valid, nameOfList + " JSON is valid"); +} + +// Fetching strip on share list +add_setup(async function () { + /* globals fetch */ + + [stripOnShareList, stripOnShareLGPLParams] = await Promise.all([ + fetchAndParseList("antitracking/StripOnShare.json"), + fetchAndParseList("antitracking/StripOnShareLGPL.json"), + ]); + + stripOnShareMergedList = stripOnShareList; + + // Combines the mozilla licensed strip on share param + // list and the LGPL licensed strip on share param list + for (const key in stripOnShareLGPLParams) { + if (Object.hasOwn(stripOnShareMergedList, key)) { + stripOnShareMergedList[key].queryParams.push( + ...stripOnShareLGPLParams[key].queryParams + ); + } else { + stripOnShareMergedList[key] = stripOnShareLGPLParams[key]; + } + } +}); + +// Check if the Strip on Share list contains any duplicate params +add_task(async function test_check_duplicates() { + for (const domain in stripOnShareMergedList) { + // Creates a set of query parameters for a given domain to check + // for duplicates + let setOfParams = new Set(stripOnShareMergedList[domain].queryParams); + + // If there are duplicates which is known because the sizes of the set + // and array don't match, then we check which parameters are duplciates + if (setOfParams.size != stripOnShareMergedList[domain].queryParams.length) { + let setToCheckDupes = new Set(); + let dupeList = new Set(); + for (let param in stripOnShareMergedList[domain].queryParams) { + let tempParam = stripOnShareMergedList[domain].queryParams[param]; + + if (setToCheckDupes.has(tempParam)) { + dupeList.add(tempParam); + } else { + setToCheckDupes.add(tempParam); + } + } + + Assert.equal( + setOfParams.size, + stripOnShareMergedList[domain].queryParams.length, + "There are duplicates rules in " + + domain + + ". The duplicate rules are " + + [...dupeList] + ); + } + + Assert.equal( + setOfParams.size, + stripOnShareMergedList[domain].queryParams.length, + "There are no duplicates rules." + ); } +}); - Assert.ok(valid, "Strip on share JSON is valid"); +// Validate the format of Strip on Share list with Schema +add_task(async function test_check_schema() { + validateSchema(stripOnShareLGPLParams, "Strip On Share LGPL"); + validateSchema(stripOnShareList, "Strip On Share"); }); |