tor-browser

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

cookie-test.js (7473B)


      1 // getAndExpireCookiesForDefaultPathTest is a helper method to get and delete
      2 // cookies using echo-cookie.html.
      3 async function getAndExpireCookiesForDefaultPathTest() {
      4  return new Promise((resolve, reject) => {
      5    try {
      6      const iframe = document.createElement('iframe');
      7      iframe.style = 'display: none';
      8      iframe.addEventListener('load', (e) => {
      9        const win = e.target.contentWindow;
     10        const iframeCookies = win.getCookies();
     11        win.expireCookies().then(() => {
     12          document.documentElement.removeChild(iframe);
     13          resolve(iframeCookies);
     14        });
     15      }, {once: true});
     16      iframe.src = '/cookies/resources/echo-cookie.html';
     17      document.documentElement.appendChild(iframe);
     18    } catch (e) {
     19      reject(e);
     20    }
     21  });
     22 }
     23 
     24 // getAndExpireCookiesForRedirectTest is a helper method to get and delete
     25 // cookies that were set from a Location header redirect.
     26 async function getAndExpireCookiesForRedirectTest(location) {
     27  return new Promise((resolve, reject) => {
     28    try {
     29      const iframe = document.createElement('iframe');
     30      iframe.style = 'display: none';
     31      const listener = (e) => {
     32        if (typeof e.data == 'object' && 'cookies' in e.data) {
     33          window.removeEventListener('message', listener);
     34          document.documentElement.removeChild(iframe);
     35          resolve(e.data.cookies);
     36        }
     37      };
     38      window.addEventListener('message', listener);
     39      iframe.addEventListener('load', (e) => {
     40        e.target.contentWindow.postMessage('getAndExpireCookiesForRedirectTest', '*');
     41      }, {once: true});
     42      iframe.src = location;
     43      document.documentElement.appendChild(iframe);
     44    } catch (e) {
     45      reject(e);
     46    }
     47  });
     48 }
     49 
     50 // httpCookieTest sets a `cookie` (via HTTP), then asserts it was or was not set
     51 // via `expectedValue` (via the DOM). Then cleans it up (via test driver). Most
     52 // tests do not set a Path attribute, so `defaultPath` defaults to true. If the
     53 // cookie values are expected to cause the HTTP request or response to fail, the
     54 // test can be made to pass when this happens via `allowFetchFailure`, which
     55 // defaults to false.
     56 //
     57 // `cookie` may be a single cookie string, or an array of cookie strings, where
     58 // the order of the array items represents the order of the Set-Cookie headers
     59 // sent by the server.
     60 //
     61 // Note: this function has a dependency on testdriver.js. Any test files calling
     62 // it should include testdriver.js and testdriver-vendor.js
     63 function httpCookieTest(cookie, expectedValue, name, defaultPath = true,
     64                        allowFetchFailure = false) {
     65  return promise_test((t) => {
     66    var skipAssertions = false;
     67    return new Promise(async (resolve, reject) => {
     68      // The result is ignored as we're expiring cookies for cleaning here.
     69      await getAndExpireCookiesForDefaultPathTest();
     70      await test_driver.delete_all_cookies();
     71      t.add_cleanup(test_driver.delete_all_cookies);
     72 
     73      let encodedCookie = encodeURIComponent(JSON.stringify(cookie));
     74      try {
     75        await fetch(`/cookies/resources/cookie.py?set=${encodedCookie}`);
     76      } catch {
     77        if (allowFetchFailure) {
     78          skipAssertions = true;
     79        } else {
     80          reject('Failed to fetch /cookies/resources/cookie.py');
     81        }
     82      }
     83      let cookies = document.cookie;
     84      if (defaultPath) {
     85        // for the tests where a Path is set from the request-uri
     86        // path, we need to go look for cookies in an iframe at that
     87        // default path.
     88        cookies = await getAndExpireCookiesForDefaultPathTest();
     89      }
     90      resolve(cookies);
     91    }).then((cookies) => {
     92      if (skipAssertions) {
     93        return;
     94      }
     95      if (Boolean(expectedValue)) {
     96        assert_equals(cookies, expectedValue, 'The cookie was set as expected.');
     97      } else {
     98        assert_equals(cookies, expectedValue, 'The cookie was rejected.');
     99      }
    100    });
    101  }, name);
    102 }
    103 
    104 // This is a variation on httpCookieTest, where a redirect happens via
    105 // the Location header and we check to see if cookies are sent via
    106 // getRedirectedCookies
    107 //
    108 // Note: the locations targeted by this function have a dependency on
    109 // path-redirect-shared.js and should be sure to include it.
    110 function httpRedirectCookieTest(cookie, expectedValue, name, location) {
    111  return promise_test(async (t) => {
    112    // The result is ignored as we're expiring cookies for cleaning here.
    113    await getAndExpireCookiesForRedirectTest(location);
    114 
    115    const encodedCookie = encodeURIComponent(JSON.stringify(cookie));
    116    const encodedLocation = encodeURIComponent(location);
    117    const setParams = `?set=${encodedCookie}&location=${encodedLocation}`;
    118    await fetch(`/cookies/resources/cookie.py${setParams}`);
    119    // for the tests where a redirect happens, we need to head
    120    // to that URI to get the cookies (and then delete them there)
    121    const cookies = await getAndExpireCookiesForRedirectTest(location);
    122    if (Boolean(expectedValue)) {
    123      assert_equals(cookies, expectedValue, 'The cookie was set as expected.');
    124    } else {
    125      assert_equals(cookies, expectedValue, 'The cookie was rejected.');
    126    }
    127  }, name);
    128 }
    129 
    130 // Sets a `cookie` via the DOM, checks it against `expectedValue` via the DOM,
    131 // then cleans it up via the DOM. This is needed in cases where going through
    132 // HTTP headers may modify the cookie line (e.g. by stripping control
    133 // characters).
    134 //
    135 // Note: this function has a dependency on testdriver.js. Any test files calling
    136 // it should include testdriver.js and testdriver-vendor.js
    137 function domCookieTest(cookie, expectedValue, name) {
    138  return promise_test(async (t) => {
    139    await test_driver.delete_all_cookies();
    140    t.add_cleanup(test_driver.delete_all_cookies);
    141 
    142    if (typeof cookie === "string") {
    143      document.cookie = cookie;
    144    } else if (Array.isArray(cookie)) {
    145      for (const singlecookie of cookie) {
    146        document.cookie = singlecookie;
    147      }
    148    } else {
    149      throw new Error('Unexpected type passed into domCookieTest as cookie: ' + typeof cookie);
    150    }
    151    let cookies = document.cookie;
    152    assert_equals(cookies, expectedValue, Boolean(expectedValue) ?
    153                                          'The cookie was set as expected.' :
    154                                          'The cookie was rejected.');
    155  }, name);
    156 }
    157 
    158 // Returns an array of control characters along with their ASCII codes. Control
    159 // characters are defined by RFC 5234 to be %x00-1F / %x7F.
    160 function getCtlCharacters() {
    161  const ctlCodes = [...Array(0x20).keys()]
    162                       .concat([0x7F]);
    163  return ctlCodes.map(i => ({ code: i, chr: String.fromCharCode(i) }))
    164 }
    165 
    166 // Returns a cookie string with name set to "t" * nameLength and value
    167 // set to "1" * valueLength. Passing in 0 for either allows for creating
    168 // a name- or value-less cookie.
    169 //
    170 // Note: Cookie length checking should ignore the "=".
    171 function cookieStringWithNameAndValueLengths(nameLength, valueLength) {
    172  return `${"t".repeat(nameLength)}=${"1".repeat(valueLength)}`;
    173 }
    174 
    175 // Finds the root window.top.opener and directs test_driver commands to it.
    176 //
    177 // If you see a message like: "Error: Tried to run in a non-testharness window
    178 // without a call to set_test_context." then you probably need to call this.
    179 function setTestContextUsingRootWindow() {
    180  let test_window = window.top;
    181  while (test_window.opener && !test_window.opener.closed) {
    182    test_window = test_window.opener.top;
    183  }
    184  test_driver.set_test_context(test_window);
    185 }