mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 07:13:20 +00:00
Bug 1713203 - Added check for host, scheme and origin attributes check before cookie change broadcast to content processes r=dveditz,dragana,nika
Differential Revision: https://phabricator.services.mozilla.com/D146346
This commit is contained in:
parent
90e860f7bd
commit
6ff16b6810
@ -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<nsICookie> 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();
|
||||
|
@ -158,6 +158,8 @@ Cookie::GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const Cookie& Cookie::AsCookie() { return *this; }
|
||||
|
||||
const nsCString& Cookie::GetFilePath() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
@ -191,6 +191,13 @@ IPCResult CookieServiceChild::RecvAddCookie(const CookieStruct& aCookie,
|
||||
const OriginAttributes& aAttrs) {
|
||||
RefPtr<Cookie> cookie = Cookie::Create(aCookie, aAttrs);
|
||||
RecordDocumentCookie(cookie, aAttrs);
|
||||
|
||||
// signal test code to check their cookie list
|
||||
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
||||
if (obsService) {
|
||||
obsService->NotifyObservers(nullptr, "cookie-content-filter-test", nullptr);
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -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<OriginAttributes> attrsList;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
nsCOMPtr<nsICookie> xpcCookie = do_QueryElementAt(aCookieList, i);
|
||||
auto* cookie = static_cast<Cookie*>(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<Cookie*>(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<Cookie*>(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<bool> allowSecure = mCookieKeysInContent.MaybeGet(cookieKey)) {
|
||||
return (!cookie.IsSecure() || *allowSecure);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) {
|
||||
nsCOMPtr<nsIURI> 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<mozIThirdPartyUtil> 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<Cookie*> 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<Cookie*>& 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<Cookie*> 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
|
||||
|
@ -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<Cookie*>& aFoundCookieList,
|
||||
nsTArray<CookieStruct>& aCookiesList);
|
||||
|
||||
nsCOMPtr<nsIEffectiveTLDService> mTLDService;
|
||||
RefPtr<CookieService> mCookieService;
|
||||
bool mProcessingCookie;
|
||||
nsTHashMap<CookieKey, bool> mCookieKeysInContent;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -101,3 +101,7 @@ add_task(async function() {
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
add_task(async function cleanup() {
|
||||
Services.prefs.clearUserPref("dom.securecontext.allowlist");
|
||||
});
|
||||
|
182
netwerk/test/browser/browser_cookie_filtering_basic.js
Normal file
182
netwerk/test/browser/browser_cookie_filtering_basic.js
Normal file
@ -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);
|
144
netwerk/test/browser/browser_cookie_filtering_cross_origin.js
Normal file
144
netwerk/test/browser/browser_cookie_filtering_cross_origin.js
Normal file
@ -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);
|
103
netwerk/test/browser/browser_cookie_filtering_insecure.js
Normal file
103
netwerk/test/browser/browser_cookie_filtering_insecure.js
Normal file
@ -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);
|
187
netwerk/test/browser/browser_cookie_filtering_oa.js
Normal file
187
netwerk/test/browser/browser_cookie_filtering_oa.js
Normal file
@ -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);
|
167
netwerk/test/browser/browser_cookie_filtering_subdomain.js
Normal file
167
netwerk/test/browser/browser_cookie_filtering_subdomain.js
Normal file
@ -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);
|
176
netwerk/test/browser/cookie_filtering_helper.jsm
Normal file
176
netwerk/test/browser/cookie_filtering_helper.jsm
Normal file
@ -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);
|
||||
}
|
35
netwerk/test/browser/cookie_filtering_resource.sjs
Normal file
35
netwerk/test/browser/cookie_filtering_resource.sjs
Normal file
@ -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 = "<!DOCTYPE html> <html> <body> true </body> </html>";
|
||||
response.write(body);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="https://example.com/browser/netwerk/test/browser/cookie_filtering_square.png" width="100px">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,2 @@
|
||||
Cache-Control: no-store
|
||||
Set-Cookie: test-cookie=comhtml
|
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<img src="https://example.org/browser/netwerk/test/browser/cookie_filtering_square.png" width="100px">
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,2 @@
|
||||
Cache-Control: no-store
|
||||
Set-Cookie: test-cookie=orghtml
|
0
netwerk/test/browser/cookie_filtering_square.png
Normal file
0
netwerk/test/browser/cookie_filtering_square.png
Normal file
@ -0,0 +1,2 @@
|
||||
Cache-Control: no-cache
|
||||
Set-Cookie: test-cookie=png
|
@ -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",
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user