[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