Bug 1469714 - Part 8: Add a test for the interaction of the Storage Access API with the allow-storage-access-by-user-activation iframe sandbox; r=baku
☠☠ backed out by dc31941ced58 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 12 Sep 2018 19:46:35 -0400
changeset 436795 61dc8b46e7f62fa523d6b7a090b72c8ad6c23592
parent 436794 453370408672ef251d80342390da9e1f183c7381
child 436796 158def1e0b8c1233b9b84f5f138e0945544ec9e8
push id34660
push userbtara@mozilla.com
push dateMon, 17 Sep 2018 21:58:52 +0000
treeherdermozilla-central@87a95e1b7ec6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1469714
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 1469714 - Part 8: Add a test for the interaction of the Storage Access API with the allow-storage-access-by-user-activation iframe sandbox; r=baku Differential Revision: https://phabricator.services.mozilla.com/D5817
toolkit/components/antitracking/test/browser/browser.ini
toolkit/components/antitracking/test/browser/browser_storageAccessSandboxed.js
toolkit/components/antitracking/test/browser/head.js
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -33,8 +33,9 @@ support-files = server.sjs
 [browser_onBeforeRequestNotificationForTrackingResources.js]
 [browser_onModifyRequestNotificationForTrackingResources.js]
 [browser_permissionInNormalWindows.js]
 [browser_permissionInPrivateWindows.js]
 [browser_subResources.js]
 support-files = subResources.sjs
 [browser_script.js]
 support-files = tracker.js
+[browser_storageAccessSandboxed.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_storageAccessSandboxed.js
@@ -0,0 +1,168 @@
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+AntiTracking.runTest("Storage Access API called in a sandboxed iframe",
+  // blocking callback
+  async _ => {
+    let dwu = SpecialPowers.getDOMWindowUtils(window);
+    let helper = dwu.setHandlingUserInput(true);
+
+    let p;
+    let threw = false;
+    try {
+      p = document.requestStorageAccess();
+    } catch (e) {
+      threw = true;
+    } finally {
+      helper.destruct();
+    }
+    ok(!threw, "requestStorageAccess should not throw");
+    threw = false;
+    try {
+      await p;
+    } catch (e) {
+      threw = true;
+    }
+    ok(threw, "requestStorageAccess shouldn't be available");
+  },
+
+  null, // non-blocking callback
+  null, // cleanup function
+  [["dom.storage_access.enabled", true]], // extra prefs
+  false, // no window open test
+  false, // no user-interaction test
+  false, // no blocking notifications
+  false, // run in normal window
+  "allow-scripts allow-same-origin"
+);
+
+AntiTracking.runTest("Storage Access API called in a sandboxed iframe with" +
+                     " allow-storage-access-by-user-activation",
+  // blocking callback
+  async _ => {
+    let dwu = SpecialPowers.getDOMWindowUtils(window);
+    let helper = dwu.setHandlingUserInput(true);
+
+    let p;
+    let threw = false;
+    try {
+      p = document.requestStorageAccess();
+    } catch (e) {
+      threw = true;
+    } finally {
+      helper.destruct();
+    }
+    ok(!threw, "requestStorageAccess should not throw");
+    threw = false;
+    try {
+      await p;
+    } catch (e) {
+      threw = true;
+    }
+    ok(!threw, "requestStorageAccess should be available");
+  },
+
+  null, // non-blocking callback
+  null, // cleanup function
+  [["dom.storage_access.enabled", true]], // extra prefs
+  false, // no window open test
+  false, // no user-interaction test
+  true, // expect blocking notifications
+  false, // run in normal window
+  "allow-scripts allow-same-origin allow-storage-access-by-user-activation"
+);
+
+AntiTracking.runTest("Verify that sandboxed contexts don't get the saved permission",
+  // blocking callback
+  async _ => {
+    let hasAccess = await document.hasStorageAccess();
+    ok(!hasAccess, "Doesn't yet have storage access");
+
+    try {
+      localStorage.foo = 42;
+      ok(false, "LocalStorage cannot be used!");
+    } catch (e) {
+      ok(true, "LocalStorage cannot be used!");
+      is(e.name, "SecurityError", "We want a security error message.");
+    }
+  },
+
+  null, // non-blocking callback
+  null, // cleanup function
+  [["dom.storage_access.enabled", true]], // extra prefs
+  false, // no window open test
+  false, // no user-interaction test
+  false, // no blocking notifications
+  false, // run in normal window
+  "allow-scripts allow-same-origin"
+);
+
+AntiTracking.runTest("Verify that sandboxed contexts with" +
+                     " allow-storage-access-by-user-activation get the" +
+                     " saved permission",
+  // blocking callback
+  async _ => {
+    let hasAccess = await document.hasStorageAccess();
+    ok(hasAccess, "Has storage access");
+
+    localStorage.foo = 42;
+    ok(true, "LocalStorage can be used!");
+  },
+
+  null, // non-blocking callback
+  null, // cleanup function
+  [["dom.storage_access.enabled", true]], // extra prefs
+  false, // no window open test
+  false, // no user-interaction test
+  false, // no blocking notifications
+  false, // run in normal window
+  "allow-scripts allow-same-origin allow-storage-access-by-user-activation"
+);
+
+AntiTracking.runTest("Verify that private browsing contexts don't get the saved permission",
+  // blocking callback
+  async _ => {
+    let hasAccess = await document.hasStorageAccess();
+    ok(!hasAccess, "Doesn't yet have storage access");
+
+    try {
+      localStorage.foo = 42;
+      ok(false, "LocalStorage cannot be used!");
+    } catch (e) {
+      ok(true, "LocalStorage cannot be used!");
+      is(e.name, "SecurityError", "We want a security error message.");
+    }
+  },
+
+  null, // non-blocking callback
+  null, // cleanup function
+  [["dom.storage_access.enabled", true]], // extra prefs
+  false, // no window open test
+  false, // no user-interaction test
+  false, // no blocking notifications
+  true, // run in private window
+  null // iframe sandbox
+);
+
+AntiTracking.runTest("Verify that non-sandboxed contexts get the" +
+                     " saved permission",
+  // blocking callback
+  async _ => {
+    let hasAccess = await document.hasStorageAccess();
+    ok(hasAccess, "Has storage access");
+
+    localStorage.foo = 42;
+    ok(true, "LocalStorage can be used!");
+  },
+
+  null, // non-blocking callback
+  // cleanup function
+  async _ => {
+    await new Promise(resolve => {
+      Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+    });
+  },
+  [["dom.storage_access.enabled", true]], // extra prefs
+  false, // no window open test
+  false, // no user-interaction test
+  false // no blocking notifications
+);
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -20,27 +20,28 @@ var gFeatures = undefined;
 
 let {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
 
 requestLongerTimeout(2);
 
 this.AntiTracking = {
   runTest(name, callbackTracking, callbackNonTracking, cleanupFunction, extraPrefs,
           windowOpenTest = true, userInteractionTest = true, expectedBlockingNotifications = true,
-          runInPrivateWindow = false) {
+          runInPrivateWindow = false, iframeSandbox = null) {
     // Here we want to test that a 3rd party context is simply blocked.
     this._createTask({
       name,
       cookieBehavior: BEHAVIOR_REJECT_TRACKER,
       blockingByContentBlocking: true,
       allowList: false,
       callback: callbackTracking,
       extraPrefs,
       expectedBlockingNotifications,
       runInPrivateWindow,
+      iframeSandbox,
     });
     this._createCleanupTask(cleanupFunction);
 
     if (callbackNonTracking) {
       let runExtraTests = true;
       let options = {};
       if (typeof callbackNonTracking == "object") {
         callbackNonTracking = callbackNonTracking.callback;
@@ -76,128 +77,139 @@ this.AntiTracking = {
           name,
           cookieBehavior: BEHAVIOR_ACCEPT,
           blockingByContentBlocking: true,
           allowList: false,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
 
         this._createTask({
           name,
           cookieBehavior: BEHAVIOR_REJECT_FOREIGN,
           blockingByContentBlocking: false,
           allowList: false,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
 
         this._createTask({
           name,
           cookieBehavior: BEHAVIOR_REJECT_TRACKER,
           blockingByContentBlocking: false,
           allowList: false,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
 
         this._createTask({
           name,
           cookieBehavior: BEHAVIOR_REJECT_FOREIGN,
           blockingByContentBlocking: false,
           allowList: true,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
 
         this._createTask({
           name,
           cookieBehavior: BEHAVIOR_REJECT_TRACKER,
           blockingByContentBlocking: false,
           allowList: true,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
 
         this._createTask({
           name,
           cookieBehavior: BEHAVIOR_ACCEPT,
           blockingByContentBlocking: false,
           allowList: false,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
 
         // Try testing using the allow list with both reject foreign and reject tracker cookie behaviors
         this._createTask({
           name,
           cookieBehavior: BEHAVIOR_REJECT_FOREIGN,
           blockingByContentBlocking: true,
           allowList: true,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
 
         this._createTask({
           name,
           cookieBehavior: BEHAVIOR_REJECT_TRACKER,
           blockingByContentBlocking: true,
           allowList: true,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
       } else {
         this._createTask({
           name,
           cookieBehavior: options.cookieBehavior,
           blockingByContentBlocking: options.blockingByContentBlocking,
           allowList: options.blockingByAllowList,
           callback: callbackNonTracking,
           extraPrefs: [],
           expectedBlockingNotifications: false,
           runInPrivateWindow,
+          iframeSandbox,
         });
         this._createCleanupTask(cleanupFunction);
       }
 
       // Phase 2: Here we want to test that a third-party context doesn't
       // get blocked with when the same origin is opened through window.open().
       if (windowOpenTest) {
-        this._createWindowOpenTask(name, callbackTracking, callbackNonTracking, runInPrivateWindow, extraPrefs);
+        this._createWindowOpenTask(name, callbackTracking, callbackNonTracking,
+                                   runInPrivateWindow, iframeSandbox, extraPrefs);
         this._createCleanupTask(cleanupFunction);
       }
 
       // Phase 3: Here we want to test that a third-party context doesn't
       // get blocked with user interaction present
       if (userInteractionTest) {
-        this._createUserInteractionTask(name, callbackTracking, callbackNonTracking, runInPrivateWindow, extraPrefs);
+        this._createUserInteractionTask(name, callbackTracking, callbackNonTracking,
+                                        runInPrivateWindow, iframeSandbox, extraPrefs);
         this._createCleanupTask(cleanupFunction);
       }
     }
   },
 
   async _setupTest(win, cookieBehavior, blockingByContentBlocking, extraPrefs) {
     await SpecialPowers.flushPrefEnv();
     await SpecialPowers.pushPrefEnv({"set": [
@@ -218,17 +230,18 @@ this.AntiTracking = {
     await UrlClassifierTestUtils.addTestTrackers();
   },
 
   _createTask(options) {
     add_task(async function() {
       info("Starting " + (options.cookieBehavior != BEHAVIOR_ACCEPT ? "blocking" : "non-blocking") + " cookieBehavior (" + options.cookieBehavior + ") and " +
                          (options.blockingByContentBlocking ? "blocking" : "non-blocking") + " contentBlocking with" +
                          (options.allowList ? "" : "out") + " allow list test " + options.name +
-                         " running in a " + (options.runInPrivateWindow ? "private" : "normal") + " window");
+                         " running in a " + (options.runInPrivateWindow ? "private" : "normal") + " window " +
+                         " with iframe sandbox set to " + options.iframeSandbox);
 
       let win = window;
       if (options.runInPrivateWindow) {
         win = OpenBrowserWindow({private: true});
         await TestUtils.topicObserved("browser-delayed-startup-finished");
       }
 
       await AntiTracking._setupTest(win, options.cookieBehavior,
@@ -258,24 +271,28 @@ this.AntiTracking = {
 
         // The previous function reloads the browser, so wait for it to load again!
         await BrowserTestUtils.browserLoaded(browser);
       }
 
       info("Creating a 3rd party content");
       await ContentTask.spawn(browser,
                               { page: TEST_3RD_PARTY_PAGE,
-                                callback: options.callback.toString() },
+                                callback: options.callback.toString(),
+                                iframeSandbox: options.iframeSandbox },
                               async function(obj) {
         await new content.Promise(resolve => {
           let ifr = content.document.createElement("iframe");
           ifr.onload = function() {
             info("Sending code to the 3rd party content");
             ifr.contentWindow.postMessage(obj.callback, "*");
           };
+          if (typeof obj.iframeSandbox == "string") {
+            ifr.setAttribute("sandbox", obj.iframeSandbox);
+          }
 
           content.addEventListener("message", function msg(event) {
             if (event.data.type == "finish") {
               content.removeEventListener("message", msg);
               resolve();
               return;
             }
 
@@ -322,17 +339,18 @@ this.AntiTracking = {
     add_task(async function() {
       info("Cleaning up.");
       if (cleanupFunction) {
         await cleanupFunction();
       }
     });
   },
 
-  _createWindowOpenTask(name, blockingCallback, nonBlockingCallback, runInPrivateWindow, extraPrefs) {
+  _createWindowOpenTask(name, blockingCallback, nonBlockingCallback, runInPrivateWindow,
+                        iframeSandbox, extraPrefs) {
     add_task(async function() {
       info("Starting window-open test " + name);
 
       let win = window;
       if (runInPrivateWindow) {
         win = OpenBrowserWindow({private: true});
         await TestUtils.topicObserved("browser-delayed-startup-finished");
       }
@@ -351,24 +369,28 @@ this.AntiTracking = {
         pageURL += "?noopener";
       }
 
       info("Creating a 3rd party content");
       await ContentTask.spawn(browser,
                               { page: pageURL,
                                 blockingCallback: blockingCallback.toString(),
                                 nonBlockingCallback: nonBlockingCallback.toString(),
+                                iframeSandbox,
                               },
                               async function(obj) {
         await new content.Promise(resolve => {
           let ifr = content.document.createElement("iframe");
           ifr.onload = function() {
             info("Sending code to the 3rd party content");
             ifr.contentWindow.postMessage(obj, "*");
           };
+          if (typeof obj.iframeSandbox == "string") {
+            ifr.setAttribute("sandbox", obj.iframeSandbox);
+          }
 
           content.addEventListener("message", function msg(event) {
             if (event.data.type == "finish") {
               content.removeEventListener("message", msg);
               resolve();
               return;
             }
 
@@ -394,17 +416,18 @@ this.AntiTracking = {
       BrowserTestUtils.removeTab(tab);
 
       if (runInPrivateWindow) {
         win.close();
       }
     });
   },
 
-  _createUserInteractionTask(name, blockingCallback, nonBlockingCallback, runInPrivateWindow, extraPrefs) {
+  _createUserInteractionTask(name, blockingCallback, nonBlockingCallback,
+                             runInPrivateWindow, iframeSandbox, extraPrefs) {
     add_task(async function() {
       info("Starting user-interaction test " + name);
 
       let win = window;
       if (runInPrivateWindow) {
         win = OpenBrowserWindow({private: true});
         await TestUtils.topicObserved("browser-delayed-startup-finished");
       }
@@ -419,20 +442,24 @@ this.AntiTracking = {
       await BrowserTestUtils.browserLoaded(browser);
 
       info("Creating a 3rd party content");
       await ContentTask.spawn(browser,
                               { page: TEST_3RD_PARTY_PAGE_UI,
                                 popup: TEST_POPUP_PAGE,
                                 blockingCallback: blockingCallback.toString(),
                                 nonBlockingCallback: nonBlockingCallback.toString(),
+                                iframeSandbox,
                               },
                               async function(obj) {
         let ifr = content.document.createElement("iframe");
         let loading = new content.Promise(resolve => { ifr.onload = resolve; });
+        if (typeof obj.iframeSandbox == "string") {
+          ifr.setAttribute("sandbox", obj.iframeSandbox);
+        }
         content.document.body.appendChild(ifr);
         ifr.src = obj.page;
         await loading;
 
         info("The 3rd party content should not have access to first party storage.");
         await new content.Promise(resolve => {
           content.addEventListener("message", function msg(event) {
             if (event.data.type == "finish") {