From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../test/xpcshell/test_ext_same_site_redirects.js | 239 +++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js') 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..a3000e4e1f --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_same_site_redirects.js @@ -0,0 +1,239 @@ +"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(``); +}); + +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 }) { + // embedder.html loads http:-frame. + allow_unsafe_parent_loads_when_extensions_not_remote(); + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions, + }, + files: { + "embedder.html": ``, + }, + }); + 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(); + + revert_allow_unsafe_parent_loads_when_extensions_not_remote(); + + 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" + ); +}); -- cgit v1.2.3