190 lines
6.6 KiB
C++
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
|