Bug 583181 - Part 3: Spoof navigator.buildID for web content. r=hsivonen
authorChris Peterson <cpeterson@mozilla.com>
Sun, 23 Sep 2018 17:32:46 -0700
changeset 495869 61b6c0110deabb51d9fafec1350c4362be7f9758
parent 495868 a02d4b28303032b70a89f34de320b4f7c3379b6a
child 495870 21f4840bb7d391c6fd198d593c677a8e8cdda427
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs583181
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 583181 - Part 3: Spoof navigator.buildID for web content. r=hsivonen But still allow chrome scripts and (unless resisting fingerprinting) "https://*.mozilla.org" pages to access the real navigator.buildID. Differential Revision: https://phabricator.services.mozilla.com/D7262
browser/components/resistfingerprinting/test/browser/browser_navigator.js
browser/components/resistfingerprinting/test/browser/file_navigator.html
dom/base/Navigator.cpp
dom/tests/mochitest/bugs/test_navigator_buildID.html
--- a/browser/components/resistfingerprinting/test/browser/browser_navigator.js
+++ b/browser/components/resistfingerprinting/test/browser/browser_navigator.js
@@ -38,17 +38,16 @@ const SPOOFED_OSCPU = {
 };
 const SPOOFED_UA_OS = {
   linux: "X11; Linux x86_64",
   win: "Windows NT 6.1; Win64; x64",
   macosx: "Macintosh; Intel Mac OS X 10.13",
   android: "Android 6.0; Mobile",
   other: "X11; Linux x86_64",
 };
-const SPOOFED_BUILDID        = "20100101";
 const SPOOFED_HW_CONCURRENCY = 2;
 
 const CONST_APPCODENAME = "Mozilla";
 const CONST_PRODUCT     = "Gecko";
 const CONST_PRODUCTSUB  = "20100101";
 const CONST_VENDOR      = "";
 const CONST_VENDORSUB   = "";
 
@@ -65,17 +64,16 @@ async function testNavigator() {
 
   is(result.appName, SPOOFED_APPNAME, "Navigator.appName is correctly spoofed.");
   is(result.appVersion, SPOOFED_APPVERSION[AppConstants.platform], "Navigator.appVersion is correctly spoofed.");
   is(result.platform, SPOOFED_PLATFORM[AppConstants.platform], "Navigator.platform is correctly spoofed.");
   is(result.userAgent, spoofedUserAgent, "Navigator.userAgent is correctly spoofed.");
   is(result.mimeTypesLength, 0, "Navigator.mimeTypes has a length of 0.");
   is(result.pluginsLength, 0, "Navigator.plugins has a length of 0.");
   is(result.oscpu, SPOOFED_OSCPU[AppConstants.platform], "Navigator.oscpu is correctly spoofed.");
-  is(result.buildID, SPOOFED_BUILDID, "Navigator.buildID is correctly spoofed.");
   is(result.hardwareConcurrency, SPOOFED_HW_CONCURRENCY, "Navigator.hardwareConcurrency is correctly spoofed.");
 
   is(result.appCodeName, CONST_APPCODENAME, "Navigator.appCodeName 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.");
 
@@ -135,16 +133,15 @@ add_task(async function runWorkerNavigat
 add_task(async function runOverrideTest() {
   await SpecialPowers.pushPrefEnv({"set":
     [
       ["general.appname.override", "appName overridden"],
       ["general.appversion.override", "appVersion overridden"],
       ["general.platform.override", "platform overridden"],
       ["general.useragent.override", "userAgent overridden"],
       ["general.oscpu.override", "oscpu overridden"],
-      ["general.buildID.override", "buildID overridden"],
     ],
   });
 
   await testNavigator();
 
   await testWorkerNavigator();
 });
--- a/browser/components/resistfingerprinting/test/browser/file_navigator.html
+++ b/browser/components/resistfingerprinting/test/browser/file_navigator.html
@@ -15,17 +15,16 @@
     result.userAgent = navigator.userAgent;
     result.product = navigator.product;
     result.productSub = navigator.productSub;
     result.vendor = navigator.vendor;
     result.vendorSub = navigator.vendorSub;
     result.mimeTypesLength = navigator.mimeTypes.length;
     result.pluginsLength = navigator.plugins.length;
     result.oscpu = navigator.oscpu;
-    result.buildID = navigator.buildID;
     result.hardwareConcurrency = navigator.hardwareConcurrency;
 
     // eslint-disable-next-line no-unsanitized/property
     document.getElementById("result").innerHTML = JSON.stringify(result);
   }
 </script>
 </head>
 <body onload="collect();">
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -568,22 +568,45 @@ Navigator::GetBuildID(nsAString& aBuildI
 {
   if (aCallerType != CallerType::System) {
     // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
     // for details about spoofed values.
     if (nsContentUtils::ShouldResistFingerprinting()) {
       aBuildID.AssignLiteral(LEGACY_BUILD_ID);
       return;
     }
+
     nsAutoString override;
     nsresult rv = Preferences::GetString("general.buildID.override", override);
     if (NS_SUCCEEDED(rv)) {
       aBuildID = override;
       return;
     }
+
+    nsAutoCString host;
+    bool isHTTPS = false;
+    if (mWindow) {
+      nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
+      if (doc) {
+        nsIURI* uri = doc->GetDocumentURI();
+        if (uri) {
+          MOZ_ALWAYS_SUCCEEDS(uri->SchemeIs("https", &isHTTPS));
+          if (isHTTPS) {
+            MOZ_ALWAYS_SUCCEEDS(uri->GetHost(host));
+          }
+        }
+      }
+    }
+
+    // Spoof the buildID on pages not loaded from "https://*.mozilla.org".
+    if (!isHTTPS ||
+        !StringEndsWith(host, NS_LITERAL_CSTRING(".mozilla.org"))) {
+      aBuildID.AssignLiteral(LEGACY_BUILD_ID);
+      return;
+    }
   }
 
   nsCOMPtr<nsIXULAppInfo> appInfo =
     do_GetService("@mozilla.org/xre/app-info;1");
   if (!appInfo) {
     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     return;
   }
--- a/dom/tests/mochitest/bugs/test_navigator_buildID.html
+++ b/dom/tests/mochitest/bugs/test_navigator_buildID.html
@@ -28,18 +28,18 @@ const LEGACY_BUILD_ID = 20181001000000;
 var isOK = false;
 try {
   var contentBuildID = navigator.buildID;
   isOK = true;
 } catch (ex) {
 }
 ok(isOK, "navigator.buildID should never throw");
 is(typeof(contentBuildID), "string", "navigator.buildID should be a string");
-ok(+contentBuildID > LEGACY_BUILD_ID,
-   `navigator.buildID should be exposed in content - got "${contentBuildID}"`);
+is(+contentBuildID, LEGACY_BUILD_ID,
+   "navigator.buildID should be spoofed in content");
 
 //
 // Access navigator.buildID from chrome.
 //
 let chromeScript = SpecialPowers.loadChromeScript(() => {
   ChromeUtils.import("resource://gre/modules/Services.jsm");
   addMessageListener("test:getBuildID", nav => {
     let browser = Services.wm.getMostRecentWindow('navigator:browser');