tor-browser

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

test_anchor_ping.html (10294B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=786347
      5 -->
      6 <head>
      7  <meta charset="utf-8">
      8  <title>Test for Bug 786347</title>
      9  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
     10  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
     11  <script type="application/javascript">
     12 
     13  /** Test for Bug 786347 */
     14 
     15 SimpleTest.waitForExplicitFinish();
     16 
     17 const {NetUtil} = ChromeUtils.importESModule(
     18  "resource://gre/modules/NetUtil.sys.mjs"
     19 );
     20 const {HttpServer} = ChromeUtils.importESModule(
     21  "resource://testing-common/httpd.sys.mjs"
     22 );
     23 
     24 addLoadEvent(function () {
     25  (async function run_tests() {
     26    while (tests.length) {
     27      let test = tests.shift();
     28      info("-- running " + test.name);
     29      await test();
     30    }
     31 
     32    SimpleTest.finish();
     33  })();
     34 });
     35 
     36 let tests = [
     37 
     38  // Ensure that sending pings is enabled.
     39  function setup() {
     40    Services.prefs.setBoolPref("browser.send_pings", true);
     41    Services.prefs.setIntPref("browser.send_pings.max_per_link", -1);
     42 
     43    SimpleTest.registerCleanupFunction(() => {
     44      Services.prefs.clearUserPref("browser.send_pings");
     45      Services.prefs.clearUserPref("browser.send_pings.max_per_link");
     46    });
     47  },
     48 
     49  // If both the address of the document containing the hyperlink being audited
     50  // and ping URL have the same origin then the request must include a Ping-From
     51  // HTTP header with, as its value, the address of the document containing the
     52  // hyperlink, and a Ping-To HTTP header with, as its value, the target URL.
     53  // The request must not include a Referer (sic) HTTP header.
     54  async function same_origin() {
     55    let from = "/ping-from/" + Math.random();
     56    let to = "/ping-to/" + Math.random();
     57    let ping = "/ping/" + Math.random();
     58 
     59    let base;
     60    let server = new HttpServer();
     61 
     62    // The page that contains the link.
     63    createFromPathHandler(server, from, to, () => ping);
     64 
     65    // The page that the link's href points to.
     66    let promiseHref = createToPathHandler(server, to);
     67 
     68    // The ping we want to receive.
     69    let promisePing = createPingPathHandler(server, ping, () => {
     70      return {from: base + from, to: base + to};
     71    });
     72 
     73    // Start the server, get its base URL and run the test.
     74    server.start(-1);
     75    base = "http://localhost:" + server.identity.primaryPort;
     76    navigate(base + from);
     77 
     78    // Wait until the target and ping url have loaded.
     79    await Promise.all([promiseHref, promisePing]);
     80 
     81    // Cleanup.
     82    await stopServer(server);
     83  },
     84 
     85  // If the origins are different, but the document containing the hyperlink
     86  // being audited was not retrieved over an encrypted connection then the
     87  // request must include a Referer (sic) HTTP header with, as its value, the
     88  // address of the document containing the hyperlink, a Ping-From HTTP header
     89  // with the same value, and a Ping-To HTTP header with, as its value, target
     90  // URL.
     91  async function diff_origin() {
     92    let from = "/ping-from/" + Math.random();
     93    let to = "/ping-to/" + Math.random();
     94    let ping = "/ping/" + Math.random();
     95 
     96    // We will use two servers to simulate two different origins.
     97    let base, base2;
     98    let server = new HttpServer();
     99    let server2 = new HttpServer();
    100 
    101    // The page that contains the link.
    102    createFromPathHandler(server, from, to, () => base2 + ping);
    103 
    104    // The page that the link's href points to.
    105    let promiseHref = createToPathHandler(server, to);
    106 
    107    // Start the first server and get its base URL.
    108    server.start(-1);
    109    base = "http://localhost:" + server.identity.primaryPort;
    110 
    111    // The ping we want to receive.
    112    let promisePing = createPingPathHandler(server2, ping, () => {
    113      return {referrer: base + from, from: base + from, to: base + to};
    114    });
    115 
    116    // Start the second server, get its base URL and run the test.
    117    server2.start(-1);
    118    base2 = "http://localhost:" + server2.identity.primaryPort;
    119    navigate(base + from);
    120 
    121    // Wait until the target and ping url have loaded.
    122    await Promise.all([promiseHref, promisePing]);
    123 
    124    // Cleanup.
    125    await stopServer(server);
    126    await stopServer(server2);
    127  },
    128 
    129  // If the origins are different and the document containing the hyperlink
    130  // being audited was retrieved over an encrypted connection then the request
    131  // must include a Ping-To HTTP header with, as its value, target URL. The
    132  // request must neither include a Referer (sic) HTTP header nor include a
    133  // Ping-From HTTP header.
    134  async function diff_origin_secure_referrer() {
    135    let ping = "/ping/" + Math.random();
    136    let server = new HttpServer();
    137 
    138    // The ping we want to receive.
    139    let promisePing = createPingPathHandler(server, ping, () => {
    140      return {to: "https://example.com/"};
    141    });
    142 
    143    // Start the server and run the test.
    144    server.start(-1);
    145 
    146    // The referrer will be loaded using a secure channel.
    147    navigate("https://example.com/chrome/dom/html/test/" +
    148             "file_anchor_ping.html?" + "http://127.0.0.1:" +
    149             server.identity.primaryPort + ping);
    150 
    151    // Wait until the ping has been sent.
    152    await promisePing;
    153 
    154    // Cleanup.
    155    await stopServer(server);
    156  },
    157 
    158  // Test that the <a ping> attribute is properly tokenized using ASCII white
    159  // space characters as separators.
    160  async function tokenize_white_space() {
    161    let from = "/ping-from/" + Math.random();
    162    let to = "/ping-to/" + Math.random();
    163 
    164    let base;
    165    let server = new HttpServer();
    166 
    167    let pings = [
    168      "/ping1/" + Math.random(),
    169      "/ping2/" + Math.random(),
    170      "/ping3/" + Math.random(),
    171      "/ping4/" + Math.random()
    172    ];
    173 
    174    // The page that contains the link.
    175    createFromPathHandler(server, from, to, () => {
    176      return " " + pings[0] + " \r " + pings[1] + " \t " +
    177                   pings[2] + " \n " + pings[3] + "   ";
    178    });
    179 
    180    // The page that the link's href points to.
    181    let promiseHref = createToPathHandler(server, to);
    182 
    183    // The pings we want to receive.
    184    let pingPathHandlers = createPingPathHandlers(server, pings, () => {
    185      return {from: base + from, to: base + to};
    186    });
    187 
    188    // Start the server, get its base URL and run the test.
    189    server.start(-1);
    190    base = "http://localhost:" + server.identity.primaryPort;
    191    navigate(base + from);
    192 
    193    // Wait until the target and ping url have loaded.
    194    await Promise.all([promiseHref, ...pingPathHandlers]);
    195 
    196    // Cleanup.
    197    await stopServer(server);
    198  }
    199 ];
    200 
    201 // Navigate the iframe used for testing to a new URL.
    202 function navigate(uri) {
    203  document.getElementById("frame").src = uri;
    204 }
    205 
    206 // Registers a path handler for the given server that will serve a page
    207 // containing an <a ping> element. The page will automatically simulate
    208 // clicking the link after it has loaded.
    209 function createFromPathHandler(server, path, href, lazyPing) {
    210  server.registerPathHandler(path, function (request, response) {
    211    response.setStatusLine(request.httpVersion, 200, "OK");
    212    response.setHeader("Content-Type", "text/html;charset=utf-8", false);
    213    response.setHeader("Cache-Control", "no-cache", false);
    214 
    215    let body = '<body onload="document.body.firstChild.click()">' +
    216               '<a href="' + href + '" ping="' + lazyPing() + '"></a></body>';
    217    response.write(body);
    218  });
    219 }
    220 
    221 // Registers a path handler for the given server that will serve a simple empty
    222 // page we can use as the href attribute for links. It returns a promise that
    223 // will be resolved once the page has been requested.
    224 function createToPathHandler(server, path) {
    225  return new Promise(resolve => {
    226 
    227    server.registerPathHandler(path, function (request, response) {
    228      response.setStatusLine(request.httpVersion, 200, "OK");
    229      response.setHeader("Content-Type", "text/html;charset=utf-8", false);
    230      response.setHeader("Cache-Control", "no-cache", false);
    231      response.write("OK");
    232 
    233      resolve();
    234    });
    235 
    236  });
    237 }
    238 
    239 // Register multiple path handlers for the given server that will receive
    240 // pings as sent when an <a ping> element is clicked. This method uses
    241 // createPingPathHandler() defined below to ensure all headers are sent
    242 // and received as expected.
    243 function createPingPathHandlers(server, paths, lazyHeaders) {
    244  return Array.from(paths, (path) => createPingPathHandler(server, path, lazyHeaders));
    245 }
    246 
    247 // Registers a path handler for the given server that will receive pings as
    248 // sent when an <a ping> element has been clicked. It will check that the
    249 // correct http method has been used, the post data is correct and all headers
    250 // are given as expected. It returns a promise that will be resolved once the
    251 // ping has been received.
    252 function createPingPathHandler(server, path, lazyHeaders) {
    253  return new Promise(resolve => {
    254 
    255    server.registerPathHandler(path, function (request, response) {
    256      let headers = lazyHeaders();
    257 
    258      is(request.method, "POST", "correct http method used");
    259      is(request.getHeader("Ping-To"), headers.to, "valid ping-to header");
    260 
    261      if ("from" in headers) {
    262        is(request.getHeader("Ping-From"), headers.from, "valid ping-from header");
    263      } else {
    264        ok(!request.hasHeader("Ping-From"), "no ping-from header");
    265      }
    266 
    267      if ("referrer" in headers) {
    268        let expectedReferrer = headers.referrer.match(/https?:\/\/[^\/]+\/?/i)[0];
    269        is(request.getHeader("Referer"), expectedReferrer, "valid referer header");
    270      } else {
    271        ok(!request.hasHeader("Referer"), "no referer header");
    272      }
    273 
    274      let bs = request.bodyInputStream;
    275      let body = NetUtil.readInputStreamToString(bs, bs.available());
    276      is(body, "PING", "correct body sent");
    277 
    278      response.setStatusLine(request.httpVersion, 200, "OK");
    279      response.setHeader("Content-Type", "text/html;charset=utf-8", false);
    280      response.setHeader("Cache-Control", "no-cache", false);
    281      response.write("OK");
    282 
    283      resolve();
    284    });
    285 
    286  });
    287 }
    288 
    289 // Returns a promise that is resolved when the given http server instance has
    290 // been stopped.
    291 function stopServer(server) {
    292  return new Promise(resolve => {
    293    server.stop(resolve);
    294  });
    295 }
    296 
    297  </script>
    298 </head>
    299 <body>
    300 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786347">Mozilla Bug 786347</a>
    301 <p id="display"></p>
    302 <iframe id="frame" />
    303 </body>
    304 </html>