зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1642051 - add PartitioningSkipList to relax partitioning on specific domains; r=baku
Differential Revision: https://phabricator.services.mozilla.com/D81591
This commit is contained in:
Родитель
544e563e54
Коммит
3d1fd6f81d
|
@ -23,6 +23,7 @@
|
|||
#include "nsPIDOMWindow.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
#include "nsScriptSecurityManager.h"
|
||||
#include "PartitioningSkipList.h"
|
||||
|
||||
#define ANTITRACKING_PERM_KEY "3rdPartyStorage"
|
||||
|
||||
|
@ -341,6 +342,11 @@ bool AntiTrackingUtils::CheckStoragePermission(nsIPrincipal* aPrincipal,
|
|||
return false;
|
||||
}
|
||||
|
||||
nsAutoCString targetOrigin;
|
||||
if (NS_WARN_IF(NS_FAILED(targetPrincipal->GetAsciiOrigin(targetOrigin)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> trackingURI;
|
||||
rv = aChannel->GetURI(getter_AddRefs(trackingURI));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
@ -358,6 +364,10 @@ bool AntiTrackingUtils::CheckStoragePermission(nsIPrincipal* aPrincipal,
|
|||
|
||||
uint32_t unusedReason = 0;
|
||||
|
||||
if (PartitioningSkipList::Check(targetOrigin, trackingOrigin)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return AntiTrackingUtils::CheckStoragePermission(
|
||||
targetPrincipal, type, NS_UsePrivateBrowsing(aChannel), &unusedReason,
|
||||
unusedReason);
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#include "PartitioningSkipList.h"
|
||||
|
||||
#include "AntiTrackingLog.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace {
|
||||
|
||||
inline void CreateSkipListKey(const nsACString& aFirstPartyOrigin,
|
||||
const nsACString& aThirdPartyOrigin,
|
||||
nsACString& aSkipListKey) {
|
||||
MOZ_ASSERT(!aFirstPartyOrigin.IsEmpty());
|
||||
MOZ_ASSERT(!aThirdPartyOrigin.IsEmpty());
|
||||
|
||||
aSkipListKey.Assign(aFirstPartyOrigin);
|
||||
aSkipListKey.Append(",");
|
||||
aSkipListKey.Append(aThirdPartyOrigin);
|
||||
}
|
||||
|
||||
inline void CreateSkipListKey(nsIURI* aFirstPartyURI, nsIURI* aThirdPartyURI,
|
||||
nsACString& aSkipListKey) {
|
||||
MOZ_ASSERT(aFirstPartyURI);
|
||||
MOZ_ASSERT(aThirdPartyURI);
|
||||
|
||||
nsAutoCString firstPartyOrigin, thirdPartyOrigin;
|
||||
nsContentUtils::GetASCIIOrigin(aFirstPartyURI, firstPartyOrigin);
|
||||
nsContentUtils::GetASCIIOrigin(aThirdPartyURI, thirdPartyOrigin);
|
||||
|
||||
CreateSkipListKey(firstPartyOrigin, thirdPartyOrigin, aSkipListKey);
|
||||
}
|
||||
|
||||
StaticRefPtr<PartitioningSkipList> gPartitioningSkipList;
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(PartitioningSkipList, nsIPartitioningSkipListObserver)
|
||||
|
||||
bool PartitioningSkipList::Check(const nsACString& aFirstPartyOrigin,
|
||||
const nsACString& aThirdPartyOrigin) {
|
||||
if (aFirstPartyOrigin.IsEmpty() || aThirdPartyOrigin.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(("Check partitioning skip list for url %s and %s",
|
||||
PromiseFlatCString(aFirstPartyOrigin).get(),
|
||||
PromiseFlatCString(aThirdPartyOrigin).get()));
|
||||
|
||||
nsAutoCString key;
|
||||
CreateSkipListKey(aFirstPartyOrigin, aThirdPartyOrigin, key);
|
||||
|
||||
if (GetOrCreate()->mSkipList.Contains(key)) {
|
||||
LOG(("URI is in skip list"));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PartitioningSkipList* PartitioningSkipList::GetOrCreate() {
|
||||
if (!gPartitioningSkipList) {
|
||||
gPartitioningSkipList = new PartitioningSkipList();
|
||||
gPartitioningSkipList->Init();
|
||||
|
||||
RunOnShutdown([&] {
|
||||
gPartitioningSkipList->Shutdown();
|
||||
gPartitioningSkipList = nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
return gPartitioningSkipList;
|
||||
}
|
||||
|
||||
nsresult PartitioningSkipList::Init() {
|
||||
mService = do_GetService("@mozilla.org/partitioning/skip-list-service;1");
|
||||
if (NS_WARN_IF(!mService)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mService->RegisterAndRunSkipListObserver(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void PartitioningSkipList::Shutdown() {
|
||||
if (mService) {
|
||||
mService->UnregisterSkipListObserver(this);
|
||||
mService = nullptr;
|
||||
}
|
||||
|
||||
mSkipList.Clear();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PartitioningSkipList::OnSkipListUpdate(const nsACString& aList) {
|
||||
mSkipList.Clear();
|
||||
|
||||
nsresult rv;
|
||||
for (const nsACString& item : aList.Split(';')) {
|
||||
auto origins = item.Split(',');
|
||||
|
||||
nsCOMPtr<nsIURI> firstPartyURI;
|
||||
rv = NS_NewURI(getter_AddRefs(firstPartyURI), origins.Get(0));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsIURI> thirdPartyURI;
|
||||
rv = NS_NewURI(getter_AddRefs(thirdPartyURI), origins.Get(1));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString key;
|
||||
CreateSkipListKey(firstPartyURI, thirdPartyURI, key);
|
||||
LOG(("onSkipListUpdate: %s", key.get()));
|
||||
|
||||
mSkipList.AppendElement(key);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_PartitioningSkipList_h
|
||||
#define mozilla_PartitioningSkipList_h
|
||||
|
||||
#include "nsIPartitioningSkipListService.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
|
||||
class nsIChannel;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PartitioningSkipList : public nsIPartitioningSkipListObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPARTITIONINGSKIPLISTOBSERVER
|
||||
|
||||
static bool Check(const nsACString& aFirstPartyOrigin,
|
||||
const nsACString& aThirdPartyOrigin);
|
||||
|
||||
private:
|
||||
static PartitioningSkipList* GetOrCreate();
|
||||
|
||||
PartitioningSkipList() = default;
|
||||
virtual ~PartitioningSkipList() = default;
|
||||
|
||||
nsresult Init();
|
||||
void Shutdown();
|
||||
|
||||
nsCOMPtr<nsIPartitioningSkipListService> mService;
|
||||
nsTArray<nsCString> mSkipList;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_PartitioningSkipList_h
|
|
@ -56,6 +56,7 @@ UNIFIED_SOURCES += [
|
|||
'ContentBlockingNotifier.cpp',
|
||||
'ContentBlockingUserInteraction.cpp',
|
||||
'DynamicFpiRedirectHeuristic.cpp',
|
||||
'PartitioningSkipList.cpp',
|
||||
'RejectForeignAllowList.cpp',
|
||||
'SettingsChangeObserver.cpp',
|
||||
'StorageAccess.cpp',
|
||||
|
|
|
@ -106,7 +106,7 @@ support-files = tracker.js
|
|||
[browser_storageAccessRemovalNavigateTopframe.js]
|
||||
[browser_storageAccessSandboxed.js]
|
||||
[browser_storageAccessThirdPartyChecks.js]
|
||||
[browser_storageAccessWithDynamicFpiHeuristics.js]
|
||||
[browser_storageAccessWithDynamicFpi.js]
|
||||
[browser_storageAccessWithHeuristics.js]
|
||||
[browser_networkIsolation.js]
|
||||
[browser_allowPermissionForTracker.js]
|
||||
|
|
|
@ -0,0 +1,436 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/* import-globals-from head.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { RemoteSettings } = ChromeUtils.import(
|
||||
"resource://services-settings/remote-settings.js"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"peuService",
|
||||
"@mozilla.org/partitioning/skip-list-service;1",
|
||||
"nsIPartitioningSkipListService"
|
||||
);
|
||||
|
||||
const TEST_REDIRECT_TOP_PAGE =
|
||||
TEST_3RD_PARTY_DOMAIN + TEST_PATH + "redirect.sjs?" + TEST_TOP_PAGE;
|
||||
const TEST_REDIRECT_3RD_PARTY_PAGE =
|
||||
TEST_DOMAIN + TEST_PATH + "redirect.sjs?" + TEST_3RD_PARTY_PARTITIONED_PAGE;
|
||||
|
||||
const COLLECTION_NAME = "partitioning-exempt-urls";
|
||||
const SKIP_LIST_PREF_NAME = "privacy.restrict3rdpartystorage.skip_list";
|
||||
|
||||
async function cleanup() {
|
||||
Services.prefs.clearUserPref(SKIP_LIST_PREF_NAME);
|
||||
await new Promise(resolve => {
|
||||
Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
|
||||
resolve()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.flushPrefEnv();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[
|
||||
"network.cookie.cookieBehavior",
|
||||
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
|
||||
],
|
||||
["privacy.restrict3rdpartystorage.heuristic.redirect", false],
|
||||
["privacy.trackingprotection.enabled", false],
|
||||
["privacy.trackingprotection.pbmode.enabled", false],
|
||||
["privacy.trackingprotection.annotate_channels", true],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(cleanup);
|
||||
});
|
||||
|
||||
function executeContentScript(browser, callback, options = {}) {
|
||||
return SpecialPowers.spawn(
|
||||
browser,
|
||||
[
|
||||
{
|
||||
callback: callback.toString(),
|
||||
...options,
|
||||
},
|
||||
],
|
||||
obj => {
|
||||
return new content.Promise(async resolve => {
|
||||
if (obj.page) {
|
||||
// third-party
|
||||
let ifr = content.document.createElement("iframe");
|
||||
ifr.onload = async () => {
|
||||
info("Sending code to the 3rd party content");
|
||||
ifr.contentWindow.postMessage(
|
||||
{ cb: obj.callback, value: obj.value },
|
||||
"*"
|
||||
);
|
||||
};
|
||||
|
||||
content.addEventListener("message", event => resolve(event.data), {
|
||||
once: true,
|
||||
});
|
||||
|
||||
content.document.body.appendChild(ifr);
|
||||
ifr.src = obj.page;
|
||||
} else {
|
||||
// first-party
|
||||
let runnableStr = `(() => {return (${obj.callback});})();`;
|
||||
let runnable = eval(runnableStr); // eslint-disable-line no-eval
|
||||
resolve(await runnable.call(content, content, obj.value));
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function readNetworkCookie(win) {
|
||||
return win
|
||||
.fetch("cookies.sjs")
|
||||
.then(r => r.text())
|
||||
.then(text => {
|
||||
return text.substring("cookie:foopy=".length);
|
||||
});
|
||||
}
|
||||
|
||||
async function writeNetworkCookie(win, value) {
|
||||
await win.fetch("cookies.sjs?" + value).then(r => r.text());
|
||||
return true;
|
||||
}
|
||||
|
||||
function createDataInFirstParty(browser, value) {
|
||||
return executeContentScript(browser, writeNetworkCookie, { value });
|
||||
}
|
||||
function getDataFromFirstParty(browser) {
|
||||
return executeContentScript(browser, readNetworkCookie, {});
|
||||
}
|
||||
function createDataInThirdParty(browser, value) {
|
||||
return executeContentScript(browser, writeNetworkCookie, {
|
||||
page: TEST_3RD_PARTY_PARTITIONED_PAGE,
|
||||
value,
|
||||
});
|
||||
}
|
||||
function getDataFromThirdParty(browser) {
|
||||
return executeContentScript(browser, readNetworkCookie, {
|
||||
page: TEST_3RD_PARTY_PARTITIONED_PAGE,
|
||||
});
|
||||
}
|
||||
|
||||
async function redirectWithUserInteraction(browser, url, wait = null) {
|
||||
await executeContentScript(
|
||||
browser,
|
||||
(content, value) => {
|
||||
content.document.userInteractionForTesting();
|
||||
|
||||
let link = content.document.createElement("a");
|
||||
link.appendChild(content.document.createTextNode("click me!"));
|
||||
link.href = value;
|
||||
content.document.body.appendChild(link);
|
||||
link.click();
|
||||
},
|
||||
{
|
||||
value: url,
|
||||
}
|
||||
);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, wait || url);
|
||||
}
|
||||
|
||||
async function checkData(browser, options) {
|
||||
if ("firstParty" in options) {
|
||||
is(
|
||||
await getDataFromFirstParty(browser),
|
||||
options.firstParty,
|
||||
"correct first-party data"
|
||||
);
|
||||
}
|
||||
if ("thirdParty" in options) {
|
||||
is(
|
||||
await getDataFromThirdParty(browser),
|
||||
options.thirdParty,
|
||||
"correct third-party data"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async function testRedirectHeuristic() {
|
||||
info("Starting Dynamic FPI Redirect Heuristic test...");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["privacy.restrict3rdpartystorage.heuristic.recently_visited", true]],
|
||||
});
|
||||
|
||||
// mark third-party as tracker
|
||||
await UrlClassifierTestUtils.addTestTrackers();
|
||||
|
||||
info("Creating a new tab");
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
info("initializing...");
|
||||
await checkData(browser, { firstParty: "", thirdParty: "" });
|
||||
|
||||
await Promise.all([
|
||||
createDataInFirstParty(browser, "firstParty"),
|
||||
createDataInThirdParty(browser, "thirdParty"),
|
||||
]);
|
||||
|
||||
await checkData(browser, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "",
|
||||
});
|
||||
|
||||
info("load third-party content as first-party");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_3RD_PARTY_PAGE,
|
||||
TEST_3RD_PARTY_PARTITIONED_PAGE
|
||||
);
|
||||
|
||||
await checkData(browser, { firstParty: "" });
|
||||
await createDataInFirstParty(browser, "heuristicFirstParty");
|
||||
await checkData(browser, { firstParty: "heuristicFirstParty" });
|
||||
|
||||
info("redirect back to first-party page");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_TOP_PAGE,
|
||||
TEST_TOP_PAGE
|
||||
);
|
||||
|
||||
info("third-party tracker should NOT able to access first-party data");
|
||||
await checkData(browser, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "",
|
||||
});
|
||||
|
||||
// remove third-party from tracker
|
||||
await UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
|
||||
info("load third-party content as first-party");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_3RD_PARTY_PAGE,
|
||||
TEST_3RD_PARTY_PARTITIONED_PAGE
|
||||
);
|
||||
|
||||
await checkData(browser, {
|
||||
firstParty: "heuristicFirstParty",
|
||||
});
|
||||
|
||||
info("redirect back to first-party page");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_TOP_PAGE,
|
||||
TEST_TOP_PAGE
|
||||
);
|
||||
|
||||
info("third-party page should able to access first-party data");
|
||||
await checkData(browser, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "heuristicFirstParty",
|
||||
});
|
||||
|
||||
info("Removing the tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
class UpdateEvent extends EventTarget {}
|
||||
function waitForEvent(element, eventName) {
|
||||
return new Promise(function(resolve) {
|
||||
element.addEventListener(eventName, e => resolve(e.detail), { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function testSkipListPref() {
|
||||
info("Starting Dynamic FPI skip list test pref");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["privacy.restrict3rdpartystorage.heuristic.recently_visited", false],
|
||||
],
|
||||
});
|
||||
|
||||
info("Creating new tabs");
|
||||
let tabThirdParty = BrowserTestUtils.addTab(
|
||||
gBrowser,
|
||||
TEST_3RD_PARTY_PARTITIONED_PAGE
|
||||
);
|
||||
gBrowser.selectedTab = tabThirdParty;
|
||||
|
||||
let browserThirdParty = gBrowser.getBrowserForTab(tabThirdParty);
|
||||
await BrowserTestUtils.browserLoaded(browserThirdParty);
|
||||
|
||||
let tabFirstParty = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
|
||||
gBrowser.selectedTab = tabFirstParty;
|
||||
|
||||
let browserFirstParty = gBrowser.getBrowserForTab(tabFirstParty);
|
||||
await BrowserTestUtils.browserLoaded(browserFirstParty);
|
||||
|
||||
info("initializing...");
|
||||
await Promise.all([
|
||||
checkData(browserFirstParty, { firstParty: "", thirdParty: "" }),
|
||||
checkData(browserThirdParty, { firstParty: "" }),
|
||||
]);
|
||||
|
||||
info("fill default data");
|
||||
await Promise.all([
|
||||
createDataInFirstParty(browserFirstParty, "firstParty"),
|
||||
createDataInThirdParty(browserFirstParty, "thirdParty"),
|
||||
createDataInFirstParty(browserThirdParty, "SkipListFirstParty"),
|
||||
]);
|
||||
|
||||
info("check data");
|
||||
await Promise.all([
|
||||
checkData(browserFirstParty, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "thirdParty",
|
||||
}),
|
||||
checkData(browserThirdParty, { firstParty: "SkipListFirstParty" }),
|
||||
]);
|
||||
|
||||
info("set skip list pref");
|
||||
Services.prefs.setStringPref(
|
||||
SKIP_LIST_PREF_NAME,
|
||||
`${TEST_DOMAIN},${TEST_3RD_PARTY_DOMAIN}`
|
||||
);
|
||||
|
||||
info("check data");
|
||||
await Promise.all([
|
||||
checkData(browserFirstParty, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "SkipListFirstParty",
|
||||
}),
|
||||
checkData(browserThirdParty, { firstParty: "SkipListFirstParty" }),
|
||||
]);
|
||||
|
||||
info("Removing the tab");
|
||||
BrowserTestUtils.removeTab(tabFirstParty);
|
||||
BrowserTestUtils.removeTab(tabThirdParty);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(async function testSkipListRemoteSettings() {
|
||||
info("Starting Dynamic FPI skip list test (remote settings)");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["privacy.restrict3rdpartystorage.heuristic.recently_visited", false],
|
||||
],
|
||||
});
|
||||
|
||||
// Make sure we have a pref initially, since the skip list service requires it.
|
||||
Services.prefs.setStringPref(SKIP_LIST_PREF_NAME, "");
|
||||
|
||||
// Add some initial data
|
||||
let db = await RemoteSettings(COLLECTION_NAME).db;
|
||||
await db.importChanges({}, 42, []);
|
||||
|
||||
// make peuSerivce start working by calling registerAndRunSkipListObserver
|
||||
let updateEvent = new UpdateEvent();
|
||||
let obs = data => {
|
||||
let event = new CustomEvent("update", { detail: data });
|
||||
updateEvent.dispatchEvent(event);
|
||||
};
|
||||
let promise = waitForEvent(updateEvent, "update");
|
||||
peuService.registerAndRunSkipListObserver(obs);
|
||||
await promise;
|
||||
|
||||
info("Creating new tabs");
|
||||
let tabThirdParty = BrowserTestUtils.addTab(
|
||||
gBrowser,
|
||||
TEST_3RD_PARTY_PARTITIONED_PAGE
|
||||
);
|
||||
gBrowser.selectedTab = tabThirdParty;
|
||||
|
||||
let browserThirdParty = gBrowser.getBrowserForTab(tabThirdParty);
|
||||
await BrowserTestUtils.browserLoaded(browserThirdParty);
|
||||
|
||||
let tabFirstParty = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
|
||||
gBrowser.selectedTab = tabFirstParty;
|
||||
|
||||
let browserFirstParty = gBrowser.getBrowserForTab(tabFirstParty);
|
||||
await BrowserTestUtils.browserLoaded(browserFirstParty);
|
||||
|
||||
info("initializing...");
|
||||
await Promise.all([
|
||||
checkData(browserFirstParty, { firstParty: "", thirdParty: "" }),
|
||||
checkData(browserThirdParty, { firstParty: "" }),
|
||||
]);
|
||||
|
||||
info("fill default data");
|
||||
await Promise.all([
|
||||
createDataInFirstParty(browserFirstParty, "firstParty"),
|
||||
createDataInThirdParty(browserFirstParty, "thirdParty"),
|
||||
createDataInFirstParty(browserThirdParty, "SkipListFirstParty"),
|
||||
]);
|
||||
|
||||
info("check data");
|
||||
await Promise.all([
|
||||
checkData(browserFirstParty, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "thirdParty",
|
||||
}),
|
||||
checkData(browserThirdParty, { firstParty: "SkipListFirstParty" }),
|
||||
]);
|
||||
|
||||
info("set skip list remote settings");
|
||||
|
||||
// set records
|
||||
promise = waitForEvent(updateEvent, "update");
|
||||
await RemoteSettings(COLLECTION_NAME).emit("sync", {
|
||||
data: {
|
||||
current: [
|
||||
{
|
||||
id: "1",
|
||||
last_modified: 100000000000000000001,
|
||||
firstPartyOrigin: TEST_DOMAIN,
|
||||
thirdPartyOrigin: TEST_3RD_PARTY_DOMAIN,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
let list = await promise;
|
||||
is(
|
||||
list,
|
||||
`${TEST_DOMAIN},${TEST_3RD_PARTY_DOMAIN}`,
|
||||
"skip list is correctly set"
|
||||
);
|
||||
|
||||
info("check data");
|
||||
await Promise.all([
|
||||
checkData(browserFirstParty, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "SkipListFirstParty",
|
||||
}),
|
||||
checkData(browserThirdParty, { firstParty: "SkipListFirstParty" }),
|
||||
]);
|
||||
|
||||
info("Removing the tab");
|
||||
BrowserTestUtils.removeTab(tabFirstParty);
|
||||
BrowserTestUtils.removeTab(tabThirdParty);
|
||||
|
||||
promise = waitForEvent(updateEvent, "update");
|
||||
await RemoteSettings(COLLECTION_NAME).emit("sync", {
|
||||
data: {
|
||||
current: [],
|
||||
},
|
||||
});
|
||||
is(await promise, "", "skip list is cleared");
|
||||
|
||||
peuService.unregisterSkipListObserver(obs);
|
||||
await cleanup();
|
||||
});
|
|
@ -1,223 +0,0 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/* import-globals-from head.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_REDIRECT_TOP_PAGE =
|
||||
TEST_3RD_PARTY_DOMAIN + TEST_PATH + "redirect.sjs?" + TEST_TOP_PAGE;
|
||||
const TEST_REDIRECT_3RD_PARTY_PAGE =
|
||||
TEST_DOMAIN + TEST_PATH + "redirect.sjs?" + TEST_3RD_PARTY_PARTITIONED_PAGE;
|
||||
|
||||
async function cleanup() {
|
||||
await new Promise(resolve => {
|
||||
Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value =>
|
||||
resolve()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async () => {
|
||||
await SpecialPowers.flushPrefEnv();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[
|
||||
"network.cookie.cookieBehavior",
|
||||
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
|
||||
],
|
||||
["privacy.restrict3rdpartystorage.heuristic.redirect", false],
|
||||
["privacy.trackingprotection.enabled", false],
|
||||
["privacy.trackingprotection.pbmode.enabled", false],
|
||||
["privacy.trackingprotection.annotate_channels", true],
|
||||
],
|
||||
});
|
||||
registerCleanupFunction(cleanup);
|
||||
});
|
||||
|
||||
function executeContentScript(browser, callback, options = {}) {
|
||||
return SpecialPowers.spawn(
|
||||
browser,
|
||||
[
|
||||
{
|
||||
callback: callback.toString(),
|
||||
...options,
|
||||
},
|
||||
],
|
||||
obj => {
|
||||
return new content.Promise(async resolve => {
|
||||
if (obj.page) {
|
||||
// third-party
|
||||
let ifr = content.document.createElement("iframe");
|
||||
ifr.onload = async () => {
|
||||
info("Sending code to the 3rd party content");
|
||||
ifr.contentWindow.postMessage({ cb: obj.callback }, "*");
|
||||
};
|
||||
|
||||
content.addEventListener("message", event => resolve(event.data), {
|
||||
once: true,
|
||||
});
|
||||
|
||||
content.document.body.appendChild(ifr);
|
||||
ifr.src = obj.page;
|
||||
} else {
|
||||
// first-party
|
||||
let runnableStr = `(() => {return (${obj.callback});})();`;
|
||||
let runnable = eval(runnableStr); // eslint-disable-line no-eval
|
||||
resolve(await runnable.call(content, content, obj.value));
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function readNetworkCookie(win) {
|
||||
return win
|
||||
.fetch("cookies.sjs")
|
||||
.then(r => r.text())
|
||||
.then(text => {
|
||||
return text.substring("cookie:foopy=".length);
|
||||
});
|
||||
}
|
||||
|
||||
async function writeNetworkCookie(win, value) {
|
||||
await win.fetch("cookies.sjs?" + value).then(r => r.text());
|
||||
return true;
|
||||
}
|
||||
|
||||
function createDataInFirstParty(browser, value) {
|
||||
return executeContentScript(browser, writeNetworkCookie, { value });
|
||||
}
|
||||
function getDataFromFirstParty(browser) {
|
||||
return executeContentScript(browser, readNetworkCookie, {});
|
||||
}
|
||||
function createDataInThirdParty(browser, value) {
|
||||
return executeContentScript(browser, writeNetworkCookie, {
|
||||
page: TEST_3RD_PARTY_PARTITIONED_PAGE,
|
||||
value,
|
||||
});
|
||||
}
|
||||
function getDataFromThirdParty(browser) {
|
||||
return executeContentScript(browser, readNetworkCookie, {
|
||||
page: TEST_3RD_PARTY_PARTITIONED_PAGE,
|
||||
});
|
||||
}
|
||||
|
||||
async function redirectWithUserInteraction(browser, url, wait = null) {
|
||||
await executeContentScript(
|
||||
browser,
|
||||
(content, value) => {
|
||||
content.document.userInteractionForTesting();
|
||||
|
||||
let link = content.document.createElement("a");
|
||||
link.appendChild(content.document.createTextNode("click me!"));
|
||||
link.href = value;
|
||||
content.document.body.appendChild(link);
|
||||
link.click();
|
||||
},
|
||||
{
|
||||
value: url,
|
||||
}
|
||||
);
|
||||
await BrowserTestUtils.browserLoaded(browser, false, wait || url);
|
||||
}
|
||||
|
||||
async function checkData(browser, options) {
|
||||
if ("firstParty" in options) {
|
||||
is(
|
||||
await getDataFromFirstParty(browser),
|
||||
options.firstParty,
|
||||
"currect first-party data"
|
||||
);
|
||||
}
|
||||
if ("thirdParty" in options) {
|
||||
is(
|
||||
await getDataFromThirdParty(browser),
|
||||
options.thirdParty,
|
||||
"currect third-party data"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
add_task(async () => {
|
||||
info("Starting Dynamic FPI Redirect Heuristic test...");
|
||||
|
||||
// mark third-party as tracker
|
||||
await UrlClassifierTestUtils.addTestTrackers();
|
||||
|
||||
info("Creating a new tab");
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
info("initializing...");
|
||||
await checkData(browser, { firstParty: "", thirdParty: "" });
|
||||
|
||||
await Promise.all([
|
||||
createDataInFirstParty(browser, "firstParty"),
|
||||
createDataInThirdParty(browser, "thirdParty"),
|
||||
]);
|
||||
|
||||
await checkData(browser, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "",
|
||||
});
|
||||
|
||||
info("load third-party content as first-party");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_3RD_PARTY_PAGE,
|
||||
TEST_3RD_PARTY_PARTITIONED_PAGE
|
||||
);
|
||||
|
||||
await checkData(browser, { firstParty: "" });
|
||||
await createDataInFirstParty(browser, "heuristicFirstParty");
|
||||
await checkData(browser, { firstParty: "heuristicFirstParty" });
|
||||
|
||||
info("redirect back to first-party page");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_TOP_PAGE,
|
||||
TEST_TOP_PAGE
|
||||
);
|
||||
|
||||
info("third-party tracker should NOT able to access first-party data");
|
||||
await checkData(browser, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "",
|
||||
});
|
||||
|
||||
// remove third-party from tracker
|
||||
await UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
|
||||
info("load third-party content as first-party");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_3RD_PARTY_PAGE,
|
||||
TEST_3RD_PARTY_PARTITIONED_PAGE
|
||||
);
|
||||
|
||||
await checkData(browser, {
|
||||
firstParty: "heuristicFirstParty",
|
||||
});
|
||||
|
||||
info("redirect back to first-party page");
|
||||
await redirectWithUserInteraction(
|
||||
browser,
|
||||
TEST_REDIRECT_TOP_PAGE,
|
||||
TEST_TOP_PAGE
|
||||
);
|
||||
|
||||
info("third-party page should able to access first-party data");
|
||||
await checkData(browser, {
|
||||
firstParty: "firstParty",
|
||||
thirdParty: "heuristicFirstParty",
|
||||
});
|
||||
|
||||
info("Removing the tab");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
Загрузка…
Ссылка в новой задаче