Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: asan
- Manifest: dom/security/test/referrer-policy/browser.toml
/**
* cross-site requests.
*/
"use strict";
requestLongerTimeout(6);
const TEST_PATH = "browser/dom/security/test/referrer-policy/";
const TEST_PAGE = `${TEST_DOMAIN}${TEST_PATH}referrer_page.sjs`;
const TEST_SAME_SITE_PAGE = `${TEST_SAME_SITE_DOMAIN}${TEST_PATH}referrer_page.sjs`;
const TEST_SAME_SITE_PAGE_HTTP = `${TEST_SAME_SITE_DOMAIN_HTTP}${TEST_PATH}referrer_page.sjs`;
const TEST_CROSS_SITE_PAGE = `${TEST_CROSS_SITE_DOMAIN}${TEST_PATH}referrer_page.sjs`;
const TEST_CROSS_SITE_PAGE_HTTP = `${TEST_CROSS_SITE_DOMAIN_HTTP}${TEST_PATH}referrer_page.sjs`;
const REFERRER_FULL = 0;
const REFERRER_ORIGIN = 1;
const REFERRER_NONE = 2;
function getExpectedReferrer(referrer, type) {
let res;
switch (type) {
case REFERRER_FULL:
res = referrer;
break;
case REFERRER_ORIGIN:
let url = new URL(referrer);
res = `${url.origin}/`;
break;
case REFERRER_NONE:
res = "";
break;
default:
ok(false, "unknown type");
}
return res;
}
async function verifyResultInPage(browser, expected) {
await SpecialPowers.spawn(browser, [expected], value => {
is(content.document.referrer, value, "The document.referrer is correct.");
let result = content.document.getElementById("result");
is(result.textContent, value, "The referer header is correct");
});
}
function getExpectedConsoleMessage(expected, isPrefOn, url) {
let msg;
if (isPrefOn) {
msg =
"Referrer Policy: Ignoring the less restricted referrer policy “" +
expected +
"” for the cross-site request: " +
url;
} else {
msg =
"Referrer Policy: Less restricted policies, including " +
"‘no-referrer-when-downgrade’, ‘origin-when-cross-origin’ and " +
"‘unsafe-url’, will be ignored soon for the cross-site request: " +
url;
}
return msg;
}
function createConsoleMessageVerificationPromise(expected, isPrefOn, url) {
if (!expected) {
return Promise.resolve();
}
return new Promise(resolve => {
let listener = {
observe(msg) {
let message = msg.QueryInterface(Ci.nsIScriptError);
if (message.category.startsWith("Security")) {
is(
message.errorMessage,
getExpectedConsoleMessage(expected, isPrefOn, url),
"The console message is correct."
);
Services.console.unregisterListener(listener);
resolve();
}
},
};
Services.console.registerListener(listener);
});
}
function verifyNoConsoleMessage() {
// Verify that there is no referrer policy console message.
let allMessages = Services.console.getMessageArray();
for (let msg of allMessages) {
let message = msg.QueryInterface(Ci.nsIScriptError);
if (
message.category.startsWith("Security") &&
message.errorMessage.startsWith("Referrer Policy:")
) {
ok(false, "There should be no console message for referrer policy.");
}
}
}
const TEST_CASES = [
// Testing that the referrer policy can be overridden with less restricted
// policy in the same-origin scenario.
{
policy: "unsafe-url",
referrer: TEST_PAGE,
test_url: TEST_PAGE,
expect: REFERRER_FULL,
original: REFERRER_FULL,
},
// Testing that the referrer policy can be overridden with less restricted
// policy in the same-site scenario.
{
policy: "unsafe-url",
referrer: TEST_PAGE,
test_url: TEST_SAME_SITE_PAGE,
expect: REFERRER_FULL,
original: REFERRER_FULL,
},
{
policy: "no-referrer-when-downgrade",
referrer: TEST_PAGE,
test_url: TEST_SAME_SITE_PAGE,
expect: REFERRER_FULL,
original: REFERRER_FULL,
},
{
policy: "origin-when-cross-origin",
referrer: TEST_PAGE,
test_url: TEST_SAME_SITE_PAGE_HTTP,
expect: REFERRER_ORIGIN,
original: REFERRER_ORIGIN,
},
// Testing that the referrer policy cannot be overridden with less restricted
// policy in the cross-site scenario.
{
policy: "unsafe-url",
referrer: TEST_PAGE,
test_url: TEST_CROSS_SITE_PAGE,
expect: REFERRER_ORIGIN,
expect_console: "unsafe-url",
original: REFERRER_FULL,
},
{
policy: "no-referrer-when-downgrade",
referrer: TEST_PAGE,
test_url: TEST_CROSS_SITE_PAGE,
expect: REFERRER_ORIGIN,
expect_console: "no-referrer-when-downgrade",
original: REFERRER_FULL,
},
{
policy: "origin-when-cross-origin",
referrer: TEST_PAGE,
test_url: TEST_CROSS_SITE_PAGE_HTTP,
expect: REFERRER_NONE,
expect_console: "origin-when-cross-origin",
original: REFERRER_ORIGIN,
},
// Testing that the referrer policy can still be overridden with more
// restricted policy in the cross-site scenario.
{
policy: "no-referrer",
referrer: TEST_PAGE,
test_url: TEST_CROSS_SITE_PAGE,
expect: REFERRER_NONE,
original: REFERRER_NONE,
},
];
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
// Disable mixed content blocking to be able to test downgrade scenario.
["security.mixed_content.block_active_content", false],
// Disable https-first since we are testing http and https referrers
["dom.security.https_first", false],
],
});
});
async function runTestIniFrame(gBrowser, enabled, expectNoConsole) {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:blank" },
async browser => {
for (let type of ["meta", "header"]) {
for (let test of TEST_CASES) {
info(`Test iframe: ${test.toSource()}`);
let referrerURL = `${test.referrer}?${type}=${test.policy}`;
let expected = enabled
? getExpectedReferrer(referrerURL, test.expect)
: getExpectedReferrer(referrerURL, test.original);
let expected_console = expectNoConsole
? undefined
: test.expect_console;
Services.console.reset();
BrowserTestUtils.startLoadingURIString(browser, referrerURL);
await BrowserTestUtils.browserLoaded(browser, false, referrerURL);
let iframeURL = test.test_url + "?show";
let consolePromise = createConsoleMessageVerificationPromise(
expected_console,
enabled,
iframeURL
);
// Create an iframe and load the url.
let bc = await SpecialPowers.spawn(
browser,
[iframeURL],
async url => {
let iframe = content.document.createElement("iframe");
let loadPromise = ContentTaskUtils.waitForEvent(iframe, "load");
iframe.src = url;
content.document.body.appendChild(iframe);
await loadPromise;
return iframe.browsingContext;
}
);
await verifyResultInPage(bc, expected);
await consolePromise;
if (!expected_console) {
verifyNoConsoleMessage();
}
}
}
}
);
}
async function runTestForLinkClick(gBrowser, enabled, expectNoConsole) {
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "about:blank" },
async browser => {
for (let type of ["meta", "header"]) {
for (let test of TEST_CASES) {
info(`Test link click: ${test.toSource()}`);
let referrerURL = `${test.referrer}?${type}=${test.policy}`;
let expected = enabled
? getExpectedReferrer(referrerURL, test.expect)
: getExpectedReferrer(referrerURL, test.original);
let expected_console = expectNoConsole
? undefined
: test.expect_console;
Services.console.reset();
BrowserTestUtils.startLoadingURIString(browser, referrerURL);
await BrowserTestUtils.browserLoaded(browser, false, referrerURL);
let linkURL = test.test_url + "?show";
let consolePromise = createConsoleMessageVerificationPromise(
expected_console,
enabled,
linkURL
);
// Create the promise to wait for the navigation finishes.
let loadedPromise = BrowserTestUtils.browserLoaded(
browser,
false,
linkURL
);
// Generate the link and click it to navigate.
await SpecialPowers.spawn(browser, [linkURL], async url => {
let link = content.document.createElement("a");
link.textContent = "Link";
link.setAttribute("href", url);
content.document.body.appendChild(link);
link.click();
});
await loadedPromise;
await verifyResultInPage(browser, expected);
await consolePromise;
if (!expected_console) {
verifyNoConsoleMessage();
}
}
}
}
);
}
async function toggleETPForPage(gBrowser, url, toggle) {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
// First, Toggle ETP off for the test page.
let browserLoadedPromise = BrowserTestUtils.browserLoaded(
tab.linkedBrowser,
false,
url
);
if (toggle) {
gProtectionsHandler.enableForCurrentPage();
} else {
gProtectionsHandler.disableForCurrentPage();
}
await browserLoadedPromise;
BrowserTestUtils.removeTab(tab);
}
add_task(async function test_iframe() {
for (let enabled of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [["network.http.referer.disallowCrossSiteRelaxingDefault", enabled]],
});
await runTestIniFrame(gBrowser, enabled);
}
});
add_task(async function test_iframe_pbmode() {
let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
for (let enabled of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [
[
"network.http.referer.disallowCrossSiteRelaxingDefault.pbmode",
enabled,
],
],
});
await runTestIniFrame(win.gBrowser, enabled);
}
await BrowserTestUtils.closeWindow(win);
});
add_task(async function test_link_click() {
for (let enabled of [true, false]) {
for (let enabled_top of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [
["network.http.referer.disallowCrossSiteRelaxingDefault", enabled],
[
"network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation",
enabled_top,
],
],
});
// We won't show the console message if the strict rule is disabled for
// the top navigation.
await runTestForLinkClick(gBrowser, enabled && enabled_top, !enabled_top);
}
}
});
add_task(async function test_link_click_pbmode() {
let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
for (let enabled of [true, false]) {
for (let enabled_top of [true, false]) {
await SpecialPowers.pushPrefEnv({
set: [
[
"network.http.referer.disallowCrossSiteRelaxingDefault.pbmode",
enabled,
],
[
"network.http.referer.disallowCrossSiteRelaxingDefault.pbmode.top_navigation",
enabled_top,
],
// Disable https first mode for private browsing mode to test downgrade
// cases.
["dom.security.https_first_pbm", false],
],
});
// We won't show the console message if the strict rule is disabled for
// the top navigation in the private browsing window.
await runTestForLinkClick(
win.gBrowser,
enabled && enabled_top,
!enabled_top
);
}
}
await BrowserTestUtils.closeWindow(win);
});
add_task(async function test_iframe_etp_toggle_off() {
await SpecialPowers.pushPrefEnv({
set: [["network.http.referer.disallowCrossSiteRelaxingDefault", true]],
});
// Open a new tab for the test page and toggle ETP off.
await toggleETPForPage(gBrowser, TEST_PAGE, false);
// Run the test to see if the protection is disabled. We won't send console
// message if the protection was disabled by the ETP toggle.
await runTestIniFrame(gBrowser, false, true);
// toggle ETP on again.
await toggleETPForPage(gBrowser, TEST_PAGE, true);
// Run the test again to see if the protection is enabled.
await runTestIniFrame(gBrowser, true);
});
add_task(async function test_link_click_etp_toggle_off() {
await SpecialPowers.pushPrefEnv({
set: [
["network.http.referer.disallowCrossSiteRelaxingDefault", true],
[
"network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation",
true,
],
],
});
// Toggle ETP off for the cross site. Note that the cross site is the place
// where we test against the ETP permission for top navigation.
await toggleETPForPage(gBrowser, TEST_CROSS_SITE_PAGE, false);
// Run the test to see if the protection is disabled. We won't send console
// message if the protection was disabled by the ETP toggle.
await runTestForLinkClick(gBrowser, false, true);
// toggle ETP on again.
await toggleETPForPage(gBrowser, TEST_CROSS_SITE_PAGE, true);
// Run the test again to see if the protection is enabled.
await runTestForLinkClick(gBrowser, true);
});