From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- browser/components/doh/test/browser/head.js | 365 ++++++++++++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 browser/components/doh/test/browser/head.js (limited to 'browser/components/doh/test/browser/head.js') diff --git a/browser/components/doh/test/browser/head.js b/browser/components/doh/test/browser/head.js new file mode 100644 index 0000000000..20d8156cc0 --- /dev/null +++ b/browser/components/doh/test/browser/head.js @@ -0,0 +1,365 @@ +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + DoHConfigController: "resource:///modules/DoHConfig.sys.mjs", + DoHController: "resource:///modules/DoHController.sys.mjs", + DoHTestUtils: "resource://testing-common/DoHTestUtils.sys.mjs", + Preferences: "resource://gre/modules/Preferences.sys.mjs", + Region: "resource://gre/modules/Region.sys.mjs", + RegionTestUtils: "resource://testing-common/RegionTestUtils.sys.mjs", + RemoteSettings: "resource://services-settings/remote-settings.sys.mjs", +}); + +XPCOMUtils.defineLazyModuleGetters(this, { + ASRouter: "resource://activity-stream/lib/ASRouter.jsm", +}); + +XPCOMUtils.defineLazyServiceGetter( + this, + "gDNSOverride", + "@mozilla.org/network/native-dns-override;1", + "nsINativeDNSResolverOverride" +); + +const { CommonUtils } = ChromeUtils.importESModule( + "resource://services-common/utils.sys.mjs" +); + +const EXAMPLE_URL = "https://example.com/"; + +const prefs = { + TESTING_PREF: "doh-rollout._testing", + ENABLED_PREF: "doh-rollout.enabled", + ROLLOUT_TRR_MODE_PREF: "doh-rollout.mode", + NETWORK_TRR_MODE_PREF: "network.trr.mode", + CONFIRMATION_NS_PREF: "network.trr.confirmationNS", + BREADCRUMB_PREF: "doh-rollout.self-enabled", + DOORHANGER_USER_DECISION_PREF: "doh-rollout.doorhanger-decision", + DISABLED_PREF: "doh-rollout.disable-heuristics", + SKIP_HEURISTICS_PREF: "doh-rollout.skipHeuristicsCheck", + CLEAR_ON_SHUTDOWN_PREF: "doh-rollout.clearModeOnShutdown", + FIRST_RUN_PREF: "doh-rollout.doneFirstRun", + PROVIDER_LIST_PREF: "doh-rollout.provider-list", + TRR_SELECT_ENABLED_PREF: "doh-rollout.trr-selection.enabled", + TRR_SELECT_URI_PREF: "doh-rollout.uri", + TRR_SELECT_COMMIT_PREF: "doh-rollout.trr-selection.commit-result", + TRR_SELECT_DRY_RUN_RESULT_PREF: "doh-rollout.trr-selection.dry-run-result", + PROVIDER_STEERING_PREF: "doh-rollout.provider-steering.enabled", + PROVIDER_STEERING_LIST_PREF: "doh-rollout.provider-steering.provider-list", + NETWORK_DEBOUNCE_TIMEOUT_PREF: "doh-rollout.network-debounce-timeout", + HEURISTICS_THROTTLE_TIMEOUT_PREF: "doh-rollout.heuristics-throttle-timeout", + HEURISTICS_THROTTLE_RATE_LIMIT_PREF: + "doh-rollout.heuristics-throttle-rate-limit", +}; + +const CFR_PREF = "browser.newtabpage.activity-stream.asrouter.providers.cfr"; +const CFR_JSON = { + id: "cfr", + enabled: true, + type: "local", + localProvider: "CFRMessageProvider", + categories: ["cfrAddons", "cfrFeatures"], +}; + +async function setup() { + await DoHController._uninit(); + await DoHConfigController._uninit(); + SpecialPowers.pushPrefEnv({ + set: [["security.notification_enable_delay", 0]], + }); + let oldCanRecord = Services.telemetry.canRecordExtended; + Services.telemetry.canRecordExtended = true; + Services.telemetry.clearEvents(); + + // Enable the CFR. + Preferences.set(CFR_PREF, JSON.stringify(CFR_JSON)); + + // Tell DoHController that this isn't real life. + Preferences.set(prefs.TESTING_PREF, true); + + // Avoid non-local connections to the TRR endpoint. + Preferences.set(prefs.CONFIRMATION_NS_PREF, "skip"); + + // Enable trr selection and provider steeringfor tests. This is off + // by default so it can be controlled via Normandy. + Preferences.set(prefs.TRR_SELECT_ENABLED_PREF, true); + Preferences.set(prefs.PROVIDER_STEERING_PREF, true); + + // Enable committing the TRR selection. This pref ships false by default so + // it can be controlled e.g. via Normandy, but for testing let's set enable. + Preferences.set(prefs.TRR_SELECT_COMMIT_PREF, true); + + // Clear mode on shutdown by default. + Preferences.set(prefs.CLEAR_ON_SHUTDOWN_PREF, true); + + // Generally don't bother with debouncing or throttling. + // The throttling test will set this explicitly. + Preferences.set(prefs.NETWORK_DEBOUNCE_TIMEOUT_PREF, -1); + Preferences.set(prefs.HEURISTICS_THROTTLE_TIMEOUT_PREF, -1); + + // Set up heuristics, all passing by default. + + // Google safesearch overrides + gDNSOverride.addIPOverride("www.google.com.", "1.1.1.1"); + gDNSOverride.addIPOverride("google.com.", "1.1.1.1"); + gDNSOverride.addIPOverride("forcesafesearch.google.com.", "1.1.1.2"); + + // YouTube safesearch overrides + gDNSOverride.addIPOverride("www.youtube.com.", "2.1.1.1"); + gDNSOverride.addIPOverride("m.youtube.com.", "2.1.1.1"); + gDNSOverride.addIPOverride("youtubei.googleapis.com.", "2.1.1.1"); + gDNSOverride.addIPOverride("youtube.googleapis.com.", "2.1.1.1"); + gDNSOverride.addIPOverride("www.youtube-nocookie.com.", "2.1.1.1"); + gDNSOverride.addIPOverride("restrict.youtube.com.", "2.1.1.2"); + gDNSOverride.addIPOverride("restrictmoderate.youtube.com.", "2.1.1.2"); + + // Zscaler override + gDNSOverride.addIPOverride("sitereview.zscaler.com.", "3.1.1.1"); + + // Global canary + gDNSOverride.addIPOverride("use-application-dns.net.", "4.1.1.1"); + + await DoHTestUtils.resetRemoteSettingsConfig(false); + + await DoHConfigController.init(); + await DoHController.init(); + + await waitForStateTelemetry(["rollback"]); + + registerCleanupFunction(async () => { + Services.telemetry.canRecordExtended = oldCanRecord; + Services.telemetry.clearEvents(); + gDNSOverride.clearOverrides(); + if (ASRouter.state.messageBlockList.includes("DOH_ROLLOUT_CONFIRMATION")) { + await ASRouter.unblockMessageById("DOH_ROLLOUT_CONFIRMATION"); + } + // The CFR pref is set to an empty array in user.js for testing profiles, + // so "reset" it back to that value. + Preferences.set(CFR_PREF, "[]"); + await DoHController._uninit(); + Services.telemetry.clearEvents(); + Preferences.reset(Object.values(prefs)); + await DoHTestUtils.resetRemoteSettingsConfig(false); + await DoHController.init(); + }); +} + +const kTestRegion = "DE"; +const kRegionalPrefNamespace = `doh-rollout.${kTestRegion.toLowerCase()}`; + +async function setupRegion() { + Region._home = null; + RegionTestUtils.setNetworkRegion(kTestRegion); + await Region._fetchRegion(); + is(Region.home, kTestRegion, "Should have correct region"); + Preferences.reset("doh-rollout.home-region"); + await DoHConfigController.loadRegion(); +} + +async function checkTRRSelectionTelemetry() { + let events; + await TestUtils.waitForCondition(() => { + events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + return events && events.length; + }); + events = events.filter( + e => + e[1] == "security.doh.trrPerformance" && + e[2] == "trrselect" && + e[3] == "dryrunresult" + ); + is(events.length, 1, "Found the expected trrselect event."); + is( + events[0][4], + "https://example.com/dns-query", + "The event records the expected decision" + ); +} + +function ensureNoTRRSelectionTelemetry() { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + if (!events) { + ok(true, "Found no trrselect events."); + return; + } + events = events.filter( + e => + e[1] == "security.doh.trrPerformance" && + e[2] == "trrselect" && + e[3] == "dryrunresult" + ); + is(events.length, 0, "Found no trrselect events."); +} + +async function checkHeuristicsTelemetry( + decision, + evaluateReason, + steeredProvider = "" +) { + let events; + await TestUtils.waitForCondition(() => { + events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + events = events?.filter( + e => e[1] == "doh" && e[2] == "evaluate_v2" && e[3] == "heuristics" + ); + return events?.length; + }); + is(events.length, 1, "Found the expected heuristics event."); + is(events[0][4], decision, "The event records the expected decision"); + if (evaluateReason) { + is(events[0][5].evaluateReason, evaluateReason, "Got the expected reason."); + } + is(events[0][5].steeredProvider, steeredProvider, "Got expected provider."); + + // After checking the event, clear all telemetry. Since we check for a single + // event above, this ensures all heuristics events are intentional and tested. + // TODO: Test events other than heuristics. Those tests would also work the + // same way, so as to test one event at a time, and this clearEvents() call + // will continue to exist as-is. + Services.telemetry.clearEvents(); +} + +async function checkHeuristicsTelemetryMultiple(expectedEvaluateReasons) { + let events; + await TestUtils.waitForCondition(() => { + events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + if (events && events.length) { + events = events.filter( + e => e[1] == "doh" && e[2] == "evaluate_v2" && e[3] == "heuristics" + ); + if (events.length == expectedEvaluateReasons.length) { + return true; + } + } + return false; + }); + is( + events.length, + expectedEvaluateReasons.length, + "Found the expected heuristics events." + ); + for (let reason of expectedEvaluateReasons) { + let event = events.find(e => e[5].evaluateReason == reason); + is(event[5].evaluateReason, reason, `${reason} event found`); + } + Services.telemetry.clearEvents(); +} + +function ensureNoHeuristicsTelemetry() { + let events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + if (!events) { + ok(true, "Found no heuristics events."); + return; + } + events = events.filter( + e => e[1] == "doh" && e[2] == "evaluate_v2" && e[3] == "heuristics" + ); + is(events.length, 0, "Found no heuristics events."); +} + +async function waitForStateTelemetry(expectedStates) { + let events; + await TestUtils.waitForCondition(() => { + events = Services.telemetry.snapshotEvents( + Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS + ).parent; + return events; + }); + events = events.filter(e => e[1] == "doh" && e[2] == "state"); + info(events); + is(events.length, expectedStates.length, "Found the expected state events."); + for (let state of expectedStates) { + let event = events.find(e => e[3] == state); + is(event[3], state, `${state} state found`); + } + Services.telemetry.clearEvents(); +} + +async function restartDoHController() { + let oldMode = Preferences.get(prefs.ROLLOUT_TRR_MODE_PREF); + await DoHController._uninit(); + let newMode = Preferences.get(prefs.ROLLOUT_TRR_MODE_PREF); + let expectClear = Preferences.get(prefs.CLEAR_ON_SHUTDOWN_PREF); + is( + newMode, + expectClear ? undefined : oldMode, + `Mode was ${expectClear ? "cleared" : "persisted"} on shutdown.` + ); + await DoHController.init(); +} + +// setPassing/FailingHeuristics are used generically to test that DoH is enabled +// or disabled correctly. We use the zscaler canary arbitrarily here, individual +// heuristics are tested separately. +function setPassingHeuristics() { + gDNSOverride.clearHostOverride("sitereview.zscaler.com."); + gDNSOverride.addIPOverride("sitereview.zscaler.com.", "3.1.1.1"); +} + +function setFailingHeuristics() { + gDNSOverride.clearHostOverride("sitereview.zscaler.com."); + gDNSOverride.addIPOverride("sitereview.zscaler.com.", "213.152.228.242"); +} + +async function waitForDoorhanger() { + const popupID = "contextual-feature-recommendation"; + const bucketID = "DOH_ROLLOUT_CONFIRMATION"; + let panel; + await BrowserTestUtils.waitForEvent(document, "popupshown", true, event => { + panel = event.originalTarget; + let popupNotification = event.originalTarget.firstChild; + return ( + popupNotification && + popupNotification.notification && + popupNotification.notification.id == popupID && + popupNotification.getAttribute("data-notification-bucket") == bucketID + ); + }); + return panel; +} + +function simulateNetworkChange() { + // The networkStatus API does not actually propagate the link status we supply + // here, but rather sends the link status from the NetworkLinkService. + // This means there's no point sending a down and then an up - the extension + // will just receive "up" twice. + // TODO: Implement a mock NetworkLinkService and use it to also simulate + // network down events. + Services.obs.notifyObservers(null, "network:link-status-changed", "up"); +} + +async function ensureTRRMode(mode) { + await TestUtils.waitForCondition(() => { + return Preferences.get(prefs.ROLLOUT_TRR_MODE_PREF) === mode; + }); + is(Preferences.get(prefs.ROLLOUT_TRR_MODE_PREF), mode, `TRR mode is ${mode}`); +} + +async function ensureNoTRRModeChange(mode) { + try { + // Try and wait for the TRR pref to change... waitForCondition should throw + // after trying for a while. + await TestUtils.waitForCondition(() => { + return Preferences.get(prefs.ROLLOUT_TRR_MODE_PREF) !== mode; + }); + // If we reach this, the waitForCondition didn't throw. Fail! + ok(false, "TRR mode changed when it shouldn't have!"); + } catch (e) { + // Assert for clarity. + is( + Preferences.get(prefs.ROLLOUT_TRR_MODE_PREF), + mode, + "No change in TRR mode" + ); + } +} -- cgit v1.2.3