summaryrefslogtreecommitdiffstats
path: root/dom/security/test/sec-fetch
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/security/test/sec-fetch/browser.ini10
-rw-r--r--dom/security/test/sec-fetch/browser_external_loads.js176
-rw-r--r--dom/security/test/sec-fetch/browser_navigation.js178
-rw-r--r--dom/security/test/sec-fetch/file_dummy_link.html9
-rw-r--r--dom/security/test/sec-fetch/file_dummy_link_location.html9
-rw-r--r--dom/security/test/sec-fetch/file_no_cache.sjs28
-rw-r--r--dom/security/test/sec-fetch/file_redirect.sjs34
-rw-r--r--dom/security/test/sec-fetch/file_trustworthy_loopback.html11
-rw-r--r--dom/security/test/sec-fetch/file_websocket_wsh.py6
-rw-r--r--dom/security/test/sec-fetch/mochitest.ini19
-rw-r--r--dom/security/test/sec-fetch/test_iframe_history_manipulation.html85
-rw-r--r--dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html95
-rw-r--r--dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html95
-rw-r--r--dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html98
-rw-r--r--dom/security/test/sec-fetch/test_trustworthy_loopback.html77
-rw-r--r--dom/security/test/sec-fetch/test_websocket.html74
16 files changed, 1004 insertions, 0 deletions
diff --git a/dom/security/test/sec-fetch/browser.ini b/dom/security/test/sec-fetch/browser.ini
new file mode 100644
index 0000000000..ab9c0b1a7d
--- /dev/null
+++ b/dom/security/test/sec-fetch/browser.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+support-files = file_no_cache.sjs
+
+[browser_navigation.js]
+[browser_external_loads.js]
+skip-if =
+ os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
+support-files =
+ file_dummy_link.html
+ file_dummy_link_location.html
diff --git a/dom/security/test/sec-fetch/browser_external_loads.js b/dom/security/test/sec-fetch/browser_external_loads.js
new file mode 100644
index 0000000000..0340b46899
--- /dev/null
+++ b/dom/security/test/sec-fetch/browser_external_loads.js
@@ -0,0 +1,176 @@
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+var gExpectedHeader = {};
+
+function checkSecFetchUser(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.startsWith("https://example.com")) {
+ return;
+ }
+
+ info(`testing headers for load of ${channel.URI.spec}`);
+
+ const secFetchHeaders = [
+ "sec-fetch-mode",
+ "sec-fetch-dest",
+ "sec-fetch-user",
+ "sec-fetch-site",
+ ];
+
+ secFetchHeaders.forEach(header => {
+ const expectedValue = gExpectedHeader[header];
+ try {
+ is(
+ channel.getRequestHeader(header),
+ expectedValue,
+ `${header} is set to ${expectedValue}`
+ );
+ } catch (e) {
+ if (expectedValue) {
+ ok(false, `${header} should be set`);
+ } else {
+ ok(true, `${header} should not be set`);
+ }
+ }
+ });
+}
+
+add_task(async function external_load() {
+ waitForExplicitFinish();
+ Services.obs.addObserver(checkSecFetchUser, "http-on-stop-request");
+
+ let headersChecked = new Promise(resolve => {
+ let reqStopped = async (subject, topic, data) => {
+ Services.obs.removeObserver(reqStopped, "http-on-stop-request");
+ resolve();
+ };
+ Services.obs.addObserver(reqStopped, "http-on-stop-request");
+ });
+
+ // System fetch. Shouldn't use Sec- headers for that.
+ gExpectedHeader = {
+ "sec-fetch-site": null,
+ "sec-fetch-mode": null,
+ "sec-fetch-dest": null,
+ "sec-fetch-user": null,
+ };
+ await window.fetch(`${TEST_PATH}file_dummy_link.html?sysfetch`);
+ await headersChecked;
+
+ // Simulate an external load in the *current* window with
+ // Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL and the system principal.
+ gExpectedHeader = {
+ "sec-fetch-site": "none",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "document",
+ "sec-fetch-user": "?1",
+ };
+
+ let loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ window.browserDOMWindow.openURI(
+ makeURI(`${TEST_PATH}file_dummy_link.html`),
+ null,
+ Ci.nsIBrowserDOMWindow.OPEN_CURRENTWINDOW,
+ Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL,
+ Services.scriptSecurityManager.getSystemPrincipal()
+ );
+ await loaded;
+
+ // Open a link in a *new* window through the context menu.
+ gExpectedHeader = {
+ "sec-fetch-site": "same-origin",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "document",
+ "sec-fetch-user": "?1",
+ };
+
+ loaded = BrowserTestUtils.waitForNewWindow({
+ url: `${TEST_PATH}file_dummy_link_location.html`,
+ });
+ BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
+ document.getElementById("context-openlink").doCommand();
+ event.target.hidePopup();
+ return true;
+ });
+ BrowserTestUtils.synthesizeMouseAtCenter(
+ "#dummylink",
+ { type: "contextmenu", button: 2 },
+ gBrowser.selectedBrowser
+ );
+
+ let win = await loaded;
+ win.close();
+
+ // Simulate an external load in a *new* window with
+ // Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL and the system principal.
+ gExpectedHeader = {
+ "sec-fetch-site": "none",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "document",
+ "sec-fetch-user": "?1",
+ };
+
+ loaded = BrowserTestUtils.waitForNewWindow({
+ url: "https://example.com/newwindow",
+ });
+ window.browserDOMWindow.openURI(
+ makeURI("https://example.com/newwindow"),
+ null,
+ Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW,
+ Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL,
+ Services.scriptSecurityManager.getSystemPrincipal()
+ );
+ win = await loaded;
+ win.close();
+
+ // Open a *new* window through window.open without user activation.
+ gExpectedHeader = {
+ "sec-fetch-site": "same-origin",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "document",
+ };
+
+ loaded = BrowserTestUtils.waitForNewWindow({
+ url: "https://example.com/windowopen",
+ });
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ content.window.open(
+ "https://example.com/windowopen",
+ "_blank",
+ "height=500,width=500"
+ );
+ });
+ win = await loaded;
+ win.close();
+
+ // Open a *new* window through window.open with user activation.
+ gExpectedHeader = {
+ "sec-fetch-site": "same-origin",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "document",
+ "sec-fetch-user": "?1",
+ };
+
+ loaded = BrowserTestUtils.waitForNewWindow({
+ url: "https://example.com/windowopen_withactivation",
+ });
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
+ content.document.notifyUserGestureActivation();
+ content.window.open(
+ "https://example.com/windowopen_withactivation",
+ "_blank",
+ "height=500,width=500"
+ );
+ content.document.clearUserGestureActivation();
+ });
+ win = await loaded;
+ win.close();
+
+ Services.obs.removeObserver(checkSecFetchUser, "http-on-stop-request");
+ finish();
+});
diff --git a/dom/security/test/sec-fetch/browser_navigation.js b/dom/security/test/sec-fetch/browser_navigation.js
new file mode 100644
index 0000000000..642dcab76e
--- /dev/null
+++ b/dom/security/test/sec-fetch/browser_navigation.js
@@ -0,0 +1,178 @@
+"use strict";
+
+const REQUEST_URL =
+ "https://example.com/browser/dom/security/test/sec-fetch/file_no_cache.sjs";
+
+let gTestCounter = 0;
+let gExpectedHeader = {};
+
+async function setup() {
+ waitForExplicitFinish();
+}
+
+function checkSecFetchUser(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.startsWith("https://example.com/")) {
+ return;
+ }
+
+ info(`testing headers for load of ${channel.URI.spec}`);
+
+ const secFetchHeaders = [
+ "sec-fetch-mode",
+ "sec-fetch-dest",
+ "sec-fetch-user",
+ "sec-fetch-site",
+ ];
+
+ secFetchHeaders.forEach(header => {
+ const expectedValue = gExpectedHeader[header];
+ try {
+ is(
+ channel.getRequestHeader(header),
+ expectedValue,
+ `${header} is set to ${expectedValue}`
+ );
+ } catch (e) {
+ if (expectedValue) {
+ ok(false, "required headers are set");
+ } else {
+ ok(true, `${header} should not be set`);
+ }
+ }
+ });
+
+ gTestCounter++;
+}
+
+async function testNavigations() {
+ gTestCounter = 0;
+
+ // Load initial site
+ let loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ BrowserTestUtils.loadURIString(gBrowser, REQUEST_URL + "?test1");
+ await loaded;
+
+ // Load another site
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.document.notifyUserGestureActivation(); // simulate user activation
+ let test2Button = content.document.getElementById("test2_button");
+ test2Button.click();
+ content.document.clearUserGestureActivation();
+ });
+ await loaded;
+ // Load another site
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.document.notifyUserGestureActivation(); // simulate user activation
+ let test3Button = content.document.getElementById("test3_button");
+ test3Button.click();
+ content.document.clearUserGestureActivation();
+ });
+ await loaded;
+
+ gExpectedHeader = {
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "document",
+ "sec-fetch-site": "same-origin",
+ "sec-fetch-user": "?1",
+ };
+
+ // Register the http request observer.
+ // All following actions should cause requests with the sec-fetch-user header
+ // set.
+ Services.obs.addObserver(checkSecFetchUser, "http-on-stop-request");
+
+ // Go back one site by clicking the back button
+ info("Clicking back button");
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ document.notifyUserGestureActivation(); // simulate user activation
+ let backButton = document.getElementById("back-button");
+ backButton.click();
+ document.clearUserGestureActivation();
+ await loaded;
+
+ // Reload the site by clicking the reload button
+ info("Clicking reload button");
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ document.notifyUserGestureActivation(); // simulate user activation
+ let reloadButton = document.getElementById("reload-button");
+ await TestUtils.waitForCondition(() => {
+ return !reloadButton.disabled;
+ });
+ reloadButton.click();
+ document.clearUserGestureActivation();
+ await loaded;
+
+ // Go forward one site by clicking the forward button
+ info("Clicking forward button");
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ document.notifyUserGestureActivation(); // simulate user activation
+ let forwardButton = document.getElementById("forward-button");
+ forwardButton.click();
+ document.clearUserGestureActivation();
+ await loaded;
+
+ // Testing history.back/forward...
+
+ info("going back with history.back");
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.document.notifyUserGestureActivation(); // simulate user activation
+ content.history.back();
+ content.document.clearUserGestureActivation();
+ });
+ await loaded;
+
+ info("going forward with history.forward");
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.document.notifyUserGestureActivation(); // simulate user activation
+ content.history.forward();
+ content.document.clearUserGestureActivation();
+ });
+ await loaded;
+
+ gExpectedHeader = {
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "document",
+ "sec-fetch-site": "same-origin",
+ };
+
+ info("going back with history.back without user activation");
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.history.back();
+ });
+ await loaded;
+
+ info("going forward with history.forward without user activation");
+ loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ content.history.forward();
+ });
+ await loaded;
+
+ ok(gTestCounter === 7, "testing that all five actions have been tested.");
+
+ Services.obs.removeObserver(checkSecFetchUser, "http-on-stop-request");
+}
+
+add_task(async function () {
+ waitForExplicitFinish();
+
+ await testNavigations();
+
+ // If fission is enabled we also want to test the navigations with the bfcache
+ // in the parent.
+ if (SpecialPowers.getBoolPref("fission.autostart")) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["fission.bfcacheInParent", true]],
+ });
+
+ await testNavigations();
+ }
+
+ finish();
+});
diff --git a/dom/security/test/sec-fetch/file_dummy_link.html b/dom/security/test/sec-fetch/file_dummy_link.html
new file mode 100644
index 0000000000..2150054226
--- /dev/null
+++ b/dom/security/test/sec-fetch/file_dummy_link.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1738694 - Sec-Fetch-User header is missing when opening a link in a new window</title>
+</head>
+<body>
+ <a id="dummylink" href="file_dummy_link_location.html">Open</a>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/file_dummy_link_location.html b/dom/security/test/sec-fetch/file_dummy_link_location.html
new file mode 100644
index 0000000000..9f9400e1c3
--- /dev/null
+++ b/dom/security/test/sec-fetch/file_dummy_link_location.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1738694 - Sec-Fetch-User header is missing when opening a link in a new window</title>
+</head>
+<body>
+ <h1>file_dummy_link_location.html</h1>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/file_no_cache.sjs b/dom/security/test/sec-fetch/file_no_cache.sjs
new file mode 100644
index 0000000000..9e75209e44
--- /dev/null
+++ b/dom/security/test/sec-fetch/file_no_cache.sjs
@@ -0,0 +1,28 @@
+const MESSAGE_PAGE = function (msg) {
+ return `
+<html>
+<script type="text/javascript">
+window.parent.postMessage({test : "${msg}"},"*");
+</script>
+<script>
+ addEventListener("back", () => {
+ history.back();
+ });
+ addEventListener("forward", () => {
+ history.forward();
+ });
+</script>
+<body>
+ <a id="test2_button" href="https://example.com/browser/dom/security/test/sec-fetch/file_no_cache.sjs?test2">Click me</a>
+ <a id="test3_button" href="https://example.com/browser/dom/security/test/sec-fetch/file_no_cache.sjs?test3">Click me</a>
+<body>
+</html>
+`;
+};
+
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-store");
+ response.setHeader("Content-Type", "text/html");
+
+ response.write(MESSAGE_PAGE(request.queryString));
+}
diff --git a/dom/security/test/sec-fetch/file_redirect.sjs b/dom/security/test/sec-fetch/file_redirect.sjs
new file mode 100644
index 0000000000..84c2c39913
--- /dev/null
+++ b/dom/security/test/sec-fetch/file_redirect.sjs
@@ -0,0 +1,34 @@
+const SITE_META_REDIRECT = `
+<html>
+ <head>
+ <meta http-equiv="refresh" content="0; url='https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs?redirect302'">
+ </head>
+ <body>
+ META REDIRECT
+ </body>
+</html>
+`;
+
+const REDIRECT_302 =
+ "https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs?pageC";
+
+function handleRequest(req, res) {
+ // avoid confusing cache behaviour
+ res.setHeader("Cache-Control", "no-cache", false);
+ res.setHeader("Content-Type", "text/html", false);
+
+ switch (req.queryString) {
+ case "meta":
+ res.write(SITE_META_REDIRECT);
+ return;
+ case "redirect302":
+ res.setStatusLine("1.1", 302, "Found");
+ res.setHeader("Location", REDIRECT_302, false);
+ return;
+ case "pageC":
+ res.write("<html><body>PAGE C</body></html>");
+ return;
+ }
+
+ res.write(`<html><body>THIS SHOULD NEVER BE DISPLAYED</body></html>`);
+}
diff --git a/dom/security/test/sec-fetch/file_trustworthy_loopback.html b/dom/security/test/sec-fetch/file_trustworthy_loopback.html
new file mode 100644
index 0000000000..88f9242650
--- /dev/null
+++ b/dom/security/test/sec-fetch/file_trustworthy_loopback.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1732069: Sec-Fetch-Site inconsistent on localhost/IPs</title>
+</head>
+<body>
+ <iframe src="http://localhost:9898/foo"></iframe>
+ <iframe src="http://localhost:9899/foo"></iframe>
+ <iframe src="http://sub.localhost/foo"></iframe>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/file_websocket_wsh.py b/dom/security/test/sec-fetch/file_websocket_wsh.py
new file mode 100644
index 0000000000..b7159c742b
--- /dev/null
+++ b/dom/security/test/sec-fetch/file_websocket_wsh.py
@@ -0,0 +1,6 @@
+def web_socket_do_extra_handshake(request):
+ pass
+
+
+def web_socket_transfer_data(request):
+ pass
diff --git a/dom/security/test/sec-fetch/mochitest.ini b/dom/security/test/sec-fetch/mochitest.ini
new file mode 100644
index 0000000000..4c27cdb2fe
--- /dev/null
+++ b/dom/security/test/sec-fetch/mochitest.ini
@@ -0,0 +1,19 @@
+[DEFAULT]
+support-files =
+ file_no_cache.sjs
+ file_redirect.sjs
+
+[test_iframe_history_manipulation.html]
+[test_iframe_src_metaRedirect.html]
+[test_iframe_srcdoc_metaRedirect.html]
+[test_iframe_window_open_metaRedirect.html]
+[test_trustworthy_loopback.html]
+skip-if =
+ os == "linux" && !fission # Bug 1805760
+ http3
+support-files = file_trustworthy_loopback.html
+[test_websocket.html]
+skip-if =
+ toolkit == 'android' # no websocket support Bug 982828
+ http3
+support-files = file_websocket_wsh.py
diff --git a/dom/security/test/sec-fetch/test_iframe_history_manipulation.html b/dom/security/test/sec-fetch/test_iframe_history_manipulation.html
new file mode 100644
index 0000000000..5ec749bf4d
--- /dev/null
+++ b/dom/security/test/sec-fetch/test_iframe_history_manipulation.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1648825 - Fetch Metadata Headers contain invalid value for Sec-Fetch-Site for history manipulation</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+
+<script class="testbody" type="text/javascript">
+
+const REQUEST_PATH = 'tests/dom/security/test/sec-fetch/file_no_cache.sjs'
+let sendHome = true;
+let testCounter = 0;
+let testFrame;
+
+var script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ Services.obs.addObserver(function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ info("request observed: " + channel.URI.spec);
+ if (!channel.URI.spec.startsWith("https://example.org")) {
+ return;
+ }
+ let headerPresent = false;
+ try {
+ is(channel.getRequestHeader("Sec-Fetch-Site"), "cross-site", "testing sec-fetch-site is cross-site");
+
+ // This should fail and cause the catch clause to be executed.
+ channel.getRequestHeader("Sec-Fetch-User");
+ headerPresent = true;
+ } catch (e) {
+ headerPresent = false;
+ }
+
+ ok(!headerPresent, "testing sec-fetch-user header is not set");
+
+ sendAsyncMessage("test-pass");
+ }, "http-on-stop-request");
+});
+
+script.addMessageListener("test-pass", () => {
+ testCounter++;
+ if(testCounter == 2) {
+ SimpleTest.finish();
+ }
+});
+
+window.addEventListener("message", function (event) {
+ iframeAction(event.data.test);
+});
+
+function iframeAction(test) {
+ info("received message " + test);
+
+ switch (test) {
+ case 'test':
+ testFrame.contentWindow.location = `https://example.org/${REQUEST_PATH}?test#bypass`;
+ if(sendHome) {
+ // We need to send the message manually here because there is no request send to the server.
+ window.postMessage({test: "home"}, "*");
+ sendHome = false;
+ }
+
+ break;
+ case 'home':
+ testFrame.contentWindow.location = `/${REQUEST_PATH}?back`;
+ break;
+ case 'back':
+ testFrame.contentWindow.history.back();
+ break;
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+testFrame = document.createElement('iframe');
+testFrame.src = `https://example.org/${REQUEST_PATH}?test`;
+document.body.appendChild(testFrame);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html b/dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html
new file mode 100644
index 0000000000..28eae80226
--- /dev/null
+++ b/dom/security/test/sec-fetch/test_iframe_src_metaRedirect.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1647128 - Fetch Metadata Headers contain invalid value for Sec-Fetch-Site for meta redirects</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * We load site A that redirects to site B using a meta refresh,
+ * finally site B redirects to site C via a 302 redirect.
+ * The first load of site A is made by an iframe: frame.src = "...".
+ * We check that all requests have the Sec-Fetch-* headers set appropriately.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+const REQUEST_URL = "https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs";
+let testPassCounter = 0;
+
+var script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ Services.obs.addObserver(function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs")) {
+ return;
+ }
+
+ // The redirection flow is the following:
+ // http://mochi.test:8888 -> https://example.com?meta -> https://example.com?redirect302 -> https://example.com?pageC
+ // So the Sec-Fetch-* headers for each request should be:
+ const expectedHeaders = {
+ "?meta": {
+ "Sec-Fetch-Site": "cross-site",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "iframe",
+ "Sec-Fetch-User": null,
+ },
+ "?redirect302": {
+ "Sec-Fetch-Site": "same-origin",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "iframe",
+ "Sec-Fetch-User": null,
+ },
+ "?pageC": {
+ "Sec-Fetch-Site": "same-origin",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "iframe",
+ "Sec-Fetch-User": null,
+ },
+ };
+
+ let matchedOne = false;
+ for (const [query, headers] of Object.entries(expectedHeaders)) {
+ if (!channel.URI.spec.endsWith(query)) {
+ continue;
+ }
+ matchedOne = true;
+
+ for (const [header, value] of Object.entries(headers)) {
+ try {
+ is(channel.getRequestHeader(header), value, `testing ${header} for the ${query} query`);
+ } catch (e) {
+ is(header, "Sec-Fetch-User", "testing Sec-Fetch-User");
+ }
+ }
+ }
+ ok(matchedOne, "testing expectedHeaders");
+
+ sendAsyncMessage("test-pass");
+ }, "http-on-stop-request");
+});
+
+script.addMessageListener("test-pass", async () => {
+ testPassCounter++;
+ if (testPassCounter < 3) {
+ return;
+ }
+
+ // If we received "test-pass" 3 times we know that all loads had Sec-Fetch-* headers set appropriately.
+ SimpleTest.finish();
+});
+
+let frame = document.createElement("iframe");
+frame.src = REQUEST_URL + "?meta";
+document.body.appendChild(frame);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html b/dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html
new file mode 100644
index 0000000000..adee5afe84
--- /dev/null
+++ b/dom/security/test/sec-fetch/test_iframe_srcdoc_metaRedirect.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1647128 - Fetch Metadata Headers contain invalid value for Sec-Fetch-Site for meta redirects</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * We load site A that redirects to site B using a meta refresh,
+ * finally site B redirects to site C via a 302 redirect.
+ * The first load of site A is made by an iframe: frame.srcdoc = "<meta ...".
+ * We check that all requests have the Sec-Fetch-* headers set appropriately.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+const REQUEST_URL = "https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs";
+let testPassCounter = 0;
+
+var script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ Services.obs.addObserver(function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs")) {
+ return;
+ }
+
+ // The redirection flow is the following:
+ // http://mochi.test:8888 -> https://example.com?meta -> https://example.com?redirect302 -> https://example.com?pageC
+ // So the Sec-Fetch-* headers for each request should be:
+ const expectedHeaders = {
+ "?meta": {
+ "Sec-Fetch-Site": "cross-site",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "iframe",
+ "Sec-Fetch-User": null,
+ },
+ "?redirect302": {
+ "Sec-Fetch-Site": "same-origin",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "iframe",
+ "Sec-Fetch-User": null,
+ },
+ "?pageC": {
+ "Sec-Fetch-Site": "same-origin",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "iframe",
+ "Sec-Fetch-User": null,
+ },
+ };
+
+ let matchedOne = false;
+ for (const [query, headers] of Object.entries(expectedHeaders)) {
+ if (!channel.URI.spec.endsWith(query)) {
+ continue;
+ }
+ matchedOne = true;
+
+ for (const [header, value] of Object.entries(headers)) {
+ try {
+ is(channel.getRequestHeader(header), value, `testing ${header} for the ${query} query`);
+ } catch (e) {
+ is(header, "Sec-Fetch-User", "testing Sec-Fetch-User");
+ }
+ }
+ }
+ ok(matchedOne, "testing expectedHeaders");
+
+ sendAsyncMessage("test-pass");
+ }, "http-on-stop-request");
+});
+
+script.addMessageListener("test-pass", async () => {
+ testPassCounter++;
+ if (testPassCounter < 3) {
+ return;
+ }
+
+ // If we received "test-pass" 3 times we know that all loads had Sec-Fetch-* headers set appropriately.
+ SimpleTest.finish();
+});
+
+let frame = document.createElement("iframe");
+frame.srcdoc = `<meta http-equiv="refresh" content="0; url='${REQUEST_URL}?meta'">`;
+document.body.appendChild(frame);
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html b/dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html
new file mode 100644
index 0000000000..b532baeb5e
--- /dev/null
+++ b/dom/security/test/sec-fetch/test_iframe_window_open_metaRedirect.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1647128 - Fetch Metadata Headers contain invalid value for Sec-Fetch-Site for meta redirects</title>
+ <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+
+<script class="testbody" type="text/javascript">
+/*
+ * Description of the test:
+ * We load site A that redirects to site B using a meta refresh,
+ * finally site B redirects to site C via a 302 redirect.
+ * The first load of site A is made through window.open.
+ * We check that all requests have the Sec-Fetch-* headers set appropriately.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+const REQUEST_URL = "https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs";
+let testPassCounter = 0;
+let testWindow;
+
+var script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ Services.obs.addObserver(function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_redirect.sjs")) {
+ return;
+ }
+
+ // The redirection flow is the following:
+ // http://mochi.test:8888 -> https://example.com?meta -> https://example.com?redirect302 -> https://example.com?pageC
+ // So the Sec-Fetch-* headers for each request should be:
+ const expectedHeaders = {
+ "?meta": {
+ "Sec-Fetch-Site": "cross-site",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "document",
+ "Sec-Fetch-User": null,
+ },
+ "?redirect302": {
+ "Sec-Fetch-Site": "same-origin",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "document",
+ "Sec-Fetch-User": null,
+ },
+ "?pageC": {
+ "Sec-Fetch-Site": "same-origin",
+ "Sec-Fetch-Mode": "navigate",
+ "Sec-Fetch-Dest": "document",
+ "Sec-Fetch-User": null,
+ },
+ };
+
+ let matchedOne = false;
+ for (const [query, headers] of Object.entries(expectedHeaders)) {
+ if (!channel.URI.spec.endsWith(query)) {
+ continue;
+ }
+ matchedOne = true;
+
+ for (const [header, value] of Object.entries(headers)) {
+ try {
+ is(channel.getRequestHeader(header), value, `testing ${header} for the ${query} query`);
+ } catch (e) {
+ is(header, "Sec-Fetch-User", "testing Sec-Fetch-User");
+ }
+ }
+ }
+ ok(matchedOne, "testing expectedHeaders");
+
+ sendAsyncMessage("test-pass");
+ }, "http-on-stop-request");
+});
+
+script.addMessageListener("test-pass", async () => {
+ testPassCounter++;
+ if (testPassCounter < 3) {
+ return;
+ }
+
+ if (testWindow) {
+ testWindow.close();
+ }
+
+ // If we received "test-pass" 3 times we know that all loads had Sec-Fetch-* headers set appropriately.
+ SimpleTest.finish();
+});
+
+testWindow = window.open(REQUEST_URL + "?meta");
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/test_trustworthy_loopback.html b/dom/security/test/sec-fetch/test_trustworthy_loopback.html
new file mode 100644
index 0000000000..95ecac17ed
--- /dev/null
+++ b/dom/security/test/sec-fetch/test_trustworthy_loopback.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1732069: Sec-Fetch-Site inconsistent on localhost/IPs</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+let testsSucceeded = 0;
+
+let win;
+function checkTestsDone() {
+ testsSucceeded++;
+ if (testsSucceeded == 3) {
+ win.close();
+ SimpleTest.finish();
+ }
+}
+
+var script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ Services.obs.addObserver(function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.includes("localhost") ||
+ channel.URI.spec.startsWith("http://localhost:9898/tests/dom/security/test/sec-fetch/file_trustworthy_loopback.html")) {
+ return;
+ }
+
+ const expectedHeaders = {
+ "localhost:9898": {
+ "sec-fetch-site": "same-origin",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "iframe",
+ },
+ "sub.localhost:-1": {
+ "sec-fetch-site": "cross-site",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "iframe",
+ },
+ "localhost:9899": {
+ "sec-fetch-site": "same-site",
+ "sec-fetch-mode": "navigate",
+ "sec-fetch-dest": "iframe",
+ },
+ };
+
+ info(`checking headers for request to ${channel.URI.spec}`);
+ const expected = expectedHeaders[channel.URI.host + ":" + channel.URI.port];
+ for (let key in expected) {
+ try {
+ is(channel.getRequestHeader(key), expected[key], `${key} header matches`);
+ } catch (e) {
+ ok(false, "failed to check headers");
+ }
+ }
+ sendAsyncMessage("test-end");
+ }, "http-on-stop-request");
+});
+
+script.addMessageListener("test-end", () => {
+ checkTestsDone();
+});
+
+SpecialPowers.pushPrefEnv({set: [
+ ["network.proxy.allow_hijacking_localhost", true],
+ ["network.proxy.testing_localhost_is_secure_when_hijacked", true],
+]}).then(function() {
+ win = window.open("http://localhost:9898/tests/dom/security/test/sec-fetch/file_trustworthy_loopback.html");
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/security/test/sec-fetch/test_websocket.html b/dom/security/test/sec-fetch/test_websocket.html
new file mode 100644
index 0000000000..33053ff20a
--- /dev/null
+++ b/dom/security/test/sec-fetch/test_websocket.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1628605: Test Sec-Fetch-* header for websockets</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+let testsSucceeded = 0;
+
+function checkTestsDone() {
+ testsSucceeded++;
+ if (testsSucceeded == 2) {
+ SimpleTest.finish();
+ }
+}
+
+var script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ Services.obs.addObserver(function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ if (!channel.URI.spec.startsWith("https://example.com/tests/dom/security/test/sec-fetch/file_websocket")) {
+ return;
+ }
+
+ // Sec-Fetch-* Headers should be present for Dest, Mode, Site
+ try {
+ let secFetchDest = channel.getRequestHeader("Sec-Fetch-Dest");
+ is(secFetchDest, "websocket", "testing sec-fetch-dest");
+
+ let secFetchMode = channel.getRequestHeader("Sec-Fetch-Mode");
+ is(secFetchMode, "websocket", "testing sec-fetch-mode");
+
+ let secFetchSite = channel.getRequestHeader("Sec-Fetch-Site");
+ is(secFetchSite, "cross-site", "testing sec-fetch-site");
+ }
+ catch (e) {
+ ok(false, "testing sec-fetch-*");
+ }
+
+ // Sec-Fetch-User should not be present
+ try {
+ channel.getRequestHeader("Sec-Fetch-User");
+ ok(false, "testing sec-fetch-user");
+ }
+ catch (e) {
+ ok(true, "testing sec-fetch-user");
+ }
+ Services.obs.removeObserver(onExamResp, "http-on-stop-request");
+
+ sendAsyncMessage("test-end");
+ }, "http-on-stop-request");
+});
+
+script.addMessageListener("test-end", () => {
+ checkTestsDone();
+});
+
+var wssSocket = new WebSocket("wss://example.com/tests/dom/security/test/sec-fetch/file_websocket");
+wssSocket.onopen = function(e) {
+ ok(true, "sanity: wssSocket onopen");
+ checkTestsDone();
+};
+wssSocket.onerror = function(e) {
+ ok(false, "sanity: wssSocket onerror");
+};
+
+</script>
+</body>
+</html>