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 --- .../test/browser/browser_navigator_iframes.js | 392 +++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 browser/components/resistfingerprinting/test/browser/browser_navigator_iframes.js (limited to 'browser/components/resistfingerprinting/test/browser/browser_navigator_iframes.js') diff --git a/browser/components/resistfingerprinting/test/browser/browser_navigator_iframes.js b/browser/components/resistfingerprinting/test/browser/browser_navigator_iframes.js new file mode 100644 index 0000000000..6d6e836cd4 --- /dev/null +++ b/browser/components/resistfingerprinting/test/browser/browser_navigator_iframes.js @@ -0,0 +1,392 @@ +/** + * Bug 1737829 and Bug 1770498 - A test case for making sure the navigator object has been + * spoofed/disabled correctly respecting cross-origin resources, iframes + * and exemption behavior. + * + * This test only tests values in the iframe, it does not test them on the framer + * We use the cross-origin domain as the base URI of a resource we fetch (on both the framer and framee) + * so we can check that the HTTP header is as expected. + * + * Covers the following cases: + * - RFP is disabled entirely + * - RFP is enabled entirely + * + * - (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain + * - (B) RFP is exempted on the framer and framee but is not on another (if needed) cross-origin domain + * - (C) RFP is exempted on the framer and (if needed) on another cross-origin domain, but not the framee + * - (D) RFP is exempted on the framer but not the framee nor another (if needed) cross-origin domain + * - (E) RFP is not exempted on the framer nor the framee but (if needed) is exempted on another cross-origin domain + * - (F) RFP is not exempted on the framer nor the framee nor another (if needed) cross-origin domain + * - (G) RFP is not exempted on the framer but is on the framee and (if needed) on another cross-origin domain + * - (H) RFP is not exempted on the framer nor another (if needed) cross-origin domain but is on the framee + */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + AppConstants: "resource://gre/modules/AppConstants.sys.mjs", + WindowsVersionInfo: + "resource://gre/modules/components-utils/WindowsVersionInfo.sys.mjs", +}); + +let osVersion = Services.sysinfo.get("version"); +if (AppConstants.platform == "macosx") { + // Convert Darwin version to macOS version: 19.x.x -> 10.15 etc. + // https://en.wikipedia.org/wiki/Darwin_%28operating_system%29 + let DarwinVersionParts = osVersion.split("."); + let DarwinMajorVersion = +DarwinVersionParts[0]; + let macOsMinorVersion = DarwinMajorVersion - 4; + if (macOsMinorVersion > 15) { + macOsMinorVersion = 15; + } + osVersion = `10.${macOsMinorVersion}`; +} + +const DEFAULT_APPVERSION = { + linux: "5.0 (X11)", + win: "5.0 (Windows)", + macosx: "5.0 (Macintosh)", + android: `5.0 (Android ${osVersion})`, + other: "5.0 (X11)", +}; + +const SPOOFED_APPVERSION = { + linux: "5.0 (X11)", + win: "5.0 (Windows)", + macosx: "5.0 (Macintosh)", + android: "5.0 (Android 10)", + other: "5.0 (X11)", +}; + +let cpuArch = Services.sysinfo.get("arch"); +if (cpuArch == "x86-64") { + // Convert CPU arch "x86-64" to "x86_64" used in Linux and Android UAs. + cpuArch = "x86_64"; +} + +const DEFAULT_PLATFORM = { + linux: `Linux ${cpuArch}`, + win: "Win32", + macosx: "MacIntel", + android: `Linux ${cpuArch}`, + other: `Linux ${cpuArch}`, +}; + +const SPOOFED_PLATFORM = { + linux: "Linux x86_64", + win: "Win32", + macosx: "MacIntel", + android: "Linux aarch64", + other: "Linux x86_64", +}; + +// If comparison with the WindowsOscpu value fails in the future, it's time to +// evaluate if exposing a new Windows version to the Web is appropriate. See +// https://bugzilla.mozilla.org/show_bug.cgi?id=1693295 +let WindowsOscpu = null; +if (AppConstants.platform == "win") { + let isWin11 = WindowsVersionInfo.get().buildNumber >= 22000; + WindowsOscpu = + cpuArch == "x86_64" || (cpuArch == "aarch64" && isWin11) + ? `Windows NT ${osVersion}; Win64; x64` + : `Windows NT ${osVersion}`; +} + +const DEFAULT_OSCPU = { + linux: `Linux ${cpuArch}`, + win: WindowsOscpu, + macosx: `Intel Mac OS X ${osVersion}`, + android: `Linux ${cpuArch}`, + other: `Linux ${cpuArch}`, +}; + +const SPOOFED_OSCPU = { + linux: "Linux x86_64", + win: "Windows NT 10.0; Win64; x64", + macosx: "Intel Mac OS X 10.15", + android: "Linux aarch64", + other: "Linux x86_64", +}; + +const DEFAULT_UA_OS = { + linux: `X11; Linux ${cpuArch}`, + win: WindowsOscpu, + macosx: `Macintosh; Intel Mac OS X ${osVersion}`, + android: `Android ${osVersion}; Mobile`, + other: `X11; Linux ${cpuArch}`, +}; + +const SPOOFED_UA_NAVIGATOR_OS = { + linux: "X11; Linux x86_64", + win: "Windows NT 10.0; Win64; x64", + macosx: "Macintosh; Intel Mac OS X 10.15", + android: "Android 10; Mobile", + other: "X11; Linux x86_64", +}; +const SPOOFED_UA_HTTPHEADER_OS = { + linux: "Windows NT 10.0", + win: "Windows NT 10.0", + macosx: "Windows NT 10.0", + android: "Android 10; Mobile", + other: "Windows NT 10.0", +}; +const SPOOFED_HW_CONCURRENCY = 2; + +const CONST_APPCODENAME = "Mozilla"; +const CONST_APPNAME = "Netscape"; +const CONST_PRODUCT = "Gecko"; +const CONST_PRODUCTSUB = "20100101"; +const CONST_VENDOR = ""; +const CONST_VENDORSUB = ""; + +const appVersion = parseInt(Services.appinfo.version); +const rvVersion = + parseInt( + Services.prefs.getIntPref("network.http.useragent.forceRVOnly", 0), + 0 + ) || appVersion; +const spoofedVersion = AppConstants.platform == "android" ? "115" : appVersion; + +const LEGACY_UA_GECKO_TRAIL = "20100101"; + +const DEFAULT_UA_GECKO_TRAIL = { + linux: LEGACY_UA_GECKO_TRAIL, + win: LEGACY_UA_GECKO_TRAIL, + macosx: LEGACY_UA_GECKO_TRAIL, + android: `${appVersion}.0`, + other: LEGACY_UA_GECKO_TRAIL, +}; + +const SPOOFED_UA_GECKO_TRAIL = { + linux: LEGACY_UA_GECKO_TRAIL, + win: LEGACY_UA_GECKO_TRAIL, + macosx: LEGACY_UA_GECKO_TRAIL, + android: `${spoofedVersion}.0`, + other: LEGACY_UA_GECKO_TRAIL, +}; + +const DEFAULT_HARDWARE_CONCURRENCY = navigator.hardwareConcurrency; + +// ============================================================================================= +// ============================================================================================= + +async function testNavigator(result, expectedResults, extraData) { + let testDesc = extraData.testDesc; + + is( + result.appVersion, + expectedResults.appVersion, + `Checking ${testDesc} navigator.appVersion.` + ); + is( + result.platform, + expectedResults.platform, + `Checking ${testDesc} navigator.platform.` + ); + is( + result.userAgent, + expectedResults.userAgentNavigator, + `Checking ${testDesc} navigator.userAgent.` + ); + is( + result.userAgentHTTPHeader, + expectedResults.userAgentHTTPHeader, + `Checking ${testDesc} userAgentHTTPHeader.` + ); + is( + result.framer_crossOrigin_userAgentHTTPHeader, + expectedResults.framer_crossOrigin_userAgentHTTPHeader, + `Checking ${testDesc} framer contacting cross-origin userAgentHTTPHeader.` + ); + is( + result.framee_crossOrigin_userAgentHTTPHeader, + expectedResults.framee_crossOrigin_userAgentHTTPHeader, + `Checking ${testDesc} framee contacting cross-origin userAgentHTTPHeader.` + ); + is( + result.mimeTypesLength, + expectedResults.mimeTypesLength, + `Navigator.mimeTypes has a length of ${expectedResults.mimeTypesLength}.` + ); + is( + result.pluginsLength, + expectedResults.pluginsLength, + `Navigator.plugins has a length of ${expectedResults.pluginsLength}.` + ); + is( + result.oscpu, + expectedResults.oscpu, + `Checking ${testDesc} navigator.oscpu.` + ); + is( + result.hardwareConcurrency, + expectedResults.hardwareConcurrency, + `Checking ${testDesc} navigator.hardwareConcurrency.` + ); + + is( + result.appCodeName, + CONST_APPCODENAME, + "Navigator.appCodeName reports correct constant value." + ); + is( + result.appName, + CONST_APPNAME, + "Navigator.appName reports correct constant value." + ); + is( + result.product, + CONST_PRODUCT, + "Navigator.product reports correct constant value." + ); + is( + result.productSub, + CONST_PRODUCTSUB, + "Navigator.productSub reports correct constant value." + ); + is( + result.vendor, + CONST_VENDOR, + "Navigator.vendor reports correct constant value." + ); + is( + result.vendorSub, + CONST_VENDORSUB, + "Navigator.vendorSub reports correct constant value." + ); + + is( + result.worker_appVersion, + expectedResults.appVersion, + `Checking ${testDesc} worker navigator.appVersion.` + ); + is( + result.worker_platform, + expectedResults.platform, + `Checking ${testDesc} worker navigator.platform.` + ); + is( + result.worker_userAgent, + expectedResults.userAgentNavigator, + `Checking ${testDesc} worker navigator.userAgent.` + ); + is( + result.worker_hardwareConcurrency, + expectedResults.hardwareConcurrency, + `Checking ${testDesc} worker navigator.hardwareConcurrency.` + ); + + is( + result.worker_appCodeName, + CONST_APPCODENAME, + "worker Navigator.appCodeName reports correct constant value." + ); + is( + result.worker_appName, + CONST_APPNAME, + "worker Navigator.appName reports correct constant value." + ); + is( + result.worker_product, + CONST_PRODUCT, + "worker Navigator.product reports correct constant value." + ); +} + +const defaultUserAgent = `Mozilla/5.0 (${ + DEFAULT_UA_OS[AppConstants.platform] +}; rv:${rvVersion}.0) Gecko/${ + DEFAULT_UA_GECKO_TRAIL[AppConstants.platform] +} Firefox/${appVersion}.0`; + +const spoofedUserAgentNavigator = `Mozilla/5.0 (${ + SPOOFED_UA_NAVIGATOR_OS[AppConstants.platform] +}; rv:${rvVersion}.0) Gecko/${ + SPOOFED_UA_GECKO_TRAIL[AppConstants.platform] +} Firefox/${appVersion}.0`; + +const spoofedUserAgentHeader = `Mozilla/5.0 (${ + SPOOFED_UA_HTTPHEADER_OS[AppConstants.platform] +}; rv:${rvVersion}.0) Gecko/${ + SPOOFED_UA_GECKO_TRAIL[AppConstants.platform] +} Firefox/${appVersion}.0`; + +// The following are convenience objects that allow you to quickly see what is +// and is not modified from a logical set of values. +// Be sure to always use `let expectedResults = structuredClone(allNotSpoofed)` to do a +// deep copy and avoiding corrupting the original 'const' object +const allNotSpoofed = { + appVersion: DEFAULT_APPVERSION[AppConstants.platform], + hardwareConcurrency: navigator.hardwareConcurrency, + mimeTypesLength: 2, + oscpu: DEFAULT_OSCPU[AppConstants.platform], + platform: DEFAULT_PLATFORM[AppConstants.platform], + pluginsLength: 5, + userAgentNavigator: defaultUserAgent, + userAgentHTTPHeader: defaultUserAgent, + framer_crossOrigin_userAgentHTTPHeader: defaultUserAgent, + framee_crossOrigin_userAgentHTTPHeader: defaultUserAgent, +}; +const allSpoofed = { + appVersion: SPOOFED_APPVERSION[AppConstants.platform], + hardwareConcurrency: SPOOFED_HW_CONCURRENCY, + mimeTypesLength: 2, + oscpu: SPOOFED_OSCPU[AppConstants.platform], + platform: SPOOFED_PLATFORM[AppConstants.platform], + pluginsLength: 5, + userAgentNavigator: spoofedUserAgentNavigator, + userAgentHTTPHeader: spoofedUserAgentHeader, + framer_crossOrigin_userAgentHTTPHeader: spoofedUserAgentHeader, + framee_crossOrigin_userAgentHTTPHeader: spoofedUserAgentHeader, +}; + +const uri = `https://${FRAMER_DOMAIN}/browser/browser/components/resistfingerprinting/test/browser/file_navigator_iframer.html`; + +requestLongerTimeout(2); + +let expectedResults = {}; + +expectedResults = structuredClone(allNotSpoofed); +add_task(defaultsTest.bind(null, uri, testNavigator, expectedResults)); + +expectedResults = structuredClone(allSpoofed); +add_task(simpleRFPTest.bind(null, uri, testNavigator, expectedResults)); + +// In the below tests, we use the cross-origin domain as the base URI of a resource we fetch (on both the framer and framee) +// so we can check that the HTTP header is as expected. + +// (A) RFP is exempted on the framer and framee and (if needed) on another cross-origin domain +expectedResults = structuredClone(allNotSpoofed); +add_task(testA.bind(null, uri, testNavigator, expectedResults)); + +// (B) RFP is exempted on the framer and framee but is not on another (if needed) cross-origin domain +expectedResults = structuredClone(allNotSpoofed); +add_task(testB.bind(null, uri, testNavigator, expectedResults)); + +// (C) RFP is exempted on the framer and (if needed) on another cross-origin domain, but not the framee +expectedResults = structuredClone(allSpoofed); +expectedResults.framer_crossOrigin_userAgentHTTPHeader = defaultUserAgent; +expectedResults.framee_crossOrigin_userAgentHTTPHeader = spoofedUserAgentHeader; +add_task(testC.bind(null, uri, testNavigator, expectedResults)); + +// (D) RFP is exempted on the framer but not the framee nor another (if needed) cross-origin domain +expectedResults = structuredClone(allSpoofed); +expectedResults.framer_crossOrigin_userAgentHTTPHeader = defaultUserAgent; +expectedResults.framee_crossOrigin_userAgentHTTPHeader = spoofedUserAgentHeader; +add_task(testD.bind(null, uri, testNavigator, expectedResults)); + +// (E) RFP is not exempted on the framer nor the framee but (if needed) is exempted on another cross-origin domain +expectedResults = structuredClone(allSpoofed); +add_task(testE.bind(null, uri, testNavigator, expectedResults)); + +// (F) RFP is not exempted on the framer nor the framee nor another (if needed) cross-origin domain +expectedResults = structuredClone(allSpoofed); +add_task(testF.bind(null, uri, testNavigator, expectedResults)); + +// (G) RFP is not exempted on the framer but is on the framee and (if needed) on another cross-origin domain +expectedResults = structuredClone(allSpoofed); +add_task(testG.bind(null, uri, testNavigator, expectedResults)); + +// (H) RFP is not exempted on the framer nor another (if needed) cross-origin domain but is on the framee +expectedResults = structuredClone(allSpoofed); +add_task(testH.bind(null, uri, testNavigator, expectedResults)); -- cgit v1.2.3