tor-browser

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

browser_captivePortal_lna.js (6976B)


      1 "use strict";
      2 
      3 const { HttpServer } = ChromeUtils.importESModule(
      4  "resource://testing-common/httpd.sys.mjs"
      5 );
      6 
      7 const PUBLIC_PAGE_URL =
      8  "https://example.com/browser/browser/base/content/test/captivePortal/file_captivePortal_lna.html";
      9 let SERVER_RESPONSE = "";
     10 const CANONICAL_HTML = "<!DOCTYPE html><html><body>hello</body></html>";
     11 let gHttpServer;
     12 let privateServer;
     13 let localServer;
     14 
     15 add_setup(async function setup() {
     16  // Set up local HTTP server
     17  gHttpServer = new HttpServer();
     18  gHttpServer.start();
     19  gHttpServer.registerPathHandler("/", (request, response) => {
     20    response.setHeader("Access-Control-Allow-Origin", "*", false);
     21    response.setHeader("Content-Type", "text/html", false);
     22    response.setStatusLine(request.httpVersion, 200, "OK");
     23    response.write(SERVER_RESPONSE);
     24  });
     25 
     26  privateServer = new HttpServer();
     27  privateServer.start();
     28  privateServer.registerPathHandler("/", (request, response) => {
     29    response.setHeader("Access-Control-Allow-Origin", "*", false);
     30    response.setHeader("Content-Type", "text/plain", false);
     31    response.setStatusLine(request.httpVersion, 200, "OK");
     32    response.write("hello");
     33  });
     34 
     35  localServer = new HttpServer();
     36  localServer.start();
     37  localServer.registerPathHandler("/", (request, response) => {
     38    response.setHeader("Access-Control-Allow-Origin", "*", false);
     39    response.setHeader("Content-Type", "text/plain", false);
     40    response.setStatusLine(request.httpVersion, 200, "OK");
     41    response.write("hello");
     42  });
     43 
     44  SERVER_RESPONSE = `<!DOCTYPE html><html><body><script>fetch('http://localhost:${privateServer.identity.primaryPort}/').then(r => r.text()).then(t => document.body.textContent = t);</script></body></html>`;
     45 
     46  await SpecialPowers.pushPrefEnv({
     47    set: [
     48      ["network.lna.blocking", true],
     49      ["network.http.rcwn.enabled", false],
     50      [
     51        "captivedetect.canonicalURL",
     52        `http://127.0.0.1:${gHttpServer.identity.primaryPort}/`,
     53      ],
     54      ["captivedetect.canonicalContent", CANONICAL_HTML],
     55      // Set up address space override to this page appear as public
     56      [
     57        "network.lna.address_space.public.override",
     58        `127.0.0.1:${gHttpServer.identity.primaryPort}`,
     59      ],
     60      [
     61        "network.lna.address_space.private.override",
     62        `127.0.0.1:${privateServer.identity.primaryPort}`,
     63      ],
     64    ],
     65  });
     66 
     67  registerCleanupFunction(async () => {
     68    await gHttpServer.stop();
     69    await privateServer.stop();
     70    await localServer.stop();
     71  });
     72 });
     73 
     74 function observeRequest(url) {
     75  return new Promise(resolve => {
     76    const observer = {
     77      observe(subject, topic) {
     78        if (topic !== "http-on-stop-request") {
     79          return;
     80        }
     81 
     82        let channel = subject.QueryInterface(Ci.nsIHttpChannel);
     83        if (!channel || channel.URI.spec !== url) {
     84          return;
     85        }
     86 
     87        Services.obs.removeObserver(observer, "http-on-stop-request");
     88        resolve(channel.status);
     89      },
     90    };
     91    Services.obs.addObserver(observer, "http-on-stop-request");
     92  });
     93 }
     94 
     95 // Tests that a captive portal tab making a request to a local network
     96 // resource does not trigger an LNA permission prompt.
     97 add_task(async function test_captivePortalTab_noLnaPrompt() {
     98  // Simulate portal detection
     99  await portalDetected();
    100 
    101  let canonicalURL = `http://127.0.0.1:${gHttpServer.identity.primaryPort}/`;
    102  let portalTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, canonicalURL);
    103 
    104  // Wait for the captive portal notification
    105  let notification = await ensurePortalNotification(window);
    106 
    107  // Click the notification button to open the captive portal tab
    108  let button = notification.querySelector("button.notification-button");
    109  button.click();
    110 
    111  let portalTab = await portalTabPromise;
    112 
    113  // Verify the tab has the isCaptivePortalTab flag set
    114  ok(
    115    portalTab.linkedBrowser.browsingContext.isCaptivePortalTab,
    116    "Captive portal tab should have isCaptivePortalTab flag set"
    117  );
    118 
    119  // Wait for the fetch to complete and the page content to be updated
    120  await BrowserTestUtils.waitForCondition(
    121    () =>
    122      SpecialPowers.spawn(portalTab.linkedBrowser, [], () => {
    123        return content.document.body.textContent === "hello";
    124      }),
    125    "Waiting for fetch response to be displayed on the page"
    126  );
    127 
    128  // Verify the page content contains "hello" from the fetch response
    129  let bodyText = await SpecialPowers.spawn(portalTab.linkedBrowser, [], () => {
    130    return content.document.body.textContent;
    131  });
    132  is(bodyText, "hello", "Page should display the fetch response");
    133 
    134  // Verify that no LNA permission prompt appeared
    135  let lnaPrompt = PopupNotifications.getNotification(
    136    "local-network",
    137    portalTab.linkedBrowser
    138  );
    139  ok(
    140    !lnaPrompt,
    141    "Should not show LNA prompt for captive portal tab accessing local network"
    142  );
    143 
    144  await SpecialPowers.spawn(
    145    portalTab.linkedBrowser,
    146    [localServer.identity.primaryPort],
    147    port => {
    148      content.console.log("url", `http://localhost:${port}/`);
    149      content.fetch(`http://localhost:${port}/`);
    150    }
    151  );
    152  await BrowserTestUtils.waitForCondition(
    153    () =>
    154      PopupNotifications.getNotification("localhost", portalTab.linkedBrowser),
    155    "Waiting for localhost prompt"
    156  );
    157 
    158  // Clean up
    159  BrowserTestUtils.removeTab(portalTab);
    160  await freePortal(true);
    161 });
    162 
    163 // Tests that a regular tab (non-captive portal) does NOT trigger an LNA
    164 // permission prompt when accessing local network resources during captive portal.
    165 add_task(async function test_regularTab_noLnaPrompt_duringCaptivePortal() {
    166  await portalDetected();
    167 
    168  let canonicalURL = `http://127.0.0.1:${gHttpServer.identity.primaryPort}/`;
    169 
    170  // Wait for the captive portal notification
    171  await ensurePortalNotification(window);
    172 
    173  const tab = await BrowserTestUtils.openNewForegroundTab(
    174    gBrowser,
    175    canonicalURL
    176  );
    177 
    178  // Verify the tab does not have the isCaptivePortalTab flag set
    179  ok(
    180    !tab.linkedBrowser.browsingContext.isCaptivePortalTab,
    181    "Regular tab should not have isCaptivePortalTab flag set"
    182  );
    183 
    184  // Wait for the fetch to complete and the page content to be updated
    185  await BrowserTestUtils.waitForCondition(
    186    () =>
    187      SpecialPowers.spawn(tab.linkedBrowser, [], () => {
    188        return content.document.body.textContent === "hello";
    189      }),
    190    "Waiting for fetch response to be displayed on the page"
    191  );
    192 
    193  // Verify the page content contains "hello" from the fetch response
    194  let bodyText = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
    195    return content.document.body.textContent;
    196  });
    197  is(bodyText, "hello", "Page should display the fetch response");
    198 
    199  // Verify that no local network LNA permission prompt appeared
    200  let lnaPrompt = PopupNotifications.getNotification(
    201    "local-network",
    202    tab.linkedBrowser
    203  );
    204  ok(
    205    !lnaPrompt,
    206    "Should not show local network LNA prompt for regular tab during captive portal"
    207  );
    208 
    209  // Clean up
    210  BrowserTestUtils.removeTab(tab);
    211  await freePortal(true);
    212 });