summaryrefslogtreecommitdiffstats
path: root/dom/security/test/cors
diff options
context:
space:
mode:
Diffstat (limited to 'dom/security/test/cors')
-rw-r--r--dom/security/test/cors/browser.ini9
-rw-r--r--dom/security/test/cors/browser_CORS-console-warnings.js101
-rw-r--r--dom/security/test/cors/bug1456721.sjs20
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs59
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_inner.html121
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_inner.jarbin0 -> 1105 bytes
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs103
-rw-r--r--dom/security/test/cors/file_CrossSiteXHR_server.sjs231
-rw-r--r--dom/security/test/cors/file_bug1456721.html74
-rw-r--r--dom/security/test/cors/file_cors_logging_test.html1311
-rw-r--r--dom/security/test/cors/file_cors_logging_test.html.css0
-rw-r--r--dom/security/test/cors/mochitest.ini16
-rw-r--r--dom/security/test/cors/test_CrossSiteXHR.html1549
-rw-r--r--dom/security/test/cors/test_CrossSiteXHR_cache.html610
-rw-r--r--dom/security/test/cors/test_CrossSiteXHR_origin.html180
15 files changed, 4384 insertions, 0 deletions
diff --git a/dom/security/test/cors/browser.ini b/dom/security/test/cors/browser.ini
new file mode 100644
index 0000000000..350d382e34
--- /dev/null
+++ b/dom/security/test/cors/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+ file_CrossSiteXHR_server.sjs
+ file_CrossSiteXHR_inner.html
+ file_cors_logging_test.html
+ file_bug1456721.html
+ bug1456721.sjs
+
+[browser_CORS-console-warnings.js]
diff --git a/dom/security/test/cors/browser_CORS-console-warnings.js b/dom/security/test/cors/browser_CORS-console-warnings.js
new file mode 100644
index 0000000000..129313d9e2
--- /dev/null
+++ b/dom/security/test/cors/browser_CORS-console-warnings.js
@@ -0,0 +1,101 @@
+/*
+ * Description of the test:
+ * Ensure that CORS warnings are printed to the web console.
+ *
+ * This test uses the same tests as the plain mochitest, but needs access to
+ * the console.
+ */
+"use strict";
+
+function console_observer(subject, topic, data) {
+ var message = subject.wrappedJSObject.arguments[0];
+ ok(false, message);
+}
+
+var webconsole = null;
+var messages_seen = 0;
+var expected_messages = 50;
+
+function on_new_message(msgObj) {
+ let text = msgObj.message;
+
+ if (text.match("Cross-Origin Request Blocked:")) {
+ ok(true, "message is: " + text);
+ messages_seen++;
+ }
+}
+
+async function do_cleanup() {
+ Services.console.unregisterListener(on_new_message);
+ await unsetCookiePref();
+}
+
+/**
+ * Set e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are set.
+ */
+function setCookiePref() {
+ return new Promise(resolve =>
+ // accept all cookies so that the CORS requests will send the right cookies
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [["network.cookie.cookieBehavior", 0]],
+ },
+ resolve
+ )
+ );
+}
+
+/**
+ * Unset e10s related preferences in the test environment.
+ * @return {Promise} promise that resolves when preferences are unset.
+ */
+function unsetCookiePref() {
+ return new Promise(resolve => {
+ SpecialPowers.popPrefEnv(resolve);
+ });
+}
+
+//jscs:disable
+add_task(async function () {
+ //jscs:enable
+ // A longer timeout is necessary for this test than the plain mochitests
+ // due to opening a new tab with the web console.
+ requestLongerTimeout(4);
+ registerCleanupFunction(do_cleanup);
+ await setCookiePref();
+ Services.console.registerListener(on_new_message);
+
+ let test_uri =
+ "http://mochi.test:8888/browser/dom/security/test/cors/file_cors_logging_test.html";
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank"
+ );
+
+ BrowserTestUtils.loadURIString(gBrowser, test_uri);
+
+ await BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ test_uri + "#finished"
+ );
+
+ // Different OS combinations
+ ok(messages_seen > 0, "Saw " + messages_seen + " messages.");
+
+ messages_seen = 0;
+ let test_two_uri =
+ "http://mochi.test:8888/browser/dom/security/test/cors/file_bug1456721.html";
+ BrowserTestUtils.loadURIString(gBrowser, test_two_uri);
+
+ await BrowserTestUtils.waitForLocationChange(
+ gBrowser,
+ test_two_uri + "#finishedTestTwo"
+ );
+ await BrowserTestUtils.waitForCondition(() => messages_seen > 0);
+
+ ok(messages_seen > 0, "Saw " + messages_seen + " messages.");
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/security/test/cors/bug1456721.sjs b/dom/security/test/cors/bug1456721.sjs
new file mode 100644
index 0000000000..de8bd5a7f4
--- /dev/null
+++ b/dom/security/test/cors/bug1456721.sjs
@@ -0,0 +1,20 @@
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ let queryStr = request.queryString;
+
+ if (queryStr === "redirect") {
+ response.setStatusLine("1.1", 302, "Found");
+ response.setHeader("Location", "bug1456721.sjs?load", false);
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+ return;
+ }
+
+ if (queryStr === "load") {
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+ response.write("foo");
+ return;
+ }
+ // we should never get here - return something unexpected
+ response.write("d'oh");
+}
diff --git a/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs b/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs
new file mode 100644
index 0000000000..c8e3243101
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_cache_server.sjs
@@ -0,0 +1,59 @@
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var [name, value] = val.split("=");
+ query[name] = unescape(value);
+ });
+
+ if ("setState" in query) {
+ setState(
+ "test/dom/security/test_CrossSiteXHR_cache:secData",
+ query.setState
+ );
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write("hi");
+
+ return;
+ }
+
+ var isPreflight = request.method == "OPTIONS";
+
+ // Send response
+
+ secData = JSON.parse(
+ getState("test/dom/security/test_CrossSiteXHR_cache:secData")
+ );
+
+ if (secData.allowOrigin) {
+ response.setHeader("Access-Control-Allow-Origin", secData.allowOrigin);
+ }
+
+ if (secData.withCred) {
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+ }
+
+ if (isPreflight) {
+ if (secData.allowHeaders) {
+ response.setHeader("Access-Control-Allow-Headers", secData.allowHeaders);
+ }
+
+ if (secData.allowMethods) {
+ response.setHeader("Access-Control-Allow-Methods", secData.allowMethods);
+ }
+
+ if (secData.cacheTime) {
+ response.setHeader(
+ "Access-Control-Max-Age",
+ secData.cacheTime.toString()
+ );
+ }
+
+ return;
+ }
+
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "application/xml", false);
+ response.write("<res>hello pass</res>\n");
+}
diff --git a/dom/security/test/cors/file_CrossSiteXHR_inner.html b/dom/security/test/cors/file_CrossSiteXHR_inner.html
new file mode 100644
index 0000000000..d3e8421362
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_inner.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<!--
+ NOTE! The content of this file is duplicated in file_CrossSiteXHR_inner.jar
+ and file_CrossSiteXHR_inner_data.sjs
+ Please update those files if you update this one.
+-->
+
+<html>
+<head>
+<script>
+function trimString(stringValue) {
+ return stringValue.replace(/^\s+|\s+$/g, '');
+};
+
+window.addEventListener("message", function(e) {
+
+ sendData = null;
+
+ req = JSON.parse(e.data);
+ var res = {
+ didFail: false,
+ events: [],
+ progressEvents: 0,
+ status: 0,
+ responseText: "",
+ statusText: "",
+ responseXML: null,
+ sendThrew: false
+ };
+
+ var xhr = new XMLHttpRequest();
+ for (type of ["load", "abort", "error", "loadstart", "loadend"]) {
+ xhr.addEventListener(type, function(e) {
+ res.events.push(e.type);
+ });
+ }
+ xhr.addEventListener("readystatechange", function(e) {
+ res.events.push("rs" + xhr.readyState);
+ });
+ xhr.addEventListener("progress", function(e) {
+ res.progressEvents++;
+ });
+ if (req.uploadProgress) {
+ xhr.upload.addEventListener(req.uploadProgress, function(e) {
+ res.progressEvents++;
+ });
+ }
+ xhr.onerror = function(e) {
+ res.didFail = true;
+ };
+ xhr.onloadend = function (event) {
+ res.status = xhr.status;
+ try {
+ res.statusText = xhr.statusText;
+ } catch (e) {
+ delete(res.statusText);
+ }
+ res.responseXML = xhr.responseXML ?
+ (new XMLSerializer()).serializeToString(xhr.responseXML) :
+ null;
+ res.responseText = xhr.responseText;
+
+ res.responseHeaders = {};
+ for (responseHeader in req.responseHeaders) {
+ res.responseHeaders[responseHeader] =
+ xhr.getResponseHeader(responseHeader);
+ }
+ res.allResponseHeaders = {};
+ var splitHeaders = xhr.getAllResponseHeaders().split("\r\n");
+ for (var i = 0; i < splitHeaders.length; i++) {
+ var headerValuePair = splitHeaders[i].split(":");
+ if(headerValuePair[1] != null) {
+ var headerName = trimString(headerValuePair[0]);
+ var headerValue = trimString(headerValuePair[1]);
+ res.allResponseHeaders[headerName] = headerValue;
+ }
+ }
+ post(e, res);
+ }
+
+ if (req.withCred)
+ xhr.withCredentials = true;
+ if (req.body)
+ sendData = req.body;
+
+ res.events.push("opening");
+ // Allow passign in falsy usernames/passwords so we can test them
+ try {
+ xhr.open(req.method, req.url, true,
+ ("username" in req) ? req.username : "",
+ ("password" in req) ? req.password : "");
+ } catch (ex) {
+ res.didFail = true;
+ post(e, res);
+ }
+
+ for (header in req.headers) {
+ xhr.setRequestHeader(header, req.headers[header]);
+ }
+
+ res.events.push("sending");
+ try {
+ xhr.send(sendData);
+ } catch (ex) {
+ res.didFail = true;
+ res.sendThrew = true;
+ post(e, res);
+ }
+
+});
+
+function post(e, res) {
+ e.source.postMessage(JSON.stringify(res), "http://mochi.test:8888");
+}
+
+</script>
+</head>
+<body>
+Inner page
+</body>
+</html>
diff --git a/dom/security/test/cors/file_CrossSiteXHR_inner.jar b/dom/security/test/cors/file_CrossSiteXHR_inner.jar
new file mode 100644
index 0000000000..bdb0eb4408
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_inner.jar
Binary files differ
diff --git a/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs b/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs
new file mode 100644
index 0000000000..4a030c4211
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs
@@ -0,0 +1,103 @@
+var data =
+ '<!DOCTYPE HTML>\n\
+<html>\n\
+<head>\n\
+<script>\n\
+window.addEventListener("message", function(e) {\n\
+\n\
+ sendData = null;\n\
+\n\
+ req = JSON.parse(e.data);\n\
+ var res = {\n\
+ didFail: false,\n\
+ events: [],\n\
+ progressEvents: 0\n\
+ };\n\
+ \n\
+ var xhr = new XMLHttpRequest();\n\
+ for (type of ["load", "abort", "error", "loadstart", "loadend"]) {\n\
+ xhr.addEventListener(type, function(e) {\n\
+ res.events.push(e.type);\n\
+ }, false);\n\
+ }\n\
+ xhr.addEventListener("readystatechange", function(e) {\n\
+ res.events.push("rs" + xhr.readyState);\n\
+ }, false);\n\
+ xhr.addEventListener("progress", function(e) {\n\
+ res.progressEvents++;\n\
+ }, false);\n\
+ if (req.uploadProgress) {\n\
+ xhr.upload.addEventListener(req.uploadProgress, function(e) {\n\
+ res.progressEvents++;\n\
+ }, false);\n\
+ }\n\
+ xhr.onerror = function(e) {\n\
+ res.didFail = true;\n\
+ };\n\
+ xhr.onloadend = function (event) {\n\
+ res.status = xhr.status;\n\
+ try {\n\
+ res.statusText = xhr.statusText;\n\
+ } catch (e) {\n\
+ delete(res.statusText);\n\
+ }\n\
+ res.responseXML = xhr.responseXML ?\n\
+ (new XMLSerializer()).serializeToString(xhr.responseXML) :\n\
+ null;\n\
+ res.responseText = xhr.responseText;\n\
+\n\
+ res.responseHeaders = {};\n\
+ for (responseHeader in req.responseHeaders) {\n\
+ res.responseHeaders[responseHeader] =\n\
+ xhr.getResponseHeader(responseHeader);\n\
+ }\n\
+ res.allResponseHeaders = {};\n\
+ var splitHeaders = xhr.getAllResponseHeaders().split("\\r\\n");\n\
+ for (var i = 0; i < splitHeaders.length; i++) {\n\
+ var headerValuePair = splitHeaders[i].split(":");\n\
+ if(headerValuePair[1] != null){\n\
+ var headerName = trimString(headerValuePair[0]);\n\
+ var headerValue = trimString(headerValuePair[1]); \n\
+ res.allResponseHeaders[headerName] = headerValue;\n\
+ }\n\
+ }\n\
+ post(e, res);\n\
+ }\n\
+\n\
+ if (req.withCred)\n\
+ xhr.withCredentials = true;\n\
+ if (req.body)\n\
+ sendData = req.body;\n\
+\n\
+ res.events.push("opening");\n\
+ xhr.open(req.method, req.url, true);\n\
+\n\
+ for (header in req.headers) {\n\
+ xhr.setRequestHeader(header, req.headers[header]);\n\
+ }\n\
+\n\
+ res.events.push("sending");\n\
+ xhr.send(sendData);\n\
+\n\
+}, false);\n\
+\n\
+function post(e, res) {\n\
+ e.source.postMessage(JSON.stringify(res), "*");\n\
+}\n\
+function trimString(stringValue) {\n\
+ return stringValue.replace("/^s+|s+$/g","");\n\
+};\n\
+\n\
+</script>\n\
+</head>\n\
+<body>\n\
+Inner page\n\
+</body>\n\
+</html>';
+
+function handleRequest(request, response) {
+ response.setStatusLine(null, 302, "Follow me");
+ response.setHeader("Location", "data:text/html," + escape(data));
+ response.setHeader("Content-Type", "text/plain");
+ response.write("Follow that guy!");
+}
diff --git a/dom/security/test/cors/file_CrossSiteXHR_server.sjs b/dom/security/test/cors/file_CrossSiteXHR_server.sjs
new file mode 100644
index 0000000000..cd1581d8da
--- /dev/null
+++ b/dom/security/test/cors/file_CrossSiteXHR_server.sjs
@@ -0,0 +1,231 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+
+// eslint-disable-next-line complexity
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var [name, value] = val.split("=");
+ query[name] = unescape(value);
+ });
+
+ var isPreflight = request.method == "OPTIONS";
+
+ var bodyStream = new BinaryInputStream(request.bodyInputStream);
+ var bodyBytes = [];
+ while ((bodyAvail = bodyStream.available()) > 0) {
+ Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
+ }
+
+ var body = decodeURIComponent(
+ escape(String.fromCharCode.apply(null, bodyBytes))
+ );
+
+ if (query.hop) {
+ query.hop = parseInt(query.hop, 10);
+ hops = JSON.parse(query.hops);
+ var curHop = hops[query.hop - 1];
+ query.allowOrigin = curHop.allowOrigin;
+ query.allowHeaders = curHop.allowHeaders;
+ query.allowMethods = curHop.allowMethods;
+ query.allowCred = curHop.allowCred;
+ query.noAllowPreflight = curHop.noAllowPreflight;
+ if (curHop.setCookie) {
+ query.setCookie = unescape(curHop.setCookie);
+ }
+ if (curHop.cookie) {
+ query.cookie = unescape(curHop.cookie);
+ }
+ query.noCookie = curHop.noCookie;
+ }
+
+ // Check that request was correct
+
+ if (!isPreflight && query.body && body != query.body) {
+ sendHttp500(
+ response,
+ "Wrong body. Expected " + query.body + " got " + body
+ );
+ return;
+ }
+
+ if (!isPreflight && "headers" in query) {
+ headers = JSON.parse(query.headers);
+ for (headerName in headers) {
+ // Content-Type is changed if there was a body
+ if (
+ !(headerName == "Content-Type" && body) &&
+ (!request.hasHeader(headerName) ||
+ request.getHeader(headerName) != headers[headerName])
+ ) {
+ var actual = request.hasHeader(headerName)
+ ? request.getHeader(headerName)
+ : "<missing header>";
+ sendHttp500(
+ response,
+ "Header " +
+ headerName +
+ " had wrong value. Expected " +
+ headers[headerName] +
+ " got " +
+ actual
+ );
+ return;
+ }
+ }
+ }
+
+ if (
+ isPreflight &&
+ "requestHeaders" in query &&
+ request.getHeader("Access-Control-Request-Headers") != query.requestHeaders
+ ) {
+ sendHttp500(
+ response,
+ "Access-Control-Request-Headers had wrong value. Expected " +
+ query.requestHeaders +
+ " got " +
+ request.getHeader("Access-Control-Request-Headers")
+ );
+ return;
+ }
+
+ if (
+ isPreflight &&
+ "requestMethod" in query &&
+ request.getHeader("Access-Control-Request-Method") != query.requestMethod
+ ) {
+ sendHttp500(
+ response,
+ "Access-Control-Request-Method had wrong value. Expected " +
+ query.requestMethod +
+ " got " +
+ request.getHeader("Access-Control-Request-Method")
+ );
+ return;
+ }
+
+ if ("origin" in query && request.getHeader("Origin") != query.origin) {
+ sendHttp500(
+ response,
+ "Origin had wrong value. Expected " +
+ query.origin +
+ " got " +
+ request.getHeader("Origin")
+ );
+ return;
+ }
+
+ if ("cookie" in query) {
+ cookies = {};
+ request
+ .getHeader("Cookie")
+ .split(/ *; */)
+ .forEach(function (val) {
+ var [name, value] = val.split("=");
+ cookies[name] = unescape(value);
+ });
+
+ query.cookie.split(",").forEach(function (val) {
+ var [name, value] = val.split("=");
+ if (cookies[name] != value) {
+ sendHttp500(
+ response,
+ "Cookie " +
+ name +
+ " had wrong value. Expected " +
+ value +
+ " got " +
+ cookies[name]
+ );
+ return;
+ }
+ });
+ }
+
+ if (query.noCookie && request.hasHeader("Cookie")) {
+ sendHttp500(
+ response,
+ "Got cookies when didn't expect to: " + request.getHeader("Cookie")
+ );
+ return;
+ }
+
+ // Send response
+
+ if (!isPreflight && query.status) {
+ response.setStatusLine(null, query.status, query.statusMessage);
+ }
+ if (isPreflight && query.preflightStatus) {
+ response.setStatusLine(null, query.preflightStatus, "preflight status");
+ }
+
+ if (query.allowOrigin && (!isPreflight || !query.noAllowPreflight)) {
+ response.setHeader("Access-Control-Allow-Origin", query.allowOrigin);
+ }
+
+ if (query.allowCred) {
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+ }
+
+ if (query.setCookie) {
+ response.setHeader("Set-Cookie", query.setCookie + "; path=/");
+ }
+
+ if (isPreflight) {
+ if (query.allowHeaders) {
+ response.setHeader("Access-Control-Allow-Headers", query.allowHeaders);
+ }
+
+ if (query.allowMethods) {
+ response.setHeader("Access-Control-Allow-Methods", query.allowMethods);
+ }
+ } else {
+ if (query.responseHeaders) {
+ let responseHeaders = JSON.parse(query.responseHeaders);
+ for (let responseHeader in responseHeaders) {
+ response.setHeader(responseHeader, responseHeaders[responseHeader]);
+ }
+ }
+
+ if (query.exposeHeaders) {
+ response.setHeader("Access-Control-Expose-Headers", query.exposeHeaders);
+ }
+ }
+
+ if (!isPreflight && query.hop && query.hop < hops.length) {
+ newURL =
+ hops[query.hop].server +
+ "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?" +
+ "hop=" +
+ (query.hop + 1) +
+ "&hops=" +
+ escape(query.hops);
+ if ("headers" in query) {
+ newURL += "&headers=" + escape(query.headers);
+ }
+ response.setStatusLine(null, 307, "redirect");
+ response.setHeader("Location", newURL);
+
+ return;
+ }
+
+ // Send response body
+ if (!isPreflight && request.method != "HEAD") {
+ response.setHeader("Content-Type", "application/xml", false);
+ response.write("<res>hello pass</res>\n");
+ }
+ if (isPreflight && "preflightBody" in query) {
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(query.preflightBody);
+ }
+}
+
+function sendHttp500(response, text) {
+ response.setStatusLine(null, 500, text);
+}
diff --git a/dom/security/test/cors/file_bug1456721.html b/dom/security/test/cors/file_bug1456721.html
new file mode 100644
index 0000000000..8926b6ffc1
--- /dev/null
+++ b/dom/security/test/cors/file_bug1456721.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test new CORS console messages</title>
+</head>
+<body onload="initTest()">
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+let gen;
+let number_of_tests = 0;
+
+function initTest() {
+ window.addEventListener("message", function(e) {
+ gen.next(e.data);
+ if (number_of_tests == 2) {
+ document.location.href += "#finishedTestTwo";
+ }
+ });
+
+ gen = runTest();
+
+ gen.next();
+}
+
+function* runTest() {
+ let loader = document.getElementById("loader");
+ let loaderWindow = loader.contentWindow;
+ loader.onload = function() { gen.next(); };
+
+ loader.src = "http://example.org/browser/dom/security/test/cors/file_CrossSiteXHR_inner.html";
+ origin = "http://example.org";
+ yield undefined;
+
+ let tests = [
+ // Loading URLs other than http(s) should throw 'CORS request
+ // not http' console message. (Even though we removed ftp support within Bug 1574475
+ // we keep this test since it tests a scheme other than http(s))
+ { baseURL: "ftp://mochi.test:8888/browser/dom/security/test/cors/file_CrossSiteXHR_server.sjs",
+ method: "GET",
+ },
+ // (https://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0)
+ // CORs preflight external redirect should throw 'CORS request
+ // external redirect not allowed' error.
+ // This will also throw 'CORS preflight channel did not succeed'
+ // and 'CORS request did not succeed' console messages.
+ {
+ baseURL: "http://mochi.test:8888/browser/dom/security/test/cors/bug1456721.sjs?redirect",
+ method: "OPTIONS",
+ },
+ ];
+
+ for (let test of tests) {
+ let req = {
+ url: test.baseURL,
+ method: test.method
+ };
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+ number_of_tests++;
+ yield;
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/cors/file_cors_logging_test.html b/dom/security/test/cors/file_cors_logging_test.html
new file mode 100644
index 0000000000..d29f93cf9c
--- /dev/null
+++ b/dom/security/test/cors/file_cors_logging_test.html
@@ -0,0 +1,1311 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test for Cross Site XMLHttpRequest</title>
+</head>
+<body onload="initTest()">
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+const runPreflightTests = 1;
+const runCookieTests = 1;
+const runRedirectTests = 1;
+
+var gen;
+
+function initTest() {
+ window.addEventListener("message", function(e) {
+ gen.next(e.data);
+ });
+
+ gen = runTest();
+
+ gen.next()
+}
+
+function initTestCallback() {
+}
+
+function* runTest() {
+ var loader = document.getElementById('loader');
+ var loaderWindow = loader.contentWindow;
+ loader.onload = function () { gen.next() };
+
+ // Test preflight-less requests
+ basePath = "/browser/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"
+ baseURL = "http://mochi.test:8888" + basePath;
+
+ // Test preflighted requests
+ loader.src = "http://example.org/browser/dom/security/test/cors/file_CrossSiteXHR_inner.html";
+ origin = "http://example.org";
+ yield undefined;
+
+ tests = [// Plain request
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ },
+
+ // undefined username
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: undefined
+ },
+
+ // undefined username and password
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: undefined,
+ password: undefined
+ },
+
+ // nonempty username
+ { pass: 0,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: "user",
+ },
+
+ // nonempty password
+ // XXXbz this passes for now, because we ignore passwords
+ // without usernames in most cases.
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ password: "password",
+ },
+
+ // Default allowed headers
+ { pass: 1,
+ method: "GET",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // Custom headers
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "X-My-Header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" },
+ allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my%-header": "myValue" },
+ allowHeaders: "x-my%-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-header z",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-he(ader",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "myheader": "" },
+ allowMethods: "myheader",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "User-Agent": "myValue" },
+ allowHeaders: "User-Agent",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "User-Agent": "myValue" },
+ },
+
+ // Multiple custom headers
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header, second-header, third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header,second-header,third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header ,second-header ,third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header , second-header , third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue" },
+ allowHeaders: ", x-my-header, , ,, second-header, , ",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue" },
+ allowHeaders: "x-my-header, second-header, unused-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "secondValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "",
+ "y-my-header": "" },
+ allowHeaders: "x-my-header",
+ },
+
+ // HEAD requests
+ { pass: 1,
+ method: "HEAD",
+ noAllowPreflight: 1,
+ },
+
+ // HEAD with safe headers
+ { pass: 1,
+ method: "HEAD",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // HEAD with custom headers
+ { pass: 1,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+
+ // POST tests
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ },
+ { pass: 1,
+ method: "POST",
+ noAllowPreflight: 1,
+ },
+
+ // POST with standard headers
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "multipart/form-data" },
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ },
+ { pass: 0,
+ method: "POST",
+ headers: { "Content-Type": "foo/bar" },
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // POST with custom headers
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Accept": "foo/bar",
+ "Accept-Language": "sv-SE",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ headers: { "Content-Type": "text/plain",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, content-type",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, $_%",
+ },
+
+ // Other methods
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowHeaders: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST, PUT, DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST, DELETE, PUT",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE, POST, PUT",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST ,PUT ,DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST,PUT,DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST , PUT , DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: " ,, PUT ,, , , DELETE , ,",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETEZ",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE PUT",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE, PUT Z",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE, PU(T",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT Z, DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PU(T, DELETE",
+ },
+ { pass: 0,
+ method: "MYMETHOD",
+ allowMethods: "myMethod",
+ },
+ { pass: 0,
+ method: "PUT",
+ allowMethods: "put",
+ },
+
+ // Progress events
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ uploadProgress: "progress",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ uploadProgress: "progress",
+ noAllowPreflight: 1,
+ },
+
+ // Status messages
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 404,
+ statusMessage: "nothin' here",
+ },
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 401,
+ statusMessage: "no can do",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ allowHeaders: "content-type",
+ status: 500,
+ statusMessage: "server boo",
+ },
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 200,
+ statusMessage: "Yes!!",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 400
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 200
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 204
+ },
+
+ // exposed headers
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: ["x-my-header"],
+ },
+ { pass: 0,
+ method: "GET",
+ origin: "http://invalid",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header y",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "y x-my-header",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-header z",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-hea(er",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header",
+ "y-my-header": "y header" },
+ exposeHeaders: " , ,,y-my-header,z-my-header, ",
+ expectedResponseHeaders: ["y-my-header"],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "Cache-Control": "cacheControl header",
+ "Content-Language": "contentLanguage header",
+ "Expires":"expires header",
+ "Last-Modified":"lastModified header",
+ "Pragma":"pragma header",
+ "Unexpected":"unexpected header" },
+ expectedResponseHeaders: ["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"],
+ },
+ // Check that sending a body in the OPTIONS response works
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ preflightBody: "I'm a preflight response body",
+ },
+ ];
+
+ if (!runPreflightTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ var req = {
+ url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+ method: test.method,
+ headers: test.headers,
+ uploadProgress: test.uploadProgress,
+ body: test.body,
+ responseHeaders: test.responseHeaders,
+ };
+
+ if (test.pass) {
+ req.url += "&origin=" + escape(origin) +
+ "&requestMethod=" + test.method;
+ }
+
+ if ("username" in test) {
+ req.username = test.username;
+ }
+
+ if ("password" in test) {
+ req.password = test.password;
+ }
+
+ if (test.noAllowPreflight)
+ req.url += "&noAllowPreflight";
+
+ if (test.pass && "headers" in test) {
+ function isUnsafeHeader(name) {
+ lName = name.toLowerCase();
+ return lName != "accept" &&
+ lName != "accept-language" &&
+ (lName != "content-type" ||
+ !["text/plain",
+ "multipart/form-data",
+ "application/x-www-form-urlencoded"]
+ .includes(test.headers[name].toLowerCase()));
+ }
+ req.url += "&headers=" + escape(JSON.stringify(test.headers));
+ reqHeaders =
+ escape(Object.keys(test.headers)
+ .filter(isUnsafeHeader)
+ .map(s => s.toLowerCase())
+ .sort()
+ .join(","));
+ req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
+ }
+ if ("allowHeaders" in test)
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ if ("allowMethods" in test)
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+ if (test.body)
+ req.url += "&body=" + escape(test.body);
+ if (test.status) {
+ req.url += "&status=" + test.status;
+ req.url += "&statusMessage=" + escape(test.statusMessage);
+ }
+ if (test.preflightStatus)
+ req.url += "&preflightStatus=" + test.preflightStatus;
+ if (test.responseHeaders)
+ req.url += "&responseHeaders=" + escape(JSON.stringify(test.responseHeaders));
+ if (test.exposeHeaders)
+ req.url += "&exposeHeaders=" + escape(test.exposeHeaders);
+ if (test.preflightBody)
+ req.url += "&preflightBody=" + escape(test.preflightBody);
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+ res = JSON.parse(yield);
+ }
+
+ // Test cookie behavior
+ tests = [{ pass: 1,
+ method: "GET",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: 1,
+ allowCred: 0,
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: 1,
+ allowCred: 1,
+ origin: "*",
+ },
+ { pass: 1,
+ method: "GET",
+ withCred: 0,
+ allowCred: 1,
+ origin: "*",
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ noCookie: 1,
+ withCred: 0,
+ allowCred: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ noCookie: 1,
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: 0,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=2",
+ withCred: 1,
+ allowCred: 1,
+ },
+ ];
+
+ if (!runCookieTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ req = {
+ url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+ method: test.method,
+ headers: test.headers,
+ withCred: test.withCred,
+ };
+
+ if (test.allowCred)
+ req.url += "&allowCred";
+
+ if (test.setCookie)
+ req.url += "&setCookie=" + escape(test.setCookie);
+ if (test.cookie)
+ req.url += "&cookie=" + escape(test.cookie);
+ if (test.noCookie)
+ req.url += "&noCookie";
+
+ if ("allowHeaders" in test)
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ if ("allowMethods" in test)
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+
+ res = JSON.parse(yield);
+ }
+
+ // Make sure to clear cookies to avoid affecting other tests
+ document.cookie = "a=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT"
+
+ // Test redirects
+
+ tests = [{ pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: origin
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: "*"
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "x"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ { pass: 1,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+
+ // test redirects with different credentials settings
+ {
+ // Initialize by setting a cookies for same- and cross- origins.
+ pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ setCookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ setCookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ noCookie: 1,
+ },
+ ],
+ withCred: 0,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ // expected fail because allow-credentials CORS header is not set
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: '*',
+ noCookie: 1,
+ },
+ ],
+ withCred: 0,
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: '*',
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ ];
+
+ if (!runRedirectTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ req = {
+ url: test.hops[0].server + basePath + "hop=1&hops=" +
+ escape(JSON.stringify(test.hops)),
+ method: test.method,
+ headers: test.headers,
+ body: test.body,
+ withCred: test.withCred,
+ };
+
+ if (test.pass) {
+ if (test.body)
+ req.url += "&body=" + escape(test.body);
+ }
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+
+ res = JSON.parse(yield);
+ }
+
+ document.location.href += "#finished";
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/cors/file_cors_logging_test.html.css b/dom/security/test/cors/file_cors_logging_test.html.css
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/dom/security/test/cors/file_cors_logging_test.html.css
diff --git a/dom/security/test/cors/mochitest.ini b/dom/security/test/cors/mochitest.ini
new file mode 100644
index 0000000000..7d224aaf47
--- /dev/null
+++ b/dom/security/test/cors/mochitest.ini
@@ -0,0 +1,16 @@
+[DEFAULT]
+support-files =
+ file_CrossSiteXHR_cache_server.sjs
+ file_CrossSiteXHR_inner.html
+ file_CrossSiteXHR_inner_data.sjs
+ file_CrossSiteXHR_server.sjs
+
+[test_CrossSiteXHR.html]
+skip-if =
+ http3
+[test_CrossSiteXHR_cache.html]
+skip-if =
+ http3
+[test_CrossSiteXHR_origin.html]
+skip-if =
+ http3
diff --git a/dom/security/test/cors/test_CrossSiteXHR.html b/dom/security/test/cors/test_CrossSiteXHR.html
new file mode 100644
index 0000000000..f92571c6f8
--- /dev/null
+++ b/dom/security/test/cors/test_CrossSiteXHR.html
@@ -0,0 +1,1549 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test for Cross Site XMLHttpRequest</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="initTest()">
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+const runPreflightTests = 1;
+const runCookieTests = 1;
+const runRedirectTests = 1;
+
+var gen;
+
+function initTest() {
+ SimpleTest.waitForExplicitFinish();
+ // Allow all cookies, then do the actual test initialization
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["network.cookie.cookieBehavior", 0],
+ // Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default"
+ ["network.cookie.sameSite.laxByDefault", false],
+ ["network.cors_preflight.authorization_covered_by_wildcard", false],
+ ]
+ }, initTestCallback);
+}
+
+function initTestCallback() {
+ window.addEventListener("message", function(e) {
+ gen.next(e.data);
+ });
+
+ gen = runTest();
+
+ gen.next()
+}
+
+// eslint-disable-next-line complexity
+function* runTest() {
+ var loader = document.getElementById('loader');
+ var loaderWindow = loader.contentWindow;
+ loader.onload = function () { gen.next() };
+
+ // Test preflight-less requests
+ basePath = "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"
+ baseURL = "http://mochi.test:8888" + basePath;
+
+ // Test preflighted requests
+ loader.src = "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html";
+ origin = "http://example.org";
+ yield undefined;
+
+ tests = [// Plain request
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ },
+
+ // undefined username
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: undefined
+ },
+
+ // undefined username and password
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: undefined,
+ password: undefined
+ },
+
+ // nonempty username
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ username: "user",
+ },
+
+ // nonempty password
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ password: "password",
+ },
+
+ // Default allowed headers
+ { pass: 1,
+ method: "GET",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // Custom headers
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "X-My-Header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header": "secondValue" },
+ allowHeaders: "x-my-header, long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header-long-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my%-header": "myValue" },
+ allowHeaders: "x-my%-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-header z",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-he(ader",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "myheader": "" },
+ allowMethods: "myheader",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "User-Agent": "myValue" },
+ allowHeaders: "User-Agent",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "User-Agent": "myValue" },
+ },
+
+ // Multiple custom headers
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header, second-header, third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header,second-header,third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header ,second-header ,third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue",
+ "third-header": "thirdValue" },
+ allowHeaders: "x-my-header , second-header , third-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue" },
+ allowHeaders: ", x-my-header, , ,, second-header, , ",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "second-header": "secondValue" },
+ allowHeaders: "x-my-header, second-header, unused-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "secondValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "",
+ "y-my-header": "" },
+ allowHeaders: "x-my-header",
+ },
+
+ // HEAD requests
+ { pass: 1,
+ method: "HEAD",
+ noAllowPreflight: 1,
+ },
+
+ // HEAD with safe headers
+ { pass: 1,
+ method: "HEAD",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // HEAD with custom headers
+ { pass: 1,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 0,
+ method: "HEAD",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header y-my-header",
+ },
+
+ // POST tests
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ },
+ { pass: 1,
+ method: "POST",
+ noAllowPreflight: 1,
+ },
+
+ // POST with standard headers
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "multipart/form-data" },
+ noAllowPreflight: 1,
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ },
+ { pass: 0,
+ method: "POST",
+ headers: { "Content-Type": "foo/bar" },
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "Accept": "foo/bar",
+ "Accept-Language": "sv-SE" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar, text/plain, garbage" },
+ noAllowPreflight: 1,
+ },
+
+ // POST with custom headers
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Accept": "foo/bar",
+ "Accept-Language": "sv-SE",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ headers: { "Content-Type": "text/plain",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, content-type",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ noAllowPreflight: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar",
+ "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, $_%",
+ },
+
+ // Test cases for "Access-Control-Allow-Headers" containing "*".
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "*",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue",
+ "Authorization": "12345" },
+ allowHeaders: "*, Authorization",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue",
+ "Authorization": "12345" },
+ allowHeaders: "Authorization, *",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue",
+ "Authorization": "12345" },
+ allowHeaders: "*",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue",
+ "Authorization": "12345" },
+ allowHeaders: "x-my-header",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "*": "myValue" },
+ allowHeaders: "*",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "*",
+ withCred: 1,
+ allowCred: 1,
+ },
+
+ // Other methods
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowHeaders: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST, PUT, DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST, DELETE, PUT",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE, POST, PUT",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST ,PUT ,DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST,PUT,DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "POST , PUT , DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: " ,, PUT ,, , , DELETE , ,",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETEZ",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE PUT",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE, PUT Z",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "DELETE, PU(T",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PUT Z, DELETE",
+ },
+ { pass: 0,
+ method: "DELETE",
+ allowMethods: "PU(T, DELETE",
+ },
+ { pass: 0,
+ method: "MYMETHOD",
+ allowMethods: "myMethod",
+ },
+ { pass: 0,
+ method: "PUT",
+ allowMethods: "put",
+ },
+
+ // Progress events
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ uploadProgress: "progress",
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ uploadProgress: "progress",
+ noAllowPreflight: 1,
+ },
+
+ // Status messages
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 404,
+ statusMessage: "nothin' here",
+ },
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 401,
+ statusMessage: "no can do",
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "foo/bar" },
+ allowHeaders: "content-type",
+ status: 500,
+ statusMessage: "server boo",
+ },
+ { pass: 1,
+ method: "GET",
+ noAllowPreflight: 1,
+ status: 200,
+ statusMessage: "Yes!!",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 400
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 200
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "header value" },
+ allowHeaders: "x-my-header",
+ preflightStatus: 204
+ },
+
+ // exposed headers
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: ["x-my-header"],
+ },
+ { pass: 0,
+ method: "GET",
+ origin: "http://invalid",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header y",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "y x-my-header",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-header z",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header" },
+ exposeHeaders: "x-my-header, y-my-hea(er",
+ expectedResponseHeaders: [],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "x-my-header": "x header",
+ "y-my-header": "y header" },
+ exposeHeaders: " , ,,y-my-header,z-my-header, ",
+ expectedResponseHeaders: ["y-my-header"],
+ },
+ { pass: 1,
+ method: "GET",
+ responseHeaders: { "Cache-Control": "cacheControl header",
+ "Content-Language": "contentLanguage header",
+ "Expires":"expires header",
+ "Last-Modified":"lastModified header",
+ "Pragma":"pragma header",
+ "Unexpected":"unexpected header" },
+ expectedResponseHeaders: ["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"],
+ },
+ // Check that sending a body in the OPTIONS response works
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ preflightBody: "I'm a preflight response body",
+ },
+ ];
+
+ if (!runPreflightTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ var req = {
+ url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+ method: test.method,
+ headers: test.headers,
+ uploadProgress: test.uploadProgress,
+ body: test.body,
+ responseHeaders: test.responseHeaders,
+ withCred: test.withCred ? test.withCred : 0,
+ };
+
+ if (test.pass) {
+ req.url += "&origin=" + escape(origin) +
+ "&requestMethod=" + test.method;
+ }
+
+ if ("username" in test) {
+ req.username = test.username;
+ }
+
+ if ("password" in test) {
+ req.password = test.password;
+ }
+
+ if (test.noAllowPreflight)
+ req.url += "&noAllowPreflight";
+
+ if (test.allowCred)
+ req.url += "&allowCred";
+
+ if (test.pass && "headers" in test) {
+ function isUnsafeHeader(name) {
+ lName = name.toLowerCase();
+ return lName != "accept" &&
+ lName != "accept-language" &&
+ (lName != "content-type" ||
+ !["text/plain",
+ "multipart/form-data",
+ "application/x-www-form-urlencoded"]
+ .includes(test.headers[name].toLowerCase()));
+ }
+ req.url += "&headers=" + escape(JSON.stringify(test.headers));
+ reqHeaders =
+ escape(Object.keys(test.headers)
+ .filter(isUnsafeHeader)
+ .map(s => s.toLowerCase())
+ .sort()
+ .join(","));
+ req.url += reqHeaders ? "&requestHeaders=" + reqHeaders : "";
+ }
+ if ("allowHeaders" in test)
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ if ("allowMethods" in test)
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+ if (test.body)
+ req.url += "&body=" + escape(test.body);
+ if (test.status) {
+ req.url += "&status=" + test.status;
+ req.url += "&statusMessage=" + escape(test.statusMessage);
+ }
+ if (test.preflightStatus)
+ req.url += "&preflightStatus=" + test.preflightStatus;
+ if (test.responseHeaders)
+ req.url += "&responseHeaders=" + escape(JSON.stringify(test.responseHeaders));
+ if (test.exposeHeaders)
+ req.url += "&exposeHeaders=" + escape(test.exposeHeaders);
+ if (test.preflightBody)
+ req.url += "&preflightBody=" + escape(test.preflightBody);
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+ res = JSON.parse(yield);
+
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + JSON.stringify(test));
+ if (test.status) {
+ is(res.status, test.status, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, test.statusMessage, "wrong status text for " + JSON.stringify(test));
+ }
+ else {
+ is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
+ }
+ if (test.method !== "HEAD") {
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + JSON.stringify(test));
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + JSON.stringify(test));
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + JSON.stringify(test));
+ }
+ else {
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + JSON.stringify(test));
+ is(res.responseText, "",
+ "wrong responseText in test for " + JSON.stringify(test));
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs4,load,loadend",
+ "wrong responseText in test for " + JSON.stringify(test));
+ }
+ if (test.responseHeaders) {
+ for (header in test.responseHeaders) {
+ if (!test.expectedResponseHeaders.includes(header)) {
+ is(res.responseHeaders[header], null,
+ "|xhr.getResponseHeader()|wrong response header (" + header + ") in test for " +
+ JSON.stringify(test));
+ is(res.allResponseHeaders[header], undefined,
+ "|xhr.getAllResponseHeaderss()|wrong response header (" + header + ") in test for " +
+ JSON.stringify(test));
+ }
+ else {
+ is(res.responseHeaders[header], test.responseHeaders[header],
+ "|xhr.getResponseHeader()|wrong response header (" + header + ") in test for " +
+ JSON.stringify(test));
+ is(res.allResponseHeaders[header.toLowerCase()], test.responseHeaders[header],
+ "|xhr.getAllResponseHeaderss()|wrong response header (" + header + ") in test for " +
+ JSON.stringify(test));
+ }
+ }
+ }
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + JSON.stringify(test));
+ is(res.status, 0, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "", "wrong status text for " + JSON.stringify(test));
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + JSON.stringify(test));
+ is(res.responseText, "",
+ "wrong responseText in test for " + JSON.stringify(test));
+ if (!res.sendThrew) {
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs4,error,loadend",
+ "wrong events in test for " + JSON.stringify(test));
+ }
+ is(res.progressEvents, 0,
+ "wrong events in test for " + JSON.stringify(test));
+ if (test.responseHeaders) {
+ for (header in test.responseHeaders) {
+ is(res.responseHeaders[header], null,
+ "wrong response header (" + header + ") in test for " +
+ JSON.stringify(test));
+ }
+ }
+ }
+ }
+
+ // Test cookie behavior
+ tests = [{ pass: 1,
+ method: "GET",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: 1,
+ allowCred: 0,
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: 1,
+ allowCred: 1,
+ origin: "*",
+ },
+ { pass: 1,
+ method: "GET",
+ withCred: 0,
+ allowCred: 1,
+ origin: "*",
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ noCookie: 1,
+ withCred: 0,
+ allowCred: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ noCookie: 1,
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: 0,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=1",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ setCookie: "a=2",
+ withCred: 1,
+ allowCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ cookie: "a=2",
+ withCred: 1,
+ allowCred: 1,
+ },
+ ];
+
+ if (!runCookieTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ req = {
+ url: baseURL + "allowOrigin=" + escape(test.origin || origin),
+ method: test.method,
+ headers: test.headers,
+ withCred: test.withCred,
+ };
+
+ if (test.allowCred)
+ req.url += "&allowCred";
+
+ if (test.setCookie)
+ req.url += "&setCookie=" + escape(test.setCookie);
+ if (test.cookie)
+ req.url += "&cookie=" + escape(test.cookie);
+ if (test.noCookie)
+ req.url += "&noCookie";
+
+ if ("allowHeaders" in test)
+ req.url += "&allowHeaders=" + escape(test.allowHeaders);
+ if ("allowMethods" in test)
+ req.url += "&allowMethods=" + escape(test.allowMethods);
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+
+ res = JSON.parse(yield);
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + JSON.stringify(test));
+ is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + JSON.stringify(test));
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + JSON.stringify(test));
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + JSON.stringify(test));
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + JSON.stringify(test));
+ is(res.status, 0, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "", "wrong status text for " + JSON.stringify(test));
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + JSON.stringify(test));
+ is(res.responseText, "",
+ "wrong responseText in test for " + JSON.stringify(test));
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs4,error,loadend",
+ "wrong events in test for " + JSON.stringify(test));
+ is(res.progressEvents, 0,
+ "wrong events in test for " + JSON.stringify(test));
+ }
+ }
+
+ // Make sure to clear cookies to avoid affecting other tests
+ document.cookie = "a=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT"
+ is(document.cookie, "", "No cookies should be left over");
+
+
+ // Test redirects
+ is(loader.src, "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html");
+ is(origin, "http://example.org");
+
+ tests = [{ pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://example.org",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: origin
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: "*"
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*"
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "x"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin
+ },
+ ],
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin
+ },
+ { server: "http://test2.example.org:8000",
+ allowOrigin: origin
+ },
+ { server: "http://sub2.xn--lt-uia.example.org",
+ allowOrigin: "*"
+ },
+ { server: "http://sub1.test1.example.org",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*",
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ { pass: 1,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: "*",
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowMethods: "DELETE",
+ noAllowPreflight: 1,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "DELETE",
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ },
+ { server: "http://sub1.test1.example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+ { pass: 1,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain" },
+ hops: [{ server: "http://example.org",
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ },
+ ],
+ },
+ { pass: 0,
+ method: "POST",
+ body: "hi there",
+ headers: { "Content-Type": "text/plain",
+ "my-header": "myValue",
+ },
+ hops: [{ server: "http://example.com",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ { server: "http://example.org",
+ allowOrigin: origin,
+ allowHeaders: "my-header",
+ },
+ ],
+ },
+
+ // test redirects with different credentials settings
+ {
+ // Initialize by setting a cookies for same- and cross- origins.
+ pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ setCookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ setCookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ noCookie: 1,
+ },
+ ],
+ withCred: 0,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ // expected fail because allow-credentials CORS header is not set
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: origin,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ { pass: 1,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: '*',
+ noCookie: 1,
+ },
+ ],
+ withCred: 0,
+ },
+ { pass: 0,
+ method: "GET",
+ hops: [{ server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: origin,
+ cookie: escape("a=1"),
+ },
+ { server: "http://example.com",
+ allowOrigin: '*',
+ allowCred: 1,
+ cookie: escape("a=2"),
+ },
+ ],
+ withCred: 1,
+ },
+ ];
+
+ if (!runRedirectTests) {
+ tests = [];
+ }
+
+ for (test of tests) {
+ req = {
+ url: test.hops[0].server + basePath + "hop=1&hops=" +
+ escape(JSON.stringify(test.hops)),
+ method: test.method,
+ headers: test.headers,
+ body: test.body,
+ withCred: test.withCred,
+ };
+
+ if (test.pass) {
+ if (test.body)
+ req.url += "&body=" + escape(test.body);
+ }
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+
+ res = JSON.parse(yield);
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + JSON.stringify(test));
+ is(res.status, 200, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "OK", "wrong status text for " + JSON.stringify(test));
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + JSON.stringify(test));
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + JSON.stringify(test));
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + JSON.stringify(test));
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + JSON.stringify(test));
+ is(res.status, 0, "wrong status in test for " + JSON.stringify(test));
+ is(res.statusText, "", "wrong status text for " + JSON.stringify(test));
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + JSON.stringify(test));
+ is(res.responseText, "",
+ "wrong responseText in test for " + JSON.stringify(test));
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs4,error,loadend",
+ "wrong events in test for " + JSON.stringify(test));
+ is(res.progressEvents, 0,
+ "wrong progressevents in test for " + JSON.stringify(test));
+ }
+ }
+
+
+ SpecialPowers.clearUserPref("network.cookie.sameSite.laxByDefault");
+ SpecialPowers.clearUserPref("browser.contentblocking.category");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/cors/test_CrossSiteXHR_cache.html b/dom/security/test/cors/test_CrossSiteXHR_cache.html
new file mode 100644
index 0000000000..77898e38ed
--- /dev/null
+++ b/dom/security/test/cors/test_CrossSiteXHR_cache.html
@@ -0,0 +1,610 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test for Cross Site XMLHttpRequest</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="gen.next()">
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+let gen;
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("This test needs to generate artificial pauses, hence it uses timeouts. There is no way around it, unfortunately. :(");
+
+window.addEventListener("message", function(e) {
+ gen.next(e.data);
+});
+
+gen = runTest();
+
+function* runTest() {
+ var loader = document.getElementById('loader');
+ var loaderWindow = loader.contentWindow;
+ loader.onload = function () { gen.next() };
+
+ loader.src = "http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html";
+ origin = "http://example.org";
+ yield undefined;
+
+ tests = [{ pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "second" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ allowHeaders: "y-my-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ allowHeaders: "y-my-header,x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "second" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ allowHeaders: "y-my-header,x-my-header",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "hello" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue",
+ "y-my-header": "second" },
+ },
+ { newTest: "*******" },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 2
+ },
+ { pause: 2.1 },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header, y-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "z-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: "\t 3600 \t ",
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: "3600 3",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: "asdf",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "first-header": "myValue" },
+ allowHeaders: "first-header",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ allowHeaders: "second-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "third-header": "myValue" },
+ allowHeaders: "third-header",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "first-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "first-header": "myValue" },
+ allowHeaders: "first-header",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ allowHeaders: "second-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "third-header": "myValue" },
+ allowHeaders: "third-header",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "GET",
+ headers: { "second-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "third-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ },
+ { pass: 0,
+ method: "PATCH",
+ },
+ { pass: 1,
+ method: "PATCH",
+ allowMethods: "PATCH",
+ },
+ { pass: 1,
+ method: "PATCH",
+ },
+ { pass: 1,
+ method: "PATCH",
+ allowMethods: "PATCH",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "PATCH",
+ },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 0,
+ method: "PUT",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "PATCH",
+ allowMethods: "PATCH",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "PATCH",
+ },
+ { newTest: "*******" },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 2
+ },
+ { pause: 2.1 },
+ { pass: 0,
+ method: "DELETE",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE, PUT",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "DELETE",
+ },
+ { pass: 1,
+ method: "PUT",
+ },
+ { pass: 0,
+ method: "PATCH",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "FIRST",
+ allowMethods: "FIRST",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "SECOND",
+ allowMethods: "SECOND",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "THIRD",
+ allowMethods: "THIRD",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "SECOND",
+ },
+ { pass: 0,
+ method: "FIRST",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "FIRST",
+ allowMethods: "FIRST",
+ cacheTime: 2,
+ },
+ { pass: 1,
+ method: "SECOND",
+ allowMethods: "SECOND",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "THIRD",
+ allowMethods: "THIRD",
+ cacheTime: 2,
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "SECOND",
+ },
+ { pass: 0,
+ method: "THIRD",
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" }
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "y-my-header": "y-value" }
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "x-value" }
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { pass: 0,
+ method: "PUT",
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { pass: 0,
+ method: "GET",
+ noOrigin: 1,
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "x-value" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "DELETE"
+ },
+ { pass: 0,
+ method: "PUT"
+ },
+ { pass: 0,
+ method: "DELETE"
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "DELETE"
+ },
+ { pass: 0,
+ method: "DELETE",
+ headers: { "my-header": "value" },
+ },
+ { pass: 0,
+ method: "DELETE"
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ },
+ { pass: 1,
+ method: "DELETE"
+ },
+ { pass: 0,
+ method: "GET",
+ noOrigin: 1,
+ },
+ { pass: 0,
+ method: "DELETE"
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ allowHeaders: "y-my-header",
+ cacheTime: 2
+ },
+ { pass: 1,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ },
+ { pause: 2.1 },
+ { pass: 1,
+ method: "GET",
+ withCred: true,
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "y-my-header": "myValue" },
+ },
+ { pass: 0,
+ method: "GET",
+ withCred: true,
+ headers: { "y-my-header": "myValue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600
+ },
+ { pass: 0,
+ method: "GET",
+ headers: { "DELETE": "myvalue" },
+ },
+ { newTest: "*******" },
+ { pass: 1,
+ method: "GET",
+ headers: { "x-my-header": "myValue" },
+ allowHeaders: "x-my-header",
+ cacheTime: 3600
+ },
+ { pass: 0,
+ method: "3600",
+ headers: { "x-my-header": "myvalue" },
+ },
+ ];
+
+ for (let i = 0; i < 110; i++) {
+ tests.push({ newTest: "*******" },
+ { pass: 1,
+ method: "DELETE",
+ allowMethods: "DELETE",
+ cacheTime: 3600,
+ });
+ }
+
+ baseURL = "http://mochi.test:8888/tests/dom/security/test/cors/" +
+ "file_CrossSiteXHR_cache_server.sjs?";
+ setStateURL = baseURL + "setState=";
+
+ var unique = Date.now();
+ for (test of tests) {
+ if (test.newTest) {
+ unique++;
+ continue;
+ }
+ if (test.pause) {
+ setTimeout(function() { gen.next() }, test.pause * 1000);
+ yield undefined;
+ continue;
+ }
+
+ req = {
+ url: baseURL + "c=" + unique,
+ method: test.method,
+ headers: test.headers,
+ withCred: test.withCred,
+ };
+
+ sec = { allowOrigin: test.noOrigin ? "" : origin,
+ allowHeaders: test.allowHeaders,
+ allowMethods: test.allowMethods,
+ cacheTime: test.cacheTime,
+ withCred: test.withCred };
+ xhr = new XMLHttpRequest();
+ xhr.open("POST", setStateURL + escape(JSON.stringify(sec)), true);
+ xhr.onloadend = function() { gen.next(); }
+ xhr.send();
+ yield undefined;
+
+ loaderWindow.postMessage(JSON.stringify(req), origin);
+
+ res = JSON.parse(yield);
+
+ testName = JSON.stringify(test) + " (index " + tests.indexOf(test) + ")";
+
+ if (test.pass) {
+ is(res.didFail, false,
+ "shouldn't have failed in test for " + testName);
+ is(res.status, 200, "wrong status in test for " + testName);
+ is(res.responseXML, "<res>hello pass</res>",
+ "wrong responseXML in test for " + testName);
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + testName);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong events in test for " + testName);
+ }
+ else {
+ is(res.didFail, true,
+ "should have failed in test for " + testName);
+ is(res.status, 0, "wrong status in test for " + testName);
+ is(res.responseXML, null,
+ "wrong responseXML in test for " + testName);
+ is(res.responseText, "",
+ "wrong responseText in test for " + testName);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs4,error,loadend",
+ "wrong events in test for " + testName);
+ is(res.progressEvents, 0,
+ "wrong events in test for " + testName);
+ }
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/security/test/cors/test_CrossSiteXHR_origin.html b/dom/security/test/cors/test_CrossSiteXHR_origin.html
new file mode 100644
index 0000000000..ba4a645965
--- /dev/null
+++ b/dom/security/test/cors/test_CrossSiteXHR_origin.html
@@ -0,0 +1,180 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+ <title>Test for Cross Site XMLHttpRequest</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display">
+<iframe id=loader></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(2);
+
+var origins =
+ [{ server: 'http://example.org' },
+ { server: 'http://example.org:80',
+ origin: 'http://example.org'
+ },
+ { server: 'http://sub1.test1.example.org' },
+ { server: 'http://test2.example.org:8000' },
+ { server: 'http://sub1.\xe4lt.example.org:8000',
+ origin: 'http://sub1.xn--lt-uia.example.org:8000'
+ },
+ { server: 'http://sub2.\xe4lt.example.org',
+ origin: 'http://sub2.xn--lt-uia.example.org'
+ },
+ { server: 'http://ex\xe4mple.test',
+ origin: 'http://xn--exmple-cua.test'
+ },
+ { server: 'http://xn--exmple-cua.test' },
+ { server: 'http://\u03c0\u03b1\u03c1\u03ac\u03b4\u03b5\u03b9\u03b3\u03bc\u03b1.\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae',
+ origin: 'http://xn--hxajbheg2az3al.xn--jxalpdlp'
+ },
+ { origin: 'null',
+ file: 'http://example.org/tests/dom/security/test/cors/file_CrossSiteXHR_inner_data.sjs'
+ },
+ ];
+
+ //['https://example.com:443'],
+ //['https://sub1.test1.example.com:443'],
+
+
+function initTest() {
+ // Allow all cookies, then do the actual test initialization
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ // Some of this test relies on redirecting to data: URLs from http.
+ ["network.allow_redirect_to_data", true],
+ ]
+ }).then(initTestCallback);
+}
+
+function initTestCallback() {
+ window.addEventListener("message", function(e) {
+ gen.next(e.data);
+ });
+
+ gen = runTest();
+ gen.next();
+}
+
+function* runTest() {
+ var loader = document.getElementById('loader');
+ var loaderWindow = loader.contentWindow;
+ loader.onload = function () { gen.next() };
+
+ // Test preflight-less requests
+ basePath = "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?"
+ baseURL = "http://mochi.test:8888" + basePath;
+
+ for (originEntry of origins) {
+ origin = originEntry.origin || originEntry.server;
+
+ loader.src = originEntry.file ||
+ (originEntry.server + "/tests/dom/security/test/cors/file_CrossSiteXHR_inner.html");
+ yield undefined;
+
+ var isNullOrigin = origin == "null";
+
+ port = /:\d+/;
+ passTests = [
+ origin,
+ "*",
+ " \t " + origin + "\t \t",
+ "\t \t* \t ",
+ ];
+ failTests = [
+ "",
+ " ",
+ port.test(origin) ? origin.replace(port, "")
+ : origin + ":1234",
+ port.test(origin) ? origin.replace(port, ":")
+ : origin + ":",
+ origin + ".",
+ origin + "/",
+ origin + "#",
+ origin + "?",
+ origin + "\\",
+ origin + "%",
+ origin + "@",
+ origin + "/hello",
+ "foo:bar@" + origin,
+ "* " + origin,
+ origin + " " + origin,
+ "allow <" + origin + ">",
+ "<" + origin + ">",
+ "<*>",
+ origin.substr(0, 5) == "https" ? origin.replace("https", "http")
+ : origin.replace("http", "https"),
+ origin.replace("://", "://www."),
+ origin.replace("://", ":// "),
+ origin.replace(/\/[^.]+\./, "/"),
+ ];
+
+ if (isNullOrigin) {
+ passTests = ["*", "\t \t* \t ", "null"];
+ failTests = failTests.filter(function(v) { return v != origin });
+ }
+
+ for (allowOrigin of passTests) {
+ req = {
+ url: baseURL +
+ "allowOrigin=" + escape(allowOrigin) +
+ "&origin=" + escape(origin),
+ method: "GET",
+ };
+ loaderWindow.postMessage(JSON.stringify(req), isNullOrigin ? "*" : origin);
+
+ res = JSON.parse(yield);
+ is(res.didFail, false, "shouldn't have failed for " + allowOrigin);
+ is(res.status, 200, "wrong status for " + allowOrigin);
+ is(res.statusText, "OK", "wrong status text for " + allowOrigin);
+ is(res.responseXML,
+ "<res>hello pass</res>",
+ "wrong responseXML in test for " + allowOrigin);
+ is(res.responseText, "<res>hello pass</res>\n",
+ "wrong responseText in test for " + allowOrigin);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs2,rs3,rs4,load,loadend",
+ "wrong responseText in test for " + allowOrigin);
+ }
+
+ for (allowOrigin of failTests) {
+ req = {
+ url: baseURL + "allowOrigin=" + escape(allowOrigin),
+ method: "GET",
+ };
+ loaderWindow.postMessage(JSON.stringify(req), isNullOrigin ? "*" : origin);
+
+ res = JSON.parse(yield);
+ is(res.didFail, true, "should have failed for " + allowOrigin);
+ is(res.responseText, "", "should have no text for " + allowOrigin);
+ is(res.status, 0, "should have no status for " + allowOrigin);
+ is(res.statusText, "", "wrong status text for " + allowOrigin);
+ is(res.responseXML, null, "should have no XML for " + allowOrigin);
+ is(res.events.join(","),
+ "opening,rs1,sending,loadstart,rs4,error,loadend",
+ "wrong events in test for " + allowOrigin);
+ is(res.progressEvents, 0,
+ "wrong events in test for " + allowOrigin);
+ }
+ }
+
+ SimpleTest.finish();
+}
+
+addLoadEvent(initTest);
+
+</script>
+</pre>
+</body>
+</html>