[tbb-commits] [tor-browser/tor-browser-31.6.0esr-4.5-1] Bug #15502, Part 2: Regression tests for blob URL isolation

mikeperry at torproject.org mikeperry at torproject.org
Wed Apr 22 02:44:21 UTC 2015


commit f4f2caa26dd2d15e8f99d0a1357361da43e4fd2f
Author: Arthur Edelstein <arthuredelstein at gmail.com>
Date:   Sat Apr 18 17:37:21 2015 -0700

    Bug #15502, Part 2: Regression tests for blob URL isolation
---
 content/base/test/bug15502_page_blobify.html     |   26 ++++++
 content/base/test/bug15502_page_deblobify.html   |   31 +++++++
 content/base/test/bug15502_tab.html              |   39 ++++++++
 content/base/test/bug15502_utils.js              |  104 ++++++++++++++++++++++
 content/base/test/bug15502_worker_blobify.html   |   28 ++++++
 content/base/test/bug15502_worker_blobify.js     |   12 +++
 content/base/test/bug15502_worker_deblobify.html |   30 +++++++
 content/base/test/bug15502_worker_deblobify.js   |   24 +++++
 content/base/test/mochitest.ini                  |    9 ++
 content/base/test/test_tor_bug15502.html         |   92 +++++++++++++++++++
 10 files changed, 395 insertions(+)

diff --git a/content/base/test/bug15502_page_blobify.html b/content/base/test/bug15502_page_blobify.html
new file mode 100644
index 0000000..d883929
--- /dev/null
+++ b/content/base/test/bug15502_page_blobify.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugs.torproject.org/15502
+-->
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Page blobifier for Tor Browser Bug 15502</title>
+  <script type="text/javascript;version=1.7" src="bug15502_utils.js"></script>
+</head>
+<body>
+<div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
+
+<script type="text/javascript;version=1.7">
+
+Task.spawn(function* () {
+  sendMessage(window.parent, "ready");
+  let message = yield receiveMessage(window.parent),
+      blobURL = stringToBlobURL(message);
+  sendMessage(window.parent, blobURL);
+  appendLine("display", message + " -> " + blobURL);
+});
+
+</script>
+</body>
+</html>
diff --git a/content/base/test/bug15502_page_deblobify.html b/content/base/test/bug15502_page_deblobify.html
new file mode 100644
index 0000000..e8cbd51
--- /dev/null
+++ b/content/base/test/bug15502_page_deblobify.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugs.torproject.org/15502
+-->
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Page deblobifier for Tor Browser Bug 15502</title>
+  <script type="text/javascript;version=1.7" src="bug15502_utils.js"></script>
+</head>
+<body>
+<div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
+
+<script type="text/javascript;version=1.7">
+
+Task.spawn(function* () {
+  sendMessage(window.parent, "ready");
+  let blobURL = yield receiveMessage(window.parent),
+      string;
+  try {
+    string = yield blobURLtoString(blobURL);
+  } catch (e) {
+    string = e.message;
+  }
+  sendMessage(window.parent, string);
+  appendLine("display", blobURL + " -> " + string);
+});
+
+</script>
+</body>
+</html>
diff --git a/content/base/test/bug15502_tab.html b/content/base/test/bug15502_tab.html
new file mode 100644
index 0000000..7bd4744
--- /dev/null
+++ b/content/base/test/bug15502_tab.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugs.torproject.org/15502
+-->
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Tab for Tor Browser Bug 15502</title>
+  <script type="text/javascript;version=1.7" src="bug15502_utils.js"></script>
+</head>
+<body>
+
+<div id="display"></div>
+<iframe id="child" width="100%"></iframe>
+
+<script type="text/javascript;version=1.7">
+
+let iframe = document.getElementById("child");
+
+let connect = function (sourceObject, destinationObject) {
+  Task.spawn(function* () {
+    for (;;) {
+      let message = yield receiveMessage(sourceObject);
+      sendMessage(destinationObject, message);
+    }
+  });
+};
+
+Task.spawn(function* () {
+  sendMessage(window.opener, "ready");
+  let firstParentMessage = yield receiveMessage(window.opener);
+  iframe.src = firstParentMessage;
+  connect(window.opener, iframe.contentWindow);
+  connect(iframe.contentWindow, window.opener);
+});
+</script>
+
+</body>
+</html>
diff --git a/content/base/test/bug15502_utils.js b/content/base/test/bug15502_utils.js
new file mode 100644
index 0000000..9d69c12
--- /dev/null
+++ b/content/base/test/bug15502_utils.js
@@ -0,0 +1,104 @@
+// Import Task.jsm
+let { Task } = SpecialPowers.Cu.import("resource://gre/modules/Task.jsm");
+
+// __listen(target, eventType, timeoutMs, useCapture)__.
+// Calls addEventListener on target, with the given eventType.
+// Returns a Promise that resolves to an Event object, if the event fires.
+// If a timeout occurs, then Promise is rejected with a "Timed out" error.
+// For use with Task.jsm.
+let listen = function (target, eventType, timeoutMs, useCapture) {
+  return new Promise(function (resolve, reject) {
+    let listenFunction = function (event) {
+      target.removeEventListener(eventType, listenFunction, useCapture);
+      resolve(event);
+    };
+    target.addEventListener(eventType, listenFunction, useCapture);
+    setTimeout(() => reject(new Error("Timed out")), timeoutMs);
+  });
+};
+
+// __receiveMessage(source)__.
+// Returns an event object for the next message received from source.
+// A Task.jsm coroutine.
+let receiveMessage = function* (source) {
+  let event;
+  do {
+    event = yield listen(self, "message", 5000, false);
+  } while (event.source !== source);
+  return event.data;
+};
+
+// __sendMessage(destination, message)__.
+// Sends a message to destination.
+let sendMessage = function (destination, message) {
+  destination.postMessage(message, "*");
+};
+
+// __appendLine(id, lineString)__.
+// Add a line of text to the innerHTML of element with id.
+let appendLine = function (id, lineString) {
+  document.getElementById(id).innerHTML += lineString + "\n";
+};
+
+// __xhr(method, url, responseType__.
+// A simple async XMLHttpRequest call.
+// Returns a promise with the response.
+let xhr = function (method, url, responseType) {
+  return new Promise(function (resolve, reject) {
+    let xhr = new XMLHttpRequest();
+    xhr.open(method, url, true);
+    xhr.onload = function () {
+      resolve(xhr.response);
+    };
+    xhr.responseType = responseType;
+    xhr.send();
+  });
+};
+
+// __blobURLtoBlob(blobURL)__.
+// Asynchronously retrieves a blob object
+// from a blob URL. Returns a promise.
+let blobURLtoBlob = function (blobURL) {
+  return xhr("GET", blobURL, "blob");
+};
+
+// __blobToString(blob)__.
+// Asynchronously reads the contents
+// of a blob object into a string. Returns a promise.
+let blobToString = function (blob) {
+  return new Promise(function (resolve, reject) {
+    let fileReader = new FileReader();
+    fileReader.onload = function () {
+      resolve(fileReader.result);
+    };
+    fileReader.readAsText(blob);
+  });
+};
+
+// __blobURLtoString(blobURL)__.
+// Asynchronous coroutine that takes a blobURL
+// and returns the contents in a string.
+let blobURLtoString = function* (blobURL) {
+  let blob = yield blobURLtoBlob(blobURL);
+  return yield blobToString(blob);
+};
+
+// __stringToBlobURL(s)__.
+// Converts string s into a blob, and returns
+// a blob URL.
+let stringToBlobURL = function (s) {
+  let blob = new Blob([s]);
+  return URL.createObjectURL(blob);
+};
+
+// __workerIO(scriptFile, inputString)__.
+// Sends inputString for the worker, and waits
+// for the worker to return an outputString.
+// Task.jsm coroutine.
+let workerIO = function* (scriptFile, inputString) {
+  let worker = new Worker(scriptFile);
+  worker.postMessage(inputString);
+  let result = yield listen(worker, "message", 5000, false);
+  worker.terminate();
+  return result.data;
+};
diff --git a/content/base/test/bug15502_worker_blobify.html b/content/base/test/bug15502_worker_blobify.html
new file mode 100644
index 0000000..3dd7926
--- /dev/null
+++ b/content/base/test/bug15502_worker_blobify.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugs.torproject.org/15502
+-->
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Worker blobifier for Tor Browser Bug 15502</title>
+  <script type="text/javascript;version=1.7" src="bug15502_utils.js"></script>
+</head>
+<body>
+<div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
+
+<pre id="test">
+<script type="text/javascript;version=1.7">
+
+Task.spawn(function* () {
+  sendMessage(window.parent, "ready");
+  let message = yield receiveMessage(window.parent),
+      blobURL = yield workerIO("bug15502_worker_blobify.js", message);
+  sendMessage(window.parent, blobURL);
+  appendLine("display", message + " -> " + blobURL);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/content/base/test/bug15502_worker_blobify.js b/content/base/test/bug15502_worker_blobify.js
new file mode 100644
index 0000000..4aef347
--- /dev/null
+++ b/content/base/test/bug15502_worker_blobify.js
@@ -0,0 +1,12 @@
+// Wait for a string to be posted to this worker.
+// Create a blob containing this string, and then
+// post back a blob URL pointing to the blob.
+self.addEventListener("message", function (e) {
+  try {
+    var blob = new Blob([e.data]),
+        blobURL = URL.createObjectURL(blob);
+    postMessage(blobURL);
+  } catch (e) {
+    postMessage(e.message);
+  }
+}, false);
diff --git a/content/base/test/bug15502_worker_deblobify.html b/content/base/test/bug15502_worker_deblobify.html
new file mode 100644
index 0000000..30ec6b4
--- /dev/null
+++ b/content/base/test/bug15502_worker_deblobify.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugs.torproject.org/15502
+-->
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Worker deblobifier for Tor Browser Bug 15502</title>
+  <script type="text/javascript;version=1.7" src="bug15502_utils.js"></script>
+</head>
+<body>
+<div id="display" style="white-space:pre; font-family:monospace; display:inline;"></div>
+
+<pre id="test">
+<script type="text/javascript;version=1.7">
+
+Task.spawn(function* () {
+  sendMessage(window.parent, "ready");
+  let blobURL = yield receiveMessage(window.parent),
+      result = yield workerIO("bug15502_worker_deblobify.js", blobURL);
+  sendMessage(window.parent, result);
+  appendLine("display", blobURL + " -> " + result);
+});
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/content/base/test/bug15502_worker_deblobify.js b/content/base/test/bug15502_worker_deblobify.js
new file mode 100644
index 0000000..8556311
--- /dev/null
+++ b/content/base/test/bug15502_worker_deblobify.js
@@ -0,0 +1,24 @@
+// Wait for a blob URL to be posted to this worker.
+// Obtain the blob, and read the string contained in it.
+// Post back the string.
+
+var postStringInBlob = function (blobObject) {
+  var fileReader = new FileReaderSync(),
+      result = fileReader.readAsText(blobObject);
+  postMessage(result);
+};
+
+self.addEventListener("message", function (e) {
+  var blobURL = e.data,
+      xhr = new XMLHttpRequest();
+  try {
+    xhr.open("GET", blobURL, true);
+    xhr.onload = function () {
+      postStringInBlob(xhr.response);
+    };
+    xhr.responseType = "blob";
+    xhr.send();
+  } catch (e) {
+    postMessage(e.message);
+  }
+}, false);
diff --git a/content/base/test/mochitest.ini b/content/base/test/mochitest.ini
index c3cffb3..e59a70a 100644
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -12,6 +12,14 @@ support-files =
   badMessageEvent.eventsource^headers^
   badMessageEvent2.eventsource
   badMessageEvent2.eventsource^headers^
+  bug15502_page_blobify.html
+  bug15502_page_deblobify.html
+  bug15502_tab.html
+  bug15502_utils.js
+  bug15502_worker_blobify.js
+  bug15502_worker_blobify.html
+  bug15502_worker_deblobify.js
+  bug15502_worker_deblobify.html
   bug282547.sjs
   bug298064-subframe.html
   bug313646.txt
@@ -604,6 +612,7 @@ skip-if = toolkit == 'android' || e10s #RANDOM
 [test_textnode_normalize_in_selection.html]
 [test_textnode_split_in_selection.html]
 [test_title.html]
+[test_tor_bug15502.html]
 [test_treewalker_nextsibling.xml]
 [test_viewport_scroll.html]
 [test_viewsource_forbidden_in_object.html]
diff --git a/content/base/test/test_tor_bug15502.html b/content/base/test/test_tor_bug15502.html
new file mode 100644
index 0000000..9ed3d70
--- /dev/null
+++ b/content/base/test/test_tor_bug15502.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugs.torproject.org/15502
+-->
+<head>
+  <meta http-equiv="content-type" content="text/html; charset=utf-8">
+  <title>Test for Tor Browser Bug 15502</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript;version=1.7" src="bug15502_utils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+
+<script class="testbody" type="application/javascript;version=1.7">
+SimpleTest.waitForExplicitFinish();
+
+// __prefs__. Import the `Serivces.prefs` object.
+let prefs = SpecialPowers.Cu.import("resource://gre/modules/Services.jsm").Services.prefs;
+
+// ## Testing constants
+let domain1 = "http://example.com",
+    domain2 = "http://example.net",
+    path = "/tests/content/base/test/",
+    page_blob = "bug15502_page_blobify.html",
+    page_deblob = "bug15502_page_deblobify.html"
+    worker_blob = "bug15502_worker_blobify.html",
+    worker_deblob = "bug15502_worker_deblobify.html";
+
+// __tabIO(domain, child, input)__.
+// Open a tab at the given `domain` and `child` page. Post an
+// `input` message to the tab.
+let tabIO = function* (domain, child, input) {
+  tab = window.open(domain + path + "bug15502_tab.html", "_blank");
+  yield receiveMessage(tab); // ready message
+  sendMessage(tab, "http://example.org" + path + child);
+  yield receiveMessage(tab); // ready message
+  sendMessage(tab, input);
+  return yield receiveMessage(tab);
+};
+
+// __blobTest(isolationOn, domainA, domainB, blobPage, deblobPage)__.
+// Run a test where we set the pref "privacy.thirdparty.isolate` to on or off,
+// and then create a blob URL in `domainA`, using the page `blobPage`,
+// and then attempt to retrieve the object from the blobURL in `domainB`, using
+// the page `deblobPage`.
+let blobTest = function* (isolationOn, domainA, domainB, blobPage, deblobPage) {
+  prefs.setIntPref("privacy.thirdparty.isolate", isolationOn ? 2 : 0);
+  let input = "" + Math.random(),
+      blobURL = yield tabIO(domainA, blobPage, input),
+      result = yield tabIO(domainB, deblobPage, blobURL),
+      description = domainA + ":" + blobPage + "->" + domainB + ":" + deblobPage + ", isolation " + (isolationOn ? "on." : "off.");
+  if (blobPage === worker_blob) {
+    // Remove this case when we write a patch that properly isolates web worker blob URLs
+    // by first party domain.
+    ok(blobURL.contains("Permission to call 'URL.createObjectURL' denied."), description + " Deny blob URL creation in web worker");
+  } else if (deblobPage === worker_deblob && isolationOn) {
+    // Remove this case when we write a patch that properly isolates web worker blob URLs
+    // by first party domain.
+    ok(result.contains("Access to restricted URI denied"), description + " Isolated blobs not available to web workers");
+  } else {
+    if (isolationOn && domainA !== domainB) {
+      ok(input !== result, description + " Deny retrieval");
+    } else {
+      ok(input === result, description + " Allow retrieval");
+    }
+  }
+};
+
+
+// ## The main test
+// Run a Task.jsm coroutine that tests various combinations of domains
+// methods, and isolation states for reading and writing blob URLs.
+Task.spawn(function* () {
+  for (let isolate of [false, true]) {
+    for (let domainB of [domain1, domain2]) {
+      for (let blob of [page_blob, worker_blob]) {
+        for (let deblob of [page_deblob, worker_deblob]) {
+          yield blobTest(isolate, domain1, domainB, blob, deblob);
+        }
+      }
+    }
+  }
+  SimpleTest.finish();
+});
+
+</script>
+
+</body>
+</html>





More information about the tbb-commits mailing list