tor-browser

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

cookie-helper.sub.js (12252B)


      1 // Set up exciting global variables for cookie tests.
      2 (_ => {
      3  var HOST = "{{host}}";
      4  var INSECURE_PORT = ":{{ports[http][0]}}";
      5  var SECURE_PORT = ":{{ports[https][0]}}";
      6  var CROSS_ORIGIN_HOST = "{{hosts[alt][]}}";
      7 
      8  window.INSECURE_ORIGIN = "http://" + HOST + INSECURE_PORT;
      9 
     10  //For secure cookie verification
     11  window.SECURE_ORIGIN = "https://" + HOST + SECURE_PORT;
     12 
     13  //standard references
     14  window.SECURE_SUBDOMAIN_ORIGIN = "https://{{domains[www1]}}" + SECURE_PORT;
     15  window.SECURE_CROSS_SITE_ORIGIN = "https://" + CROSS_ORIGIN_HOST + SECURE_PORT;
     16  window.CROSS_SITE_HOST = CROSS_ORIGIN_HOST;
     17 
     18  // Set the global cookie name.
     19  window.HTTP_COOKIE = "cookie_via_http";
     20 })();
     21 
     22 // A tiny helper which returns the result of fetching |url| with credentials.
     23 function credFetch(url) {
     24  return fetch(url, {"credentials": "include"})
     25    .then(response => {
     26      if (response.status !== 200) {
     27        throw new Error(response.statusText);
     28      }
     29      return response;
     30    });
     31 }
     32 
     33 // Returns a URL on |origin| which redirects to a given absolute URL.
     34 function redirectTo(origin, url) {
     35  return origin + "/cookies/resources/redirectWithCORSHeaders.py?status=307&location=" + encodeURIComponent(url);
     36 }
     37 
     38 // Returns a URL on |origin| which navigates the window to the given URL (by
     39 // setting window.location).
     40 function navigateTo(origin, url) {
     41  return origin + "/cookies/resources/navigate.html?location=" + encodeURIComponent(url);
     42 }
     43 
     44 // Returns whether a cookie with name `name` with value `value` is in the cookie
     45 // string (presumably obtained via document.cookie).
     46 function cookieStringHasCookie(name, value, cookieString) {
     47  return new RegExp(`(?:^|; )${name}=${value}(?:$|;)`).test(cookieString);
     48 }
     49 
     50 // Asserts that `document.cookie` contains or does not contain (according to
     51 // the value of |present|) a cookie named |name| with a value of |value|.
     52 function assert_dom_cookie(name, value, present) {
     53  assert_equals(cookieStringHasCookie(name, value, document.cookie), present, "`" + name + "=" + value + "` in `document.cookie`");
     54 }
     55 
     56 function assert_cookie(origin, obj, name, value, present) {
     57  assert_equals(obj[name], present ? value : undefined, "`" + name + "=" + value + "` in request to `" + origin + "`.");
     58 }
     59 
     60 // Remove the cookie named |name| from |origin|, then set it on |origin| anew.
     61 // If |origin| matches `self.origin`, also assert (via `document.cookie`) that
     62 // the cookie was correctly removed and reset.
     63 async function create_cookie(origin, name, value, extras) {
     64  alert("Create_cookie: " + origin + "/cookies/resources/drop.py?name=" + name);
     65  await credFetch(origin + "/cookies/resources/drop.py?name=" + name);
     66  if (origin == self.origin)
     67    assert_dom_cookie(name, value, false);
     68  await credFetch(origin + "/cookies/resources/set.py?" + name + "=" + value + ";path=/;" + extras);
     69  if (origin == self.origin)
     70    assert_dom_cookie(name, value, true);
     71 }
     72 
     73 //
     74 // Prefix-specific test helpers
     75 //
     76 window.dom_prefix_counter = 0;
     77 function set_prefixed_cookie_via_dom_test(options) {
     78  promise_test(t => {
     79    var name = options.prefix + "prefixtestcookie";
     80    erase_cookie_from_js(name, options.params);
     81    t.add_cleanup(() => erase_cookie_from_js(name, options.params));
     82    var value = "foo" + ++window.dom_prefix_counter;
     83    document.cookie = name + "=" + value + ";" + options.params;
     84 
     85    assert_dom_cookie(name, value, options.shouldExistInDOM);
     86 
     87    return credFetch("/cookies/resources/list.py")
     88      .then(r => r.json())
     89      .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
     90  }, options.title);
     91 }
     92 
     93 window.http_prefix_counter = 0;
     94 function set_prefixed_cookie_via_http_test(options) {
     95  promise_test(t => {
     96    var name = options.prefix + "prefixtestcookie";
     97    var value = "bar" + ++window.http_prefix_counter;
     98 
     99    t.add_cleanup(() => {
    100      var cookie = name + "=0;expires=" + new Date(0).toUTCString() + ";" +
    101        options.params;
    102 
    103      return credFetch(options.origin + "/cookies/resources/set.py?" + cookie);
    104    });
    105 
    106    return credFetch(options.origin + "/cookies/resources/set.py?" + name + "=" + value + ";" + options.params)
    107      .then(_ => credFetch(options.origin + "/cookies/resources/list.py"))
    108      .then(r => r.json())
    109      .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
    110  }, options.title);
    111 }
    112 
    113 //
    114 // SameSite-specific test helpers:
    115 //
    116 
    117 // status for "network" cookies.
    118 window.SameSiteStatus = {
    119  CROSS_SITE: "cross-site",
    120  LAX: "lax",
    121  STRICT: "strict"
    122 };
    123 // status for "document.cookie".
    124 window.DomSameSiteStatus = {
    125  CROSS_SITE: "cross-site",
    126  SAME_SITE: "same-site",
    127 };
    128 
    129 const wait_for_message = (type, origin) => {
    130  return new Promise((resolve, reject) => {
    131    window.addEventListener('message', e => {
    132      if (origin && e.origin != origin) {
    133        reject("Message from unexpected origin in wait_for_message:" + e.origin);
    134        return;
    135      }
    136 
    137      if (e.data.type && e.data.type === type)
    138        resolve(e);
    139    }, { once: true });
    140  });
    141 };
    142 
    143 // Reset SameSite test cookies on |origin|. If |origin| matches `self.origin`, assert
    144 // (via `document.cookie`) that they were properly removed and reset.
    145 async function resetSameSiteCookies(origin, value) {
    146  let w = window.open(origin + "/cookies/samesite/resources/puppet.html");
    147  try {
    148    await wait_for_message("READY", origin);
    149    w.postMessage({type: "drop", useOwnOrigin: true}, "*");
    150    await wait_for_message("drop-complete", origin);
    151    if (origin == self.origin) {
    152      assert_dom_cookie("samesite_strict", value, false);
    153      assert_dom_cookie("samesite_lax", value, false);
    154      assert_dom_cookie("samesite_none", value, false);
    155      assert_dom_cookie("samesite_unspecified", value, false);
    156    }
    157 
    158    w.postMessage({type: "set", value: value, useOwnOrigin: true}, "*");
    159    await wait_for_message("set-complete", origin);
    160    if (origin == self.origin) {
    161      assert_dom_cookie("samesite_strict", value, true);
    162      assert_dom_cookie("samesite_lax", value, true);
    163      assert_dom_cookie("samesite_none", value, true);
    164      assert_dom_cookie("samesite_unspecified", value, true);
    165    }
    166  } finally {
    167    w.close();
    168  }
    169 }
    170 
    171 // Given an |expectedStatus| and |expectedValue|, assert the |cookies| contains
    172 // the proper set of cookie names and values. Expects SameSite-Lax-by-default.
    173 function verifySameSiteCookieState(expectedStatus, expectedValue, cookies, domCookieStatus) {
    174    assert_equals(cookies["samesite_none"], expectedValue, "SameSite=None cookies are always sent.");
    175    if (expectedStatus == SameSiteStatus.CROSS_SITE) {
    176      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with cross-site requests.");
    177      assert_not_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are not sent with cross-site requests.");
    178      assert_not_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are not sent with cross-site requests.");
    179    } else if (expectedStatus == SameSiteStatus.LAX) {
    180      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with lax requests.");
    181      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with lax requests.");
    182      assert_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are are sent with lax requests.")
    183    } else if (expectedStatus == SameSiteStatus.STRICT) {
    184      assert_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are sent with strict requests.");
    185      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with strict requests.");
    186      assert_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are are sent with strict requests.")
    187    }
    188 
    189    if (cookies["domcookies"]) {
    190      verifyDocumentCookieSameSite(domCookieStatus, expectedValue, cookies['domcookies']);
    191  }
    192 }
    193 
    194 function verifyDocumentCookieSameSite(expectedStatus, expectedValue, domcookies) {
    195  const cookies = domcookies.split(";")
    196                            .map(cookie => cookie.trim().split("="))
    197                            .reduce((obj, cookie) => {
    198                              obj[cookie[0]] = cookie[1];
    199                              return obj;
    200                            }, {});
    201 
    202  if (expectedStatus == DomSameSiteStatus.SAME_SITE) {
    203    assert_equals(cookies["samesite_none"], expectedValue, "SameSite=None cookies are always included in document.cookie.");
    204    assert_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are always included in document.cookie.");
    205    assert_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are always included in document.cookie.");
    206    assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are always included in document.cookie.");
    207  } else if (expectedStatus == DomSameSiteStatus.CROSS_SITE) {
    208    assert_equals(cookies["samesite_none"], expectedValue, "SameSite=None cookies are always included in document.cookie.");
    209    assert_not_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are not included in document.cookie when cross-site.");
    210    assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not included in document.cookie when cross-site.");
    211    assert_not_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are not included in document.cookie when cross-site.");
    212  }
    213 }
    214 
    215 //
    216 // LeaveSecureCookiesAlone-specific test helpers:
    217 //
    218 
    219 window.SecureStatus = {
    220  INSECURE_COOKIE_ONLY: "1",
    221  BOTH_COOKIES: "2",
    222 };
    223 
    224 //Reset SameSite test cookies on |origin|. If |origin| matches `self.origin`, assert
    225 //(via `document.cookie`) that they were properly removed and reset.
    226 function resetSecureCookies(origin, value) {
    227 return credFetch(origin + "/cookies/resources/dropSecure.py")
    228 .then(_ => {
    229   if (origin == self.origin) {
    230     assert_dom_cookie("alone_secure", value, false);
    231     assert_dom_cookie("alone_insecure", value, false);
    232   }
    233 })
    234 .then(_ => {
    235     return credFetch(origin + "/cookie/resources/setSecure.py?" + value)
    236 })
    237 }
    238 
    239 // Reset SameSite=None test cookies on |origin|. If |origin| matches
    240 // `self.origin`, assert (via `document.cookie`) that they were properly
    241 // removed.
    242 function resetSameSiteNoneCookies(origin, value) {
    243  return credFetch(origin + "/cookies/resources/dropSameSiteNone.py")
    244    .then(_ => {
    245      if (origin == self.origin) {
    246        assert_dom_cookie("samesite_none_insecure", value, false);
    247        assert_dom_cookie("samesite_none_secure", value, false);
    248      }
    249    })
    250    .then(_ => {
    251      return credFetch(origin + "/cookies/resources/setSameSiteNone.py?" + value);
    252    })
    253 }
    254 
    255 // Reset test cookies with multiple SameSite attributes on |origin|.
    256 // If |origin| matches `self.origin`, assert (via `document.cookie`)
    257 // that they were properly removed.
    258 function resetSameSiteMultiAttributeCookies(origin, value) {
    259  return credFetch(origin + "/cookies/resources/dropSameSiteMultiAttribute.py")
    260    .then(_ => {
    261      if (origin == self.origin) {
    262        assert_dom_cookie("samesite_unsupported", value, false);
    263        assert_dom_cookie("samesite_unsupported_none", value, false);
    264        assert_dom_cookie("samesite_unsupported_lax", value, false);
    265        assert_dom_cookie("samesite_unsupported_strict", value, false);
    266        assert_dom_cookie("samesite_none_unsupported", value, false);
    267        assert_dom_cookie("samesite_lax_unsupported", value, false);
    268        assert_dom_cookie("samesite_strict_unsupported", value, false);
    269        assert_dom_cookie("samesite_lax_none", value, false);
    270      }
    271    })
    272    .then(_ => {
    273      return credFetch(origin + "/cookies/resources/setSameSiteMultiAttribute.py?" + value);
    274    })
    275 }
    276 
    277 //
    278 // DOM based cookie manipulation APIs
    279 //
    280 
    281 // erase cookie value and set for expiration
    282 function erase_cookie_from_js(name, params) {
    283  document.cookie = `${name}=0; expires=${new Date(0).toUTCString()}; ${params};`;
    284  var re = new RegExp("(?:^|; )" + name);
    285  assert_equals(re.test(document.cookie), false, "Sanity check: " + name + " has been deleted.");
    286 }