summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js')
-rw-r--r--toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js233
1 files changed, 233 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js b/toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js
new file mode 100644
index 0000000000..df77f8b0dd
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js
@@ -0,0 +1,233 @@
+"use strict";
+
+/**
+ * This test tests various redirection scenarios, and checks whether sameSite
+ * cookies are sent.
+ *
+ * The file has the following tests:
+ * - verify_firstparty_web_behavior - base case, confirms normal web behavior.
+ * - samesite_is_foreign_without_host_permissions
+ * - wildcard_host_permissions_enable_samesite_cookies
+ * - explicit_host_permissions_enable_samesite_cookies
+ * - some_host_permissions_enable_some_samesite_cookies
+ */
+
+// This simulates a common pattern used for sites that require authentication.
+// After logging in, there may be multiple redirects, HTTP and scripted.
+const SITE_START = "start.example.net";
+// set "start" cookies + 302 redirects to found.
+const SITE_FOUND = "found.example.net";
+// set "found" cookies + uses a HTML redirect to redir.
+const SITE_REDIR = "redir.example.net";
+// set "redir" cookies + 302 redirects to final.
+const SITE_FINAL = "final.example.net";
+
+const SITE = "example.net";
+
+const URL_START = `http://${SITE_START}/start`;
+
+const server = createHttpServer({
+ hosts: [SITE_START, SITE_FOUND, SITE_REDIR, SITE_FINAL],
+});
+
+function getCookies(request) {
+ return request.hasHeader("Cookie") ? request.getHeader("Cookie") : "";
+}
+
+function sendCookies(response, prefix, suffix = "") {
+ const cookies = [
+ prefix + "-none=1; sameSite=none; domain=" + SITE + suffix,
+ prefix + "-lax=1; sameSite=lax; domain=" + SITE + suffix,
+ prefix + "-strict=1; sameSite=strict; domain=" + SITE + suffix,
+ ];
+ for (let cookie of cookies) {
+ response.setHeader("Set-Cookie", cookie, true);
+ }
+}
+
+function deleteCookies(response, prefix) {
+ sendCookies(response, prefix, "; expires=Thu, 01 Jan 1970 00:00:00 GMT");
+}
+
+var receivedCookies = [];
+
+server.registerPathHandler("/start", (request, response) => {
+ Assert.equal(request.host, SITE_START);
+ Assert.equal(getCookies(request), "", "No cookies at start of test");
+
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ sendCookies(response, "start");
+ response.setHeader("Location", `http://${SITE_FOUND}/found`);
+});
+
+server.registerPathHandler("/found", (request, response) => {
+ Assert.equal(request.host, SITE_FOUND);
+ receivedCookies.push(getCookies(request));
+
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ deleteCookies(response, "start");
+ sendCookies(response, "found");
+ response.write(`<script>location = "http://${SITE_REDIR}/redir";</script>`);
+});
+
+server.registerPathHandler("/redir", (request, response) => {
+ Assert.equal(request.host, SITE_REDIR);
+ receivedCookies.push(getCookies(request));
+
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ deleteCookies(response, "found");
+ sendCookies(response, "redir");
+ response.setHeader("Location", `http://${SITE_FINAL}/final`);
+});
+
+server.registerPathHandler("/final", (request, response) => {
+ Assert.equal(request.host, SITE_FINAL);
+ receivedCookies.push(getCookies(request));
+
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ deleteCookies(response, "redir");
+ // In test some_host_permissions_enable_some_samesite_cookies, the cookies
+ // from the start haven't been cleared due to the lack of host permissions.
+ // Do that here instead.
+ deleteCookies(response, "start");
+ response.setHeader("Location", "/final_and_clean");
+});
+
+// Should be called before any request is made.
+function promiseFinalResponse() {
+ Assert.deepEqual(receivedCookies, [], "Test starts without observed cookies");
+ return new Promise(resolve => {
+ server.registerPathHandler("/final_and_clean", (request, response) => {
+ Assert.equal(request.host, SITE_FINAL);
+ Assert.equal(getCookies(request), "", "Cookies cleaned up");
+ resolve(receivedCookies.splice(0));
+ });
+ });
+}
+
+// Load the page as a child frame of an extension, for the given permissions.
+async function getCookiesForLoadInExtension({ permissions }) {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ permissions,
+ },
+ files: {
+ "embedder.html": `<iframe src="${URL_START}"></iframe>`,
+ },
+ });
+ await extension.startup();
+ let cookiesPromise = promiseFinalResponse();
+ let contentPage = await ExtensionTestUtils.loadContentPage(
+ `moz-extension://${extension.uuid}/embedder.html`,
+ { extension }
+ );
+ let cookies = await cookiesPromise;
+ await contentPage.close();
+ await extension.unload();
+ return cookies;
+}
+
+add_task(async function setup() {
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref("network.cookie.sameSite.laxByDefault", true);
+
+ // Test server runs on http, so disable Secure requirement of sameSite=none.
+ Services.prefs.setBoolPref(
+ "network.cookie.sameSite.noneRequiresSecure",
+ false
+ );
+});
+
+// First verify that our expectations match with the actual behavior on the web.
+add_task(async function verify_firstparty_web_behavior() {
+ let cookiesPromise = promiseFinalResponse();
+ let contentPage = await ExtensionTestUtils.loadContentPage(URL_START);
+ let cookies = await cookiesPromise;
+ await contentPage.close();
+ Assert.deepEqual(
+ cookies,
+ // Same expectations as in host_permissions_enable_samesite_cookies
+ [
+ "start-none=1; start-lax=1; start-strict=1",
+ "found-none=1; found-lax=1; found-strict=1",
+ "redir-none=1; redir-lax=1; redir-strict=1",
+ ],
+ "Expected cookies from a first-party load on the web"
+ );
+});
+
+// Verify that an extension without permission behaves like a third-party page.
+add_task(async function samesite_is_foreign_without_host_permissions() {
+ let cookies = await getCookiesForLoadInExtension({
+ permissions: [],
+ });
+
+ Assert.deepEqual(
+ cookies,
+ ["start-none=1", "found-none=1", "redir-none=1"],
+ "SameSite cookies excluded without permissions"
+ );
+});
+
+// When an extension has permissions for the site, cookies should be included.
+add_task(async function wildcard_host_permissions_enable_samesite_cookies() {
+ let cookies = await getCookiesForLoadInExtension({
+ permissions: ["*://*.example.net/*"], // = *.SITE
+ });
+
+ Assert.deepEqual(
+ cookies,
+ // Same expectations as in verify_firstparty_web_behavior.
+ [
+ "start-none=1; start-lax=1; start-strict=1",
+ "found-none=1; found-lax=1; found-strict=1",
+ "redir-none=1; redir-lax=1; redir-strict=1",
+ ],
+ "Expected cookies from a load in an extension frame"
+ );
+});
+
+// When an extension has permissions for the site, cookies should be included.
+add_task(async function explicit_host_permissions_enable_samesite_cookies() {
+ let cookies = await getCookiesForLoadInExtension({
+ permissions: [
+ "*://start.example.net/*",
+ "*://found.example.net/*",
+ "*://redir.example.net/*",
+ "*://final.example.net/*",
+ ],
+ });
+
+ Assert.deepEqual(
+ cookies,
+ // Same expectations as in verify_firstparty_web_behavior.
+ [
+ "start-none=1; start-lax=1; start-strict=1",
+ "found-none=1; found-lax=1; found-strict=1",
+ "redir-none=1; redir-lax=1; redir-strict=1",
+ ],
+ "Expected cookies from a load in an extension frame"
+ );
+});
+
+// When an extension does not have host permissions for all sites, but only
+// some, then same-site cookies are only included in requests with the right
+// permissions.
+add_task(async function some_host_permissions_enable_some_samesite_cookies() {
+ let cookies = await getCookiesForLoadInExtension({
+ permissions: ["*://start.example.net/*", "*://final.example.net/*"],
+ });
+
+ Assert.deepEqual(
+ cookies,
+ [
+ // Missing permission for "found.example.net":
+ "start-none=1",
+ // Missing permission for "redir.example.net":
+ "found-none=1",
+ // "final.example.net" can see cookies from "start.example.net":
+ "start-lax=1; start-strict=1; redir-none=1",
+ ],
+ "Expected some cookies from a load in an extension frame"
+ );
+});