diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_cookies_firstParty.js | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_cookies_firstParty.js b/toolkit/components/extensions/test/xpcshell/test_ext_cookies_firstParty.js new file mode 100644 index 0000000000..77bd94d9c4 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_cookies_firstParty.js @@ -0,0 +1,348 @@ +"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("<html></html>"); + 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( + "<html><script>location = '/checkCookies';</script></html>" + ); + }); + + 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(`<html><script> + fetch("/checkCookies").catch(e => console.log("fetch() error: " + e)); + </script></html>`); + }); + + server.registerPathHandler("/nestedfetch", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.write( + "<html><iframe src='http://example.net/nestedfetch2'></iframe></html>" + ); + }); + + server.registerPathHandler("/nestedfetch2", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.write( + "<html><iframe src='http://example.org/fetch'></iframe></html>" + ); + }); + + server.registerPathHandler("/ready", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.write("<html></html>"); + + 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": `<body><script src="page.js"></script></body>`, + "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("<html></html>"); + }); + + server.registerPathHandler("/pageWithFrames", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + + response.write(` + <html> + <iframe src="http://example.com/contentScriptHere"></iframe> + <iframe src="http://example.net/contentScriptHere"></iframe> + </html> + `); + }); + + 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("<html><body></body></html>"); + }); + + 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(); +}); |