tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

UrlClassifierFeatureEmailTrackingDataCollection.cpp (10172B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "UrlClassifierFeatureEmailTrackingDataCollection.h"
      8 
      9 #include "mozilla/AntiTrackingUtils.h"
     10 #include "mozilla/ClearOnShutdown.h"
     11 #include "mozilla/ContentBlockingNotifier.h"
     12 #include "mozilla/dom/BrowsingContext.h"
     13 #include "mozilla/dom/WindowGlobalParent.h"
     14 #include "mozilla/net/UrlClassifierCommon.h"
     15 #include "mozilla/StaticPrefs_privacy.h"
     16 #include "mozilla/StaticPtr.h"
     17 #include "mozilla/glean/AntitrackingMetrics.h"
     18 
     19 #include "nsIChannel.h"
     20 #include "nsIClassifiedChannel.h"
     21 #include "nsILoadInfo.h"
     22 
     23 namespace mozilla::net {
     24 
     25 namespace {
     26 
     27 #define EMAILTRACKING_DATACOLLECTION_FEATURE_NAME \
     28  "emailtracking-data-collection"
     29 
     30 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST \
     31  "urlclassifier.features.emailtracking.datacollection.blocklistTables"
     32 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_TEST_ENTRIES \
     33  "urlclassifier.features.emailtracking.datacollection.blocklistHosts"
     34 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST \
     35  "urlclassifier.features.emailtracking.datacollection.allowlistTables"
     36 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_TEST_ENTRIES \
     37  "urlclassifier.features.emailtracking.datacollection.allowlistHosts"
     38 #define URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_EXCEPTION_URLS \
     39  "urlclassifier.features.emailtracking.datacollection.skipURLs"
     40 #define TABLE_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_PREF \
     41  "emailtracking-data-collection-blocklist-pref"
     42 #define TABLE_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_PREF \
     43  "emailtracking-data-collection-allowlist-pref"
     44 
     45 StaticRefPtr<UrlClassifierFeatureEmailTrackingDataCollection>
     46    gFeatureEmailTrackingDataCollection;
     47 StaticAutoPtr<nsCString> gEmailWebAppDomainsPref;
     48 static constexpr char kEmailWebAppDomainPrefName[] =
     49    "privacy.trackingprotection.emailtracking.webapp.domains";
     50 
     51 void EmailWebAppDomainPrefChangeCallback(const char* aPrefName, void*) {
     52  MOZ_ASSERT(NS_IsMainThread());
     53  MOZ_ASSERT(!strcmp(aPrefName, kEmailWebAppDomainPrefName));
     54  MOZ_ASSERT(gEmailWebAppDomainsPref);
     55 
     56  Preferences::GetCString(kEmailWebAppDomainPrefName, *gEmailWebAppDomainsPref);
     57 }
     58 
     59 }  // namespace
     60 
     61 UrlClassifierFeatureEmailTrackingDataCollection::
     62    UrlClassifierFeatureEmailTrackingDataCollection()
     63    : UrlClassifierFeatureAntiTrackingBase(
     64          nsLiteralCString(EMAILTRACKING_DATACOLLECTION_FEATURE_NAME),
     65          nsLiteralCString(
     66              URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST),
     67          nsLiteralCString(
     68              URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST),
     69          nsLiteralCString(
     70              URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_TEST_ENTRIES),
     71          nsLiteralCString(
     72              URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_TEST_ENTRIES),
     73          nsLiteralCString(TABLE_EMAILTRACKING_DATACOLLECTION_BLOCKLIST_PREF),
     74          nsLiteralCString(TABLE_EMAILTRACKING_DATACOLLECTION_ENTITYLIST_PREF),
     75          nsLiteralCString(
     76              URLCLASSIFIER_EMAILTRACKING_DATACOLLECTION_EXCEPTION_URLS)) {}
     77 
     78 /* static */
     79 const char* UrlClassifierFeatureEmailTrackingDataCollection::Name() {
     80  return EMAILTRACKING_DATACOLLECTION_FEATURE_NAME;
     81 }
     82 
     83 /* static */
     84 void UrlClassifierFeatureEmailTrackingDataCollection::MaybeInitialize() {
     85  UC_LOG_LEAK(
     86      ("UrlClassifierFeatureEmailTrackingDataCollection::MaybeInitialize"));
     87 
     88  if (!gFeatureEmailTrackingDataCollection) {
     89    gFeatureEmailTrackingDataCollection =
     90        new UrlClassifierFeatureEmailTrackingDataCollection();
     91    gFeatureEmailTrackingDataCollection->InitializePreferences();
     92  }
     93 }
     94 
     95 /* static */
     96 void UrlClassifierFeatureEmailTrackingDataCollection::MaybeShutdown() {
     97  UC_LOG_LEAK(
     98      ("UrlClassifierFeatureEmailTrackingDataCollection::MaybeShutdown"));
     99 
    100  if (gFeatureEmailTrackingDataCollection) {
    101    gFeatureEmailTrackingDataCollection->ShutdownPreferences();
    102    gFeatureEmailTrackingDataCollection = nullptr;
    103  }
    104 }
    105 
    106 /* static */
    107 already_AddRefed<UrlClassifierFeatureEmailTrackingDataCollection>
    108 UrlClassifierFeatureEmailTrackingDataCollection::MaybeCreate(
    109    nsIChannel* aChannel) {
    110  MOZ_ASSERT(aChannel);
    111 
    112  UC_LOG_LEAK(
    113      ("UrlClassifierFeatureEmailTrackingDataCollection::MaybeCreate - channel "
    114       "%p",
    115       aChannel));
    116 
    117  if (!StaticPrefs::
    118          privacy_trackingprotection_emailtracking_data_collection_enabled()) {
    119    return nullptr;
    120  }
    121 
    122  RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    123  bool isThirdParty = loadInfo->GetIsThirdPartyContextToTopWindow();
    124 
    125  // We don't need to collect data if the email tracker is loaded in first-party
    126  // context.
    127  if (!isThirdParty) {
    128    return nullptr;
    129  }
    130 
    131  MaybeInitialize();
    132  MOZ_ASSERT(gFeatureEmailTrackingDataCollection);
    133 
    134  RefPtr<UrlClassifierFeatureEmailTrackingDataCollection> self =
    135      gFeatureEmailTrackingDataCollection;
    136  return self.forget();
    137 }
    138 
    139 /* static */
    140 already_AddRefed<nsIUrlClassifierFeature>
    141 UrlClassifierFeatureEmailTrackingDataCollection::GetIfNameMatches(
    142    const nsACString& aName) {
    143  if (!aName.EqualsLiteral(EMAILTRACKING_DATACOLLECTION_FEATURE_NAME)) {
    144    return nullptr;
    145  }
    146 
    147  MaybeInitialize();
    148  MOZ_ASSERT(gFeatureEmailTrackingDataCollection);
    149 
    150  RefPtr<UrlClassifierFeatureEmailTrackingDataCollection> self =
    151      gFeatureEmailTrackingDataCollection;
    152  return self.forget();
    153 }
    154 
    155 NS_IMETHODIMP
    156 UrlClassifierFeatureEmailTrackingDataCollection::ProcessChannel(
    157    nsIChannel* aChannel, const nsTArray<nsCString>& aList,
    158    const nsTArray<nsCString>& aHashes, bool* aShouldContinue) {
    159  NS_ENSURE_ARG_POINTER(aChannel);
    160  NS_ENSURE_ARG_POINTER(aShouldContinue);
    161 
    162  // This is not a blocking feature.
    163  *aShouldContinue = true;
    164 
    165  UC_LOG(
    166      ("UrlClassifierFeatureEmailTrackingDataCollection::ProcessChannel - "
    167       "collecting data from channel %p",
    168       aChannel));
    169 
    170  static std::vector<UrlClassifierCommon::ClassificationData>
    171      sClassificationData = {
    172          {"base-email-track-"_ns,
    173           nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_EMAILTRACKING},
    174          {"content-email-track-"_ns,
    175           nsIClassifiedChannel::ClassificationFlags::
    176               CLASSIFIED_EMAILTRACKING_CONTENT},
    177      };
    178 
    179  // Get if the top window is a email webapp.
    180  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    181 
    182  RefPtr<dom::BrowsingContext> bc;
    183  loadInfo->GetBrowsingContext(getter_AddRefs(bc));
    184  if (!bc || bc->IsDiscarded()) {
    185    return NS_OK;
    186  }
    187 
    188  RefPtr<dom::WindowGlobalParent> topWindowParent =
    189      bc->Canonical()->GetTopWindowContext();
    190  if (!topWindowParent || topWindowParent->IsDiscarded()) {
    191    return NS_OK;
    192  }
    193 
    194  // Cache the email webapp domains pref value and register the callback
    195  // function to update the cached value when the pref changes.
    196  if (!gEmailWebAppDomainsPref) {
    197    gEmailWebAppDomainsPref = new nsCString();
    198 
    199    Preferences::RegisterCallbackAndCall(EmailWebAppDomainPrefChangeCallback,
    200                                         kEmailWebAppDomainPrefName);
    201    RunOnShutdown([]() {
    202      Preferences::UnregisterCallback(EmailWebAppDomainPrefChangeCallback,
    203                                      kEmailWebAppDomainPrefName);
    204      gEmailWebAppDomainsPref = nullptr;
    205    });
    206  }
    207 
    208  bool isTopEmailWebApp = topWindowParent->DocumentPrincipal()->IsURIInList(
    209      *gEmailWebAppDomainsPref);
    210 
    211  uint32_t flags = UrlClassifierCommon::TablesToClassificationFlags(
    212      aList, sClassificationData,
    213      nsIClassifiedChannel::ClassificationFlags::CLASSIFIED_EMAILTRACKING);
    214 
    215  if (flags & nsIClassifiedChannel::ClassificationFlags::
    216                  CLASSIFIED_EMAILTRACKING_CONTENT) {
    217    glean::contentblocking::email_tracker_count
    218        .EnumGet(isTopEmailWebApp
    219                     ? glean::contentblocking::EmailTrackerCountLabel::
    220                           eContentEmailWebapp
    221                     : glean::contentblocking::EmailTrackerCountLabel::
    222                           eContentNormal)
    223        .Add();
    224 
    225    // Notify the load event to record the content blocking log.
    226    //
    227    // Note that we need to change the code here if we decided to block content
    228    // email trackers in the future.
    229    ContentBlockingNotifier::OnEvent(
    230        aChannel,
    231        nsIWebProgressListener::STATE_LOADED_EMAILTRACKING_LEVEL_2_CONTENT);
    232  } else {
    233    glean::contentblocking::email_tracker_count
    234        .EnumGet(
    235            isTopEmailWebApp
    236                ? glean::contentblocking::EmailTrackerCountLabel::
    237                      eBaseEmailWebapp
    238                : glean::contentblocking::EmailTrackerCountLabel::eBaseNormal)
    239        .Add();
    240    // Notify to record content blocking log. Note that we don't need to notify
    241    // if email tracking is enabled because the email tracking protection
    242    // feature will be responsible for notifying the blocking event.
    243    //
    244    // Note that we need to change the code here if we decided to block content
    245    // email trackers in the future.
    246    if (!StaticPrefs::privacy_trackingprotection_emailtracking_enabled()) {
    247      ContentBlockingNotifier::OnEvent(
    248          aChannel,
    249          nsIWebProgressListener::STATE_LOADED_EMAILTRACKING_LEVEL_1_CONTENT);
    250    }
    251  }
    252 
    253  return NS_OK;
    254 }
    255 
    256 NS_IMETHODIMP
    257 UrlClassifierFeatureEmailTrackingDataCollection::GetURIByListType(
    258    nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
    259    nsIUrlClassifierFeature::URIType* aURIType, nsIURI** aURI) {
    260  NS_ENSURE_ARG_POINTER(aChannel);
    261  NS_ENSURE_ARG_POINTER(aURIType);
    262  NS_ENSURE_ARG_POINTER(aURI);
    263 
    264  if (aListType == nsIUrlClassifierFeature::blocklist) {
    265    *aURIType = nsIUrlClassifierFeature::blocklistURI;
    266    return aChannel->GetURI(aURI);
    267  }
    268 
    269  MOZ_ASSERT(aListType == nsIUrlClassifierFeature::entitylist);
    270 
    271  *aURIType = nsIUrlClassifierFeature::pairwiseEntitylistURI;
    272  return UrlClassifierCommon::CreatePairwiseEntityListURI(aChannel, aURI);
    273 }
    274 
    275 }  // namespace mozilla::net