summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/cookies/resources/cookie-helper.sub.js
blob: 3338cf0e800cba0d59b9a86c8270c262772b3d7b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// Set up exciting global variables for cookie tests.
(_ => {
  var HOST = "{{host}}";
  var INSECURE_PORT = ":{{ports[http][0]}}";
  var SECURE_PORT = ":{{ports[https][0]}}";
  var CROSS_ORIGIN_HOST = "{{hosts[alt][]}}";

  window.INSECURE_ORIGIN = "http://" + HOST + INSECURE_PORT;

  //For secure cookie verification
  window.SECURE_ORIGIN = "https://" + HOST + SECURE_PORT;

  //standard references
  window.SECURE_SUBDOMAIN_ORIGIN = "https://{{domains[www1]}}" + SECURE_PORT;
  window.SECURE_CROSS_SITE_ORIGIN = "https://" + CROSS_ORIGIN_HOST + SECURE_PORT;
  window.CROSS_SITE_HOST = CROSS_ORIGIN_HOST;

  // Set the global cookie name.
  window.HTTP_COOKIE = "cookie_via_http";
})();

// A tiny helper which returns the result of fetching |url| with credentials.
function credFetch(url) {
  return fetch(url, {"credentials": "include"})
    .then(response => {
      if (response.status !== 200) {
        throw new Error(response.statusText);
      }
      return response;
    });
}

// Returns a URL on |origin| which redirects to a given absolute URL.
function redirectTo(origin, url) {
  return origin + "/cookies/resources/redirectWithCORSHeaders.py?status=307&location=" + encodeURIComponent(url);
}

// Returns a URL on |origin| which navigates the window to the given URL (by
// setting window.location).
function navigateTo(origin, url) {
  return origin + "/cookies/resources/navigate.html?location=" + encodeURIComponent(url);
}

// Returns whether a cookie with name `name` with value `value` is in the cookie
// string (presumably obtained via document.cookie).
function cookieStringHasCookie(name, value, cookieString) {
  return new RegExp(`(?:^|; )${name}=${value}(?:$|;)`).test(cookieString);
}

// Asserts that `document.cookie` contains or does not contain (according to
// the value of |present|) a cookie named |name| with a value of |value|.
function assert_dom_cookie(name, value, present) {
  assert_equals(cookieStringHasCookie(name, value, document.cookie), present, "`" + name + "=" + value + "` in `document.cookie`");
}

function assert_cookie(origin, obj, name, value, present) {
  assert_equals(obj[name], present ? value : undefined, "`" + name + "=" + value + "` in request to `" + origin + "`.");
}

// Remove the cookie named |name| from |origin|, then set it on |origin| anew.
// If |origin| matches `self.origin`, also assert (via `document.cookie`) that
// the cookie was correctly removed and reset.
async function create_cookie(origin, name, value, extras) {
  alert("Create_cookie: " + origin + "/cookies/resources/drop.py?name=" + name);
  await credFetch(origin + "/cookies/resources/drop.py?name=" + name);
  if (origin == self.origin)
    assert_dom_cookie(name, value, false);
  await credFetch(origin + "/cookies/resources/set.py?" + name + "=" + value + ";path=/;" + extras);
  if (origin == self.origin)
    assert_dom_cookie(name, value, true);
}

//
// Prefix-specific test helpers
//
function set_prefixed_cookie_via_dom_test(options) {
  promise_test(t => {
    var name = options.prefix + "prefixtestcookie";
    erase_cookie_from_js(name, options.params);
    t.add_cleanup(() => erase_cookie_from_js(name, options.params));
    var value = "" + Math.random();
    document.cookie = name + "=" + value + ";" + options.params;

    assert_dom_cookie(name, value, options.shouldExistInDOM);

    return credFetch("/cookies/resources/list.py")
      .then(r => r.json())
      .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
  }, options.title);
}

function set_prefixed_cookie_via_http_test(options) {
  promise_test(t => {
    var name = options.prefix + "prefixtestcookie";
    var value = "" + Math.random();

    t.add_cleanup(() => {
      var cookie = name + "=0;expires=" + new Date(0).toUTCString() + ";" +
        options.params;

      return credFetch(options.origin + "/cookies/resources/set.py?" + cookie);
    });

    return credFetch(options.origin + "/cookies/resources/set.py?" + name + "=" + value + ";" + options.params)
      .then(_ => credFetch(options.origin + "/cookies/resources/list.py"))
      .then(r => r.json())
      .then(cookies => assert_equals(cookies[name], options.shouldExistViaHTTP ? value : undefined));
  }, options.title);
}

//
// SameSite-specific test helpers:
//

// status for "network" cookies.
window.SameSiteStatus = {
  CROSS_SITE: "cross-site",
  LAX: "lax",
  STRICT: "strict"
};
// status for "document.cookie".
window.DomSameSiteStatus = {
  CROSS_SITE: "cross-site",
  SAME_SITE: "same-site",
};

const wait_for_message = (type, origin) => {
  return new Promise((resolve, reject) => {
    window.addEventListener('message', e => {
      if (origin && e.origin != origin) {
        reject("Message from unexpected origin in wait_for_message:" + e.origin);
        return;
      }

      if (e.data.type && e.data.type === type)
        resolve(e);
    }, { once: true });
  });
};

// Reset SameSite test cookies on |origin|. If |origin| matches `self.origin`, assert
// (via `document.cookie`) that they were properly removed and reset.
async function resetSameSiteCookies(origin, value) {
  let w = window.open(origin + "/cookies/samesite/resources/puppet.html");
  try {
    await wait_for_message("READY", origin);
    w.postMessage({type: "drop", useOwnOrigin: true}, "*");
    await wait_for_message("drop-complete", origin);
    if (origin == self.origin) {
      assert_dom_cookie("samesite_strict", value, false);
      assert_dom_cookie("samesite_lax", value, false);
      assert_dom_cookie("samesite_none", value, false);
      assert_dom_cookie("samesite_unspecified", value, false);
    }

    w.postMessage({type: "set", value: value, useOwnOrigin: true}, "*");
    await wait_for_message("set-complete", origin);
    if (origin == self.origin) {
      assert_dom_cookie("samesite_strict", value, true);
      assert_dom_cookie("samesite_lax", value, true);
      assert_dom_cookie("samesite_none", value, true);
      assert_dom_cookie("samesite_unspecified", value, true);
    }
  } finally {
    w.close();
  }
}

// Given an |expectedStatus| and |expectedValue|, assert the |cookies| contains
// the proper set of cookie names and values. Expects SameSite-Lax-by-default.
function verifySameSiteCookieState(expectedStatus, expectedValue, cookies, domCookieStatus) {
    assert_equals(cookies["samesite_none"], expectedValue, "SameSite=None cookies are always sent.");
    if (expectedStatus == SameSiteStatus.CROSS_SITE) {
      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with cross-site requests.");
      assert_not_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are not sent with cross-site requests.");
      assert_not_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are not sent with cross-site requests.");
    } else if (expectedStatus == SameSiteStatus.LAX) {
      assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not sent with lax requests.");
      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with lax requests.");
      assert_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are are sent with lax requests.")
    } else if (expectedStatus == SameSiteStatus.STRICT) {
      assert_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are sent with strict requests.");
      assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are sent with strict requests.");
      assert_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are are sent with strict requests.")
    }

    if (cookies["domcookies"]) {
      verifyDocumentCookieSameSite(domCookieStatus, expectedValue, cookies['domcookies']);
  }
}

function verifyDocumentCookieSameSite(expectedStatus, expectedValue, domcookies) {
  const cookies = domcookies.split(";")
                            .map(cookie => cookie.trim().split("="))
                            .reduce((obj, cookie) => {
                              obj[cookie[0]] = cookie[1];
                              return obj;
                            }, {});

  if (expectedStatus == DomSameSiteStatus.SAME_SITE) {
    assert_equals(cookies["samesite_none"], expectedValue, "SameSite=None cookies are always included in document.cookie.");
    assert_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are always included in document.cookie.");
    assert_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are always included in document.cookie.");
    assert_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are always included in document.cookie.");
  } else if (expectedStatus == DomSameSiteStatus.CROSS_SITE) {
    assert_equals(cookies["samesite_none"], expectedValue, "SameSite=None cookies are always included in document.cookie.");
    assert_not_equals(cookies["samesite_unspecified"], expectedValue, "Unspecified-SameSite cookies are not included in document.cookie when cross-site.");
    assert_not_equals(cookies["samesite_strict"], expectedValue, "SameSite=Strict cookies are not included in document.cookie when cross-site.");
    assert_not_equals(cookies["samesite_lax"], expectedValue, "SameSite=Lax cookies are not included in document.cookie when cross-site.");
  }
}

//
// LeaveSecureCookiesAlone-specific test helpers:
//

window.SecureStatus = {
  INSECURE_COOKIE_ONLY: "1",
  BOTH_COOKIES: "2",
};

//Reset SameSite test cookies on |origin|. If |origin| matches `self.origin`, assert
//(via `document.cookie`) that they were properly removed and reset.
function resetSecureCookies(origin, value) {
return credFetch(origin + "/cookies/resources/dropSecure.py")
 .then(_ => {
   if (origin == self.origin) {
     assert_dom_cookie("alone_secure", value, false);
     assert_dom_cookie("alone_insecure", value, false);
   }
 })
 .then(_ => {
     return credFetch(origin + "/cookie/resources/setSecure.py?" + value)
 })
}

// Reset SameSite=None test cookies on |origin|. If |origin| matches
// `self.origin`, assert (via `document.cookie`) that they were properly
// removed.
function resetSameSiteNoneCookies(origin, value) {
  return credFetch(origin + "/cookies/resources/dropSameSiteNone.py")
    .then(_ => {
      if (origin == self.origin) {
        assert_dom_cookie("samesite_none_insecure", value, false);
        assert_dom_cookie("samesite_none_secure", value, false);
      }
    })
    .then(_ => {
      return credFetch(origin + "/cookies/resources/setSameSiteNone.py?" + value);
    })
}

// Reset test cookies with multiple SameSite attributes on |origin|.
// If |origin| matches `self.origin`, assert (via `document.cookie`)
// that they were properly removed.
function resetSameSiteMultiAttributeCookies(origin, value) {
  return credFetch(origin + "/cookies/resources/dropSameSiteMultiAttribute.py")
    .then(_ => {
      if (origin == self.origin) {
        assert_dom_cookie("samesite_unsupported", value, false);
        assert_dom_cookie("samesite_unsupported_none", value, false);
        assert_dom_cookie("samesite_unsupported_lax", value, false);
        assert_dom_cookie("samesite_unsupported_strict", value, false);
        assert_dom_cookie("samesite_none_unsupported", value, false);
        assert_dom_cookie("samesite_lax_unsupported", value, false);
        assert_dom_cookie("samesite_strict_unsupported", value, false);
        assert_dom_cookie("samesite_lax_none", value, false);
      }
    })
    .then(_ => {
      return credFetch(origin + "/cookies/resources/setSameSiteMultiAttribute.py?" + value);
    })
}

//
// DOM based cookie manipulation APIs
//

// erase cookie value and set for expiration
function erase_cookie_from_js(name, params) {
  document.cookie = `${name}=0; expires=${new Date(0).toUTCString()}; ${params};`;
  var re = new RegExp("(?:^|; )" + name);
  assert_equals(re.test(document.cookie), false, "Sanity check: " + name + " has been deleted.");
}