"use strict"; const server = createHttpServer({ hosts: ["example.org", "example.net", "example.com"], }); function promiseSetCookies() { return new Promise(resolve => { server.registerPathHandler("/setCookies", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.setHeader("Set-Cookie", "none=a; sameSite=none", true); response.setHeader("Set-Cookie", "lax=b; sameSite=lax", true); response.setHeader("Set-Cookie", "strict=c; sameSite=strict", true); response.write(""); resolve(); }); }); } function promiseLoadedCookies() { return new Promise(resolve => { let cookies; server.registerPathHandler("/checkCookies", (request, response) => { cookies = request.hasHeader("Cookie") ? request.getHeader("Cookie") : ""; response.setStatusLine(request.httpVersion, 302, "Moved Permanently"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.setHeader("Location", "/ready"); }); server.registerPathHandler("/navigate", (request, response) => { cookies = request.hasHeader("Cookie") ? request.getHeader("Cookie") : ""; response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write( "" ); }); server.registerPathHandler("/fetch", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); // /checkCookies ultimately redirects to /ready, which resolves the // promise returned by promiseLoadedCookies(). At that point, the test // can choose to close the ContentPage that hosts us, and abort the // pending fetch(). When extensions.webextensions.remote is false, the // PromiseTestUtils.assertNoUncaughtRejections() check may detect this // and cause the test to fail unexpectedly. To avoid this issue, we catch // the error unconditionally. response.write(``); }); server.registerPathHandler("/nestedfetch", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write( "" ); }); server.registerPathHandler("/nestedfetch2", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write( "" ); }); server.registerPathHandler("/ready", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write(""); resolve(cookies); }); }); } add_task(async function setup() { Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); Services.prefs.setBoolPref("network.cookie.sameSite.laxByDefault", true); // We don't want to have 'secure' cookies because our test http server doesn't run in https. Services.prefs.setBoolPref( "network.cookie.sameSite.noneRequiresSecure", false ); // Let's set 3 cookies before loading the extension. let cookiesPromise = promiseSetCookies(); let contentPage = await ExtensionTestUtils.loadContentPage( "http://example.org/setCookies" ); await cookiesPromise; await contentPage.close(); Assert.equal(Services.cookies.cookies.length, 3); }); add_task(async function test_cookies_firstParty() { // Loads a http:-URL in moz-extension://[uuid]/page.html allow_unsafe_parent_loads_when_extensions_not_remote(); async function pageScript() { const ifr = document.createElement("iframe"); ifr.src = "http://example.org/" + location.search.slice(1); document.body.appendChild(ifr); } let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["*://example.org/"], }, files: { "page.html": ``, "page.js": pageScript, }, }); await extension.startup(); // This page will load example.org in an iframe. let url = `moz-extension://${extension.uuid}/page.html`; let cookiesPromise = promiseLoadedCookies(); let contentPage = await ExtensionTestUtils.loadContentPage( url + "?checkCookies", { extension } ); // Let's check the cookies received during the last loading. Assert.equal(await cookiesPromise, "none=a; lax=b; strict=c"); await contentPage.close(); // Let's navigate. cookiesPromise = promiseLoadedCookies(); contentPage = await ExtensionTestUtils.loadContentPage(url + "?navigate", { extension, }); // Let's check the cookies received during the last loading. Assert.equal(await cookiesPromise, "none=a; lax=b; strict=c"); await contentPage.close(); // Let's run a fetch() cookiesPromise = promiseLoadedCookies(); contentPage = await ExtensionTestUtils.loadContentPage(url + "?fetch", { extension, }); // Let's check the cookies received during the last loading. Assert.equal(await cookiesPromise, "none=a; lax=b; strict=c"); await contentPage.close(); // Let's run a fetch() from a nested iframe (extension -> example.net -> // example.org -> fetch) cookiesPromise = promiseLoadedCookies(); contentPage = await ExtensionTestUtils.loadContentPage(url + "?nestedfetch", { extension, }); // Let's check the cookies received during the last loading. Assert.equal(await cookiesPromise, "none=a"); await contentPage.close(); // Let's run a fetch() from a nested iframe (extension -> example.org -> fetch) cookiesPromise = promiseLoadedCookies(); contentPage = await ExtensionTestUtils.loadContentPage( url + "?nestedfetch2", { extension, } ); // Let's check the cookies received during the last loading. Assert.equal(await cookiesPromise, "none=a; lax=b; strict=c"); await contentPage.close(); await extension.unload(); revert_allow_unsafe_parent_loads_when_extensions_not_remote(); }); add_task(async function test_cookies_iframes() { server.registerPathHandler("/echocookies", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write( request.hasHeader("Cookie") ? request.getHeader("Cookie") : "" ); }); server.registerPathHandler("/contentScriptHere", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write(""); }); server.registerPathHandler("/pageWithFrames", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write(` `); }); let extension = ExtensionTestUtils.loadExtension({ manifest: { permissions: ["*://example.org/"], content_scripts: [ { js: ["contentScript.js"], matches: [ "*://example.com/contentScriptHere", "*://example.net/contentScriptHere", ], run_at: "document_end", all_frames: true, }, ], }, files: { "contentScript.js": async () => { const res = await fetch("http://example.org/echocookies"); const cookies = await res.text(); browser.test.assertEq( "none=a", cookies, "expected cookies in content script" ); browser.test.sendMessage("extfetch:" + location.hostname); }, }, }); await extension.startup(); let contentPage = await ExtensionTestUtils.loadContentPage( "http://example.com/pageWithFrames" ); await Promise.all([ extension.awaitMessage("extfetch:example.com"), extension.awaitMessage("extfetch:example.net"), ]); await contentPage.close(); await extension.unload(); }); add_task(async function test_cookies_background() { async function background() { const res = await fetch("http://example.org/echocookies", { credentials: "include", }); const cookies = await res.text(); browser.test.sendMessage("fetchcookies", cookies); } const tests = [ { permissions: ["http://example.org/*"], cookies: "none=a; lax=b; strict=c", }, { permissions: [], cookies: "none=a", }, ]; for (let test of tests) { let extension = ExtensionTestUtils.loadExtension({ background, manifest: { permissions: test.permissions, }, }); server.registerPathHandler("/echocookies", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.setHeader( "Access-Control-Allow-Origin", `moz-extension://${extension.uuid}`, false ); response.setHeader("Access-Control-Allow-Credentials", "true", false); response.write( request.hasHeader("Cookie") ? request.getHeader("Cookie") : "" ); }); await extension.startup(); equal( await extension.awaitMessage("fetchcookies"), test.cookies, "extension with permissions can see SameSite-restricted cookies" ); await extension.unload(); } }); add_task(async function test_cookies_contentScript() { server.registerPathHandler("/empty", (request, response) => { response.setStatusLine(request.httpVersion, 200, "OK"); response.setHeader("Content-Type", "text/html; charset=utf-8", false); response.write(""); }); async function contentScript() { let res = await fetch("http://example.org/checkCookies"); browser.test.assertEq(location.origin + "/ready", res.url, "request OK"); browser.test.sendMessage("fetch-done"); } let extension = ExtensionTestUtils.loadExtension({ manifest: { content_scripts: [ { run_at: "document_end", js: ["contentscript.js"], matches: ["*://*/*"], }, ], }, files: { "contentscript.js": contentScript, }, }); await extension.startup(); let cookiesPromise = promiseLoadedCookies(); let contentPage = await ExtensionTestUtils.loadContentPage( "http://example.org/empty" ); await extension.awaitMessage("fetch-done"); // Let's check the cookies received during the last loading. Assert.equal(await cookiesPromise, "none=a; lax=b; strict=c"); await contentPage.close(); await extension.unload(); });