tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_referrer_disallow_cross_site_relaxing.js (13691B)


      1 /**
      2 * Bug 1720294 - Testing disallow relaxing default referrer policy for
      3 *               cross-site requests.
      4 */
      5 
      6 "use strict";
      7 
      8 requestLongerTimeout(6);
      9 
     10 const TEST_DOMAIN = "https://example.com/";
     11 const TEST_SAME_SITE_DOMAIN = "https://test1.example.com/";
     12 const TEST_SAME_SITE_DOMAIN_HTTP = "http://test1.example.com/";
     13 const TEST_CROSS_SITE_DOMAIN = "https://test1.example.org/";
     14 const TEST_CROSS_SITE_DOMAIN_HTTP = "http://test1.example.org/";
     15 
     16 const TEST_PATH = "browser/dom/security/test/referrer-policy/";
     17 
     18 const TEST_PAGE = `${TEST_DOMAIN}${TEST_PATH}referrer_page.sjs`;
     19 const TEST_SAME_SITE_PAGE = `${TEST_SAME_SITE_DOMAIN}${TEST_PATH}referrer_page.sjs`;
     20 const TEST_SAME_SITE_PAGE_HTTP = `${TEST_SAME_SITE_DOMAIN_HTTP}${TEST_PATH}referrer_page.sjs`;
     21 const TEST_CROSS_SITE_PAGE = `${TEST_CROSS_SITE_DOMAIN}${TEST_PATH}referrer_page.sjs`;
     22 const TEST_CROSS_SITE_PAGE_HTTP = `${TEST_CROSS_SITE_DOMAIN_HTTP}${TEST_PATH}referrer_page.sjs`;
     23 
     24 const REFERRER_FULL = 0;
     25 const REFERRER_ORIGIN = 1;
     26 const REFERRER_NONE = 2;
     27 
     28 function getExpectedReferrer(referrer, type) {
     29  let res;
     30 
     31  switch (type) {
     32    case REFERRER_FULL:
     33      res = referrer;
     34      break;
     35    case REFERRER_ORIGIN: {
     36      let url = new URL(referrer);
     37      res = `${url.origin}/`;
     38      break;
     39    }
     40    case REFERRER_NONE:
     41      res = "";
     42      break;
     43    default:
     44      ok(false, "unknown type");
     45  }
     46 
     47  return res;
     48 }
     49 
     50 async function verifyResultInPage(browser, expected) {
     51  await SpecialPowers.spawn(browser, [expected], value => {
     52    is(content.document.referrer, value, "The document.referrer is correct.");
     53 
     54    let result = content.document.getElementById("result");
     55    is(result.textContent, value, "The referer header is correct");
     56  });
     57 }
     58 
     59 function getExpectedConsoleMessage(expected, isPrefOn, url) {
     60  let msg;
     61 
     62  if (isPrefOn) {
     63    msg =
     64      "Referrer Policy: Ignoring the less restricted referrer policy “" +
     65      expected +
     66      "” for the cross-site request: " +
     67      url;
     68  } else {
     69    msg =
     70      "Referrer Policy: Less restricted policies, including " +
     71      "‘no-referrer-when-downgrade’, ‘origin-when-cross-origin’ and " +
     72      "‘unsafe-url’, will be ignored soon for the cross-site request: " +
     73      url;
     74  }
     75 
     76  return msg;
     77 }
     78 
     79 function createConsoleMessageVerificationPromise(expected, isPrefOn, url) {
     80  if (!expected) {
     81    return Promise.resolve();
     82  }
     83 
     84  return new Promise(resolve => {
     85    let listener = {
     86      observe(msg) {
     87        let message = msg.QueryInterface(Ci.nsIScriptError);
     88 
     89        if (message.category.startsWith("Security")) {
     90          is(
     91            message.errorMessage,
     92            getExpectedConsoleMessage(expected, isPrefOn, url),
     93            "The console message is correct."
     94          );
     95          Services.console.unregisterListener(listener);
     96          resolve();
     97        }
     98      },
     99    };
    100 
    101    Services.console.registerListener(listener);
    102  });
    103 }
    104 
    105 function verifyNoConsoleMessage() {
    106  // Verify that there is no referrer policy console message.
    107  let allMessages = Services.console.getMessageArray();
    108 
    109  for (let msg of allMessages) {
    110    let message = msg.QueryInterface(Ci.nsIScriptError);
    111    if (
    112      message.category.startsWith("Security") &&
    113      message.errorMessage.startsWith("Referrer Policy:")
    114    ) {
    115      ok(false, "There should be no console message for referrer policy.");
    116    }
    117  }
    118 }
    119 
    120 const TEST_CASES = [
    121  // Testing that the referrer policy can be overridden with less restricted
    122  // policy in the same-origin scenario.
    123  {
    124    policy: "unsafe-url",
    125    referrer: TEST_PAGE,
    126    test_url: TEST_PAGE,
    127    expect: REFERRER_FULL,
    128    original: REFERRER_FULL,
    129  },
    130  // Testing that the referrer policy can be overridden with less restricted
    131  // policy in the same-site scenario.
    132  {
    133    policy: "unsafe-url",
    134    referrer: TEST_PAGE,
    135    test_url: TEST_SAME_SITE_PAGE,
    136    expect: REFERRER_FULL,
    137    original: REFERRER_FULL,
    138  },
    139  {
    140    policy: "no-referrer-when-downgrade",
    141    referrer: TEST_PAGE,
    142    test_url: TEST_SAME_SITE_PAGE,
    143    expect: REFERRER_FULL,
    144    original: REFERRER_FULL,
    145  },
    146  {
    147    policy: "origin-when-cross-origin",
    148    referrer: TEST_PAGE,
    149    test_url: TEST_SAME_SITE_PAGE_HTTP,
    150    expect: REFERRER_ORIGIN,
    151    original: REFERRER_ORIGIN,
    152  },
    153  // Testing that the referrer policy cannot be overridden with less restricted
    154  // policy in the cross-site scenario.
    155  {
    156    policy: "unsafe-url",
    157    referrer: TEST_PAGE,
    158    test_url: TEST_CROSS_SITE_PAGE,
    159    expect: REFERRER_ORIGIN,
    160    expect_console: "unsafe-url",
    161    original: REFERRER_FULL,
    162  },
    163  {
    164    policy: "no-referrer-when-downgrade",
    165    referrer: TEST_PAGE,
    166    test_url: TEST_CROSS_SITE_PAGE,
    167    expect: REFERRER_ORIGIN,
    168    expect_console: "no-referrer-when-downgrade",
    169    original: REFERRER_FULL,
    170  },
    171  {
    172    policy: "origin-when-cross-origin",
    173    referrer: TEST_PAGE,
    174    test_url: TEST_CROSS_SITE_PAGE_HTTP,
    175    expect: REFERRER_NONE,
    176    expect_console: "origin-when-cross-origin",
    177    original: REFERRER_ORIGIN,
    178  },
    179  // Testing that the referrer policy can still be overridden with more
    180  // restricted policy in the cross-site scenario.
    181  {
    182    policy: "no-referrer",
    183    referrer: TEST_PAGE,
    184    test_url: TEST_CROSS_SITE_PAGE,
    185    expect: REFERRER_NONE,
    186    original: REFERRER_NONE,
    187  },
    188 ];
    189 
    190 add_setup(async function () {
    191  await SpecialPowers.pushPrefEnv({
    192    set: [
    193      // Disable mixed content blocking to be able to test downgrade scenario.
    194      ["security.mixed_content.block_active_content", false],
    195      // Disable https-first since we are testing http and https referrers
    196      ["dom.security.https_first", false],
    197      ["browser.safebrowsing.only_top_level", false],
    198    ],
    199  });
    200 });
    201 
    202 async function runTestIniFrame(gBrowser, enabled, expectNoConsole) {
    203  await BrowserTestUtils.withNewTab(
    204    { gBrowser, url: "about:blank" },
    205    async browser => {
    206      for (let type of ["meta", "header"]) {
    207        for (let test of TEST_CASES) {
    208          info(`Test iframe: ${test.toSource()}`);
    209          let referrerURL = `${test.referrer}?${type}=${test.policy}`;
    210          let expected = enabled
    211            ? getExpectedReferrer(referrerURL, test.expect)
    212            : getExpectedReferrer(referrerURL, test.original);
    213 
    214          let expected_console = expectNoConsole
    215            ? undefined
    216            : test.expect_console;
    217 
    218          Services.console.reset();
    219 
    220          BrowserTestUtils.startLoadingURIString(browser, referrerURL);
    221          await BrowserTestUtils.browserLoaded(browser, false, referrerURL);
    222 
    223          let iframeURL = test.test_url + "?show";
    224 
    225          let consolePromise = createConsoleMessageVerificationPromise(
    226            expected_console,
    227            enabled,
    228            iframeURL
    229          );
    230          // Create an iframe and load the url.
    231          let bc = await SpecialPowers.spawn(
    232            browser,
    233            [iframeURL],
    234            async url => {
    235              let iframe = content.document.createElement("iframe");
    236              let loadPromise = ContentTaskUtils.waitForEvent(iframe, "load");
    237              iframe.src = url;
    238              content.document.body.appendChild(iframe);
    239 
    240              await loadPromise;
    241 
    242              return iframe.browsingContext;
    243            }
    244          );
    245 
    246          await verifyResultInPage(bc, expected);
    247          await consolePromise;
    248          if (!expected_console) {
    249            verifyNoConsoleMessage();
    250          }
    251        }
    252      }
    253    }
    254  );
    255 }
    256 
    257 async function runTestForLinkClick(gBrowser, enabled, expectNoConsole) {
    258  await BrowserTestUtils.withNewTab(
    259    { gBrowser, url: "about:blank" },
    260    async browser => {
    261      for (let type of ["meta", "header"]) {
    262        for (let test of TEST_CASES) {
    263          info(`Test link click: ${test.toSource()}`);
    264          let referrerURL = `${test.referrer}?${type}=${test.policy}`;
    265          let expected = enabled
    266            ? getExpectedReferrer(referrerURL, test.expect)
    267            : getExpectedReferrer(referrerURL, test.original);
    268 
    269          let expected_console = expectNoConsole
    270            ? undefined
    271            : test.expect_console;
    272 
    273          Services.console.reset();
    274 
    275          BrowserTestUtils.startLoadingURIString(browser, referrerURL);
    276          await BrowserTestUtils.browserLoaded(browser, false, referrerURL);
    277 
    278          let linkURL = test.test_url + "?show";
    279 
    280          let consolePromise = createConsoleMessageVerificationPromise(
    281            expected_console,
    282            enabled,
    283            linkURL
    284          );
    285 
    286          // Create the promise to wait for the navigation finishes.
    287          let loadedPromise = BrowserTestUtils.browserLoaded(
    288            browser,
    289            false,
    290            linkURL
    291          );
    292 
    293          // Generate the link and click it to navigate.
    294          await SpecialPowers.spawn(browser, [linkURL], async url => {
    295            let link = content.document.createElement("a");
    296            link.textContent = "Link";
    297            link.setAttribute("href", url);
    298 
    299            content.document.body.appendChild(link);
    300            link.click();
    301          });
    302 
    303          await loadedPromise;
    304 
    305          await verifyResultInPage(browser, expected);
    306          await consolePromise;
    307          if (!expected_console) {
    308            verifyNoConsoleMessage();
    309          }
    310        }
    311      }
    312    }
    313  );
    314 }
    315 
    316 async function toggleETPForPage(gBrowser, url, toggle) {
    317  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
    318 
    319  // First, Toggle ETP off for the test page.
    320  let browserLoadedPromise = BrowserTestUtils.browserLoaded(
    321    tab.linkedBrowser,
    322    false,
    323    url
    324  );
    325 
    326  if (toggle) {
    327    gProtectionsHandler.enableForCurrentPage();
    328  } else {
    329    gProtectionsHandler.disableForCurrentPage();
    330  }
    331 
    332  await browserLoadedPromise;
    333  BrowserTestUtils.removeTab(tab);
    334 }
    335 
    336 add_task(async function test_iframe() {
    337  for (let enabled of [true, false]) {
    338    await SpecialPowers.pushPrefEnv({
    339      set: [["network.http.referer.disallowCrossSiteRelaxingDefault", enabled]],
    340    });
    341 
    342    await runTestIniFrame(gBrowser, enabled);
    343  }
    344 });
    345 
    346 add_task(async function test_iframe_pbmode() {
    347  let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
    348 
    349  for (let enabled of [true, false]) {
    350    await SpecialPowers.pushPrefEnv({
    351      set: [
    352        [
    353          "network.http.referer.disallowCrossSiteRelaxingDefault.pbmode",
    354          enabled,
    355        ],
    356      ],
    357    });
    358 
    359    await runTestIniFrame(win.gBrowser, enabled);
    360  }
    361 
    362  await BrowserTestUtils.closeWindow(win);
    363 });
    364 
    365 add_task(async function test_link_click() {
    366  for (let enabled of [true, false]) {
    367    for (let enabled_top of [true, false]) {
    368      await SpecialPowers.pushPrefEnv({
    369        set: [
    370          ["network.http.referer.disallowCrossSiteRelaxingDefault", enabled],
    371          [
    372            "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation",
    373            enabled_top,
    374          ],
    375        ],
    376      });
    377 
    378      // We won't show the console message if the strict rule is disabled for
    379      // the top navigation.
    380      await runTestForLinkClick(gBrowser, enabled && enabled_top, !enabled_top);
    381    }
    382  }
    383 });
    384 
    385 add_task(async function test_link_click_pbmode() {
    386  let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
    387 
    388  for (let enabled of [true, false]) {
    389    for (let enabled_top of [true, false]) {
    390      await SpecialPowers.pushPrefEnv({
    391        set: [
    392          [
    393            "network.http.referer.disallowCrossSiteRelaxingDefault.pbmode",
    394            enabled,
    395          ],
    396          [
    397            "network.http.referer.disallowCrossSiteRelaxingDefault.pbmode.top_navigation",
    398            enabled_top,
    399          ],
    400          // Disable https first mode for private browsing mode to test downgrade
    401          // cases.
    402          ["dom.security.https_first_pbm", false],
    403        ],
    404      });
    405 
    406      // We won't show the console message if the strict rule is disabled for
    407      // the top navigation in the private browsing window.
    408      await runTestForLinkClick(
    409        win.gBrowser,
    410        enabled && enabled_top,
    411        !enabled_top
    412      );
    413    }
    414  }
    415 
    416  await BrowserTestUtils.closeWindow(win);
    417 });
    418 
    419 add_task(async function test_iframe_etp_toggle_off() {
    420  await SpecialPowers.pushPrefEnv({
    421    set: [["network.http.referer.disallowCrossSiteRelaxingDefault", true]],
    422  });
    423 
    424  // Open a new tab for the test page and toggle ETP off.
    425  await toggleETPForPage(gBrowser, TEST_PAGE, false);
    426 
    427  // Run the test to see if the protection is disabled. We won't send console
    428  // message if the protection was disabled by the ETP toggle.
    429  await runTestIniFrame(gBrowser, false, true);
    430 
    431  // toggle ETP on again.
    432  await toggleETPForPage(gBrowser, TEST_PAGE, true);
    433 
    434  // Run the test again to see if the protection is enabled.
    435  await runTestIniFrame(gBrowser, true);
    436 });
    437 
    438 add_task(async function test_link_click_etp_toggle_off() {
    439  await SpecialPowers.pushPrefEnv({
    440    set: [
    441      ["network.http.referer.disallowCrossSiteRelaxingDefault", true],
    442      [
    443        "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation",
    444        true,
    445      ],
    446    ],
    447  });
    448 
    449  // Toggle ETP off for the cross site. Note that the cross site is the place
    450  // where we test against the ETP permission for top navigation.
    451  await toggleETPForPage(gBrowser, TEST_CROSS_SITE_PAGE, false);
    452 
    453  // Run the test to see if the protection is disabled. We won't send console
    454  // message if the protection was disabled by the ETP toggle.
    455  await runTestForLinkClick(gBrowser, false, true);
    456 
    457  // toggle ETP on again.
    458  await toggleETPForPage(gBrowser, TEST_CROSS_SITE_PAGE, true);
    459 
    460  // Run the test again to see if the protection is enabled.
    461  await runTestForLinkClick(gBrowser, true);
    462 });