1
0
Fork 0
firefox/toolkit/components/antitracking/DynamicFpiNavigationHeuristic.cpp
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

190 lines
6.6 KiB
C++

/* -*- 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 "DynamicFpiNavigationHeuristic.h"
#include "mozIThirdPartyUtil.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/BounceTrackingRecord.h"
#include "mozilla/BounceTrackingState.h"
#include "mozilla/Components.h"
#include "mozilla/glean/AntitrackingMetrics.h"
#include "mozilla/net/UrlClassifierCommon.h"
#include "nsIChannel.h"
#include "nsIClassifiedChannel.h"
#include "nsNetUtil.h"
#include "nsISHistory.h"
#include "nsIURIClassifier.h"
namespace mozilla {
namespace {
// A helper function to check if a channel is a first-party tracking channel.
bool IsFirstPartyTrackingChannel(nsIChannel* aChannel) {
nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
do_QueryInterface(aChannel);
if (!classifiedChannel) {
return false;
}
// We're looking at the first-party classification flags because the
// navigation heuristic is only interested in first-party redirects.
uint32_t firstPartyClassificationFlags =
classifiedChannel->GetFirstPartyClassificationFlags();
if (net::UrlClassifierCommon::IsTrackingClassificationFlag(
firstPartyClassificationFlags, NS_UsePrivateBrowsing(aChannel))) {
return true;
}
return false;
}
} // anonymous namespace
// static
void DynamicFpiNavigationHeuristic::MaybeGrantStorageAccess(
dom::CanonicalBrowsingContext* aBrowsingContext, nsIChannel* aChannel) {
// Make sure we only fire the heuristic when it is enabled.
if (!StaticPrefs::privacy_antitracking_enableWebcompat() ||
!StaticPrefs::privacy_restrict3rdpartystorage_heuristic_navigation()) {
return;
}
// Validate our args and make sure we have a bounce tracking state.
NS_ENSURE_TRUE_VOID(aBrowsingContext);
NS_ENSURE_FALSE_VOID(aBrowsingContext->IsSubframe());
RefPtr<BounceTrackingState> bounceTrackingState =
aBrowsingContext->GetBounceTrackingState();
NS_ENSURE_TRUE_VOID(bounceTrackingState);
NS_ENSURE_TRUE_VOID(aChannel);
// Don't trigger navigation heuristic for first-party trackers if the pref
// says so.
if (StaticPrefs::
privacy_restrict3rdpartystorage_heuristic_exclude_third_party_trackers() &&
IsFirstPartyTrackingChannel(aChannel)) {
return;
}
nsCOMPtr<nsIPrincipal> resultPrincipal;
nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
aChannel, getter_AddRefs(resultPrincipal));
if (NS_FAILED(rv) || !resultPrincipal ||
!resultPrincipal->GetIsContentPrincipal()) {
return;
}
const Maybe<BounceTrackingRecord>& maybeRecord =
bounceTrackingState->GetBounceTrackingRecord();
if (maybeRecord.isNothing()) {
return;
}
const BounceTrackingRecord& record = maybeRecord.ref();
// Get the session history and the current index (of the opening document)
nsCOMPtr<nsISHistory> shistory = aBrowsingContext->GetSessionHistory();
if (!shistory) {
return;
}
int32_t index = -1;
rv = shistory->GetIndex(&index);
if (NS_FAILED(rv)) {
return;
}
// Loop backward in the session history, looking for the initial visit to
// the same site host as the opening document, and building a set of site
// hosts we interacted with along the way.
bool foundResultSiteInHistory = false;
nsTArray<RefPtr<nsIURI>> candidateURIs;
RefPtr<nsISHEntry> entry;
for (int32_t i = 0; i <= index; i++) {
shistory->GetEntryAtIndex(index - i, getter_AddRefs(entry));
if (!entry) {
continue;
}
RefPtr<nsIURI> entryURI = entry->GetResultPrincipalURI();
// Fall back to an unmodified entry's URI.
// Warning: you should not copy-paste this code elsewhere, nor should you
// use GetURI in security-critical contexts where you really want something
// like the resultPrincipalURI. We are only doing that here because we do
// not have a OriginalURI set, are giving a permission based on a heuristic,
// and constrain ourselves to http(s) URIs.
if (!entryURI) {
entryURI = entry->GetURI();
if (!entryURI) {
continue;
}
}
nsAutoCString scheme;
nsresult rv = entryURI->GetScheme(scheme);
if (NS_FAILED(rv) ||
(!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https"))) {
continue;
}
bool isThirdPartyEntry = false;
rv = resultPrincipal->IsThirdPartyURI(entryURI, &isThirdPartyEntry);
if (NS_SUCCEEDED(rv) && !isThirdPartyEntry) {
nsAutoCString entryScheme;
rv = entryURI->GetScheme(entryScheme);
if (NS_SUCCEEDED(rv) && resultPrincipal->SchemeIs(entryScheme.get())) {
foundResultSiteInHistory = true;
break;
}
}
nsAutoCString entrySiteHost;
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
components::ThirdPartyUtil::Service();
if (!thirdPartyUtil) {
continue;
}
rv = thirdPartyUtil->GetBaseDomain(entryURI, entrySiteHost);
if (NS_FAILED(rv)) {
continue;
}
if (record.GetUserActivationHosts().Contains(entrySiteHost)) {
candidateURIs.AppendElement(entryURI);
}
}
// Fire the heuristic for all interacted-with hosts of the current extended
// navigation
if (foundResultSiteInHistory) {
for (nsIURI* uri : candidateURIs) {
// Construct the right principal, using the opening document's OAs
RefPtr<nsIPrincipal> embedeePrincipal =
BasePrincipal::CreateContentPrincipal(
uri, resultPrincipal->OriginAttributesRef());
Unused << StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess(
resultPrincipal, embedeePrincipal,
StorageAccessAPIHelper::StorageAccessPromptChoices::eAllow, false,
StaticPrefs::privacy_restrict3rdpartystorage_expiration_visited());
glean::contentblocking::storage_access_granted_count
.EnumGet(glean::contentblocking::StorageAccessGrantedCountLabel::
eStoragegranted)
.Add();
glean::contentblocking::storage_access_granted_count
.EnumGet(glean::contentblocking::StorageAccessGrantedCountLabel::
eNavigation)
.Add();
StorageAccessGrantTelemetryClassification::MaybeReportTracker(
static_cast<uint16_t>(
glean::contentblocking::StorageAccessGrantedCountLabel::
eNavigationCt),
uri);
}
}
}
} // namespace mozilla