tor-browser

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

commit df083c2c45d1e7e81624a19cac3bf30cc7504c80
parent fc569a3a0b1831cb1174a5ffdde497786a94c21c
Author: Meg Viar <lmegviar@gmail.com>
Date:   Tue, 30 Sep 2025 22:05:19 +0000

Bug 1990897 - Ensure legacy telemetry can be ungated via the new user TOU modal flow r=dmose,toolkit-telemetry-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D266232

Diffstat:
Mtoolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs | 42++++++++++++++++++++++++++++++++++--------
Mtoolkit/components/telemetry/tests/unit/test_HealthPing.js | 14++++++++++++++
Mtoolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js | 151++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/components/telemetry/tests/unit/xpcshell.toml | 4++++
Mtoolkit/crashreporter/test/unit/test_crashreporter_crash.js | 6++++++
5 files changed, 209 insertions(+), 8 deletions(-)

diff --git a/toolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs b/toolkit/components/telemetry/app/TelemetryReportingPolicy.sys.mjs @@ -748,11 +748,19 @@ var TelemetryReportingPolicyImpl = { }, /** - * Check if we are allowed to upload data. In order to submit data both these conditions - * should be true: - * - The data submission preference should be true. - * - The datachoices infobar should have been displayed. + * Check if we are allowed to upload data. + * Prerequisite: data submission is enabled (this.dataSubmissionEnabled). * + * In order to submit data, at least ONE of these conditions should be true: + * 1. The TOU flow is bypassed via a pref or Nimbus variable AND the legacy + * notification flow bypass pref is set, so users bypass BOTH flows. + * 2. The TOU flow is bypassed via a pref or Nimbus variable and the legacy + * notification flow bypass pref is NOT set, so has been been shown the + * legacy flow (the data submission pref should be true and the + * datachoices infobar should have been displayed). + * 3. The user has accepted the Terms of Use AND the user has opted-in to + * sharing technical interaction data (the upload enabled pref should be + * true). * @return {Boolean} True if we are allowed to upload data, false otherwise. */ canUpload() { @@ -762,13 +770,31 @@ var TelemetryReportingPolicyImpl = { return false; } - // Submission is enabled. We enable upload if user is notified or we need to bypass - // the policy. - const bypassNotification = Services.prefs.getBoolPref( + const bypassLegacyFlow = Services.prefs.getBoolPref( TelemetryUtils.Preferences.BypassNotification, false ); - return this.isUserNotifiedOfCurrentPolicy || bypassNotification; + // TOU flow is bypassed if the Nimbus preonboarding feature is disabled + // (disabled by default for Linux via the fallback + // browser.preonboarding.enabled pref) or if the explicit bypass pref is + // set. + const bypassTOUFlow = + Services.prefs.getBoolPref(TOU_BYPASS_NOTIFICATION_PREF, false) || + !this._nimbusVariables.enabled; + const allowInteractionData = Services.prefs.getBoolPref( + "datareporting.healthreport.uploadEnabled", + false + ); + + // Condition 1 + const canUploadBypassLegacyAndTOU = bypassLegacyFlow && bypassTOUFlow; + // Condition 2 + const canUploadLegacy = + bypassTOUFlow && !bypassLegacyFlow && this.isUserNotifiedOfCurrentPolicy; + // Condition 3 + const canUploadTOU = this.hasUserAcceptedCurrentTOU && allowInteractionData; + + return canUploadBypassLegacyAndTOU || canUploadLegacy || canUploadTOU; }, isFirstRun() { diff --git a/toolkit/components/telemetry/tests/unit/test_HealthPing.js b/toolkit/components/telemetry/tests/unit/test_HealthPing.js @@ -45,6 +45,15 @@ add_setup(async function setup() { true ); + // Bypass TOU flow and legacy datareporting flow so that telemetry upload is allowed + Services.prefs.setBoolPref("termsofuse.bypassNotification", true); + Services.prefs.setBoolPref( + "datareporting.policy.dataSubmissionPolicyBypassNotification", + true + ); + // Upload must be enabled to permit sending telemetry + Services.prefs.setBoolPref("datareporting.healthreport.uploadEnabled", true); + await TelemetryController.testSetup(); PingServer.start(); TelemetrySend.setServer("http://localhost:" + PingServer.port); @@ -56,6 +65,11 @@ add_setup(async function setup() { registerCleanupFunction(async function cleanup() { await PingServer.stop(); + Services.prefs.clearUserPref("termsofuse.bypassNotification"); + Services.prefs.clearUserPref( + "datareporting.policy.dataSubmissionPolicyBypassNotification" + ); + Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled"); }); add_task(async function test_sendImmediately() { diff --git a/toolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js b/toolkit/components/telemetry/tests/unit/test_TOUNotificationFlow.js @@ -674,3 +674,154 @@ add_task( sinon.restore(); } ); + +add_task( + async function test_canUpload_unblocked_by_tou_accepted_and_uploadEnabled() { + if (AppConstants.platform === "linux") { + info("Skipping test for Linux where TOU flow is disabled by default"); + return; + } + TelemetryReportingPolicy.reset(); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref( + TelemetryUtils.Preferences.BypassNotification + ); + Services.prefs.clearUserPref("datareporting.healthreport.uploadEnabled"); + Services.prefs.clearUserPref("termsofuse.acceptedDate"); + Services.prefs.clearUserPref("termsofuse.acceptedVersion"); + TelemetryReportingPolicy.reset(); + }); + + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.BypassNotification, + false + ); + + // This is true by default for most users + Services.prefs.setBoolPref( + "datareporting.healthreport.uploadEnabled", + true + ); + + Assert.ok( + !TelemetryReportingPolicy.canUpload(), + "Before accepting TOU legacy upload is blocked" + ); + + Assert.ok( + !Services.prefs.getIntPref( + "datareporting.policy.dataSubmissionPolicyAcceptedVersion", + 0 + ), + "Legacy policy flow accepted version not set" + ); + + Assert.ok( + !Services.prefs.getBoolPref( + "datareporting.policy.dataSubmissionPolicyNotifiedDate", + 0 + ), + "Legacy policy flow accepted date not set" + ); + + Services.prefs.setStringPref("termsofuse.acceptedDate", String(Date.now())); + Services.prefs.setIntPref( + "termsofuse.acceptedVersion", + Services.prefs.getIntPref(TOU_CURRENT_VERSION_PREF) + ); + + Assert.ok( + TelemetryReportingPolicy.userHasAcceptedTOU(), + "TOU acceptance is recorded" + ); + + Assert.ok(TelemetryReportingPolicy.canUpload(), "Legacy upload allowed"); + + Services.prefs.setBoolPref( + "datareporting.healthreport.uploadEnabled", + false + ); + + Assert.ok( + !TelemetryReportingPolicy.canUpload(), + "Legacy upload blocked when user opts out of sharing interaction data" + ); + } +); + +add_task(async function test_canUpload_allowed_when_both_bypass_prefs_true() { + TelemetryReportingPolicy.reset(); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref(TelemetryUtils.Preferences.BypassNotification); + Services.prefs.clearUserPref("termsofuse.bypassNotification"); + TelemetryReportingPolicy.reset(); + }); + + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.BypassNotification, + true + ); + Services.prefs.setBoolPref("termsofuse.bypassNotification", true); + + Assert.ok( + TelemetryReportingPolicy.canUpload(), + "Upload allowed when both legacy and TOU notification flows are bypassed" + ); + + Services.prefs.setBoolPref("termsofuse.bypassNotification", false); + + Assert.ok( + !TelemetryReportingPolicy.canUpload(), + "Upload blocked when only one bypass is true and no other allow path conditions are met" + ); +}); + +add_task( + async function test_canUpload_allowed_when_tou_bypass_and_legacy_notified() { + TelemetryReportingPolicy.reset(); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref( + TelemetryUtils.Preferences.BypassNotification + ); + Services.prefs.clearUserPref("termsofuse.bypassNotification"); + Services.prefs.clearUserPref( + "datareporting.policy.dataSubmissionPolicyNotifiedDate" + ); + Services.prefs.clearUserPref( + "TelemetryUtils.Preferences.DataSubmissionEnabled" + ); + TelemetryReportingPolicy.reset(); + }); + + Services.prefs.setBoolPref("termsofuse.bypassNotification", true); + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.BypassNotification, + false + ); + Services.prefs.setBoolPref( + TelemetryUtils.Preferences.DataSubmissionEnabled, + true + ); + + Assert.ok( + !TelemetryReportingPolicy.canUpload(), + "Upload not allowed when TOU bypass is true, legacy bypass is false, and legacy policy has NOT been notified" + ); + // Mark legacy “notified” via the actual code path triggered via + // testInfobarShown. This calls _recordNotificationData, which updates the + // legacy notification accepted time and version prefs. It also calls + // _userNotified which in turn calls lazy.TelemetrySend.notifyCanUpload. + // This simulates the user being notified of the legacy infobar flow. + TelemetryReportingPolicy.testInfobarShown(); + // Ensure the policy re-reads state after pref mutations. + TelemetryReportingPolicy.reset(); + + Assert.ok( + TelemetryReportingPolicy.canUpload(), + "Upload allowed when TOU bypass is true, legacy bypass is false, and legacy policy has been notified" + ); + } +); diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.toml b/toolkit/components/telemetry/tests/unit/xpcshell.toml @@ -54,6 +54,8 @@ tags = "addons" skip-if = ["os == 'android'"] ["test_TOUNotificationFlow.js"] +skip-if = ["os == 'android'"] +# The TOU notification flow is desktop only ["test_TelemetryClientID_reset.js"] skip-if = ["os == 'android'"] # Disabled as Android/GeckoView doesn't run TelemetryController @@ -84,6 +86,8 @@ skip-if = [ ["test_TelemetryHistograms.js"] ["test_TelemetryReportingPolicy.js"] +skip-if = ["os == 'android'"] +# Desktop only tags = "addons" ["test_TelemetryScalars.js"] diff --git a/toolkit/crashreporter/test/unit/test_crashreporter_crash.js b/toolkit/crashreporter/test/unit/test_crashreporter_crash.js @@ -16,6 +16,10 @@ add_task(async function run_test() { is_win7_or_newer = true; } + registerCleanupFunction(async () => { + Services.prefs.clearUserPref("termsofuse.bypassNotification"); + }); + // try a basic crash await do_content_crash(null, function (mdump, extra) { Assert.ok(mdump.exists()); @@ -108,6 +112,7 @@ add_task(async function run_test() { function () { // Enable the FHR, official policy bypass (since we're in a test) and // specify a telemetry server & client ID. + Services.prefs.setBoolPref("termsofuse.bypassNotification", true); Services.prefs.setBoolPref( "datareporting.policy.dataSubmissionPolicyBypassNotification", true @@ -178,6 +183,7 @@ add_task(async function run_test() { "datareporting.policy.dataSubmissionPolicyBypassNotification", true ); + Services.prefs.setBoolPref("termsofuse.bypassNotification", true); Services.prefs.setBoolPref( "datareporting.healthreport.uploadEnabled", false