Bug 1628653 - test for dFPI redirect heuristic; r=baku
authorLiang-Heng Chen <xeonchen@gmail.com>
Thu, 04 Jun 2020 21:05:20 +0000
changeset 534018 17a0d6113569c699dc27a66a1ab12d4294ca49a1
parent 534017 e5cca411b0950353bbb0ff03616bf60250c3a591
child 534019 1b7929bcdad384af472dea00a279256461c7d503
push id37481
push userncsoregi@mozilla.com
push dateFri, 05 Jun 2020 04:39:26 +0000
treeherdermozilla-central@fecffba489bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1628653
milestone79.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 1628653 - test for dFPI redirect heuristic; r=baku Differential Revision: https://phabricator.services.mozilla.com/D78261
toolkit/components/antitracking/test/browser/browser.ini
toolkit/components/antitracking/test/browser/browser_storageAccessWithDynamicFpiHeuristics.js
toolkit/components/antitracking/test/browser/head.js
toolkit/components/antitracking/test/browser/redirect.sjs
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -31,16 +31,17 @@ support-files =
   3rdPartyWorker.html
   3rdPartyOpen.html
   3rdPartyOpenUI.html
   empty.js
   empty-altsvc.js
   empty-altsvc.js^headers^
   empty.html
   popup.html
+  redirect.sjs
   server.sjs
   storageAccessAPIHelpers.js
   3rdPartyStorage.html
   3rdPartyStorageWO.html
   3rdPartyPartitioned.html
   localStorage.html
   raptor.jpg
   !/browser/modules/test/browser/head.js
@@ -100,16 +101,17 @@ support-files = tracker.js
 [browser_serviceWorkersWithStorageAccessGranted.js]
 [browser_storageAccessDoorHanger.js]
 [browser_storageAccessPromiseRejectHandlerUserInteraction.js]
 [browser_storageAccessPromiseResolveHandlerUserInteraction.js]
 [browser_storageAccessRemovalNavigateSubframe.js]
 [browser_storageAccessRemovalNavigateTopframe.js]
 [browser_storageAccessSandboxed.js]
 [browser_storageAccessThirdPartyChecks.js]
+[browser_storageAccessWithDynamicFpiHeuristics.js]
 [browser_storageAccessWithHeuristics.js]
 [browser_networkIsolation.js]
 [browser_allowPermissionForTracker.js]
 [browser_denyPermissionForTracker.js]
 [browser_localStorageEvents.js]
 [browser_partitionedLocalStorage.js]
 [browser_partitionedLocalStorage_events.js]
 support-files = localStorageEvents.html
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_storageAccessWithDynamicFpiHeuristics.js
@@ -0,0 +1,189 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from head.js */
+
+"use strict";
+
+const TEST_REDIRECT_TOP_PAGE =
+  TEST_3RD_PARTY_DOMAIN + TEST_PATH + "redirect.sjs?" + TEST_TOP_PAGE;
+const TEST_REDIRECT_3RD_PARTY_PAGE =
+  TEST_DOMAIN + TEST_PATH + "redirect.sjs?" + TEST_3RD_PARTY_PARTITIONED_PAGE;
+
+async function cleanup() {
+  await new Promise(resolve => {
+    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
+      resolve()
+    );
+  });
+}
+
+add_task(async () => {
+  await SpecialPowers.flushPrefEnv();
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      [
+        "network.cookie.cookieBehavior",
+        Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
+      ],
+    ],
+  });
+  registerCleanupFunction(cleanup);
+});
+
+function executeContentScript(browser, callback, options = {}) {
+  return SpecialPowers.spawn(
+    browser,
+    [
+      {
+        callback: callback.toString(),
+        ...options,
+      },
+    ],
+    obj => {
+      return new content.Promise(async resolve => {
+        if (obj.page) {
+          // third-party
+          let ifr = content.document.createElement("iframe");
+          ifr.onload = async () => {
+            info("Sending code to the 3rd party content");
+            ifr.contentWindow.postMessage({ cb: obj.callback }, "*");
+          };
+
+          content.addEventListener("message", event => resolve(event.data), {
+            once: true,
+          });
+
+          content.document.body.appendChild(ifr);
+          ifr.src = obj.page;
+        } else {
+          // first-party
+          let runnableStr = `(() => {return (${obj.callback});})();`;
+          let runnable = eval(runnableStr); // eslint-disable-line no-eval
+          resolve(await runnable.call(content, content, obj.value));
+        }
+      });
+    }
+  );
+}
+
+function readNetworkCookie(win) {
+  return win
+    .fetch("cookies.sjs")
+    .then(r => r.text())
+    .then(text => {
+      return text.substring("cookie:foopy=".length);
+    });
+}
+
+async function writeNetworkCookie(win, value) {
+  await win.fetch("cookies.sjs?" + value).then(r => r.text());
+  return true;
+}
+
+function createDataInFirstParty(browser, value) {
+  return executeContentScript(browser, writeNetworkCookie, { value });
+}
+function getDataFromFirstParty(browser) {
+  return executeContentScript(browser, readNetworkCookie, {});
+}
+function createDataInThirdParty(browser, value) {
+  return executeContentScript(browser, writeNetworkCookie, {
+    page: TEST_3RD_PARTY_PARTITIONED_PAGE,
+    value,
+  });
+}
+function getDataFromThirdParty(browser) {
+  return executeContentScript(browser, readNetworkCookie, {
+    page: TEST_3RD_PARTY_PARTITIONED_PAGE,
+  });
+}
+
+async function redirectWithUserInteraction(browser, url, wait = null) {
+  await executeContentScript(
+    browser,
+    (content, value) => {
+      content.document.userInteractionForTesting();
+
+      let link = content.document.createElement("a");
+      link.appendChild(content.document.createTextNode("click me!"));
+      link.href = value;
+      content.document.body.appendChild(link);
+      link.click();
+    },
+    {
+      value: url,
+    }
+  );
+  await BrowserTestUtils.browserLoaded(browser, false, wait || url);
+}
+
+async function checkData(browser, options) {
+  if ("firstParty" in options) {
+    is(
+      await getDataFromFirstParty(browser),
+      options.firstParty,
+      "currect first-party data"
+    );
+  }
+  if ("thirdParty" in options) {
+    is(
+      await getDataFromThirdParty(browser),
+      options.thirdParty,
+      "currect third-party data"
+    );
+  }
+}
+
+add_task(async () => {
+  info("Starting Dynamic FPI Redirect Heuristic test...");
+
+  info("Creating a new tab");
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  info("initializing...");
+  await checkData(browser, { firstParty: "", thirdParty: "" });
+
+  await Promise.all([
+    createDataInFirstParty(browser, "firstParty"),
+    createDataInThirdParty(browser, "thirdParty"),
+  ]);
+
+  await checkData(browser, {
+    firstParty: "firstParty",
+    thirdParty: "undefined",
+  });
+
+  info("load third-party content as first-party");
+  await redirectWithUserInteraction(
+    browser,
+    TEST_REDIRECT_3RD_PARTY_PAGE,
+    TEST_3RD_PARTY_PARTITIONED_PAGE
+  );
+
+  await checkData(browser, { firstParty: "" });
+  await createDataInFirstParty(browser, "heuristicFirstParty");
+  await checkData(browser, { firstParty: "heuristicFirstParty" });
+
+  info("redirect back to first-party page");
+  await redirectWithUserInteraction(
+    browser,
+    TEST_REDIRECT_TOP_PAGE,
+    TEST_TOP_PAGE
+  );
+
+  info("third-party page should able to access first-party data");
+  await checkData(browser, {
+    firstParty: "firstParty",
+    thirdParty: "heuristicFirstParty",
+  });
+
+  info("Removing the tab");
+  BrowserTestUtils.removeTab(tab);
+});
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -50,26 +50,30 @@ const TEST_4TH_PARTY_PAGE = TEST_4TH_PAR
 const TEST_ANOTHER_3RD_PARTY_PAGE =
   TEST_ANOTHER_3RD_PARTY_DOMAIN + TEST_PATH + "3rdParty.html";
 const TEST_ANOTHER_3RD_PARTY_PAGE_HTTPS =
   TEST_ANOTHER_3RD_PARTY_DOMAIN_HTTPS + TEST_PATH + "3rdParty.html";
 const TEST_3RD_PARTY_STORAGE_PAGE =
   TEST_3RD_PARTY_DOMAIN_HTTP + TEST_PATH + "3rdPartyStorage.html";
 const TEST_3RD_PARTY_PAGE_WORKER =
   TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdPartyWorker.html";
+const TEST_3RD_PARTY_PARTITIONED_PAGE =
+  TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdPartyPartitioned.html";
 const TEST_4TH_PARTY_STORAGE_PAGE =
   TEST_4TH_PARTY_DOMAIN + TEST_PATH + "3rdPartyStorage.html";
 const TEST_4TH_PARTY_PARTITIONED_PAGE =
   TEST_4TH_PARTY_DOMAIN + TEST_PATH + "3rdPartyPartitioned.html";
 
 const BEHAVIOR_ACCEPT = Ci.nsICookieService.BEHAVIOR_ACCEPT;
 const BEHAVIOR_REJECT = Ci.nsICookieService.BEHAVIOR_REJECT;
 const BEHAVIOR_LIMIT_FOREIGN = Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
 const BEHAVIOR_REJECT_FOREIGN = Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
 const BEHAVIOR_REJECT_TRACKER = Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
+const BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN =
+  Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
 
 let originalRequestLongerTimeout = requestLongerTimeout;
 // eslint-disable-next-line no-global-assign
 requestLongerTimeout = function AntiTrackingRequestLongerTimeout(factor) {
   let ccovMultiplier = AppConstants.MOZ_CODE_COVERAGE ? 2 : 1;
   let fissionMultiplier = SpecialPowers.useRemoteSubframes ? 2 : 1;
   originalRequestLongerTimeout(ccovMultiplier * fissionMultiplier * factor);
 };
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/redirect.sjs
@@ -0,0 +1,4 @@
+function handleRequest(aRequest, aResponse) {
+  aResponse.setStatusLine(aRequest.httpVersion, 302);
+  aResponse.setHeader("Location", aRequest.queryString);
+}