diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /netwerk/cookie/test/browser | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk/cookie/test/browser')
19 files changed, 1127 insertions, 0 deletions
diff --git a/netwerk/cookie/test/browser/browser.ini b/netwerk/cookie/test/browser/browser.ini new file mode 100644 index 0000000000..2f81a7f173 --- /dev/null +++ b/netwerk/cookie/test/browser/browser.ini @@ -0,0 +1,21 @@ +[DEFAULT] +support-files = + file_empty.html + file_empty.js + head.js + +[browser_broadcastChannel.js] +[browser_cookies.js] +support-files = server.sjs +[browser_domCache.js] +[browser_indexedDB.js] +[browser_originattributes.js] +[browser_storage.js] +[browser_serviceWorker.js] +[browser_sharedWorker.js] +[browser_sameSiteConsole.js] +support-files = sameSite.sjs +[browser_oversize.js] +support-files = oversize.sjs +[browser_cookie_insecure_overwrites_secure.js] +[browser_cookies_ipv6.js] diff --git a/netwerk/cookie/test/browser/browser_broadcastChannel.js b/netwerk/cookie/test/browser/browser_broadcastChannel.js new file mode 100644 index 0000000000..ee561e6f0c --- /dev/null +++ b/netwerk/cookie/test/browser/browser_broadcastChannel.js @@ -0,0 +1,80 @@ +// BroadcastChannel is not considered part of CookieJar. It's not allowed to +// communicate with other windows with different cookie jar settings. +"use strict"; + +CookiePolicyHelper.runTest("BroadcastChannel", { + cookieJarAccessAllowed: async w => { + new w.BroadcastChannel("hello"); + ok(true, "BroadcastChannel be used"); + }, + + cookieJarAccessDenied: async w => { + try { + new w.BroadcastChannel("hello"); + ok(false, "BroadcastChannel cannot be used!"); + } catch (e) { + ok(true, "BroadcastChannel cannot be used!"); + is(e.name, "SecurityError", "We want a security error message."); + } + }, +}); + +CookiePolicyHelper.runTest("BroadcastChannel in workers", { + cookieJarAccessAllowed: async w => { + function nonBlockingCode() { + new BroadcastChannel("hello"); + postMessage(true); + } + + let blob = new w.Blob([ + nonBlockingCode.toString() + "; nonBlockingCode();", + ]); + ok(blob, "Blob has been created"); + + let blobURL = w.URL.createObjectURL(blob); + ok(blobURL, "Blob URL has been created"); + + let worker = new w.Worker(blobURL); + ok(worker, "Worker has been created"); + + await new w.Promise((resolve, reject) => { + worker.onmessage = function (e) { + if (e) { + resolve(); + } else { + reject(); + } + }; + }); + }, + + cookieJarAccessDenied: async w => { + function blockingCode() { + try { + new BroadcastChannel("hello"); + postMessage(false); + } catch (e) { + postMessage(e.name == "SecurityError"); + } + } + + let blob = new w.Blob([blockingCode.toString() + "; blockingCode();"]); + ok(blob, "Blob has been created"); + + let blobURL = w.URL.createObjectURL(blob); + ok(blobURL, "Blob URL has been created"); + + let worker = new w.Worker(blobURL); + ok(worker, "Worker has been created"); + + await new w.Promise((resolve, reject) => { + worker.onmessage = function (e) { + if (e) { + resolve(); + } else { + reject(); + } + }; + }); + }, +}); diff --git a/netwerk/cookie/test/browser/browser_cookie_insecure_overwrites_secure.js b/netwerk/cookie/test/browser/browser_cookie_insecure_overwrites_secure.js new file mode 100644 index 0000000000..9d365d441a --- /dev/null +++ b/netwerk/cookie/test/browser/browser_cookie_insecure_overwrites_secure.js @@ -0,0 +1,116 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); + +const urlPath = "/browser/netwerk/cookie/test/browser/file_empty.html"; +const baseDomain = "example.com"; + +// eslint doesn't like http +/* eslint-disable */ +const URL_INSECURE_COM = "http://" + baseDomain + urlPath; +/* eslint-enable */ +const URL_SECURE_COM = "https://" + baseDomain + urlPath; + +// common cookie strings +const COOKIE_BASIC = "foo=one"; +const COOKIE_OTHER = "foo=two"; +const COOKIE_THIRD = "foo=three"; +const COOKIE_FORTH = "foo=four"; + +function securify(cookie) { + return cookie + "; Secure"; +} + +registerCleanupFunction(() => { + info("Cleaning up the test"); +}); + +async function setup() { + Services.prefs.setIntPref("network.cookie.cookieBehavior", 0); + + Services.prefs.setBoolPref( + "network.cookieJarSettings.unblocked_for_testing", + true + ); + + Services.prefs.setBoolPref("network.cookie.sameSite.laxByDefault", false); + Services.prefs.setBoolPref( + "network.cookie.sameSite.noneRequiresSecure", + false + ); + Services.prefs.setBoolPref("network.cookie.sameSite.schemeful", true); + Services.cookies.removeAll(); +} +add_task(setup); + +// note: +// 1. The URL scheme will not matter for insecure cookies, since +// cookies are not "schemeful" in this sense. +// So an insecure cookie set anywhere will be visible on http and https sites +// Secure cookies are different, they will only be visible from https sites +// and will prevent cookie setting of the same name on insecure sites. +// +// 2. The different processes (tabs) shouldn't matter since +// cookie adds/changes are distributed to other processes on a need-to-know +// basis. + +add_task(async function test_insecure_cant_overwrite_secure_via_doc() { + // insecure + const tab1 = BrowserTestUtils.addTab(gBrowser, URL_INSECURE_COM); + const browser = gBrowser.getBrowserForTab(tab1); + await BrowserTestUtils.browserLoaded(browser); + + // secure + const tab2 = BrowserTestUtils.addTab(gBrowser, URL_SECURE_COM); + const browser2 = gBrowser.getBrowserForTab(tab2); + await BrowserTestUtils.browserLoaded(browser2); + + // init with insecure cookie on insecure origin child process + await SpecialPowers.spawn( + browser, + [COOKIE_BASIC, COOKIE_BASIC], + (cookie, expected) => { + content.document.cookie = cookie; + is(content.document.cookie, expected); + } + ); + + // insecure cookie visible on secure origin process (sanity check) + await SpecialPowers.spawn(browser2, [COOKIE_BASIC], expected => { + is(content.document.cookie, expected); + }); + + // overwrite insecure cookie on secure origin with secure cookie (sanity check) + await SpecialPowers.spawn( + browser2, + [securify(COOKIE_OTHER), COOKIE_OTHER], + (cookie, expected) => { + content.document.cookie = cookie; + is(content.document.cookie, expected); + } + ); + + // insecure cookie will NOT overwrite the secure one on insecure origin + // and cookie.document appears blank + await SpecialPowers.spawn(browser, [COOKIE_THIRD, ""], (cookie, expected) => { + content.document.cookie = cookie; // quiet failure here + is(content.document.cookie, expected); + }); + + // insecure cookie will overwrite secure cookie on secure origin + // a bit weird, but this is normal + await SpecialPowers.spawn( + browser2, + [COOKIE_FORTH, COOKIE_FORTH], + (cookie, expected) => { + content.document.cookie = cookie; + is(content.document.cookie, expected); + } + ); + + BrowserTestUtils.removeTab(tab1); + BrowserTestUtils.removeTab(tab2); + Services.cookies.removeAll(); +}); diff --git a/netwerk/cookie/test/browser/browser_cookies.js b/netwerk/cookie/test/browser/browser_cookies.js new file mode 100644 index 0000000000..8a0d8332bf --- /dev/null +++ b/netwerk/cookie/test/browser/browser_cookies.js @@ -0,0 +1,53 @@ +"use strict"; + +CookiePolicyHelper.runTest("document.cookies", { + cookieJarAccessAllowed: async _ => { + let hasCookie = !!content.document.cookie.length; + + await content + .fetch("server.sjs") + .then(r => r.text()) + .then(text => { + is( + text, + hasCookie ? "cookie-present" : "cookie-not-present", + "document.cookie is consistent with fetch requests" + ); + }); + + content.document.cookie = "name=value"; + ok(content.document.cookie.includes("name=value"), "Some cookies for me"); + ok(content.document.cookie.includes("foopy=1"), "Some cookies for me"); + + await content + .fetch("server.sjs") + .then(r => r.text()) + .then(text => { + is(text, "cookie-present", "We should have cookies"); + }); + + ok(!!content.document.cookie.length, "Some Cookies for me"); + }, + + cookieJarAccessDenied: async _ => { + is(content.document.cookie, "", "No cookies for me"); + content.document.cookie = "name=value"; + is(content.document.cookie, "", "No cookies for me"); + + await content + .fetch("server.sjs") + .then(r => r.text()) + .then(text => { + is(text, "cookie-not-present", "We should not have cookies"); + }); + // Let's do it twice. + await content + .fetch("server.sjs") + .then(r => r.text()) + .then(text => { + is(text, "cookie-not-present", "We should not have cookies"); + }); + + is(content.document.cookie, "", "Still no cookies for me"); + }, +}); diff --git a/netwerk/cookie/test/browser/browser_cookies_ipv6.js b/netwerk/cookie/test/browser/browser_cookies_ipv6.js new file mode 100644 index 0000000000..044c3ce577 --- /dev/null +++ b/netwerk/cookie/test/browser/browser_cookies_ipv6.js @@ -0,0 +1,55 @@ +"use strict"; + +let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); + +let gHttpServer = null; +let ip = "[::1]"; + +function contentHandler(metadata, response) { + response.setStatusLine(metadata.httpVersion, 200, "Ok"); + response.setHeader("Content-Type", "text/html", false); + let body = ` + <!DOCTYPE HTML> + <html> + <head> + <meta charset='utf-8'> + <title>Cookie ipv6 Test</title> + </head> + <body> + </body> + </html>`; + response.bodyOutputStream.write(body, body.length); +} + +add_task(async _ => { + if (!gHttpServer) { + gHttpServer = new HttpServer(); + gHttpServer.registerPathHandler("/content", contentHandler); + gHttpServer._start(-1, ip); + } + + registerCleanupFunction(() => { + gHttpServer.stop(() => { + gHttpServer = null; + }); + }); + + let serverPort = gHttpServer.identity.primaryPort; + let testURL = `http://${ip}:${serverPort}/content`; + + // Let's open our tab. + const tab = BrowserTestUtils.addTab(gBrowser, testURL); + gBrowser.selectedTab = tab; + + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Test if we can set and get document.cookie successfully. + await SpecialPowers.spawn(browser, [], () => { + content.document.cookie = "foo=bar"; + is(content.document.cookie, "foo=bar"); + }); + + // Let's close the tab. + BrowserTestUtils.removeTab(tab); +}); diff --git a/netwerk/cookie/test/browser/browser_domCache.js b/netwerk/cookie/test/browser/browser_domCache.js new file mode 100644 index 0000000000..5f1aa84d83 --- /dev/null +++ b/netwerk/cookie/test/browser/browser_domCache.js @@ -0,0 +1,25 @@ +"use strict"; + +CookiePolicyHelper.runTest("DOM Cache", { + cookieJarAccessAllowed: async w => { + await w.caches.open("wow").then( + _ => { + ok(true, "DOM Cache can be used!"); + }, + _ => { + ok(false, "DOM Cache can be used!"); + } + ); + }, + + cookieJarAccessDenied: async w => { + await w.caches.open("wow").then( + _ => { + ok(false, "DOM Cache cannot be used!"); + }, + _ => { + ok(true, "DOM Cache cannot be used!"); + } + ); + }, +}); diff --git a/netwerk/cookie/test/browser/browser_indexedDB.js b/netwerk/cookie/test/browser/browser_indexedDB.js new file mode 100644 index 0000000000..7f417077eb --- /dev/null +++ b/netwerk/cookie/test/browser/browser_indexedDB.js @@ -0,0 +1,84 @@ +"use strict"; + +CookiePolicyHelper.runTest("IndexedDB", { + cookieJarAccessAllowed: async w => { + w.indexedDB.open("test", "1"); + ok(true, "IDB should be allowed"); + }, + + cookieJarAccessDenied: async w => { + try { + w.indexedDB.open("test", "1"); + ok(false, "IDB should be blocked"); + } catch (e) { + ok(true, "IDB should be blocked"); + is(e.name, "SecurityError", "We want a security error message."); + } + }, +}); + +CookiePolicyHelper.runTest("IndexedDB in workers", { + cookieJarAccessAllowed: async w => { + function nonBlockCode() { + indexedDB.open("test", "1"); + postMessage(true); + } + + let blob = new w.Blob([nonBlockCode.toString() + "; nonBlockCode();"]); + ok(blob, "Blob has been created"); + + let blobURL = w.URL.createObjectURL(blob); + ok(blobURL, "Blob URL has been created"); + + let worker = new w.Worker(blobURL); + ok(worker, "Worker has been created"); + + await new w.Promise((resolve, reject) => { + worker.onmessage = function (e) { + if (e.data) { + resolve(); + } else { + reject(); + } + }; + + worker.onerror = function (e) { + reject(); + }; + }); + }, + + cookieJarAccessDenied: async w => { + function blockCode() { + try { + indexedDB.open("test", "1"); + postMessage(false); + } catch (e) { + postMessage(e.name == "SecurityError"); + } + } + + let blob = new w.Blob([blockCode.toString() + "; blockCode();"]); + ok(blob, "Blob has been created"); + + let blobURL = w.URL.createObjectURL(blob); + ok(blobURL, "Blob URL has been created"); + + let worker = new w.Worker(blobURL); + ok(worker, "Worker has been created"); + + await new w.Promise((resolve, reject) => { + worker.onmessage = function (e) { + if (e.data) { + resolve(); + } else { + reject(); + } + }; + + worker.onerror = function (e) { + reject(); + }; + }); + }, +}); diff --git a/netwerk/cookie/test/browser/browser_originattributes.js b/netwerk/cookie/test/browser/browser_originattributes.js new file mode 100644 index 0000000000..fab7e67b2e --- /dev/null +++ b/netwerk/cookie/test/browser/browser_originattributes.js @@ -0,0 +1,121 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const USER_CONTEXTS = ["default", "personal", "work"]; + +const COOKIE_NAMES = ["cookie0", "cookie1", "cookie2"]; + +const TEST_URL = + "http://example.com/browser/netwerk/cookie/test/browser/file_empty.html"; + +// opens `uri' in a new tab with the provided userContextId and focuses it. +// returns the newly opened tab +async function openTabInUserContext(uri, userContextId) { + // open the tab in the correct userContextId + let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId }); + + // select tab and make sure its browser is focused + gBrowser.selectedTab = tab; + tab.ownerGlobal.focus(); + + let browser = gBrowser.getBrowserForTab(tab); + // wait for tab load + await BrowserTestUtils.browserLoaded(browser); + + return { tab, browser }; +} + +add_setup(async function () { + // make sure userContext is enabled. + await new Promise(resolve => { + SpecialPowers.pushPrefEnv( + { set: [["privacy.userContext.enabled", true]] }, + resolve + ); + }); +}); + +add_task(async function test() { + // load the page in 3 different contexts and set a cookie + // which should only be visible in that context + for (let userContextId of Object.keys(USER_CONTEXTS)) { + // open our tab in the given user context + let { tab, browser } = await openTabInUserContext(TEST_URL, userContextId); + + await SpecialPowers.spawn( + browser, + [{ names: COOKIE_NAMES, value: USER_CONTEXTS[userContextId] }], + function (opts) { + for (let name of opts.names) { + content.document.cookie = name + "=" + opts.value; + } + } + ); + + // remove the tab + gBrowser.removeTab(tab); + } + + let expectedValues = USER_CONTEXTS.slice(0); + await checkCookies(expectedValues, "before removal"); + + // remove cookies that belongs to user context id #1 + Services.cookies.removeCookiesWithOriginAttributes( + JSON.stringify({ userContextId: 1 }) + ); + + expectedValues[1] = undefined; + await checkCookies(expectedValues, "after removal"); +}); + +async function checkCookies(expectedValues, time) { + for (let userContextId of Object.keys(expectedValues)) { + let cookiesFromTitle = await getCookiesFromJS(userContextId); + let cookiesFromManager = getCookiesFromManager(userContextId); + + let expectedValue = expectedValues[userContextId]; + for (let name of COOKIE_NAMES) { + is( + cookiesFromTitle[name], + expectedValue, + `User context ${userContextId}: ${name} should be correct from title ${time}` + ); + is( + cookiesFromManager[name], + expectedValue, + `User context ${userContextId}: ${name} should be correct from manager ${time}` + ); + } + } +} + +function getCookiesFromManager(userContextId) { + let cookies = {}; + let allCookies = Services.cookies.getCookiesWithOriginAttributes( + JSON.stringify({ userContextId }) + ); + for (let cookie of allCookies) { + cookies[cookie.name] = cookie.value; + } + return cookies; +} + +async function getCookiesFromJS(userContextId) { + let { tab, browser } = await openTabInUserContext(TEST_URL, userContextId); + + // get the cookies + let cookieString = await SpecialPowers.spawn(browser, [], function () { + return content.document.cookie; + }); + + // check each item in the title and validate it meets expectatations + let cookies = {}; + for (let cookie of cookieString.split(";")) { + let [name, value] = cookie.trim().split("="); + cookies[name] = value; + } + + gBrowser.removeTab(tab); + return cookies; +} diff --git a/netwerk/cookie/test/browser/browser_oversize.js b/netwerk/cookie/test/browser/browser_oversize.js new file mode 100644 index 0000000000..f6e1f8a70b --- /dev/null +++ b/netwerk/cookie/test/browser/browser_oversize.js @@ -0,0 +1,96 @@ +"use strict"; + +const OVERSIZE_DOMAIN = "http://example.com/"; +const OVERSIZE_PATH = "browser/netwerk/cookie/test/browser/"; +const OVERSIZE_TOP_PAGE = OVERSIZE_DOMAIN + OVERSIZE_PATH + "oversize.sjs"; + +add_task(async _ => { + const expected = []; + + const consoleListener = { + observe(what) { + if (!(what instanceof Ci.nsIConsoleMessage)) { + return; + } + + info("Console Listener: " + what); + for (let i = expected.length - 1; i >= 0; --i) { + const e = expected[i]; + + if (what.message.includes(e.match)) { + ok(true, "Message received: " + e.match); + expected.splice(i, 1); + e.resolve(); + } + } + }, + }; + + Services.console.registerListener(consoleListener); + + registerCleanupFunction(() => + Services.console.unregisterListener(consoleListener) + ); + + const netPromises = [ + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “a” is invalid because its size is too big. Max size is 4096 B.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “b” is invalid because its path size is too big. Max size is 1024 B.", + }); + }), + ]; + + // Let's open our tab. + const tab = BrowserTestUtils.addTab(gBrowser, OVERSIZE_TOP_PAGE); + gBrowser.selectedTab = tab; + + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Let's wait for the first set of console events. + await Promise.all(netPromises); + + // the DOM list of events. + const domPromises = [ + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “d” is invalid because its size is too big. Max size is 4096 B.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “e” is invalid because its path size is too big. Max size is 1024 B.", + }); + }), + ]; + + // Let's use document.cookie + SpecialPowers.spawn(browser, [], () => { + const maxBytesPerCookie = 4096; + const maxBytesPerCookiePath = 1024; + content.document.cookie = "d=" + Array(maxBytesPerCookie + 1).join("x"); + content.document.cookie = + "e=f; path=/" + Array(maxBytesPerCookiePath + 1).join("x"); + }); + + // Let's wait for the dom events. + await Promise.all(domPromises); + + // Let's close the tab. + BrowserTestUtils.removeTab(tab); +}); diff --git a/netwerk/cookie/test/browser/browser_sameSiteConsole.js b/netwerk/cookie/test/browser/browser_sameSiteConsole.js new file mode 100644 index 0000000000..84527296b2 --- /dev/null +++ b/netwerk/cookie/test/browser/browser_sameSiteConsole.js @@ -0,0 +1,133 @@ +"use strict"; + +const SAMESITE_DOMAIN = "http://example.com/"; +const SAMESITE_PATH = "browser/netwerk/cookie/test/browser/"; +const SAMESITE_TOP_PAGE = SAMESITE_DOMAIN + SAMESITE_PATH + "sameSite.sjs"; + +add_task(async _ => { + await SpecialPowers.pushPrefEnv({ + set: [ + ["network.cookie.sameSite.laxByDefault", true], + ["network.cookie.sameSite.noneRequiresSecure", true], + ], + }); + + const expected = []; + + const consoleListener = { + observe(what) { + if (!(what instanceof Ci.nsIConsoleMessage)) { + return; + } + + info("Console Listener: " + what); + for (let i = expected.length - 1; i >= 0; --i) { + const e = expected[i]; + + if (what.message.includes(e.match)) { + ok(true, "Message received: " + e.match); + expected.splice(i, 1); + e.resolve(); + } + } + }, + }; + + Services.console.registerListener(consoleListener); + + registerCleanupFunction(() => + Services.console.unregisterListener(consoleListener) + ); + + const netPromises = [ + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “a” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “b” rejected because it has the “SameSite=None” attribute but is missing the “secure” attribute.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Invalid “SameSite“ value for cookie “c”. The supported values are: “Lax“, “Strict“, “None“.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “c” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.", + }); + }), + ]; + + // Let's open our tab. + const tab = BrowserTestUtils.addTab(gBrowser, SAMESITE_TOP_PAGE); + gBrowser.selectedTab = tab; + + const browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Let's wait for the first set of console events. + await Promise.all(netPromises); + + // the DOM list of events. + const domPromises = [ + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “d” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “e” rejected because it has the “SameSite=None” attribute but is missing the “secure” attribute.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Invalid “SameSite“ value for cookie “f”. The supported values are: “Lax“, “Strict“, “None“.", + }); + }), + + new Promise(resolve => { + expected.push({ + resolve, + match: + "Cookie “f” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.", + }); + }), + ]; + + // Let's use document.cookie + SpecialPowers.spawn(browser, [], () => { + content.document.cookie = "d=4"; + content.document.cookie = "e=5; sameSite=none"; + content.document.cookie = "f=6; sameSite=batmat"; + }); + + // Let's wait for the dom events. + await Promise.all(domPromises); + + // Let's close the tab. + BrowserTestUtils.removeTab(tab); +}); diff --git a/netwerk/cookie/test/browser/browser_serviceWorker.js b/netwerk/cookie/test/browser/browser_serviceWorker.js new file mode 100644 index 0000000000..2a5c963535 --- /dev/null +++ b/netwerk/cookie/test/browser/browser_serviceWorker.js @@ -0,0 +1,45 @@ +"use strict"; + +CookiePolicyHelper.runTest("ServiceWorker", { + prefs: [ + ["dom.serviceWorkers.exemptFromPerDomainMax", true], + ["dom.ipc.processCount", 1], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ], + + cookieJarAccessAllowed: async w => { + await w.navigator.serviceWorker + .register("file_empty.js") + .then( + reg => { + ok(true, "ServiceWorker can be used!"); + return reg; + }, + _ => { + ok(false, "ServiceWorker cannot be used! " + _); + } + ) + .then( + reg => reg.unregister(), + _ => { + ok(false, "unregister failed"); + } + ) + .catch(e => ok(false, "Promise rejected: " + e)); + }, + + cookieJarAccessDenied: async w => { + await w.navigator.serviceWorker + .register("file_empty.js") + .then( + _ => { + ok(false, "ServiceWorker cannot be used!"); + }, + _ => { + ok(true, "ServiceWorker cannot be used!"); + } + ) + .catch(e => ok(false, "Promise rejected: " + e)); + }, +}); diff --git a/netwerk/cookie/test/browser/browser_sharedWorker.js b/netwerk/cookie/test/browser/browser_sharedWorker.js new file mode 100644 index 0000000000..88a8b3f0e7 --- /dev/null +++ b/netwerk/cookie/test/browser/browser_sharedWorker.js @@ -0,0 +1,18 @@ +"use strict"; + +CookiePolicyHelper.runTest("SharedWorker", { + cookieJarAccessAllowed: async w => { + new w.SharedWorker("a.js", "foo"); + ok(true, "SharedWorker is allowed"); + }, + + cookieJarAccessDenied: async w => { + try { + new w.SharedWorker("a.js", "foo"); + ok(false, "SharedWorker cannot be used!"); + } catch (e) { + ok(true, "SharedWorker cannot be used!"); + is(e.name, "SecurityError", "We want a security error message."); + } + }, +}); diff --git a/netwerk/cookie/test/browser/browser_storage.js b/netwerk/cookie/test/browser/browser_storage.js new file mode 100644 index 0000000000..1e37b1a367 --- /dev/null +++ b/netwerk/cookie/test/browser/browser_storage.js @@ -0,0 +1,43 @@ +"use strict"; + +CookiePolicyHelper.runTest("SessionStorage", { + cookieJarAccessAllowed: async w => { + try { + w.sessionStorage.foo = 42; + ok(true, "SessionStorage works"); + } catch (e) { + ok(false, "SessionStorage works"); + } + }, + + cookieJarAccessDenied: async w => { + try { + w.sessionStorage.foo = 42; + ok(false, "SessionStorage doesn't work"); + } catch (e) { + ok(true, "SessionStorage doesn't work"); + is(e.name, "SecurityError", "We want a security error message."); + } + }, +}); + +CookiePolicyHelper.runTest("LocalStorage", { + cookieJarAccessAllowed: async w => { + try { + w.localStorage.foo = 42; + ok(true, "LocalStorage works"); + } catch (e) { + ok(false, "LocalStorage works"); + } + }, + + cookieJarAccessDenied: async w => { + try { + w.localStorage.foo = 42; + ok(false, "LocalStorage doesn't work"); + } catch (e) { + ok(true, "LocalStorage doesn't work"); + is(e.name, "SecurityError", "We want a security error message."); + } + }, +}); diff --git a/netwerk/cookie/test/browser/file_empty.html b/netwerk/cookie/test/browser/file_empty.html new file mode 100644 index 0000000000..78b64149c4 --- /dev/null +++ b/netwerk/cookie/test/browser/file_empty.html @@ -0,0 +1,2 @@ +<html><body> +</body></html> diff --git a/netwerk/cookie/test/browser/file_empty.js b/netwerk/cookie/test/browser/file_empty.js new file mode 100644 index 0000000000..3053583c76 --- /dev/null +++ b/netwerk/cookie/test/browser/file_empty.js @@ -0,0 +1 @@ +/* nothing here */ diff --git a/netwerk/cookie/test/browser/head.js b/netwerk/cookie/test/browser/head.js new file mode 100644 index 0000000000..609ad683db --- /dev/null +++ b/netwerk/cookie/test/browser/head.js @@ -0,0 +1,201 @@ +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +const BEHAVIOR_ACCEPT = Ci.nsICookieService.BEHAVIOR_ACCEPT; +const BEHAVIOR_REJECT = Ci.nsICookieService.BEHAVIOR_REJECT; + +const PERM_DEFAULT = Ci.nsICookiePermission.ACCESS_DEFAULT; +const PERM_ALLOW = Ci.nsICookiePermission.ACCESS_ALLOW; +const PERM_DENY = Ci.nsICookiePermission.ACCESS_DENY; + +const TEST_DOMAIN = "https://example.com/"; +const TEST_PATH = "browser/netwerk/cookie/test/browser/"; +const TEST_TOP_PAGE = TEST_DOMAIN + TEST_PATH + "file_empty.html"; + +// Helper to eval() provided cookieJarAccessAllowed and cookieJarAccessDenied +// toString()ed optionally async function in freshly created tabs with +// BEHAVIOR_ACCEPT and BEHAVIOR_REJECT configured, respectively, in a number of +// permutations. This includes verifying that changing the permission while the +// page is open still results in the state of the permission when the +// document/global was created still applying. Code will execute in the +// ContentTask.spawn frame-script context, use content to access the underlying +// page. +this.CookiePolicyHelper = { + runTest(testName, config) { + // Testing allowed to blocked by cookie behavior + this._createTest( + testName, + config.cookieJarAccessAllowed, + config.cookieJarAccessDenied, + config.prefs, + { + fromBehavior: BEHAVIOR_ACCEPT, + toBehavior: BEHAVIOR_REJECT, + fromPermission: PERM_DEFAULT, + toPermission: PERM_DEFAULT, + } + ); + + // Testing blocked to allowed by cookie behavior + this._createTest( + testName, + config.cookieJarAccessDenied, + config.cookieJarAccessAllowed, + config.prefs, + { + fromBehavior: BEHAVIOR_REJECT, + toBehavior: BEHAVIOR_ACCEPT, + fromPermission: PERM_DEFAULT, + toPermission: PERM_DEFAULT, + } + ); + + // Testing allowed to blocked by cookie permission + this._createTest( + testName, + config.cookieJarAccessAllowed, + config.cookieJarAccessDenied, + config.prefs, + { + fromBehavior: BEHAVIOR_REJECT, + toBehavior: BEHAVIOR_REJECT, + fromPermission: PERM_ALLOW, + toPermission: PERM_DEFAULT, + } + ); + + // Testing blocked to allowed by cookie permission + this._createTest( + testName, + config.cookieJarAccessDenied, + config.cookieJarAccessAllowed, + config.prefs, + { + fromBehavior: BEHAVIOR_ACCEPT, + toBehavior: BEHAVIOR_ACCEPT, + fromPermission: PERM_DENY, + toPermission: PERM_DEFAULT, + } + ); + }, + + _createTest(testName, goodCb, badCb, prefs, config) { + add_task(async _ => { + info("Starting " + testName + ": " + config.toSource()); + + await SpecialPowers.flushPrefEnv(); + + if (prefs) { + await SpecialPowers.pushPrefEnv({ set: prefs }); + } + + // Let's set the first cookie pref. + PermissionTestUtils.add(TEST_DOMAIN, "cookie", config.fromPermission); + await SpecialPowers.pushPrefEnv({ + set: [["network.cookie.cookieBehavior", config.fromBehavior]], + }); + + // Let's open a tab and load content. + let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE); + gBrowser.selectedTab = tab; + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Let's create an iframe. + await SpecialPowers.spawn( + browser, + [{ url: TEST_TOP_PAGE }], + async obj => { + return new content.Promise(resolve => { + let ifr = content.document.createElement("iframe"); + ifr.setAttribute("id", "iframe"); + ifr.src = obj.url; + ifr.onload = () => resolve(); + content.document.body.appendChild(ifr); + }); + } + ); + + // Let's exec the "good" callback. + info( + "Executing the test after setting the cookie behavior to " + + config.fromBehavior + + " and permission to " + + config.fromPermission + ); + await SpecialPowers.spawn( + browser, + [{ callback: goodCb.toString() }], + async obj => { + let runnableStr = `(() => {return (${obj.callback});})();`; + let runnable = eval(runnableStr); // eslint-disable-line no-eval + await runnable(content); + + let ifr = content.document.getElementById("iframe"); + await runnable(ifr.contentWindow); + } + ); + + // Now, let's change the cookie settings + PermissionTestUtils.add(TEST_DOMAIN, "cookie", config.toPermission); + await SpecialPowers.pushPrefEnv({ + set: [["network.cookie.cookieBehavior", config.toBehavior]], + }); + + // We still want the good callback to succeed. + info( + "Executing the test after setting the cookie behavior to " + + config.toBehavior + + " and permission to " + + config.toPermission + ); + await SpecialPowers.spawn( + browser, + [{ callback: goodCb.toString() }], + async obj => { + let runnableStr = `(() => {return (${obj.callback});})();`; + let runnable = eval(runnableStr); // eslint-disable-line no-eval + await runnable(content); + + let ifr = content.document.getElementById("iframe"); + await runnable(ifr.contentWindow); + } + ); + + // Let's close the tab. + BrowserTestUtils.removeTab(tab); + + // Let's open a new tab and load content again. + tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE); + gBrowser.selectedTab = tab; + + browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Let's exec the "bad" callback. + info("Executing the test in a new tab"); + await SpecialPowers.spawn( + browser, + [{ callback: badCb.toString() }], + async obj => { + let runnableStr = `(() => {return (${obj.callback});})();`; + let runnable = eval(runnableStr); // eslint-disable-line no-eval + await runnable(content); + } + ); + + // Let's close the tab. + BrowserTestUtils.removeTab(tab); + + // Cleanup. + await new Promise(resolve => { + Services.clearData.deleteData( + Ci.nsIClearDataService.CLEAR_ALL, + resolve + ); + }); + }); + }, +}; diff --git a/netwerk/cookie/test/browser/oversize.sjs b/netwerk/cookie/test/browser/oversize.sjs new file mode 100644 index 0000000000..dfe2f31645 --- /dev/null +++ b/netwerk/cookie/test/browser/oversize.sjs @@ -0,0 +1,17 @@ +function handleRequest(aRequest, aResponse) { + aResponse.setStatusLine(aRequest.httpVersion, 200); + + const maxBytesPerCookie = 4096; + const maxBytesPerCookiePath = 1024; + + aResponse.setHeader( + "Set-Cookie", + "a=" + Array(maxBytesPerCookie + 1).join("x"), + true + ); + aResponse.setHeader( + "Set-Cookie", + "b=c; path=/" + Array(maxBytesPerCookiePath + 1).join("x"), + true + ); +} diff --git a/netwerk/cookie/test/browser/sameSite.sjs b/netwerk/cookie/test/browser/sameSite.sjs new file mode 100644 index 0000000000..a19624d2cb --- /dev/null +++ b/netwerk/cookie/test/browser/sameSite.sjs @@ -0,0 +1,7 @@ +function handleRequest(aRequest, aResponse) { + aResponse.setStatusLine(aRequest.httpVersion, 200); + + aResponse.setHeader("Set-Cookie", "a=1", true); + aResponse.setHeader("Set-Cookie", "b=2; sameSite=none", true); + aResponse.setHeader("Set-Cookie", "c=3; sameSite=batman", true); +} diff --git a/netwerk/cookie/test/browser/server.sjs b/netwerk/cookie/test/browser/server.sjs new file mode 100644 index 0000000000..86835914bb --- /dev/null +++ b/netwerk/cookie/test/browser/server.sjs @@ -0,0 +1,9 @@ +function handleRequest(aRequest, aResponse) { + aResponse.setStatusLine(aRequest.httpVersion, 200); + if (aRequest.hasHeader("Cookie")) { + aResponse.write("cookie-present"); + } else { + aResponse.setHeader("Set-Cookie", "foopy=1"); + aResponse.write("cookie-not-present"); + } +} |