forked from mirrors/gecko-dev
The patch adds a test function to nsIRFPService.idl for generating fingerprinting randomization key for the given channel. It also adds tests for ensuring the ClearDataService properly clears the random key. Differential Revision: https://phabricator.services.mozilla.com/D196303
490 lines
14 KiB
JavaScript
490 lines
14 KiB
JavaScript
let { ForgetAboutSite } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/ForgetAboutSite.sys.mjs"
|
|
);
|
|
|
|
requestLongerTimeout(2);
|
|
|
|
const TEST_DOMAIN = "https://example.com";
|
|
const TEST_DOMAIN_ANOTHER = "https://example.org";
|
|
const TEST_DOMAIN_THIRD = "https://example.net";
|
|
|
|
const TEST_PAGE =
|
|
getRootDirectory(gTestPath).replace(
|
|
"chrome://mochitests/content",
|
|
TEST_DOMAIN
|
|
) + "testPage.html";
|
|
const TEST_DOMAIN_ANOTHER_PAGE =
|
|
getRootDirectory(gTestPath).replace(
|
|
"chrome://mochitests/content",
|
|
TEST_DOMAIN_ANOTHER
|
|
) + "testPage.html";
|
|
const TEST_DOMAIN_THIRD_PAGE =
|
|
getRootDirectory(gTestPath).replace(
|
|
"chrome://mochitests/content",
|
|
TEST_DOMAIN_THIRD
|
|
) + "testPage.html";
|
|
|
|
/**
|
|
* A helper function to get the random key in a hex string format and test if
|
|
* the random key works properly.
|
|
*
|
|
* @param {Browser} browser The browser element of the testing tab.
|
|
* @param {string} firstPartyDomain The first-party domain loaded on the tab
|
|
* @param {string} thirdPartyDomain The third-party domain to test
|
|
* @returns {string} The random key hex string
|
|
*/
|
|
async function getRandomKeyHexFromBrowser(
|
|
browser,
|
|
firstPartyDomain,
|
|
thirdPartyDomain
|
|
) {
|
|
// Get the key from the cookieJarSettings of the browser element.
|
|
let key = browser.cookieJarSettings.fingerprintingRandomizationKey;
|
|
let keyHex = key.map(bytes => bytes.toString(16).padStart(2, "0")).join("");
|
|
|
|
// Get the key from the cookieJarSettings of the top-level document.
|
|
let keyTop = await SpecialPowers.spawn(browser, [], _ => {
|
|
return content.document.cookieJarSettings.fingerprintingRandomizationKey;
|
|
});
|
|
let keyTopHex = keyTop
|
|
.map(bytes => bytes.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
|
|
is(
|
|
keyTopHex,
|
|
keyHex,
|
|
"The fingerprinting random key should match between the browser element and the top-level document."
|
|
);
|
|
|
|
// Get the key from the cookieJarSettings of an about:blank iframe.
|
|
let keyAboutBlank = await SpecialPowers.spawn(browser, [], async _ => {
|
|
let ifr = content.document.createElement("iframe");
|
|
|
|
let loaded = new content.Promise(resolve => {
|
|
ifr.onload = resolve;
|
|
});
|
|
content.document.body.appendChild(ifr);
|
|
ifr.src = "about:blank";
|
|
|
|
await loaded;
|
|
|
|
return SpecialPowers.spawn(ifr, [], _ => {
|
|
return content.document.cookieJarSettings.fingerprintingRandomizationKey;
|
|
});
|
|
});
|
|
|
|
let keyAboutBlankHex = keyAboutBlank
|
|
.map(bytes => bytes.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
is(
|
|
keyAboutBlankHex,
|
|
keyHex,
|
|
"The fingerprinting random key should match between the browser element and the about:blank iframe document."
|
|
);
|
|
|
|
// Get the key from the cookieJarSettings of the javascript URL iframe
|
|
// document.
|
|
let keyJavascriptURL = await SpecialPowers.spawn(browser, [], async _ => {
|
|
let ifr = content.document.getElementById("testFrame");
|
|
|
|
return ifr.contentDocument.cookieJarSettings.fingerprintingRandomizationKey;
|
|
});
|
|
|
|
let keyJavascriptURLHex = keyJavascriptURL
|
|
.map(bytes => bytes.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
is(
|
|
keyJavascriptURLHex,
|
|
keyHex,
|
|
"The fingerprinting random key should match between the browser element and the javascript URL iframe document."
|
|
);
|
|
|
|
// Get the key from the cookieJarSettings of an first-party iframe.
|
|
let keyFirstPartyFrame = await SpecialPowers.spawn(
|
|
browser,
|
|
[firstPartyDomain],
|
|
async domain => {
|
|
let ifr = content.document.createElement("iframe");
|
|
|
|
let loaded = new content.Promise(resolve => {
|
|
ifr.onload = resolve;
|
|
});
|
|
content.document.body.appendChild(ifr);
|
|
ifr.src = domain;
|
|
|
|
await loaded;
|
|
|
|
return SpecialPowers.spawn(ifr, [], _ => {
|
|
return content.document.cookieJarSettings
|
|
.fingerprintingRandomizationKey;
|
|
});
|
|
}
|
|
);
|
|
|
|
let keyFirstPartyFrameHex = keyFirstPartyFrame
|
|
.map(bytes => bytes.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
is(
|
|
keyFirstPartyFrameHex,
|
|
keyHex,
|
|
"The fingerprinting random key should match between the browser element and the first-party iframe document."
|
|
);
|
|
|
|
// Get the key from the cookieJarSettings of an third-party iframe
|
|
let keyThirdPartyFrame = await SpecialPowers.spawn(
|
|
browser,
|
|
[thirdPartyDomain],
|
|
async domain => {
|
|
let ifr = content.document.createElement("iframe");
|
|
|
|
let loaded = new content.Promise(resolve => {
|
|
ifr.onload = resolve;
|
|
});
|
|
content.document.body.appendChild(ifr);
|
|
ifr.src = domain;
|
|
|
|
await loaded;
|
|
|
|
return SpecialPowers.spawn(ifr, [], _ => {
|
|
return content.document.cookieJarSettings
|
|
.fingerprintingRandomizationKey;
|
|
});
|
|
}
|
|
);
|
|
|
|
let keyThirdPartyFrameHex = keyThirdPartyFrame
|
|
.map(bytes => bytes.toString(16).padStart(2, "0"))
|
|
.join("");
|
|
is(
|
|
keyThirdPartyFrameHex,
|
|
keyHex,
|
|
"The fingerprinting random key should match between the browser element and the third-party iframe document."
|
|
);
|
|
|
|
return keyHex;
|
|
}
|
|
|
|
// Test accessing the fingerprinting randomization key will throw if
|
|
// fingerprinting resistance is disabled.
|
|
add_task(async function test_randomization_disabled_with_rfp_disabled() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
["privacy.resistFingerprinting", false],
|
|
["privacy.resistFingerprinting.pbmode", false],
|
|
["privacy.fingerprintingProtection", false],
|
|
["privacy.fingerprintingProtection.pbmode", false],
|
|
],
|
|
});
|
|
|
|
// Ensure accessing the fingerprinting randomization key of the browser
|
|
// element will throw if fingerprinting randomization is disabled.
|
|
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
|
|
|
|
try {
|
|
let key =
|
|
tab.linkedBrowser.cookieJarSettings.fingerprintingRandomizationKey;
|
|
ok(
|
|
false,
|
|
`Accessing the fingerprinting randomization key should throw when fingerprinting resistance is disabled. ${key}`
|
|
);
|
|
} catch (e) {
|
|
ok(
|
|
true,
|
|
"It should throw when getting the key when fingerprinting resistance is disabled."
|
|
);
|
|
}
|
|
|
|
// Ensure accessing the fingerprinting randomization key of the top-level
|
|
// document will throw if fingerprinting randomization is disabled.
|
|
try {
|
|
await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
|
|
return content.document.cookieJarSettings.fingerprintingRandomizationKey;
|
|
});
|
|
} catch (e) {
|
|
ok(
|
|
true,
|
|
"It should throw when getting the key when fingerprinting resistance is disabled."
|
|
);
|
|
}
|
|
|
|
BrowserTestUtils.removeTab(tab);
|
|
});
|
|
|
|
// Test the fingerprinting randomization key generation.
|
|
add_task(async function test_generate_randomization_key() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["privacy.resistFingerprinting", true]],
|
|
});
|
|
|
|
for (let testPrivateWin of [true, false]) {
|
|
let win = window;
|
|
|
|
if (testPrivateWin) {
|
|
win = await BrowserTestUtils.openNewBrowserWindow({
|
|
private: true,
|
|
});
|
|
}
|
|
|
|
let tabOne = await BrowserTestUtils.openNewForegroundTab(
|
|
win.gBrowser,
|
|
TEST_PAGE
|
|
);
|
|
let keyHexOne;
|
|
|
|
try {
|
|
keyHexOne = await getRandomKeyHexFromBrowser(
|
|
tabOne.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
ok(true, `The fingerprinting random key: ${keyHexOne}`);
|
|
} catch (e) {
|
|
ok(
|
|
false,
|
|
"Shouldn't fail when getting the random key from the cookieJarSettings"
|
|
);
|
|
}
|
|
|
|
// Open the test domain again and check if the key remains the same.
|
|
let tabTwo = await BrowserTestUtils.openNewForegroundTab(
|
|
win.gBrowser,
|
|
TEST_PAGE
|
|
);
|
|
try {
|
|
let keyHexTwo = await getRandomKeyHexFromBrowser(
|
|
tabTwo.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
is(
|
|
keyHexTwo,
|
|
keyHexOne,
|
|
`The key should remain the same after reopening the tab.`
|
|
);
|
|
} catch (e) {
|
|
ok(
|
|
false,
|
|
"Shouldn't fail when getting the random key from the cookieJarSettings"
|
|
);
|
|
}
|
|
|
|
// Open a tab with a different domain to see if the key changes.
|
|
let tabAnother = await BrowserTestUtils.openNewForegroundTab(
|
|
win.gBrowser,
|
|
TEST_DOMAIN_ANOTHER_PAGE
|
|
);
|
|
try {
|
|
let keyHexAnother = await getRandomKeyHexFromBrowser(
|
|
tabAnother.linkedBrowser,
|
|
TEST_DOMAIN_ANOTHER_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
isnot(
|
|
keyHexAnother,
|
|
keyHexOne,
|
|
`The key should be different when loading a different domain`
|
|
);
|
|
} catch (e) {
|
|
ok(
|
|
false,
|
|
"Shouldn't fail when getting the random key from the cookieJarSettings"
|
|
);
|
|
}
|
|
|
|
BrowserTestUtils.removeTab(tabOne);
|
|
BrowserTestUtils.removeTab(tabTwo);
|
|
BrowserTestUtils.removeTab(tabAnother);
|
|
if (testPrivateWin) {
|
|
await BrowserTestUtils.closeWindow(win);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Test the fingerprinting randomization key will change after private session
|
|
// ends.
|
|
add_task(async function test_reset_key_after_pbm_session_ends() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["privacy.resistFingerprinting", true]],
|
|
});
|
|
|
|
let privateWin = await BrowserTestUtils.openNewBrowserWindow({
|
|
private: true,
|
|
});
|
|
|
|
// Open a tab in the private window.
|
|
let tab = await BrowserTestUtils.openNewForegroundTab(
|
|
privateWin.gBrowser,
|
|
TEST_PAGE
|
|
);
|
|
|
|
let keyHex = await getRandomKeyHexFromBrowser(
|
|
tab.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
|
|
// Close the window and open another private window.
|
|
BrowserTestUtils.removeTab(tab);
|
|
|
|
let promisePBExit = TestUtils.topicObserved("last-pb-context-exited");
|
|
await BrowserTestUtils.closeWindow(privateWin);
|
|
await promisePBExit;
|
|
|
|
privateWin = await BrowserTestUtils.openNewBrowserWindow({
|
|
private: true,
|
|
});
|
|
|
|
// Open a tab again in the new private window.
|
|
tab = await BrowserTestUtils.openNewForegroundTab(
|
|
privateWin.gBrowser,
|
|
TEST_PAGE
|
|
);
|
|
|
|
let keyHexNew = await getRandomKeyHexFromBrowser(
|
|
tab.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
|
|
// Ensure the keys are different.
|
|
isnot(keyHexNew, keyHex, "Ensure the new key is different from the old one.");
|
|
|
|
BrowserTestUtils.removeTab(tab);
|
|
await BrowserTestUtils.closeWindow(privateWin);
|
|
});
|
|
|
|
// Test accessing the fingerprinting randomization key will throw in normal
|
|
// windows if we exempt fingerprinting protection in normal windows.
|
|
add_task(async function test_randomization_with_exempted_normal_window() {
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [
|
|
["privacy.resistFingerprinting", false],
|
|
["privacy.resistFingerprinting.pbmode", true],
|
|
["privacy.fingerprintingProtection", false],
|
|
["privacy.fingerprintingProtection.pbmode", false],
|
|
],
|
|
});
|
|
|
|
// Ensure accessing the fingerprinting randomization key of the browser
|
|
// element will throw if fingerprinting randomization is exempted from normal
|
|
// windows.
|
|
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
|
|
|
|
try {
|
|
let key =
|
|
tab.linkedBrowser.cookieJarSettings.fingerprintingRandomizationKey;
|
|
ok(
|
|
false,
|
|
`Accessing the fingerprinting randomization key should throw when fingerprinting resistance is exempted in normal windows. ${key}`
|
|
);
|
|
} catch (e) {
|
|
ok(
|
|
true,
|
|
"It should throw when getting the key when fingerprinting resistance is exempted in normal windows."
|
|
);
|
|
}
|
|
|
|
// Ensure accessing the fingerprinting randomization key of the top-level
|
|
// document will throw if fingerprinting randomization is exempted from normal
|
|
// windows.
|
|
try {
|
|
await SpecialPowers.spawn(tab.linkedBrowser, [], _ => {
|
|
return content.document.cookieJarSettings.fingerprintingRandomizationKey;
|
|
});
|
|
} catch (e) {
|
|
ok(
|
|
true,
|
|
"It should throw when getting the key when fingerprinting resistance is exempted in normal windows."
|
|
);
|
|
}
|
|
|
|
BrowserTestUtils.removeTab(tab);
|
|
|
|
// Open a private window and check the key can be accessed there.
|
|
let privateWin = await BrowserTestUtils.openNewBrowserWindow({
|
|
private: true,
|
|
});
|
|
|
|
// Open a tab in the private window.
|
|
tab = await BrowserTestUtils.openNewForegroundTab(
|
|
privateWin.gBrowser,
|
|
TEST_PAGE
|
|
);
|
|
|
|
// Access the key, this shouldn't throw an error.
|
|
await getRandomKeyHexFromBrowser(
|
|
tab.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
|
|
BrowserTestUtils.removeTab(tab);
|
|
await BrowserTestUtils.closeWindow(privateWin);
|
|
});
|
|
|
|
// Test that the random key gets reset when the site data gets cleared.
|
|
add_task(async function test_reset_random_key_when_clear_site_data() {
|
|
// Enable fingerprinting randomization key generation.
|
|
await SpecialPowers.pushPrefEnv({
|
|
set: [["privacy.resistFingerprinting", true]],
|
|
});
|
|
|
|
// Open a tab and get randomization key from the test domain.
|
|
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
|
|
|
|
let keyHex = await getRandomKeyHexFromBrowser(
|
|
tab.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
|
|
// Open another tab and get randomization key from another domain.
|
|
let anotherTab = await BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
TEST_DOMAIN_ANOTHER_PAGE
|
|
);
|
|
|
|
let keyHexAnother = await getRandomKeyHexFromBrowser(
|
|
anotherTab.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
|
|
BrowserTestUtils.removeTab(tab);
|
|
BrowserTestUtils.removeTab(anotherTab);
|
|
|
|
// Call ForgetAboutSite for the test domain.
|
|
await ForgetAboutSite.removeDataFromDomain("example.com");
|
|
|
|
// Open the tab for the test domain again and verify the key is reset.
|
|
tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
|
|
let keyHexNew = await getRandomKeyHexFromBrowser(
|
|
tab.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
|
|
// Open the tab for another domain again and verify the key is intact.
|
|
anotherTab = await BrowserTestUtils.openNewForegroundTab(
|
|
gBrowser,
|
|
TEST_DOMAIN_ANOTHER_PAGE
|
|
);
|
|
let keyHexAnotherNew = await getRandomKeyHexFromBrowser(
|
|
anotherTab.linkedBrowser,
|
|
TEST_PAGE,
|
|
TEST_DOMAIN_THIRD_PAGE
|
|
);
|
|
|
|
// Ensure the keys are different for the test domain.
|
|
isnot(keyHexNew, keyHex, "Ensure the new key is different from the old one.");
|
|
|
|
// Ensure the key for another domain isn't changed.
|
|
is(
|
|
keyHexAnother,
|
|
keyHexAnotherNew,
|
|
"Ensure the key of another domain isn't reset."
|
|
);
|
|
|
|
BrowserTestUtils.removeTab(tab);
|
|
BrowserTestUtils.removeTab(anotherTab);
|
|
});
|