diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 6bf6df96c2d0..c930191bb027 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -162,6 +162,7 @@ #include "mozilla/net/NeckoMessageUtils.h" #include "mozilla/net/NeckoParent.h" #include "mozilla/net/PCookieServiceParent.h" +#include "mozilla/net/CookieKey.h" #include "mozilla/TelemetryComms.h" #include "mozilla/TelemetryEventEnums.h" #include "mozilla/RemoteLazyInputStreamParent.h" @@ -3946,11 +3947,18 @@ ContentParent::Observe(nsISupports* aSubject, const char* aTopic, nsCOMPtr xpcCookie = do_QueryInterface(aSubject); NS_ASSERTION(xpcCookie, "couldn't get cookie"); + + // only broadcast the cookie change to content processes that need it + const Cookie& cookie = xpcCookie->AsCookie(); + if (!cs->CookieMatchesContentList(cookie)) { + return NS_OK; + } + if (!nsCRT::strcmp(aData, u"deleted")) { - cs->RemoveCookie(xpcCookie); + cs->RemoveCookie(cookie); } else if ((!nsCRT::strcmp(aData, u"added")) || (!nsCRT::strcmp(aData, u"changed"))) { - cs->AddCookie(xpcCookie); + cs->AddCookie(cookie); } } else if (!strcmp(aTopic, NS_NETWORK_LINK_TYPE_TOPIC)) { UpdateNetworkLinkType(); diff --git a/netwerk/cookie/Cookie.cpp b/netwerk/cookie/Cookie.cpp index 6f3b4e4f2f97..e2280f9c3820 100644 --- a/netwerk/cookie/Cookie.cpp +++ b/netwerk/cookie/Cookie.cpp @@ -158,6 +158,8 @@ Cookie::GetOriginAttributes(JSContext* aCx, JS::MutableHandle aVal) { return NS_OK; } +const Cookie& Cookie::AsCookie() { return *this; } + const nsCString& Cookie::GetFilePath() { MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); diff --git a/netwerk/cookie/CookieServiceChild.cpp b/netwerk/cookie/CookieServiceChild.cpp index c0320778b25b..f6b15bb838b5 100644 --- a/netwerk/cookie/CookieServiceChild.cpp +++ b/netwerk/cookie/CookieServiceChild.cpp @@ -191,6 +191,13 @@ IPCResult CookieServiceChild::RecvAddCookie(const CookieStruct& aCookie, const OriginAttributes& aAttrs) { RefPtr cookie = Cookie::Create(aCookie, aAttrs); RecordDocumentCookie(cookie, aAttrs); + + // signal test code to check their cookie list + nsCOMPtr obsService = services::GetObserverService(); + if (obsService) { + obsService->NotifyObservers(nullptr, "cookie-content-filter-test", nullptr); + } + return IPC_OK(); } diff --git a/netwerk/cookie/CookieServiceParent.cpp b/netwerk/cookie/CookieServiceParent.cpp index 8389eb342c0d..c2fd5908d806 100644 --- a/netwerk/cookie/CookieServiceParent.cpp +++ b/netwerk/cookie/CookieServiceParent.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "CookieCommons.h" +#include "CookieLogging.h" #include "mozilla/net/CookieService.h" #include "mozilla/net/CookieServiceParent.h" #include "mozilla/net/NeckoParent.h" @@ -13,7 +14,9 @@ #include "mozIThirdPartyUtil.h" #include "nsArrayUtils.h" #include "nsIChannel.h" +#include "nsIEffectiveTLDService.h" #include "nsNetCID.h" +#include "nsMixedContentBlocker.h" using namespace mozilla::ipc; @@ -28,6 +31,10 @@ CookieServiceParent::CookieServiceParent() { // Get the CookieService instance directly, so we can call internal methods. mCookieService = CookieService::GetSingleton(); NS_ASSERTION(mCookieService, "couldn't get nsICookieService"); + + mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); + MOZ_ALWAYS_TRUE(mTLDService); + mProcessingCookie = false; } @@ -40,10 +47,10 @@ void CookieServiceParent::RemoveBatchDeletedCookies(nsIArray* aCookieList) { nsTArray attrsList; for (uint32_t i = 0; i < len; i++) { nsCOMPtr xpcCookie = do_QueryElementAt(aCookieList, i); - auto* cookie = static_cast(xpcCookie.get()); - attrs = cookie->OriginAttributesRef(); - cookieStruct = cookie->ToIPC(); - if (cookie->IsHttpOnly()) { + const auto& cookie = xpcCookie->AsCookie(); + attrs = cookie.OriginAttributesRef(); + cookieStruct = cookie.ToIPC(); + if (cookie.IsHttpOnly()) { // Child only needs to exist if an HttpOnly cookie exists, not its value cookieStruct.value() = ""; } @@ -55,26 +62,36 @@ void CookieServiceParent::RemoveBatchDeletedCookies(nsIArray* aCookieList) { void CookieServiceParent::RemoveAll() { Unused << SendRemoveAll(); } -void CookieServiceParent::RemoveCookie(nsICookie* aCookie) { - auto* cookie = static_cast(aCookie); - const OriginAttributes& attrs = cookie->OriginAttributesRef(); - CookieStruct cookieStruct = cookie->ToIPC(); - if (cookie->IsHttpOnly()) { +void CookieServiceParent::RemoveCookie(const Cookie& cookie) { + const OriginAttributes& attrs = cookie.OriginAttributesRef(); + CookieStruct cookieStruct = cookie.ToIPC(); + if (cookie.IsHttpOnly()) { cookieStruct.value() = ""; } Unused << SendRemoveCookie(cookieStruct, attrs); } -void CookieServiceParent::AddCookie(nsICookie* aCookie) { - auto* cookie = static_cast(aCookie); - const OriginAttributes& attrs = cookie->OriginAttributesRef(); - CookieStruct cookieStruct = cookie->ToIPC(); - if (cookie->IsHttpOnly()) { +void CookieServiceParent::AddCookie(const Cookie& cookie) { + const OriginAttributes& attrs = cookie.OriginAttributesRef(); + CookieStruct cookieStruct = cookie.ToIPC(); + if (cookie.IsHttpOnly()) { cookieStruct.value() = ""; } Unused << SendAddCookie(cookieStruct, attrs); } +bool CookieServiceParent::CookieMatchesContentList(const Cookie& cookie) { + nsCString baseDomain; + MOZ_ALWAYS_SUCCEEDS(CookieCommons::GetBaseDomainFromHost( + mTLDService, cookie.Host(), baseDomain)); + + CookieKey cookieKey(baseDomain, cookie.OriginAttributesRef()); + if (Maybe allowSecure = mCookieKeysInContent.MaybeGet(cookieKey)) { + return (!cookie.IsSecure() || *allowSecure); + } + return false; +} + void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) { nsCOMPtr uri; aChannel->GetURI(getter_AddRefs(uri)); @@ -89,7 +106,6 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) { StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes( aChannel, attrs); - // Send matching cookies to Child. nsCOMPtr thirdPartyUtil; thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID); @@ -97,6 +113,9 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) { ThirdPartyAnalysisResult result = thirdPartyUtil->AnalyzeChannel( aChannel, false, nullptr, nullptr, &rejectedReason); + UpdateCookieInContentList(uri, attrs); + + // Send matching cookies to Child. nsTArray foundCookieList; mCookieService->GetCookiesForURI( uri, aChannel, result.contains(ThirdPartyAnalysis::IsForeign), @@ -110,6 +129,21 @@ void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) { Unused << SendTrackCookiesLoad(matchingCookiesList, attrs); } +// we append outgoing cookie info into a list here so the ContentParent can +// filter cookies passing to unnecessary ContentProcesses +void CookieServiceParent::UpdateCookieInContentList( + nsIURI* uri, const OriginAttributes& originAttrs) { + nsCString baseDomain; + bool requireAHostMatch = false; + MOZ_ALWAYS_SUCCEEDS(CookieCommons::GetBaseDomain(mTLDService, uri, baseDomain, + requireAHostMatch)); + + CookieKey cookieKey(baseDomain, originAttrs); + bool& allowSecure = mCookieKeysInContent.LookupOrInsert(cookieKey, false); + allowSecure = + allowSecure || nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(uri); +} + // static void CookieServiceParent::SerialializeCookieList( const nsTArray& aFoundCookieList, @@ -138,6 +172,10 @@ IPCResult CookieServiceParent::RecvPrepareCookieList( return IPC_FAIL(this, "aHost must not be null"); } + // we append outgoing cookie info into a list here so the ContentParent can + // filter cookies that do not need to go to certain ContentProcesses + UpdateCookieInContentList(aHost, aAttrs); + nsTArray foundCookieList; // Note: passing nullptr as aChannel to GetCookiesForURI() here is fine since // this argument is only used for proper reporting of cookie loads, but the diff --git a/netwerk/cookie/CookieServiceParent.h b/netwerk/cookie/CookieServiceParent.h index af48756e0ee0..9c5d759d6cd7 100644 --- a/netwerk/cookie/CookieServiceParent.h +++ b/netwerk/cookie/CookieServiceParent.h @@ -7,6 +7,7 @@ #define mozilla_net_CookieServiceParent_h #include "mozilla/net/PCookieServiceParent.h" +#include "mozilla/net/CookieKey.h" class nsIArray; class nsICookie; @@ -14,6 +15,8 @@ namespace mozilla { class OriginAttributes; } +class nsIEffectiveTLDService; + namespace mozilla { namespace net { @@ -33,9 +36,9 @@ class CookieServiceParent : public PCookieServiceParent { void RemoveAll(); - void RemoveCookie(nsICookie* aCookie); + void RemoveCookie(const Cookie& aCookie); - void AddCookie(nsICookie* aCookie); + void AddCookie(const Cookie& aCookie); // This will return true if the CookieServiceParent is currently processing // an update from the content process. This is used in ContentParent to make @@ -43,6 +46,10 @@ class CookieServiceParent : public PCookieServiceParent { // processes, not the one they originated from. bool ProcessingCookie() { return mProcessingCookie; } + bool CookieMatchesContentList(const Cookie& cookie); + void UpdateCookieInContentList(nsIURI* aHostURI, + const OriginAttributes& aOriginAttrs); + protected: virtual void ActorDestroy(ActorDestroyReason aWhy) override; @@ -62,8 +69,10 @@ class CookieServiceParent : public PCookieServiceParent { static void SerialializeCookieList(const nsTArray& aFoundCookieList, nsTArray& aCookiesList); + nsCOMPtr mTLDService; RefPtr mCookieService; bool mProcessingCookie; + nsTHashMap mCookieKeysInContent; }; } // namespace net diff --git a/netwerk/cookie/nsICookie.idl b/netwerk/cookie/nsICookie.idl index 0b2e5e97bc9d..1ecb4a937f8c 100644 --- a/netwerk/cookie/nsICookie.idl +++ b/netwerk/cookie/nsICookie.idl @@ -10,6 +10,14 @@ * Main cookie object interface. */ +%{C++ +namespace mozilla::net { +class Cookie; +} +%} + +[ref] native const_Cookie(const mozilla::net::Cookie); + typedef long nsCookieStatus; typedef long nsCookiePolicy; @@ -80,6 +88,9 @@ interface nsICookie : nsISupports { [implicit_jscontext] readonly attribute jsval originAttributes; + [noscript, notxpcom, nostdcall, binaryname(AsCookie)] + const_Cookie AsCookie(); + /** * true if the cookie is a session cookie. * note that expiry time will also be honored diff --git a/netwerk/test/browser/browser.ini b/netwerk/test/browser/browser.ini index 43df7d5a7cfa..9818da2de4aa 100644 --- a/netwerk/test/browser/browser.ini +++ b/netwerk/test/browser/browser.ini @@ -55,6 +55,13 @@ support-files = 103_preload_csp_imgsrc_none.html 103_preload_csp_imgsrc_none.html^headers^ 103_preload_csp_imgsrc_none.html^informationalResponse^ + cookie_filtering_resource.sjs + cookie_filtering_secure_resource_com.html + cookie_filtering_secure_resource_com.html^headers^ + cookie_filtering_secure_resource_org.html + cookie_filtering_secure_resource_org.html^headers^ + cookie_filtering_square.png + cookie_filtering_square.png^headers^ [browser_about_cache.js] [browser_bug1535877.js] @@ -112,6 +119,11 @@ skip-if = [browser_103_assets.js] skip-if = os == 'linux' && bits == 64 && !debug # Bug 1744028 and Bug 1746324 +[browser_cookie_filtering_basic.js] +[browser_cookie_filtering_insecure.js] +[browser_cookie_filtering_oa.js] +[browser_cookie_filtering_cross_origin.js] +[browser_cookie_filtering_subdomain.js] [browser_103_user_load.js] support-files = early_hint_preload_test_helper.jsm diff --git a/netwerk/test/browser/browser_103_telemetry.js b/netwerk/test/browser/browser_103_telemetry.js index 19f79a0b4244..26e073722f10 100644 --- a/netwerk/test/browser/browser_103_telemetry.js +++ b/netwerk/test/browser/browser_103_telemetry.js @@ -101,3 +101,7 @@ add_task(async function() { gBrowser.removeCurrentTab(); }); + +add_task(async function cleanup() { + Services.prefs.clearUserPref("dom.securecontext.allowlist"); +}); diff --git a/netwerk/test/browser/browser_cookie_filtering_basic.js b/netwerk/test/browser/browser_cookie_filtering_basic.js new file mode 100644 index 000000000000..28ad20cb3107 --- /dev/null +++ b/netwerk/test/browser/browser_cookie_filtering_basic.js @@ -0,0 +1,182 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +const { + HTTPS_EXAMPLE_ORG, + HTTPS_EXAMPLE_COM, + HTTP_EXAMPLE_COM, + browserTestPath, + waitForAllExpectedTests, + cleanupObservers, + checkExpectedCookies, + fetchHelper, + preclean_test, + cleanup_test, +} = ChromeUtils.import("resource://testing-common/cookie_filtering_helper.jsm"); + +// run suite with content listener +// 1. initializes the content process and observer +// 2. runs the test gamut +// 3. cleans up the content process +async function runSuiteWithContentListener(name, triggerSuiteFunc, expected) { + return async function(browser) { + info("Running content suite: " + name); + await SpecialPowers.spawn(browser, [expected, name], checkExpectedCookies); + await triggerSuiteFunc(); + await SpecialPowers.spawn(browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(browser, [], cleanupObservers); + info("Complete content suite: " + name); + }; +} + +// TEST: Different domains (org) +// * example.org cookies go to example.org process +// * exmaple.com cookies do not go to example.org process +async function test_basic_suite_org() { + // example.org - start content process when loading page + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTPS_EXAMPLE_ORG), + }, + await runSuiteWithContentListener( + "basic suite org", + triggerBasicSuite, + basicSuiteMatchingDomain(HTTPS_EXAMPLE_ORG) + ) + ); +} + +// TEST: Different domains (com) +// * example.com cookies go to example.com process +// * example.org cookies do not go to example.com process +// * insecure example.com cookies go to secure com process +async function test_basic_suite_com() { + // example.com - start content process when loading page + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTPS_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "basic suite com", + triggerBasicSuite, + basicSuiteMatchingDomain(HTTPS_EXAMPLE_COM).concat( + basicSuiteMatchingDomain(HTTP_EXAMPLE_COM) + ) + ) + ); +} + +// TEST: Duplicate domain (org) +// * example.org cookies go to multiple example.org processes +async function test_basic_suite_org_duplicate() { + let expected = basicSuiteMatchingDomain(HTTPS_EXAMPLE_ORG); + let t1 = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + browserTestPath(HTTPS_EXAMPLE_ORG) + ); + let testStruct1 = { + name: "example.org primary", + browser: gBrowser.getBrowserForTab(t1), + tab: t1, + expected, + }; + + // example.org dup + let t3 = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + browserTestPath(HTTPS_EXAMPLE_ORG) + ); + let testStruct3 = { + name: "example.org dup", + browser: gBrowser.getBrowserForTab(t3), + tab: t3, + expected, + }; + + let parentpid = Services.appinfo.processID; + let pid1 = testStruct1.browser.frameLoader.remoteTab.osPid; + let pid3 = testStruct3.browser.frameLoader.remoteTab.osPid; + ok( + parentpid != pid1, + "Parent pid should differ from content process for 1st example.org" + ); + ok( + parentpid != pid3, + "Parent pid should differ from content process for 2nd example.org" + ); + ok(pid1 != pid3, "Content pids should differ from each other"); + + await SpecialPowers.spawn( + testStruct1.browser, + [testStruct1.expected, testStruct1.name], + checkExpectedCookies + ); + + await SpecialPowers.spawn( + testStruct3.browser, + [testStruct3.expected, testStruct3.name], + checkExpectedCookies + ); + + await triggerBasicSuite(); + + // wait for all tests and cleanup + await SpecialPowers.spawn(testStruct1.browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(testStruct3.browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(testStruct1.browser, [], cleanupObservers); + await SpecialPowers.spawn(testStruct3.browser, [], cleanupObservers); + BrowserTestUtils.removeTab(testStruct1.tab); + BrowserTestUtils.removeTab(testStruct3.tab); +} + +function basicSuite() { + var suite = []; + suite.push(["test-cookie=aaa", HTTPS_EXAMPLE_ORG]); + suite.push(["test-cookie=bbb", HTTPS_EXAMPLE_ORG]); + suite.push(["test-cookie=dad", HTTPS_EXAMPLE_ORG]); + suite.push(["test-cookie=rad", HTTPS_EXAMPLE_ORG]); + suite.push(["test-cookie=orgwontsee", HTTPS_EXAMPLE_COM]); + suite.push(["test-cookie=sentinelorg", HTTPS_EXAMPLE_ORG]); + suite.push(["test-cookie=sentinelcom", HTTPS_EXAMPLE_COM]); + return suite; +} + +function basicSuiteMatchingDomain(domain) { + var suite = basicSuite(); + var result = []; + for (var [cookie, dom] of suite) { + if (dom == domain) { + result.push(cookie); + } + } + return result; +} + +// triggers set-cookie, which will trigger cookie-changed messages +// messages will be filtered against the cookie list created from above +// only unfiltered messages should make it to the content process +async function triggerBasicSuite() { + let triggerCookies = basicSuite(); + for (var [cookie, domain] of triggerCookies) { + let secure = false; + if (domain.includes("https")) { + secure = true; + } + + //trigger + var url = browserTestPath(domain) + "cookie_filtering_resource.sjs"; + await fetchHelper(url, cookie, secure); + } +} + +add_task(preclean_test); +add_task(test_basic_suite_org); // 5 +add_task(test_basic_suite_com); // 2 +add_task(test_basic_suite_org_duplicate); // 13 +add_task(cleanup_test); diff --git a/netwerk/test/browser/browser_cookie_filtering_cross_origin.js b/netwerk/test/browser/browser_cookie_filtering_cross_origin.js new file mode 100644 index 000000000000..b34d6942d965 --- /dev/null +++ b/netwerk/test/browser/browser_cookie_filtering_cross_origin.js @@ -0,0 +1,144 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +const { + HTTPS_EXAMPLE_ORG, + HTTPS_EXAMPLE_COM, + HTTP_EXAMPLE_COM, + browserTestPath, + waitForAllExpectedTests, + cleanupObservers, + checkExpectedCookies, + preclean_test, + cleanup_test, +} = ChromeUtils.import("resource://testing-common/cookie_filtering_helper.jsm"); + +async function runSuiteWithContentListener(name, trigger_suite_func, expected) { + return async function(browser) { + info("Running content suite: " + name); + await SpecialPowers.spawn(browser, [expected, name], checkExpectedCookies); + await trigger_suite_func(); + await SpecialPowers.spawn(browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(browser, [], cleanupObservers); + info("Complete content suite: " + name); + }; +} + +// TEST: Cross Origin Resource (com) +// * process receives only COR cookies pertaining to same page +async function test_cross_origin_resource_com() { + let comExpected = []; + comExpected.push("test-cookie=comhtml"); // 1 + comExpected.push("test-cookie=png"); // 2 + comExpected.push("test-cookie=orghtml"); // 3 + // nothing for 4, 5, 6, 7 -> goes to .org process + comExpected.push("test-cookie=png"); // 8 + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTPS_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "COR example.com", + triggerCrossOriginSuite, + comExpected + ) + ); + Services.cookies.removeAll(); +} + +// TEST: Cross Origin Resource (org) +// * received COR cookies only pertaining to the process's page +async function test_cross_origin_resource_org() { + let orgExpected = []; + // nothing for 1, 2 and 3 -> goes to .com + orgExpected.push("test-cookie=png"); // 4 + orgExpected.push("test-cookie=orghtml"); // 5 + orgExpected.push("test-cookie=png"); // 6 + orgExpected.push("test-cookie=comhtml"); // 7 + // 8 nothing for 8 -> goes to .com process + orgExpected.push("test-cookie=png"); // 9. Sentinel to verify no more came in + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTPS_EXAMPLE_ORG), + }, + await runSuiteWithContentListener( + "COR example.org", + triggerCrossOriginSuite, + orgExpected + ) + ); +} + +// seems to block better than fetch for secondary resource +// using for more predicatable recving +async function requestBrowserPageWithFilename( + testName, + requestFrom, + filename, + param = "" +) { + let url = requestFrom + "/browser/netwerk/test/browser/" + filename; + + // add param if necessary + if (param != "") { + url += "?" + param; + } + + info("requesting " + url + " (" + testName + ")"); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url, + }, + async () => {} + ); +} + +async function triggerCrossOriginSuite() { + // SameSite - 1 com page, 2 com png + await requestBrowserPageWithFilename( + "SameSite resource (com)", + HTTPS_EXAMPLE_COM, + "cookie_filtering_secure_resource_com.html" + ); + + // COR - 3 com page, 4 org png + await requestBrowserPageWithFilename( + "COR (com-org)", + HTTPS_EXAMPLE_COM, + "cookie_filtering_secure_resource_org.html" + ); + + // SameSite - 5 org page, 6 org png + await requestBrowserPageWithFilename( + "SameSite resource (org)", + HTTPS_EXAMPLE_ORG, + "cookie_filtering_secure_resource_org.html" + ); + + // COR - 7 org page, 8 com png + await requestBrowserPageWithFilename( + "SameSite resource (org-com)", + HTTPS_EXAMPLE_ORG, + "cookie_filtering_secure_resource_com.html" + ); + + // Sentinel to verify no more cookies come in after last true test + await requestBrowserPageWithFilename( + "COR sentinel", + HTTPS_EXAMPLE_ORG, + "cookie_filtering_square.png" + ); +} + +add_task(preclean_test); +add_task(test_cross_origin_resource_com); // 4 +add_task(test_cross_origin_resource_org); // 5 +add_task(cleanup_test); diff --git a/netwerk/test/browser/browser_cookie_filtering_insecure.js b/netwerk/test/browser/browser_cookie_filtering_insecure.js new file mode 100644 index 000000000000..e4fc28ae2107 --- /dev/null +++ b/netwerk/test/browser/browser_cookie_filtering_insecure.js @@ -0,0 +1,103 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +const { + HTTPS_EXAMPLE_ORG, + HTTPS_EXAMPLE_COM, + HTTP_EXAMPLE_COM, + browserTestPath, + waitForAllExpectedTests, + cleanupObservers, + checkExpectedCookies, + fetchHelper, + preclean_test, + cleanup_test, +} = ChromeUtils.import("resource://testing-common/cookie_filtering_helper.jsm"); + +async function runSuiteWithContentListener(name, trigger_suite_func, expected) { + return async function(browser) { + info("Running content suite: " + name); + await SpecialPowers.spawn(browser, [expected, name], checkExpectedCookies); + await trigger_suite_func(); + await SpecialPowers.spawn(browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(browser, [], cleanupObservers); + info("Complete content suite: " + name); + }; +} + +// TEST: In/Secure (insecure com) +// * secure example.com cookie do not go to insecure example.com process +// * insecure cookies go to insecure process +// * secure request with insecure cookie will go to insecure process +async function test_insecure_suite_insecure_com() { + var expected = []; + expected.push("test-cookie=png1"); + expected.push("test-cookie=png2"); + // insecure com will not recieve the secure com request with secure cookie + expected.push("test-cookie=png3"); + info(expected); + + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTP_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "insecure suite insecure com", + triggerInsecureSuite, + expected + ) + ); +} + +// TEST: In/Secure (secure com) +// * secure page will recieve all secure/insecure cookies +async function test_insecure_suite_secure_com() { + var expected = []; + expected.push("test-cookie=png1"); + expected.push("test-cookie=png2"); + expected.push("test-cookie=secure-png"); + expected.push("test-cookie=png3"); + info(expected); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTPS_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "insecure suite secure com", + triggerInsecureSuite, + expected + ) + ); +} + +async function triggerInsecureSuite() { + const cookieSjsFilename = "cookie_filtering_resource.sjs"; + + // insecure page, insecure cookie + var url = browserTestPath(HTTP_EXAMPLE_COM) + cookieSjsFilename; + await fetchHelper(url, "test-cookie=png1", false); + + // secure page req, insecure cookie + url = browserTestPath(HTTPS_EXAMPLE_COM) + cookieSjsFilename; + await fetchHelper(url, "test-cookie=png2", false); + + // secure page, secure cookie + url = browserTestPath(HTTPS_EXAMPLE_COM) + cookieSjsFilename; + await fetchHelper(url, "test-cookie=secure-png", true); + + // not testing insecure server returning secure cookie -- + + // sentinel + url = browserTestPath(HTTPS_EXAMPLE_COM) + cookieSjsFilename; + await fetchHelper(url, "test-cookie=png3", false); +} + +add_task(preclean_test); +add_task(test_insecure_suite_insecure_com); // 3 +add_task(test_insecure_suite_secure_com); // 4 +add_task(cleanup_test); diff --git a/netwerk/test/browser/browser_cookie_filtering_oa.js b/netwerk/test/browser/browser_cookie_filtering_oa.js new file mode 100644 index 000000000000..a3142ed73cd2 --- /dev/null +++ b/netwerk/test/browser/browser_cookie_filtering_oa.js @@ -0,0 +1,187 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +const { + HTTPS_EXAMPLE_ORG, + HTTPS_EXAMPLE_COM, + HTTP_EXAMPLE_COM, + browserTestPath, + waitForAllExpectedTests, + cleanupObservers, + checkExpectedCookies, + triggerSetCookieFromHttp, + triggerSetCookieFromHttpPrivate, + preclean_test, + cleanup_test, +} = ChromeUtils.import("resource://testing-common/cookie_filtering_helper.jsm"); + +// TEST: OriginAttributes +// * example.com OA-changed cookies don't go to example.com & vice-versa +async function test_origin_attributes() { + var suite = oaSuite(); + + // example.com - start content process when loading page + let t2 = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + browserTestPath(HTTPS_EXAMPLE_COM) + ); + let testStruct2 = { + name: "OA example.com", + browser: gBrowser.getBrowserForTab(t2), + tab: t2, + expected: [suite[0], suite[4]], + }; + + // open a tab with altered OA: userContextId + let t4 = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + opening: (function() { + return function() { + // info("calling addTab from lambda"); + gBrowser.selectedTab = BrowserTestUtils.addTab( + gBrowser, + HTTPS_EXAMPLE_COM, + { userContextId: 1 } + ); + }; + })(), + }); + let testStruct4 = { + name: "OA example.com (contextId)", + browser: gBrowser.getBrowserForTab(t4), + tab: t4, + expected: [suite[2], suite[5]], + }; + + // example.com + await SpecialPowers.spawn( + testStruct2.browser, + [testStruct2.expected, testStruct2.name], + checkExpectedCookies + ); + // example.com with different OA: userContextId + await SpecialPowers.spawn( + testStruct4.browser, + [testStruct4.expected, testStruct4.name], + checkExpectedCookies + ); + + await triggerOriginAttributesEmulatedSuite(); + + await SpecialPowers.spawn(testStruct2.browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(testStruct4.browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(testStruct2.browser, [], cleanupObservers); + await SpecialPowers.spawn(testStruct4.browser, [], cleanupObservers); + BrowserTestUtils.removeTab(testStruct2.tab); + BrowserTestUtils.removeTab(testStruct4.tab); +} + +// TEST: Private +// * example.com private cookies don't go to example.com process & vice-v +async function test_private() { + var suite = oaSuite(); + + // example.com + let t2 = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + browserTestPath(HTTPS_EXAMPLE_COM) + ); + let testStruct2 = { + name: "non-priv example.com", + browser: gBrowser.getBrowserForTab(t2), + tab: t2, + expected: [suite[0], suite[4]], + }; + + // private window example.com + let privateBrowserWindow = await BrowserTestUtils.openNewBrowserWindow({ + private: true, + }); + let privateTab = (privateBrowserWindow.gBrowser.selectedTab = BrowserTestUtils.addTab( + privateBrowserWindow.gBrowser, + browserTestPath(HTTPS_EXAMPLE_COM) + )); + let testStruct5 = { + name: "private example.com", + browser: privateBrowserWindow.gBrowser.getBrowserForTab(privateTab), + tab: privateTab, + expected: [suite[3], suite[6]], + }; + await BrowserTestUtils.browserLoaded(testStruct5.tab.linkedBrowser); + + let parentpid = Services.appinfo.processID; + let privatePid = testStruct5.browser.frameLoader.remoteTab.osPid; + let pid = testStruct2.browser.frameLoader.remoteTab.osPid; + ok(parentpid != privatePid, "Parent and private processes are unique"); + ok(parentpid != pid, "Parent and non-private processes are unique"); + ok(privatePid != pid, "Private and non-private processes are unique"); + + // example.com + await SpecialPowers.spawn( + testStruct2.browser, + [testStruct2.expected, testStruct2.name], + checkExpectedCookies + ); + + // example.com private + await SpecialPowers.spawn( + testStruct5.browser, + [testStruct5.expected, testStruct5.name], + checkExpectedCookies + ); + + await triggerOriginAttributesEmulatedSuite(); + + // wait for all tests and cleanup + await SpecialPowers.spawn(testStruct2.browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(testStruct5.browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(testStruct2.browser, [], cleanupObservers); + await SpecialPowers.spawn(testStruct5.browser, [], cleanupObservers); + BrowserTestUtils.removeTab(testStruct2.tab); + BrowserTestUtils.removeTab(testStruct5.tab); + await BrowserTestUtils.closeWindow(privateBrowserWindow); +} + +function oaSuite() { + var suite = []; + suite.push("test-cookie=orgwontsee"); // 0 + suite.push("test-cookie=firstparty"); // 1 + suite.push("test-cookie=usercontext"); // 2 + suite.push("test-cookie=privateonly"); // 3 + suite.push("test-cookie=sentinelcom"); // 4 + suite.push("test-cookie=sentineloa"); // 5 + suite.push("test-cookie=sentinelprivate"); // 6 + return suite; +} + +// emulated because we are not making actual page requests +// we are just directly invoking the api +async function triggerOriginAttributesEmulatedSuite() { + var suite = oaSuite(); + + let uriCom = NetUtil.newURI(HTTPS_EXAMPLE_COM); + triggerSetCookieFromHttp(uriCom, suite[0]); + + // FPD (OA) changed + triggerSetCookieFromHttp(uriCom, suite[1], "foo.com"); + + // context id (OA) changed + triggerSetCookieFromHttp(uriCom, suite[2], "", 1); + + // private + triggerSetCookieFromHttpPrivate(uriCom, suite[3]); + + // sentinels + triggerSetCookieFromHttp(uriCom, suite[4]); + triggerSetCookieFromHttp(uriCom, suite[5], "", 1); + triggerSetCookieFromHttpPrivate(uriCom, suite[6]); +} + +add_task(preclean_test); +add_task(test_origin_attributes); // 4 +add_task(test_private); // 7 +add_task(cleanup_test); diff --git a/netwerk/test/browser/browser_cookie_filtering_subdomain.js b/netwerk/test/browser/browser_cookie_filtering_subdomain.js new file mode 100644 index 000000000000..b44fb5c31036 --- /dev/null +++ b/netwerk/test/browser/browser_cookie_filtering_subdomain.js @@ -0,0 +1,167 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +const { + HTTPS_EXAMPLE_ORG, + HTTPS_EXAMPLE_COM, + HTTP_EXAMPLE_COM, + browserTestPath, + waitForAllExpectedTests, + cleanupObservers, + checkExpectedCookies, + fetchHelper, + preclean_test, + cleanup_test, +} = ChromeUtils.import("resource://testing-common/cookie_filtering_helper.jsm"); + +const HTTPS_SUBDOMAIN_1_EXAMPLE_COM = "https://test1.example.com"; +const HTTP_SUBDOMAIN_1_EXAMPLE_COM = "http://test1.example.com"; +const HTTPS_SUBDOMAIN_2_EXAMPLE_COM = "https://test2.example.com"; +const HTTP_SUBDOMAIN_2_EXAMPLE_COM = "http://test2.example.com"; + +// run suite with content listener +// 1. initializes the content process and observer +// 2. runs the test gamut +// 3. cleans up the content process +async function runSuiteWithContentListener(name, triggerSuiteFunc, expected) { + return async function(browser) { + info("Running content suite: " + name); + await SpecialPowers.spawn(browser, [expected, name], checkExpectedCookies); + await triggerSuiteFunc(); + await SpecialPowers.spawn(browser, [], waitForAllExpectedTests); + await SpecialPowers.spawn(browser, [], cleanupObservers); + info("Complete content suite: " + name); + }; +} + +// TEST: domain receives subdomain cookies +async function test_domain() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTPS_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "test_domain", + triggerSuite, + cookiesFromSuite() + ) + ); +} + +// TEST: insecure domain receives base and sub-domain insecure cookies +async function test_insecure_domain() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTP_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "test_insecure_domain", + triggerSuite, + suiteMatchingDomain(HTTP_EXAMPLE_COM).concat( + suiteMatchingDomain(HTTP_SUBDOMAIN_1_EXAMPLE_COM) + ) + ) + ); +} + +// TEST: subdomain receives base domain and other sub-domain cookies +async function test_subdomain() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTPS_SUBDOMAIN_2_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "test_subdomain", + triggerSuite, + cookiesFromSuite() + ) + ); +} + +// TEST: insecure subdomain receives base and sub-domain insecure cookies +async function test_insecure_subdomain() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: browserTestPath(HTTP_SUBDOMAIN_2_EXAMPLE_COM), + }, + await runSuiteWithContentListener( + "test_insecure_domain", + triggerSuite, + suiteMatchingDomain(HTTP_EXAMPLE_COM).concat( + suiteMatchingDomain(HTTP_SUBDOMAIN_1_EXAMPLE_COM) + ) + ) + ); +} + +function suite() { + var suite = []; + suite.push(["test-cookie=domain", HTTPS_EXAMPLE_COM]); + suite.push(["test-cookie=subdomain", HTTPS_SUBDOMAIN_1_EXAMPLE_COM]); + suite.push(["test-cookie-insecure=insecure_domain", HTTP_EXAMPLE_COM]); + suite.push([ + "test-cookie-insecure=insecure_subdomain", + HTTP_SUBDOMAIN_1_EXAMPLE_COM, + ]); + suite.push(["test-cookie=sentinel", HTTPS_EXAMPLE_COM]); + return suite; +} + +function cookiesFromSuite() { + var cookies = []; + for (var [cookie] of suite()) { + cookies.push(cookie); + } + return cookies; +} + +function suiteMatchingDomain(domain) { + var s = suite(); + var result = []; + for (var [cookie, dom] of s) { + if (dom == domain) { + result.push(cookie); + } + } + return result; +} + +function justSitename(maybeSchemefulMaybeSubdomainSite) { + let mssArray = maybeSchemefulMaybeSubdomainSite.split("://"); + let maybesubdomain = mssArray[mssArray.length - 1]; + let msdArray = maybesubdomain.split("."); + return msdArray.slice(msdArray.length - 2, msdArray.length).join("."); +} + +// triggers set-cookie, which will trigger cookie-changed messages +// messages will be filtered against the cookie list created from above +// only unfiltered messages should make it to the content process +async function triggerSuite() { + let triggerCookies = suite(); + for (var [cookie, schemefulDomain] of triggerCookies) { + let secure = false; + if (schemefulDomain.includes("https")) { + secure = true; + } + + var url = + browserTestPath(schemefulDomain) + "cookie_filtering_resource.sjs"; + await fetchHelper(url, cookie, secure, justSitename(schemefulDomain)); + Services.cookies.removeAll(); // clean cookies across secure/insecure runs + } +} + +add_task(preclean_test); +add_task(test_domain); // 5 +add_task(test_insecure_domain); // 2 +add_task(test_subdomain); // 5 +add_task(test_insecure_subdomain); // 2 +add_task(cleanup_test); diff --git a/netwerk/test/browser/cookie_filtering_helper.jsm b/netwerk/test/browser/cookie_filtering_helper.jsm new file mode 100644 index 000000000000..80a5f6a8bd19 --- /dev/null +++ b/netwerk/test/browser/cookie_filtering_helper.jsm @@ -0,0 +1,176 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); +const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); +const info = console.log; + +var EXPORTED_SYMBOLS = [ + "HTTPS_EXAMPLE_ORG", + "HTTPS_EXAMPLE_COM", + "HTTP_EXAMPLE_COM", + "browserTestPath", + "waitForAllExpectedTests", + "cleanupObservers", + "triggerSetCookieFromHttp", + "triggerSetCookieFromHttpPrivate", + "checkExpectedCookies", + "fetchHelper", + "preclean_test", + "cleanup_test", +]; +var HTTPS_EXAMPLE_ORG = "https://example.org"; +var HTTPS_EXAMPLE_COM = "https://example.com"; +var HTTP_EXAMPLE_COM = "http://example.com"; + +function browserTestPath(uri) { + return uri + "/browser/netwerk/test/browser/"; +} + +function waitForAllExpectedTests() { + return ContentTaskUtils.waitForCondition(() => { + return content.testDone === true; + }); +} + +function cleanupObservers() { + Services.obs.notifyObservers(null, "cookie-content-filter-cleanup"); +} + +async function preclean_test() { + // enable all cookies for the set-cookie trigger via setCookieStringFromHttp + 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", false); + + Services.cookies.removeAll(); +} + +async function cleanup_test() { + Services.prefs.clearUserPref("network.cookie.cookieBehavior"); + Services.prefs.clearUserPref( + "network.cookieJarSettings.unblocked_for_testing" + ); + + Services.prefs.clearUserPref("network.cookie.sameSite.laxByDefault"); + Services.prefs.clearUserPref("network.cookie.sameSite.noneRequiresSecure"); + Services.prefs.clearUserPref("network.cookie.sameSite.schemeful"); + + Services.cookies.removeAll(); +} + +async function fetchHelper(url, cookie, secure, domain = "") { + let headers = new Headers(); + + headers.append("return-set-cookie", cookie); + + if (!secure) { + headers.append("return-insecure-cookie", cookie); + } + + if (domain != "") { + headers.append("return-cookie-domain", domain); + } + + info("fetching " + url); + await fetch(url, { headers }); +} + +// cookie header strings with multiple name=value pairs delimited by \n +// will trigger multiple "cookie-changed" signals +function triggerSetCookieFromHttp(uri, cookie, fpd = "", ucd = 0) { + info("about to trigger set-cookie: " + uri + " " + cookie); + let channel = NetUtil.newChannel({ + uri, + loadUsingSystemPrincipal: true, + contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, + }); + + if (fpd != "") { + channel.loadInfo.originAttributes = { firstPartyDomain: fpd }; + } + + if (ucd != 0) { + channel.loadInfo.originAttributes = { userContextId: ucd }; + } + Services.cookies.setCookieStringFromHttp(uri, cookie, channel); +} + +async function triggerSetCookieFromHttpPrivate(uri, cookie) { + info("about to trigger set-cookie: " + uri + " " + cookie); + let channel = NetUtil.newChannel({ + uri, + loadUsingSystemPrincipal: true, + contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, + }).QueryInterface(Ci.nsIPrivateBrowsingChannel); + channel.loadInfo.originAttributes = { privateBrowsingId: 1 }; + channel.setPrivate(true); + Services.cookies.setCookieStringFromHttp(uri, cookie, channel); +} + +// observer/listener function that will be run on the content processes +// listens and checks for the expected cookies +function checkExpectedCookies(expected, browserName) { + const COOKIE_FILTER_TEST_MESSAGE = "cookie-content-filter-test"; + const COOKIE_FILTER_TEST_CLEANUP = "cookie-content-filter-cleanup"; + + // Counting the expected number of tests is vital to the integrity of these + // tests due to the fact that this test suite relies on triggering tests + // to occur on multiple content processes. + // As such, test modifications/bugs often lead to silent failures. + // Hence, we count to ensure we didn't break anything + // To reduce risk here, we modularize each test as much as possible to + // increase liklihood that a silent failure will trigger a no-test + // error/warning + content.testDone = false; + let testNumber = 0; + + // setup observer that continues listening/testing + function obs(subject, topic) { + // cleanup trigger recieved -> tear down the observer + if (topic == COOKIE_FILTER_TEST_CLEANUP) { + info("cleaning up: " + browserName); + Services.obs.removeObserver(obs, COOKIE_FILTER_TEST_MESSAGE); + Services.obs.removeObserver(obs, COOKIE_FILTER_TEST_CLEANUP); + return; + } + + // test trigger recv'd -> perform test on cookie contents + if (topic == COOKIE_FILTER_TEST_MESSAGE) { + info("Checking if cookie visible: " + browserName); + let result = content.document.cookie; + let resultStr = + "Result " + + result + + " == expected: " + + expected[testNumber] + + " in " + + browserName; + ok(result == expected[testNumber], resultStr); + testNumber++; + if (testNumber >= expected.length) { + info("finishing browser tests: " + browserName); + content.testDone = true; + } + return; + } + + ok(false, "Didn't handle cookie message properly"); // + } + + info("setting up observers: " + browserName); + Services.obs.addObserver(obs, COOKIE_FILTER_TEST_MESSAGE); + Services.obs.addObserver(obs, COOKIE_FILTER_TEST_CLEANUP); +} diff --git a/netwerk/test/browser/cookie_filtering_resource.sjs b/netwerk/test/browser/cookie_filtering_resource.sjs new file mode 100644 index 000000000000..979d56dc9c4a --- /dev/null +++ b/netwerk/test/browser/cookie_filtering_resource.sjs @@ -0,0 +1,35 @@ +/* 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/. */ + +"use strict"; + +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/html", false); + + // configure set-cookie domain + let domain = ""; + if (request.hasHeader("return-cookie-domain")) { + domain = "; Domain=" + request.getHeader("return-cookie-domain"); + } + + // configure set-cookie sameSite + let authStr = "; Secure"; + if (request.hasHeader("return-insecure-cookie")) { + authStr = ""; + } + + // use headers to decide if we have them + if (request.hasHeader("return-set-cookie")) { + response.setHeader( + "Set-Cookie", + request.getHeader("return-set-cookie") + authStr + domain, + false + ); + } + + let body = " true "; + response.write(body); +} diff --git a/netwerk/test/browser/cookie_filtering_secure_resource_com.html b/netwerk/test/browser/cookie_filtering_secure_resource_com.html new file mode 100644 index 000000000000..e25a71964483 --- /dev/null +++ b/netwerk/test/browser/cookie_filtering_secure_resource_com.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/netwerk/test/browser/cookie_filtering_secure_resource_com.html^headers^ b/netwerk/test/browser/cookie_filtering_secure_resource_com.html^headers^ new file mode 100644 index 000000000000..2bdf1180649a --- /dev/null +++ b/netwerk/test/browser/cookie_filtering_secure_resource_com.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-store +Set-Cookie: test-cookie=comhtml diff --git a/netwerk/test/browser/cookie_filtering_secure_resource_org.html b/netwerk/test/browser/cookie_filtering_secure_resource_org.html new file mode 100644 index 000000000000..7221dc370d1e --- /dev/null +++ b/netwerk/test/browser/cookie_filtering_secure_resource_org.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/netwerk/test/browser/cookie_filtering_secure_resource_org.html^headers^ b/netwerk/test/browser/cookie_filtering_secure_resource_org.html^headers^ new file mode 100644 index 000000000000..924c150ccccf --- /dev/null +++ b/netwerk/test/browser/cookie_filtering_secure_resource_org.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-store +Set-Cookie: test-cookie=orghtml diff --git a/netwerk/test/browser/cookie_filtering_square.png b/netwerk/test/browser/cookie_filtering_square.png new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/netwerk/test/browser/cookie_filtering_square.png^headers^ b/netwerk/test/browser/cookie_filtering_square.png^headers^ new file mode 100644 index 000000000000..912856ae4ad3 --- /dev/null +++ b/netwerk/test/browser/cookie_filtering_square.png^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-cache +Set-Cookie: test-cookie=png diff --git a/netwerk/test/moz.build b/netwerk/test/moz.build index 04a799b8dfb5..146144e3dfe8 100644 --- a/netwerk/test/moz.build +++ b/netwerk/test/moz.build @@ -15,6 +15,7 @@ XPCSHELL_TESTS_MANIFESTS += [ ] TESTING_JS_MODULES += [ + "browser/cookie_filtering_helper.jsm", "browser/early_hint_preload_test_helper.jsm", "unit/test_http3_prio_helpers.js", ]