tough-cookie 3.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cookie.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Copyright (c) 2015, Salesforce.com, Inc.
2
+ * Copyright (c) 2015-2020, Salesforce.com, Inc.
3
3
  * All rights reserved.
4
4
  *
5
5
  * Redistribution and use in source and binary forms, with or without
@@ -28,55 +28,94 @@
28
28
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
29
  * POSSIBILITY OF SUCH DAMAGE.
30
30
  */
31
- 'use strict';
32
- var urlParse = require('url').parse;
33
- var util = require('util');
34
- var ipRegex = require('ip-regex')({ exact: true });
35
- var pubsuffix = require('./pubsuffix-psl');
36
- var Store = require('./store').Store;
37
- var MemoryCookieStore = require('./memstore').MemoryCookieStore;
38
- var pathMatch = require('./pathMatch').pathMatch;
39
- var VERSION = require('./version');
40
-
41
- var punycode;
42
- try {
43
- punycode = require('punycode');
44
- } catch(e) {
45
- console.warn("tough-cookie: can't load punycode; won't use punycode for domain normalization");
46
- }
31
+ "use strict";
32
+ const punycode = require("punycode/");
33
+ const urlParse = require("url-parse");
34
+ const pubsuffix = require("./pubsuffix-psl");
35
+ const Store = require("./store").Store;
36
+ const MemoryCookieStore = require("./memstore").MemoryCookieStore;
37
+ const pathMatch = require("./pathMatch").pathMatch;
38
+ const validators = require("./validators.js");
39
+ const VERSION = require("./version");
40
+ const { fromCallback } = require("universalify");
41
+ const { getCustomInspectSymbol } = require("./utilHelper");
47
42
 
48
43
  // From RFC6265 S4.1.1
49
44
  // note that it excludes \x3B ";"
50
- var COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/;
45
+ const COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/;
51
46
 
52
- var CONTROL_CHARS = /[\x00-\x1F]/;
47
+ const CONTROL_CHARS = /[\x00-\x1F]/;
53
48
 
54
49
  // From Chromium // '\r', '\n' and '\0' should be treated as a terminator in
55
50
  // the "relaxed" mode, see:
56
51
  // https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60
57
- var TERMINATORS = ['\n', '\r', '\0'];
52
+ const TERMINATORS = ["\n", "\r", "\0"];
58
53
 
59
54
  // RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"'
60
55
  // Note ';' is \x3B
61
- var PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/;
56
+ const PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/;
62
57
 
63
58
  // date-time parsing constants (RFC6265 S5.1.1)
64
59
 
65
- var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/;
66
-
67
- var MONTH_TO_NUM = {
68
- jan:0, feb:1, mar:2, apr:3, may:4, jun:5,
69
- jul:6, aug:7, sep:8, oct:9, nov:10, dec:11
60
+ const DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/;
61
+
62
+ const MONTH_TO_NUM = {
63
+ jan: 0,
64
+ feb: 1,
65
+ mar: 2,
66
+ apr: 3,
67
+ may: 4,
68
+ jun: 5,
69
+ jul: 6,
70
+ aug: 7,
71
+ sep: 8,
72
+ oct: 9,
73
+ nov: 10,
74
+ dec: 11
70
75
  };
71
- var NUM_TO_MONTH = [
72
- 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'
73
- ];
74
- var NUM_TO_DAY = [
75
- 'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
76
- ];
77
76
 
78
- var MAX_TIME = 2147483647000; // 31-bit max
79
- var MIN_TIME = 0; // 31-bit min
77
+ const MAX_TIME = 2147483647000; // 31-bit max
78
+ const MIN_TIME = 0; // 31-bit min
79
+ const SAME_SITE_CONTEXT_VAL_ERR =
80
+ 'Invalid sameSiteContext option for getCookies(); expected one of "strict", "lax", or "none"';
81
+
82
+ function checkSameSiteContext(value) {
83
+ validators.validate(validators.isNonEmptyString(value), value);
84
+ const context = String(value).toLowerCase();
85
+ if (context === "none" || context === "lax" || context === "strict") {
86
+ return context;
87
+ } else {
88
+ return null;
89
+ }
90
+ }
91
+
92
+ const PrefixSecurityEnum = Object.freeze({
93
+ SILENT: "silent",
94
+ STRICT: "strict",
95
+ DISABLED: "unsafe-disabled"
96
+ });
97
+
98
+ // Dumped from ip-regex@4.0.0, with the following changes:
99
+ // * all capturing groups converted to non-capturing -- "(?:)"
100
+ // * support for IPv6 Scoped Literal ("%eth1") removed
101
+ // * lowercase hexadecimal only
102
+ const IP_REGEX_LOWERCASE = /(?:^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$)|(?:^(?:(?:[a-f\d]{1,4}:){7}(?:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|:[a-f\d]{1,4}|:)|(?:[a-f\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,2}|:)|(?:[a-f\d]{1,4}:){4}(?:(?::[a-f\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,3}|:)|(?:[a-f\d]{1,4}:){3}(?:(?::[a-f\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,4}|:)|(?:[a-f\d]{1,4}:){2}(?:(?::[a-f\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,5}|:)|(?:[a-f\d]{1,4}:){1}(?:(?::[a-f\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,6}|:)|(?::(?:(?::[a-f\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}|(?::[a-f\d]{1,4}){1,7}|:)))$)/;
103
+ const IP_V6_REGEX = `
104
+ \\[?(?:
105
+ (?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)|
106
+ (?:[a-fA-F\\d]{1,4}:){6}(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|:[a-fA-F\\d]{1,4}|:)|
107
+ (?:[a-fA-F\\d]{1,4}:){5}(?::(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,2}|:)|
108
+ (?:[a-fA-F\\d]{1,4}:){4}(?:(?::[a-fA-F\\d]{1,4}){0,1}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,3}|:)|
109
+ (?:[a-fA-F\\d]{1,4}:){3}(?:(?::[a-fA-F\\d]{1,4}){0,2}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,4}|:)|
110
+ (?:[a-fA-F\\d]{1,4}:){2}(?:(?::[a-fA-F\\d]{1,4}){0,3}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,5}|:)|
111
+ (?:[a-fA-F\\d]{1,4}:){1}(?:(?::[a-fA-F\\d]{1,4}){0,4}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,6}|:)|
112
+ (?::(?:(?::[a-fA-F\\d]{1,4}){0,5}:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d|\\d)){3}|(?::[a-fA-F\\d]{1,4}){1,7}|:))
113
+ )(?:%[0-9a-zA-Z]{1,})?\\]?
114
+ `
115
+ .replace(/\s*\/\/.*$/gm, "")
116
+ .replace(/\n/g, "")
117
+ .trim();
118
+ const IP_V6_REGEX_OBJECT = new RegExp(`^${IP_V6_REGEX}$`);
80
119
 
81
120
  /*
82
121
  * Parses a Natural number (i.e., non-negative integer) with either the
@@ -89,11 +128,11 @@ var MIN_TIME = 0; // 31-bit min
89
128
  * "( non-digit *OCTET )" trailer.
90
129
  */
91
130
  function parseDigits(token, minDigits, maxDigits, trailingOK) {
92
- var count = 0;
131
+ let count = 0;
93
132
  while (count < token.length) {
94
- var c = token.charCodeAt(count);
133
+ const c = token.charCodeAt(count);
95
134
  // "non-digit = %x00-2F / %x3A-FF"
96
- if (c <= 0x2F || c >= 0x3A) {
135
+ if (c <= 0x2f || c >= 0x3a) {
97
136
  break;
98
137
  }
99
138
  count++;
@@ -108,12 +147,12 @@ function parseDigits(token, minDigits, maxDigits, trailingOK) {
108
147
  return null;
109
148
  }
110
149
 
111
- return parseInt(token.substr(0,count), 10);
150
+ return parseInt(token.substr(0, count), 10);
112
151
  }
113
152
 
114
153
  function parseTime(token) {
115
- var parts = token.split(':');
116
- var result = [0,0,0];
154
+ const parts = token.split(":");
155
+ const result = [0, 0, 0];
117
156
 
118
157
  /* RF6256 S5.1.1:
119
158
  * time = hms-time ( non-digit *OCTET )
@@ -125,12 +164,12 @@ function parseTime(token) {
125
164
  return null;
126
165
  }
127
166
 
128
- for (var i = 0; i < 3; i++) {
167
+ for (let i = 0; i < 3; i++) {
129
168
  // "time-field" must be strictly "1*2DIGIT", HOWEVER, "hms-time" can be
130
169
  // followed by "( non-digit *OCTET )" so therefore the last time-field can
131
170
  // have a trailer
132
- var trailingOK = (i == 2);
133
- var num = parseDigits(parts[i], 1, 2, trailingOK);
171
+ const trailingOK = i == 2;
172
+ const num = parseDigits(parts[i], 1, 2, trailingOK);
134
173
  if (num === null) {
135
174
  return null;
136
175
  }
@@ -141,8 +180,10 @@ function parseTime(token) {
141
180
  }
142
181
 
143
182
  function parseMonth(token) {
144
- token = String(token).substr(0,3).toLowerCase();
145
- var num = MONTH_TO_NUM[token];
183
+ token = String(token)
184
+ .substr(0, 3)
185
+ .toLowerCase();
186
+ const num = MONTH_TO_NUM[token];
146
187
  return num >= 0 ? num : null;
147
188
  }
148
189
 
@@ -158,25 +199,25 @@ function parseDate(str) {
158
199
  * 2. Process each date-token sequentially in the order the date-tokens
159
200
  * appear in the cookie-date
160
201
  */
161
- var tokens = str.split(DATE_DELIM);
202
+ const tokens = str.split(DATE_DELIM);
162
203
  if (!tokens) {
163
204
  return;
164
205
  }
165
206
 
166
- var hour = null;
167
- var minute = null;
168
- var second = null;
169
- var dayOfMonth = null;
170
- var month = null;
171
- var year = null;
207
+ let hour = null;
208
+ let minute = null;
209
+ let second = null;
210
+ let dayOfMonth = null;
211
+ let month = null;
212
+ let year = null;
172
213
 
173
- for (var i=0; i<tokens.length; i++) {
174
- var token = tokens[i].trim();
214
+ for (let i = 0; i < tokens.length; i++) {
215
+ const token = tokens[i].trim();
175
216
  if (!token.length) {
176
217
  continue;
177
218
  }
178
219
 
179
- var result;
220
+ let result;
180
221
 
181
222
  /* 2.1. If the found-time flag is not set and the token matches the time
182
223
  * production, set the found-time flag and set the hour- value,
@@ -260,8 +301,12 @@ function parseDate(str) {
260
301
  * So, in order as above:
261
302
  */
262
303
  if (
263
- dayOfMonth === null || month === null || year === null || second === null ||
264
- dayOfMonth < 1 || dayOfMonth > 31 ||
304
+ dayOfMonth === null ||
305
+ month === null ||
306
+ year === null ||
307
+ second === null ||
308
+ dayOfMonth < 1 ||
309
+ dayOfMonth > 31 ||
265
310
  year < 1601 ||
266
311
  hour > 23 ||
267
312
  minute > 59 ||
@@ -274,13 +319,8 @@ function parseDate(str) {
274
319
  }
275
320
 
276
321
  function formatDate(date) {
277
- var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d;
278
- var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h;
279
- var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m;
280
- var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s;
281
- return NUM_TO_DAY[date.getUTCDay()] + ', ' +
282
- d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+
283
- h+':'+m+':'+s+' GMT';
322
+ validators.validate(validators.isDate(date), date);
323
+ return date.toUTCString();
284
324
  }
285
325
 
286
326
  // S5.1.2 Canonicalized Host Names
@@ -288,7 +328,11 @@ function canonicalDomain(str) {
288
328
  if (str == null) {
289
329
  return null;
290
330
  }
291
- str = str.trim().replace(/^\./,''); // S4.1.2.3 & S5.2.3: ignore leading .
331
+ str = str.trim().replace(/^\./, ""); // S4.1.2.3 & S5.2.3: ignore leading .
332
+
333
+ if (IP_V6_REGEX_OBJECT.test(str)) {
334
+ str = str.replace("[", "").replace("]", "");
335
+ }
292
336
 
293
337
  // convert to IDN if any non-ASCII characters
294
338
  if (punycode && /[^\u0001-\u007f]/.test(str)) {
@@ -309,7 +353,11 @@ function domainMatch(str, domStr, canonicalize) {
309
353
  }
310
354
 
311
355
  /*
312
- * "The domain string and the string are identical. (Note that both the
356
+ * S5.1.3:
357
+ * "A string domain-matches a given domain string if at least one of the
358
+ * following conditions hold:"
359
+ *
360
+ * " o The domain string and the string are identical. (Note that both the
313
361
  * domain string and the string will have been canonicalized to lower case at
314
362
  * this point)"
315
363
  */
@@ -317,35 +365,35 @@ function domainMatch(str, domStr, canonicalize) {
317
365
  return true;
318
366
  }
319
367
 
320
- /* "All of the following [three] conditions hold:" (order adjusted from the RFC) */
321
-
322
- /* "* The string is a host name (i.e., not an IP address)." */
323
- if (ipRegex.test(str)) {
324
- return false;
325
- }
368
+ /* " o All of the following [three] conditions hold:" */
326
369
 
327
370
  /* "* The domain string is a suffix of the string" */
328
- var idx = str.indexOf(domStr);
371
+ const idx = str.lastIndexOf(domStr);
329
372
  if (idx <= 0) {
330
373
  return false; // it's a non-match (-1) or prefix (0)
331
374
  }
332
375
 
333
- // e.g "a.b.c".indexOf("b.c") === 2
376
+ // next, check it's a proper suffix
377
+ // e.g., "a.b.c".indexOf("b.c") === 2
334
378
  // 5 === 3+2
335
- if (str.length !== domStr.length + idx) { // it's not a suffix
336
- return false;
379
+ if (str.length !== domStr.length + idx) {
380
+ return false; // it's not a suffix
381
+ }
382
+
383
+ /* " * The last character of the string that is not included in the
384
+ * domain string is a %x2E (".") character." */
385
+ if (str.substr(idx - 1, 1) !== ".") {
386
+ return false; // doesn't align on "."
337
387
  }
338
388
 
339
- /* "* The last character of the string that is not included in the domain
340
- * string is a %x2E (".") character." */
341
- if (str.substr(idx-1,1) !== '.') {
342
- return false;
389
+ /* " * The string is a host name (i.e., not an IP address)." */
390
+ if (IP_REGEX_LOWERCASE.test(str)) {
391
+ return false; // it's an IP address
343
392
  }
344
393
 
345
394
  return true;
346
395
  }
347
396
 
348
-
349
397
  // RFC6265 S5.1.4 Paths and Path-Match
350
398
 
351
399
  /*
@@ -357,7 +405,7 @@ function domainMatch(str, domStr, canonicalize) {
357
405
  function defaultPath(path) {
358
406
  // "2. If the uri-path is empty or if the first character of the uri-path is not
359
407
  // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
360
- if (!path || path.substr(0,1) !== "/") {
408
+ if (!path || path.substr(0, 1) !== "/") {
361
409
  return "/";
362
410
  }
363
411
 
@@ -367,7 +415,7 @@ function defaultPath(path) {
367
415
  return path;
368
416
  }
369
417
 
370
- var rightSlash = path.lastIndexOf("/");
418
+ const rightSlash = path.lastIndexOf("/");
371
419
  if (rightSlash === 0) {
372
420
  return "/";
373
421
  }
@@ -378,10 +426,11 @@ function defaultPath(path) {
378
426
  }
379
427
 
380
428
  function trimTerminator(str) {
381
- for (var t = 0; t < TERMINATORS.length; t++) {
382
- var terminatorIdx = str.indexOf(TERMINATORS[t]);
429
+ if (validators.isEmptyString(str)) return str;
430
+ for (let t = 0; t < TERMINATORS.length; t++) {
431
+ const terminatorIdx = str.indexOf(TERMINATORS[t]);
383
432
  if (terminatorIdx !== -1) {
384
- str = str.substr(0,terminatorIdx);
433
+ str = str.substr(0, terminatorIdx);
385
434
  }
386
435
  }
387
436
 
@@ -390,48 +439,57 @@ function trimTerminator(str) {
390
439
 
391
440
  function parseCookiePair(cookiePair, looseMode) {
392
441
  cookiePair = trimTerminator(cookiePair);
442
+ validators.validate(validators.isString(cookiePair), cookiePair);
393
443
 
394
- var firstEq = cookiePair.indexOf('=');
444
+ let firstEq = cookiePair.indexOf("=");
395
445
  if (looseMode) {
396
- if (firstEq === 0) { // '=' is immediately at start
446
+ if (firstEq === 0) {
447
+ // '=' is immediately at start
397
448
  cookiePair = cookiePair.substr(1);
398
- firstEq = cookiePair.indexOf('='); // might still need to split on '='
449
+ firstEq = cookiePair.indexOf("="); // might still need to split on '='
399
450
  }
400
- } else { // non-loose mode
401
- if (firstEq <= 0) { // no '=' or is at start
451
+ } else {
452
+ // non-loose mode
453
+ if (firstEq <= 0) {
454
+ // no '=' or is at start
402
455
  return; // needs to have non-empty "cookie-name"
403
456
  }
404
457
  }
405
458
 
406
- var cookieName, cookieValue;
459
+ let cookieName, cookieValue;
407
460
  if (firstEq <= 0) {
408
461
  cookieName = "";
409
462
  cookieValue = cookiePair.trim();
410
463
  } else {
411
464
  cookieName = cookiePair.substr(0, firstEq).trim();
412
- cookieValue = cookiePair.substr(firstEq+1).trim();
465
+ cookieValue = cookiePair.substr(firstEq + 1).trim();
413
466
  }
414
467
 
415
468
  if (CONTROL_CHARS.test(cookieName) || CONTROL_CHARS.test(cookieValue)) {
416
469
  return;
417
470
  }
418
471
 
419
- var c = new Cookie();
472
+ const c = new Cookie();
420
473
  c.key = cookieName;
421
474
  c.value = cookieValue;
422
475
  return c;
423
476
  }
424
477
 
425
478
  function parse(str, options) {
426
- if (!options || typeof options !== 'object') {
479
+ if (!options || typeof options !== "object") {
427
480
  options = {};
428
481
  }
482
+
483
+ if (validators.isEmptyString(str) || !validators.isString(str)) {
484
+ return null;
485
+ }
486
+
429
487
  str = str.trim();
430
488
 
431
489
  // We use a regex to parse the "name-value-pair" part of S5.2
432
- var firstSemi = str.indexOf(';'); // S5.2 step 1
433
- var cookiePair = (firstSemi === -1) ? str : str.substr(0, firstSemi);
434
- var c = parseCookiePair(cookiePair, !!options.loose);
490
+ const firstSemi = str.indexOf(";"); // S5.2 step 1
491
+ const cookiePair = firstSemi === -1 ? str : str.substr(0, firstSemi);
492
+ const c = parseCookiePair(cookiePair, !!options.loose);
435
493
  if (!c) {
436
494
  return;
437
495
  }
@@ -443,7 +501,7 @@ function parse(str, options) {
443
501
  // S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string
444
502
  // (including the %x3B (";") in question)." plus later on in the same section
445
503
  // "discard the first ";" and trim".
446
- var unparsed = str.slice(firstSemi + 1).trim();
504
+ const unparsed = str.slice(firstSemi + 1).trim();
447
505
 
448
506
  // "If the unparsed-attributes string is empty, skip the rest of these
449
507
  // steps."
@@ -459,21 +517,22 @@ function parse(str, options) {
459
517
  * cookie-attribute-list". Therefore, in this implementation, we overwrite
460
518
  * the previous value.
461
519
  */
462
- var cookie_avs = unparsed.split(';');
520
+ const cookie_avs = unparsed.split(";");
463
521
  while (cookie_avs.length) {
464
- var av = cookie_avs.shift().trim();
465
- if (av.length === 0) { // happens if ";;" appears
522
+ const av = cookie_avs.shift().trim();
523
+ if (av.length === 0) {
524
+ // happens if ";;" appears
466
525
  continue;
467
526
  }
468
- var av_sep = av.indexOf('=');
469
- var av_key, av_value;
527
+ const av_sep = av.indexOf("=");
528
+ let av_key, av_value;
470
529
 
471
530
  if (av_sep === -1) {
472
531
  av_key = av;
473
532
  av_value = null;
474
533
  } else {
475
- av_key = av.substr(0,av_sep);
476
- av_value = av.substr(av_sep+1);
534
+ av_key = av.substr(0, av_sep);
535
+ av_value = av.substr(av_sep + 1);
477
536
  }
478
537
 
479
538
  av_key = av_key.trim().toLowerCase();
@@ -482,88 +541,140 @@ function parse(str, options) {
482
541
  av_value = av_value.trim();
483
542
  }
484
543
 
485
- switch(av_key) {
486
- case 'expires': // S5.2.1
487
- if (av_value) {
488
- var exp = parseDate(av_value);
489
- // "If the attribute-value failed to parse as a cookie date, ignore the
490
- // cookie-av."
491
- if (exp) {
492
- // over and underflow not realistically a concern: V8's getTime() seems to
493
- // store something larger than a 32-bit time_t (even with 32-bit node)
494
- c.expires = exp;
544
+ switch (av_key) {
545
+ case "expires": // S5.2.1
546
+ if (av_value) {
547
+ const exp = parseDate(av_value);
548
+ // "If the attribute-value failed to parse as a cookie date, ignore the
549
+ // cookie-av."
550
+ if (exp) {
551
+ // over and underflow not realistically a concern: V8's getTime() seems to
552
+ // store something larger than a 32-bit time_t (even with 32-bit node)
553
+ c.expires = exp;
554
+ }
495
555
  }
496
- }
497
- break;
498
-
499
- case 'max-age': // S5.2.2
500
- if (av_value) {
501
- // "If the first character of the attribute-value is not a DIGIT or a "-"
502
- // character ...[or]... If the remainder of attribute-value contains a
503
- // non-DIGIT character, ignore the cookie-av."
504
- if (/^-?[0-9]+$/.test(av_value)) {
505
- var delta = parseInt(av_value, 10);
506
- // "If delta-seconds is less than or equal to zero (0), let expiry-time
507
- // be the earliest representable date and time."
508
- c.setMaxAge(delta);
556
+ break;
557
+
558
+ case "max-age": // S5.2.2
559
+ if (av_value) {
560
+ // "If the first character of the attribute-value is not a DIGIT or a "-"
561
+ // character ...[or]... If the remainder of attribute-value contains a
562
+ // non-DIGIT character, ignore the cookie-av."
563
+ if (/^-?[0-9]+$/.test(av_value)) {
564
+ const delta = parseInt(av_value, 10);
565
+ // "If delta-seconds is less than or equal to zero (0), let expiry-time
566
+ // be the earliest representable date and time."
567
+ c.setMaxAge(delta);
568
+ }
509
569
  }
510
- }
511
- break;
512
-
513
- case 'domain': // S5.2.3
514
- // "If the attribute-value is empty, the behavior is undefined. However,
515
- // the user agent SHOULD ignore the cookie-av entirely."
516
- if (av_value) {
517
- // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
518
- // (".") character."
519
- var domain = av_value.trim().replace(/^\./, '');
520
- if (domain) {
521
- // "Convert the cookie-domain to lower case."
522
- c.domain = domain.toLowerCase();
570
+ break;
571
+
572
+ case "domain": // S5.2.3
573
+ // "If the attribute-value is empty, the behavior is undefined. However,
574
+ // the user agent SHOULD ignore the cookie-av entirely."
575
+ if (av_value) {
576
+ // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
577
+ // (".") character."
578
+ const domain = av_value.trim().replace(/^\./, "");
579
+ if (domain) {
580
+ // "Convert the cookie-domain to lower case."
581
+ c.domain = domain.toLowerCase();
582
+ }
523
583
  }
524
- }
525
- break;
526
-
527
- case 'path': // S5.2.4
528
- /*
529
- * "If the attribute-value is empty or if the first character of the
530
- * attribute-value is not %x2F ("/"):
531
- * Let cookie-path be the default-path.
532
- * Otherwise:
533
- * Let cookie-path be the attribute-value."
534
- *
535
- * We'll represent the default-path as null since it depends on the
536
- * context of the parsing.
537
- */
538
- c.path = av_value && av_value[0] === "/" ? av_value : null;
539
- break;
540
-
541
- case 'secure': // S5.2.5
542
- /*
543
- * "If the attribute-name case-insensitively matches the string "Secure",
544
- * the user agent MUST append an attribute to the cookie-attribute-list
545
- * with an attribute-name of Secure and an empty attribute-value."
546
- */
547
- c.secure = true;
548
- break;
549
-
550
- case 'httponly': // S5.2.6 -- effectively the same as 'secure'
551
- c.httpOnly = true;
552
- break;
584
+ break;
585
+
586
+ case "path": // S5.2.4
587
+ /*
588
+ * "If the attribute-value is empty or if the first character of the
589
+ * attribute-value is not %x2F ("/"):
590
+ * Let cookie-path be the default-path.
591
+ * Otherwise:
592
+ * Let cookie-path be the attribute-value."
593
+ *
594
+ * We'll represent the default-path as null since it depends on the
595
+ * context of the parsing.
596
+ */
597
+ c.path = av_value && av_value[0] === "/" ? av_value : null;
598
+ break;
599
+
600
+ case "secure": // S5.2.5
601
+ /*
602
+ * "If the attribute-name case-insensitively matches the string "Secure",
603
+ * the user agent MUST append an attribute to the cookie-attribute-list
604
+ * with an attribute-name of Secure and an empty attribute-value."
605
+ */
606
+ c.secure = true;
607
+ break;
608
+
609
+ case "httponly": // S5.2.6 -- effectively the same as 'secure'
610
+ c.httpOnly = true;
611
+ break;
612
+
613
+ case "samesite": // RFC6265bis-02 S5.3.7
614
+ const enforcement = av_value ? av_value.toLowerCase() : "";
615
+ switch (enforcement) {
616
+ case "strict":
617
+ c.sameSite = "strict";
618
+ break;
619
+ case "lax":
620
+ c.sameSite = "lax";
621
+ break;
622
+ case "none":
623
+ c.sameSite = "none";
624
+ break;
625
+ default:
626
+ c.sameSite = undefined;
627
+ break;
628
+ }
629
+ break;
553
630
 
554
- default:
555
- c.extensions = c.extensions || [];
556
- c.extensions.push(av);
557
- break;
631
+ default:
632
+ c.extensions = c.extensions || [];
633
+ c.extensions.push(av);
634
+ break;
558
635
  }
559
636
  }
560
637
 
561
638
  return c;
562
639
  }
563
640
 
641
+ /**
642
+ * If the cookie-name begins with a case-sensitive match for the
643
+ * string "__Secure-", abort these steps and ignore the cookie
644
+ * entirely unless the cookie's secure-only-flag is true.
645
+ * @param cookie
646
+ * @returns boolean
647
+ */
648
+ function isSecurePrefixConditionMet(cookie) {
649
+ validators.validate(validators.isObject(cookie), cookie);
650
+ return !cookie.key.startsWith("__Secure-") || cookie.secure;
651
+ }
652
+
653
+ /**
654
+ * If the cookie-name begins with a case-sensitive match for the
655
+ * string "__Host-", abort these steps and ignore the cookie
656
+ * entirely unless the cookie meets all the following criteria:
657
+ * 1. The cookie's secure-only-flag is true.
658
+ * 2. The cookie's host-only-flag is true.
659
+ * 3. The cookie-attribute-list contains an attribute with an
660
+ * attribute-name of "Path", and the cookie's path is "/".
661
+ * @param cookie
662
+ * @returns boolean
663
+ */
664
+ function isHostPrefixConditionMet(cookie) {
665
+ validators.validate(validators.isObject(cookie));
666
+ return (
667
+ !cookie.key.startsWith("__Host-") ||
668
+ (cookie.secure &&
669
+ cookie.hostOnly &&
670
+ cookie.path != null &&
671
+ cookie.path === "/")
672
+ );
673
+ }
674
+
564
675
  // avoid the V8 deoptimization monster!
565
676
  function jsonParse(str) {
566
- var obj;
677
+ let obj;
567
678
  try {
568
679
  obj = JSON.parse(str);
569
680
  } catch (e) {
@@ -573,12 +684,12 @@ function jsonParse(str) {
573
684
  }
574
685
 
575
686
  function fromJSON(str) {
576
- if (!str) {
687
+ if (!str || validators.isEmptyString(str)) {
577
688
  return null;
578
689
  }
579
690
 
580
- var obj;
581
- if (typeof str === 'string') {
691
+ let obj;
692
+ if (typeof str === "string") {
582
693
  obj = jsonParse(str);
583
694
  if (obj instanceof Error) {
584
695
  return null;
@@ -588,24 +699,18 @@ function fromJSON(str) {
588
699
  obj = str;
589
700
  }
590
701
 
591
- var c = new Cookie();
592
- for (var i=0; i<Cookie.serializableProperties.length; i++) {
593
- var prop = Cookie.serializableProperties[i];
594
- if (obj[prop] === undefined ||
595
- obj[prop] === Cookie.prototype[prop])
596
- {
702
+ const c = new Cookie();
703
+ for (let i = 0; i < Cookie.serializableProperties.length; i++) {
704
+ const prop = Cookie.serializableProperties[i];
705
+ if (obj[prop] === undefined || obj[prop] === cookieDefaults[prop]) {
597
706
  continue; // leave as prototype default
598
707
  }
599
708
 
600
- if (prop === 'expires' ||
601
- prop === 'creation' ||
602
- prop === 'lastAccessed')
603
- {
709
+ if (prop === "expires" || prop === "creation" || prop === "lastAccessed") {
604
710
  if (obj[prop] === null) {
605
711
  c[prop] = null;
606
712
  } else {
607
- c[prop] = obj[prop] == "Infinity" ?
608
- "Infinity" : new Date(obj[prop]);
713
+ c[prop] = obj[prop] == "Infinity" ? "Infinity" : new Date(obj[prop]);
609
714
  }
610
715
  } else {
611
716
  c[prop] = obj[prop];
@@ -624,20 +729,22 @@ function fromJSON(str) {
624
729
  * creation-times."
625
730
  */
626
731
 
627
- function cookieCompare(a,b) {
628
- var cmp = 0;
732
+ function cookieCompare(a, b) {
733
+ validators.validate(validators.isObject(a), a);
734
+ validators.validate(validators.isObject(b), b);
735
+ let cmp = 0;
629
736
 
630
737
  // descending for length: b CMP a
631
- var aPathLen = a.path ? a.path.length : 0;
632
- var bPathLen = b.path ? b.path.length : 0;
738
+ const aPathLen = a.path ? a.path.length : 0;
739
+ const bPathLen = b.path ? b.path.length : 0;
633
740
  cmp = bPathLen - aPathLen;
634
741
  if (cmp !== 0) {
635
742
  return cmp;
636
743
  }
637
744
 
638
745
  // ascending for time: a CMP b
639
- var aTime = a.creation ? a.creation.getTime() : MAX_TIME;
640
- var bTime = b.creation ? b.creation.getTime() : MAX_TIME;
746
+ const aTime = a.creation ? a.creation.getTime() : MAX_TIME;
747
+ const bTime = b.creation ? b.creation.getTime() : MAX_TIME;
641
748
  cmp = aTime - bTime;
642
749
  if (cmp !== 0) {
643
750
  return cmp;
@@ -652,22 +759,20 @@ function cookieCompare(a,b) {
652
759
  // Gives the permutation of all possible pathMatch()es of a given path. The
653
760
  // array is in longest-to-shortest order. Handy for indexing.
654
761
  function permutePath(path) {
655
- if (path === '/') {
656
- return ['/'];
657
- }
658
- if (path.lastIndexOf('/') === path.length-1) {
659
- path = path.substr(0,path.length-1);
762
+ validators.validate(validators.isString(path));
763
+ if (path === "/") {
764
+ return ["/"];
660
765
  }
661
- var permutations = [path];
766
+ const permutations = [path];
662
767
  while (path.length > 1) {
663
- var lindex = path.lastIndexOf('/');
768
+ const lindex = path.lastIndexOf("/");
664
769
  if (lindex === 0) {
665
770
  break;
666
771
  }
667
- path = path.substr(0,lindex);
772
+ path = path.substr(0, lindex);
668
773
  permutations.push(path);
669
774
  }
670
- permutations.push('/');
775
+ permutations.push("/");
671
776
  return permutations;
672
777
  }
673
778
 
@@ -679,783 +784,946 @@ function getCookieContext(url) {
679
784
  // Therefore, we will just skip decoding for such URIs.
680
785
  try {
681
786
  url = decodeURI(url);
682
- }
683
- catch(err) {
787
+ } catch (err) {
684
788
  // Silently swallow error
685
789
  }
686
790
 
687
791
  return urlParse(url);
688
792
  }
689
793
 
690
- function Cookie(options) {
691
- options = options || {};
794
+ const cookieDefaults = {
795
+ // the order in which the RFC has them:
796
+ key: "",
797
+ value: "",
798
+ expires: "Infinity",
799
+ maxAge: null,
800
+ domain: null,
801
+ path: null,
802
+ secure: false,
803
+ httpOnly: false,
804
+ extensions: null,
805
+ // set by the CookieJar:
806
+ hostOnly: null,
807
+ pathIsDefault: null,
808
+ creation: null,
809
+ lastAccessed: null,
810
+ sameSite: undefined
811
+ };
692
812
 
693
- Object.keys(options).forEach(function(prop) {
694
- if (Cookie.prototype.hasOwnProperty(prop) &&
695
- Cookie.prototype[prop] !== options[prop] &&
696
- prop.substr(0,1) !== '_')
697
- {
698
- this[prop] = options[prop];
813
+ class Cookie {
814
+ constructor(options = {}) {
815
+ const customInspectSymbol = getCustomInspectSymbol();
816
+ if (customInspectSymbol) {
817
+ this[customInspectSymbol] = this.inspect;
699
818
  }
700
- }, this);
701
819
 
702
- this.creation = this.creation || new Date();
820
+ Object.assign(this, cookieDefaults, options);
821
+ this.creation = this.creation || new Date();
703
822
 
704
- // used to break creation ties in cookieCompare():
705
- Object.defineProperty(this, 'creationIndex', {
706
- configurable: false,
707
- enumerable: false, // important for assert.deepEqual checks
708
- writable: true,
709
- value: ++Cookie.cookiesCreated
710
- });
711
- }
823
+ // used to break creation ties in cookieCompare():
824
+ Object.defineProperty(this, "creationIndex", {
825
+ configurable: false,
826
+ enumerable: false, // important for assert.deepEqual checks
827
+ writable: true,
828
+ value: ++Cookie.cookiesCreated
829
+ });
830
+ }
712
831
 
713
- Cookie.cookiesCreated = 0; // incremented each time a cookie is created
832
+ inspect() {
833
+ const now = Date.now();
834
+ const hostOnly = this.hostOnly != null ? this.hostOnly : "?";
835
+ const createAge = this.creation
836
+ ? `${now - this.creation.getTime()}ms`
837
+ : "?";
838
+ const accessAge = this.lastAccessed
839
+ ? `${now - this.lastAccessed.getTime()}ms`
840
+ : "?";
841
+ return `Cookie="${this.toString()}; hostOnly=${hostOnly}; aAge=${accessAge}; cAge=${createAge}"`;
842
+ }
714
843
 
715
- Cookie.parse = parse;
716
- Cookie.fromJSON = fromJSON;
844
+ toJSON() {
845
+ const obj = {};
717
846
 
718
- Cookie.prototype.key = "";
719
- Cookie.prototype.value = "";
720
-
721
- // the order in which the RFC has them:
722
- Cookie.prototype.expires = "Infinity"; // coerces to literal Infinity
723
- Cookie.prototype.maxAge = null; // takes precedence over expires for TTL
724
- Cookie.prototype.domain = null;
725
- Cookie.prototype.path = null;
726
- Cookie.prototype.secure = false;
727
- Cookie.prototype.httpOnly = false;
728
- Cookie.prototype.extensions = null;
729
-
730
- // set by the CookieJar:
731
- Cookie.prototype.hostOnly = null; // boolean when set
732
- Cookie.prototype.pathIsDefault = null; // boolean when set
733
- Cookie.prototype.creation = null; // Date when set; defaulted by Cookie.parse
734
- Cookie.prototype.lastAccessed = null; // Date when set
735
- Object.defineProperty(Cookie.prototype, 'creationIndex', {
736
- configurable: true,
737
- enumerable: false,
738
- writable: true,
739
- value: 0
740
- });
847
+ for (const prop of Cookie.serializableProperties) {
848
+ if (this[prop] === cookieDefaults[prop]) {
849
+ continue; // leave as prototype default
850
+ }
741
851
 
742
- Cookie.serializableProperties = Object.keys(Cookie.prototype)
743
- .filter(function(prop) {
744
- return !(
745
- Cookie.prototype[prop] instanceof Function ||
746
- prop === 'creationIndex' ||
747
- prop.substr(0,1) === '_'
748
- );
749
- });
750
-
751
- Cookie.prototype.inspect = function inspect() {
752
- var now = Date.now();
753
- return 'Cookie="'+this.toString() +
754
- '; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?') +
755
- '; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?') +
756
- '; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?') +
757
- '"';
758
- };
852
+ if (
853
+ prop === "expires" ||
854
+ prop === "creation" ||
855
+ prop === "lastAccessed"
856
+ ) {
857
+ if (this[prop] === null) {
858
+ obj[prop] = null;
859
+ } else {
860
+ obj[prop] =
861
+ this[prop] == "Infinity" // intentionally not ===
862
+ ? "Infinity"
863
+ : this[prop].toISOString();
864
+ }
865
+ } else if (prop === "maxAge") {
866
+ if (this[prop] !== null) {
867
+ // again, intentionally not ===
868
+ obj[prop] =
869
+ this[prop] == Infinity || this[prop] == -Infinity
870
+ ? this[prop].toString()
871
+ : this[prop];
872
+ }
873
+ } else {
874
+ if (this[prop] !== cookieDefaults[prop]) {
875
+ obj[prop] = this[prop];
876
+ }
877
+ }
878
+ }
759
879
 
760
- // Use the new custom inspection symbol to add the custom inspect function if
761
- // available.
762
- if (util.inspect.custom) {
763
- Cookie.prototype[util.inspect.custom] = Cookie.prototype.inspect;
764
- }
880
+ return obj;
881
+ }
765
882
 
766
- Cookie.prototype.toJSON = function() {
767
- var obj = {};
883
+ clone() {
884
+ return fromJSON(this.toJSON());
885
+ }
768
886
 
769
- var props = Cookie.serializableProperties;
770
- for (var i=0; i<props.length; i++) {
771
- var prop = props[i];
772
- if (this[prop] === Cookie.prototype[prop]) {
773
- continue; // leave as prototype default
887
+ validate() {
888
+ if (!COOKIE_OCTETS.test(this.value)) {
889
+ return false;
890
+ }
891
+ if (
892
+ this.expires != Infinity &&
893
+ !(this.expires instanceof Date) &&
894
+ !parseDate(this.expires)
895
+ ) {
896
+ return false;
897
+ }
898
+ if (this.maxAge != null && this.maxAge <= 0) {
899
+ return false; // "Max-Age=" non-zero-digit *DIGIT
900
+ }
901
+ if (this.path != null && !PATH_VALUE.test(this.path)) {
902
+ return false;
774
903
  }
775
904
 
776
- if (prop === 'expires' ||
777
- prop === 'creation' ||
778
- prop === 'lastAccessed')
779
- {
780
- if (this[prop] === null) {
781
- obj[prop] = null;
782
- } else {
783
- obj[prop] = this[prop] == "Infinity" ? // intentionally not ===
784
- "Infinity" : this[prop].toISOString();
785
- }
786
- } else if (prop === 'maxAge') {
787
- if (this[prop] !== null) {
788
- // again, intentionally not ===
789
- obj[prop] = (this[prop] == Infinity || this[prop] == -Infinity) ?
790
- this[prop].toString() : this[prop];
905
+ const cdomain = this.cdomain();
906
+ if (cdomain) {
907
+ if (cdomain.match(/\.$/)) {
908
+ return false; // S4.1.2.3 suggests that this is bad. domainMatch() tests confirm this
791
909
  }
792
- } else {
793
- if (this[prop] !== Cookie.prototype[prop]) {
794
- obj[prop] = this[prop];
910
+ const suffix = pubsuffix.getPublicSuffix(cdomain);
911
+ if (suffix == null) {
912
+ // it's a public suffix
913
+ return false;
795
914
  }
796
915
  }
916
+ return true;
797
917
  }
798
918
 
799
- return obj;
800
- };
801
-
802
- Cookie.prototype.clone = function() {
803
- return fromJSON(this.toJSON());
804
- };
805
-
806
- Cookie.prototype.validate = function validate() {
807
- if (!COOKIE_OCTETS.test(this.value)) {
808
- return false;
809
- }
810
- if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires)) {
811
- return false;
812
- }
813
- if (this.maxAge != null && this.maxAge <= 0) {
814
- return false; // "Max-Age=" non-zero-digit *DIGIT
919
+ setExpires(exp) {
920
+ if (exp instanceof Date) {
921
+ this.expires = exp;
922
+ } else {
923
+ this.expires = parseDate(exp) || "Infinity";
924
+ }
815
925
  }
816
- if (this.path != null && !PATH_VALUE.test(this.path)) {
817
- return false;
926
+
927
+ setMaxAge(age) {
928
+ if (age === Infinity || age === -Infinity) {
929
+ this.maxAge = age.toString(); // so JSON.stringify() works
930
+ } else {
931
+ this.maxAge = age;
932
+ }
818
933
  }
819
934
 
820
- var cdomain = this.cdomain();
821
- if (cdomain) {
822
- if (cdomain.match(/\.$/)) {
823
- return false; // S4.1.2.3 suggests that this is bad. domainMatch() tests confirm this
935
+ cookieString() {
936
+ let val = this.value;
937
+ if (val == null) {
938
+ val = "";
824
939
  }
825
- var suffix = pubsuffix.getPublicSuffix(cdomain);
826
- if (suffix == null) { // it's a public suffix
827
- return false;
940
+ if (this.key === "") {
941
+ return val;
828
942
  }
943
+ return `${this.key}=${val}`;
829
944
  }
830
- return true;
831
- };
832
945
 
833
- Cookie.prototype.setExpires = function setExpires(exp) {
834
- if (exp instanceof Date) {
835
- this.expires = exp;
836
- } else {
837
- this.expires = parseDate(exp) || "Infinity";
838
- }
839
- };
946
+ // gives Set-Cookie header format
947
+ toString() {
948
+ let str = this.cookieString();
840
949
 
841
- Cookie.prototype.setMaxAge = function setMaxAge(age) {
842
- if (age === Infinity || age === -Infinity) {
843
- this.maxAge = age.toString(); // so JSON.stringify() works
844
- } else {
845
- this.maxAge = age;
846
- }
847
- };
950
+ if (this.expires != Infinity) {
951
+ if (this.expires instanceof Date) {
952
+ str += `; Expires=${formatDate(this.expires)}`;
953
+ } else {
954
+ str += `; Expires=${this.expires}`;
955
+ }
956
+ }
848
957
 
849
- // gives Cookie header format
850
- Cookie.prototype.cookieString = function cookieString() {
851
- var val = this.value;
852
- if (val == null) {
853
- val = '';
854
- }
855
- if (this.key === '') {
856
- return val;
857
- }
858
- return this.key+'='+val;
859
- };
958
+ if (this.maxAge != null && this.maxAge != Infinity) {
959
+ str += `; Max-Age=${this.maxAge}`;
960
+ }
860
961
 
861
- // gives Set-Cookie header format
862
- Cookie.prototype.toString = function toString() {
863
- var str = this.cookieString();
962
+ if (this.domain && !this.hostOnly) {
963
+ str += `; Domain=${this.domain}`;
964
+ }
965
+ if (this.path) {
966
+ str += `; Path=${this.path}`;
967
+ }
864
968
 
865
- if (this.expires != Infinity) {
866
- if (this.expires instanceof Date) {
867
- str += '; Expires='+formatDate(this.expires);
868
- } else {
869
- str += '; Expires='+this.expires;
969
+ if (this.secure) {
970
+ str += "; Secure";
971
+ }
972
+ if (this.httpOnly) {
973
+ str += "; HttpOnly";
974
+ }
975
+ if (this.sameSite && this.sameSite !== "none") {
976
+ const ssCanon = Cookie.sameSiteCanonical[this.sameSite.toLowerCase()];
977
+ str += `; SameSite=${ssCanon ? ssCanon : this.sameSite}`;
978
+ }
979
+ if (this.extensions) {
980
+ this.extensions.forEach(ext => {
981
+ str += `; ${ext}`;
982
+ });
870
983
  }
871
- }
872
984
 
873
- if (this.maxAge != null && this.maxAge != Infinity) {
874
- str += '; Max-Age='+this.maxAge;
985
+ return str;
875
986
  }
876
987
 
877
- if (this.domain && !this.hostOnly) {
878
- str += '; Domain='+this.domain;
879
- }
880
- if (this.path) {
881
- str += '; Path='+this.path;
882
- }
988
+ // TTL() partially replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
989
+ // elsewhere)
990
+ // S5.3 says to give the "latest representable date" for which we use Infinity
991
+ // For "expired" we use 0
992
+ TTL(now) {
993
+ /* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires
994
+ * attribute, the Max-Age attribute has precedence and controls the
995
+ * expiration date of the cookie.
996
+ * (Concurs with S5.3 step 3)
997
+ */
998
+ if (this.maxAge != null) {
999
+ return this.maxAge <= 0 ? 0 : this.maxAge * 1000;
1000
+ }
883
1001
 
884
- if (this.secure) {
885
- str += '; Secure';
886
- }
887
- if (this.httpOnly) {
888
- str += '; HttpOnly';
889
- }
890
- if (this.extensions) {
891
- this.extensions.forEach(function(ext) {
892
- str += '; '+ext;
893
- });
894
- }
1002
+ let expires = this.expires;
1003
+ if (expires != Infinity) {
1004
+ if (!(expires instanceof Date)) {
1005
+ expires = parseDate(expires) || Infinity;
1006
+ }
895
1007
 
896
- return str;
897
- };
1008
+ if (expires == Infinity) {
1009
+ return Infinity;
1010
+ }
898
1011
 
899
- // TTL() partially replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
900
- // elsewhere)
901
- // S5.3 says to give the "latest representable date" for which we use Infinity
902
- // For "expired" we use 0
903
- Cookie.prototype.TTL = function TTL(now) {
904
- /* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires
905
- * attribute, the Max-Age attribute has precedence and controls the
906
- * expiration date of the cookie.
907
- * (Concurs with S5.3 step 3)
908
- */
909
- if (this.maxAge != null) {
910
- return this.maxAge<=0 ? 0 : this.maxAge*1000;
1012
+ return expires.getTime() - (now || Date.now());
1013
+ }
1014
+
1015
+ return Infinity;
911
1016
  }
912
1017
 
913
- var expires = this.expires;
914
- if (expires != Infinity) {
915
- if (!(expires instanceof Date)) {
916
- expires = parseDate(expires) || Infinity;
1018
+ // expiryTime() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
1019
+ // elsewhere)
1020
+ expiryTime(now) {
1021
+ if (this.maxAge != null) {
1022
+ const relativeTo = now || this.creation || new Date();
1023
+ const age = this.maxAge <= 0 ? -Infinity : this.maxAge * 1000;
1024
+ return relativeTo.getTime() + age;
917
1025
  }
918
1026
 
919
- if (expires == Infinity) {
1027
+ if (this.expires == Infinity) {
920
1028
  return Infinity;
921
1029
  }
922
-
923
- return expires.getTime() - (now || Date.now());
1030
+ return this.expires.getTime();
924
1031
  }
925
1032
 
926
- return Infinity;
927
- };
1033
+ // expiryDate() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
1034
+ // elsewhere), except it returns a Date
1035
+ expiryDate(now) {
1036
+ const millisec = this.expiryTime(now);
1037
+ if (millisec == Infinity) {
1038
+ return new Date(MAX_TIME);
1039
+ } else if (millisec == -Infinity) {
1040
+ return new Date(MIN_TIME);
1041
+ } else {
1042
+ return new Date(millisec);
1043
+ }
1044
+ }
928
1045
 
929
- // expiryTime() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
930
- // elsewhere)
931
- Cookie.prototype.expiryTime = function expiryTime(now) {
932
- if (this.maxAge != null) {
933
- var relativeTo = now || this.creation || new Date();
934
- var age = (this.maxAge <= 0) ? -Infinity : this.maxAge*1000;
935
- return relativeTo.getTime() + age;
1046
+ // This replaces the "persistent-flag" parts of S5.3 step 3
1047
+ isPersistent() {
1048
+ return this.maxAge != null || this.expires != Infinity;
936
1049
  }
937
1050
 
938
- if (this.expires == Infinity) {
939
- return Infinity;
1051
+ // Mostly S5.1.2 and S5.2.3:
1052
+ canonicalizedDomain() {
1053
+ if (this.domain == null) {
1054
+ return null;
1055
+ }
1056
+ return canonicalDomain(this.domain);
940
1057
  }
941
- return this.expires.getTime();
942
- };
943
1058
 
944
- // expiryDate() replaces the "expiry-time" parts of S5.3 step 3 (setCookie()
945
- // elsewhere), except it returns a Date
946
- Cookie.prototype.expiryDate = function expiryDate(now) {
947
- var millisec = this.expiryTime(now);
948
- if (millisec == Infinity) {
949
- return new Date(MAX_TIME);
950
- } else if (millisec == -Infinity) {
951
- return new Date(MIN_TIME);
952
- } else {
953
- return new Date(millisec);
1059
+ cdomain() {
1060
+ return this.canonicalizedDomain();
954
1061
  }
955
- };
1062
+ }
956
1063
 
957
- // This replaces the "persistent-flag" parts of S5.3 step 3
958
- Cookie.prototype.isPersistent = function isPersistent() {
959
- return (this.maxAge != null || this.expires != Infinity);
1064
+ Cookie.cookiesCreated = 0;
1065
+ Cookie.parse = parse;
1066
+ Cookie.fromJSON = fromJSON;
1067
+ Cookie.serializableProperties = Object.keys(cookieDefaults);
1068
+ Cookie.sameSiteLevel = {
1069
+ strict: 3,
1070
+ lax: 2,
1071
+ none: 1
960
1072
  };
961
1073
 
962
- // Mostly S5.1.2 and S5.2.3:
963
- Cookie.prototype.cdomain =
964
- Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() {
965
- if (this.domain == null) {
966
- return null;
967
- }
968
- return canonicalDomain(this.domain);
1074
+ Cookie.sameSiteCanonical = {
1075
+ strict: "Strict",
1076
+ lax: "Lax"
969
1077
  };
970
1078
 
971
- function CookieJar(store, options) {
972
- if (typeof options === "boolean") {
973
- options = {rejectPublicSuffixes: options};
974
- } else if (options == null) {
975
- options = {};
976
- }
977
- if (options.rejectPublicSuffixes != null) {
978
- this.rejectPublicSuffixes = options.rejectPublicSuffixes;
979
- }
980
- if (options.looseMode != null) {
981
- this.enableLooseMode = options.looseMode;
982
- }
983
-
984
- if (!store) {
985
- store = new MemoryCookieStore();
1079
+ function getNormalizedPrefixSecurity(prefixSecurity) {
1080
+ if (prefixSecurity != null) {
1081
+ const normalizedPrefixSecurity = prefixSecurity.toLowerCase();
1082
+ /* The three supported options */
1083
+ switch (normalizedPrefixSecurity) {
1084
+ case PrefixSecurityEnum.STRICT:
1085
+ case PrefixSecurityEnum.SILENT:
1086
+ case PrefixSecurityEnum.DISABLED:
1087
+ return normalizedPrefixSecurity;
1088
+ }
986
1089
  }
987
- this.store = store;
1090
+ /* Default is SILENT */
1091
+ return PrefixSecurityEnum.SILENT;
988
1092
  }
989
- CookieJar.prototype.store = null;
990
- CookieJar.prototype.rejectPublicSuffixes = true;
991
- CookieJar.prototype.enableLooseMode = false;
992
- var CAN_BE_SYNC = [];
993
-
994
- CAN_BE_SYNC.push('setCookie');
995
- CookieJar.prototype.setCookie = function(cookie, url, options, cb) {
996
- var err;
997
- var context = getCookieContext(url);
998
- if (options instanceof Function) {
999
- cb = options;
1000
- options = {};
1001
- }
1002
1093
 
1003
- var host = canonicalDomain(context.hostname);
1004
- var loose = this.enableLooseMode;
1005
- if (options.loose != null) {
1006
- loose = options.loose;
1007
- }
1094
+ class CookieJar {
1095
+ constructor(store, options = { rejectPublicSuffixes: true }) {
1096
+ if (typeof options === "boolean") {
1097
+ options = { rejectPublicSuffixes: options };
1098
+ }
1099
+ validators.validate(validators.isObject(options), options);
1100
+ this.rejectPublicSuffixes = options.rejectPublicSuffixes;
1101
+ this.enableLooseMode = !!options.looseMode;
1102
+ this.allowSpecialUseDomain = !!options.allowSpecialUseDomain;
1103
+ this.store = store || new MemoryCookieStore();
1104
+ this.prefixSecurity = getNormalizedPrefixSecurity(options.prefixSecurity);
1105
+ this._cloneSync = syncWrap("clone");
1106
+ this._importCookiesSync = syncWrap("_importCookies");
1107
+ this.getCookiesSync = syncWrap("getCookies");
1108
+ this.getCookieStringSync = syncWrap("getCookieString");
1109
+ this.getSetCookieStringsSync = syncWrap("getSetCookieStrings");
1110
+ this.removeAllCookiesSync = syncWrap("removeAllCookies");
1111
+ this.setCookieSync = syncWrap("setCookie");
1112
+ this.serializeSync = syncWrap("serialize");
1113
+ }
1114
+
1115
+ setCookie(cookie, url, options, cb) {
1116
+ validators.validate(validators.isNonEmptyString(url), cb, options);
1117
+ let err;
1118
+
1119
+ if (validators.isFunction(url)) {
1120
+ cb = url;
1121
+ return cb(new Error("No URL was specified"));
1122
+ }
1008
1123
 
1009
- // S5.3 step 1
1010
- if (typeof(cookie) === 'string' || cookie instanceof String) {
1011
- cookie = Cookie.parse(cookie, { loose: loose });
1012
- if (!cookie) {
1013
- err = new Error("Cookie failed to parse");
1014
- return cb(options.ignoreError ? null : err);
1124
+ const context = getCookieContext(url);
1125
+ if (validators.isFunction(options)) {
1126
+ cb = options;
1127
+ options = {};
1015
1128
  }
1016
- }
1017
- else if (!(cookie instanceof Cookie)) {
1018
- // If you're seeing this error, and are passing in a Cookie object,
1019
- // it *might* be a Cookie object from another loaded version of tough-cookie.
1020
- err = new Error("First argument to setCookie must be a Cookie object or string");
1021
- return cb(options.ignoreError ? null : err);
1022
- }
1023
1129
 
1024
- // S5.3 step 2
1025
- var now = options.now || new Date(); // will assign later to save effort in the face of errors
1130
+ validators.validate(validators.isFunction(cb), cb);
1026
1131
 
1027
- // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
1132
+ if (
1133
+ !validators.isNonEmptyString(cookie) &&
1134
+ !validators.isObject(cookie) &&
1135
+ cookie instanceof String &&
1136
+ cookie.length == 0
1137
+ ) {
1138
+ return cb(null);
1139
+ }
1028
1140
 
1029
- // S5.3 step 4: NOOP; domain is null by default
1141
+ const host = canonicalDomain(context.hostname);
1142
+ const loose = options.loose || this.enableLooseMode;
1030
1143
 
1031
- // S5.3 step 5: public suffixes
1032
- if (this.rejectPublicSuffixes && cookie.domain) {
1033
- var suffix = pubsuffix.getPublicSuffix(cookie.cdomain());
1034
- if (suffix == null) { // e.g. "com"
1035
- err = new Error("Cookie has domain set to a public suffix");
1036
- return cb(options.ignoreError ? null : err);
1144
+ let sameSiteContext = null;
1145
+ if (options.sameSiteContext) {
1146
+ sameSiteContext = checkSameSiteContext(options.sameSiteContext);
1147
+ if (!sameSiteContext) {
1148
+ return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR));
1149
+ }
1037
1150
  }
1038
- }
1039
1151
 
1040
- // S5.3 step 6:
1041
- if (cookie.domain) {
1042
- if (!domainMatch(host, cookie.cdomain(), false)) {
1043
- err = new Error("Cookie not in this host's domain. Cookie:"+cookie.cdomain()+" Request:"+host);
1152
+ // S5.3 step 1
1153
+ if (typeof cookie === "string" || cookie instanceof String) {
1154
+ cookie = Cookie.parse(cookie, { loose: loose });
1155
+ if (!cookie) {
1156
+ err = new Error("Cookie failed to parse");
1157
+ return cb(options.ignoreError ? null : err);
1158
+ }
1159
+ } else if (!(cookie instanceof Cookie)) {
1160
+ // If you're seeing this error, and are passing in a Cookie object,
1161
+ // it *might* be a Cookie object from another loaded version of tough-cookie.
1162
+ err = new Error(
1163
+ "First argument to setCookie must be a Cookie object or string"
1164
+ );
1044
1165
  return cb(options.ignoreError ? null : err);
1045
1166
  }
1046
1167
 
1047
- if (cookie.hostOnly == null) { // don't reset if already set
1048
- cookie.hostOnly = false;
1168
+ // S5.3 step 2
1169
+ const now = options.now || new Date(); // will assign later to save effort in the face of errors
1170
+
1171
+ // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
1172
+
1173
+ // S5.3 step 4: NOOP; domain is null by default
1174
+
1175
+ // S5.3 step 5: public suffixes
1176
+ if (this.rejectPublicSuffixes && cookie.domain) {
1177
+ const suffix = pubsuffix.getPublicSuffix(cookie.cdomain(), {
1178
+ allowSpecialUseDomain: this.allowSpecialUseDomain,
1179
+ ignoreError: options.ignoreError
1180
+ });
1181
+ if (suffix == null && !IP_V6_REGEX_OBJECT.test(cookie.domain)) {
1182
+ // e.g. "com"
1183
+ err = new Error("Cookie has domain set to a public suffix");
1184
+ return cb(options.ignoreError ? null : err);
1185
+ }
1049
1186
  }
1050
1187
 
1051
- } else {
1052
- cookie.hostOnly = true;
1053
- cookie.domain = host;
1054
- }
1188
+ // S5.3 step 6:
1189
+ if (cookie.domain) {
1190
+ if (!domainMatch(host, cookie.cdomain(), false)) {
1191
+ err = new Error(
1192
+ `Cookie not in this host's domain. Cookie:${cookie.cdomain()} Request:${host}`
1193
+ );
1194
+ return cb(options.ignoreError ? null : err);
1195
+ }
1055
1196
 
1056
- //S5.2.4 If the attribute-value is empty or if the first character of the
1057
- //attribute-value is not %x2F ("/"):
1058
- //Let cookie-path be the default-path.
1059
- if (!cookie.path || cookie.path[0] !== '/') {
1060
- cookie.path = defaultPath(context.pathname);
1061
- cookie.pathIsDefault = true;
1062
- }
1197
+ if (cookie.hostOnly == null) {
1198
+ // don't reset if already set
1199
+ cookie.hostOnly = false;
1200
+ }
1201
+ } else {
1202
+ cookie.hostOnly = true;
1203
+ cookie.domain = host;
1204
+ }
1063
1205
 
1064
- // S5.3 step 8: NOOP; secure attribute
1065
- // S5.3 step 9: NOOP; httpOnly attribute
1206
+ //S5.2.4 If the attribute-value is empty or if the first character of the
1207
+ //attribute-value is not %x2F ("/"):
1208
+ //Let cookie-path be the default-path.
1209
+ if (!cookie.path || cookie.path[0] !== "/") {
1210
+ cookie.path = defaultPath(context.pathname);
1211
+ cookie.pathIsDefault = true;
1212
+ }
1066
1213
 
1067
- // S5.3 step 10
1068
- if (options.http === false && cookie.httpOnly) {
1069
- err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
1070
- return cb(options.ignoreError ? null : err);
1071
- }
1214
+ // S5.3 step 8: NOOP; secure attribute
1215
+ // S5.3 step 9: NOOP; httpOnly attribute
1216
+
1217
+ // S5.3 step 10
1218
+ if (options.http === false && cookie.httpOnly) {
1219
+ err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
1220
+ return cb(options.ignoreError ? null : err);
1221
+ }
1072
1222
 
1073
- var store = this.store;
1223
+ // 6252bis-02 S5.4 Step 13 & 14:
1224
+ if (
1225
+ cookie.sameSite !== "none" &&
1226
+ cookie.sameSite !== undefined &&
1227
+ sameSiteContext
1228
+ ) {
1229
+ // "If the cookie's "same-site-flag" is not "None", and the cookie
1230
+ // is being set from a context whose "site for cookies" is not an
1231
+ // exact match for request-uri's host's registered domain, then
1232
+ // abort these steps and ignore the newly created cookie entirely."
1233
+ if (sameSiteContext === "none") {
1234
+ err = new Error(
1235
+ "Cookie is SameSite but this is a cross-origin request"
1236
+ );
1237
+ return cb(options.ignoreError ? null : err);
1238
+ }
1239
+ }
1074
1240
 
1075
- if (!store.updateCookie) {
1076
- store.updateCookie = function(oldCookie, newCookie, cb) {
1077
- this.putCookie(newCookie, cb);
1078
- };
1079
- }
1241
+ /* 6265bis-02 S5.4 Steps 15 & 16 */
1242
+ const ignoreErrorForPrefixSecurity =
1243
+ this.prefixSecurity === PrefixSecurityEnum.SILENT;
1244
+ const prefixSecurityDisabled =
1245
+ this.prefixSecurity === PrefixSecurityEnum.DISABLED;
1246
+ /* If prefix checking is not disabled ...*/
1247
+ if (!prefixSecurityDisabled) {
1248
+ let errorFound = false;
1249
+ let errorMsg;
1250
+ /* Check secure prefix condition */
1251
+ if (!isSecurePrefixConditionMet(cookie)) {
1252
+ errorFound = true;
1253
+ errorMsg = "Cookie has __Secure prefix but Secure attribute is not set";
1254
+ } else if (!isHostPrefixConditionMet(cookie)) {
1255
+ /* Check host prefix condition */
1256
+ errorFound = true;
1257
+ errorMsg =
1258
+ "Cookie has __Host prefix but either Secure or HostOnly attribute is not set or Path is not '/'";
1259
+ }
1260
+ if (errorFound) {
1261
+ return cb(
1262
+ options.ignoreError || ignoreErrorForPrefixSecurity
1263
+ ? null
1264
+ : new Error(errorMsg)
1265
+ );
1266
+ }
1267
+ }
1080
1268
 
1081
- function withCookie(err, oldCookie) {
1082
- if (err) {
1083
- return cb(err);
1269
+ const store = this.store;
1270
+
1271
+ if (!store.updateCookie) {
1272
+ store.updateCookie = function(oldCookie, newCookie, cb) {
1273
+ this.putCookie(newCookie, cb);
1274
+ };
1084
1275
  }
1085
1276
 
1086
- var next = function(err) {
1277
+ function withCookie(err, oldCookie) {
1087
1278
  if (err) {
1088
1279
  return cb(err);
1089
- } else {
1090
- cb(null, cookie);
1091
1280
  }
1092
- };
1093
1281
 
1094
- if (oldCookie) {
1095
- // S5.3 step 11 - "If the cookie store contains a cookie with the same name,
1096
- // domain, and path as the newly created cookie:"
1097
- if (options.http === false && oldCookie.httpOnly) { // step 11.2
1098
- err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
1099
- return cb(options.ignoreError ? null : err);
1282
+ const next = function(err) {
1283
+ if (err) {
1284
+ return cb(err);
1285
+ } else {
1286
+ cb(null, cookie);
1287
+ }
1288
+ };
1289
+
1290
+ if (oldCookie) {
1291
+ // S5.3 step 11 - "If the cookie store contains a cookie with the same name,
1292
+ // domain, and path as the newly created cookie:"
1293
+ if (options.http === false && oldCookie.httpOnly) {
1294
+ // step 11.2
1295
+ err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
1296
+ return cb(options.ignoreError ? null : err);
1297
+ }
1298
+ cookie.creation = oldCookie.creation; // step 11.3
1299
+ cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker
1300
+ cookie.lastAccessed = now;
1301
+ // Step 11.4 (delete cookie) is implied by just setting the new one:
1302
+ store.updateCookie(oldCookie, cookie, next); // step 12
1303
+ } else {
1304
+ cookie.creation = cookie.lastAccessed = now;
1305
+ store.putCookie(cookie, next); // step 12
1100
1306
  }
1101
- cookie.creation = oldCookie.creation; // step 11.3
1102
- cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker
1103
- cookie.lastAccessed = now;
1104
- // Step 11.4 (delete cookie) is implied by just setting the new one:
1105
- store.updateCookie(oldCookie, cookie, next); // step 12
1106
-
1107
- } else {
1108
- cookie.creation = cookie.lastAccessed = now;
1109
- store.putCookie(cookie, next); // step 12
1110
1307
  }
1308
+
1309
+ store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
1111
1310
  }
1112
1311
 
1113
- store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
1114
- };
1312
+ // RFC6365 S5.4
1313
+ getCookies(url, options, cb) {
1314
+ validators.validate(validators.isNonEmptyString(url), cb, url);
1315
+ const context = getCookieContext(url);
1316
+ if (validators.isFunction(options)) {
1317
+ cb = options;
1318
+ options = {};
1319
+ }
1320
+ validators.validate(validators.isObject(options), cb, options);
1321
+ validators.validate(validators.isFunction(cb), cb);
1322
+
1323
+ const host = canonicalDomain(context.hostname);
1324
+ const path = context.pathname || "/";
1325
+
1326
+ let secure = options.secure;
1327
+ if (
1328
+ secure == null &&
1329
+ context.protocol &&
1330
+ (context.protocol == "https:" || context.protocol == "wss:")
1331
+ ) {
1332
+ secure = true;
1333
+ }
1115
1334
 
1116
- // RFC6365 S5.4
1117
- CAN_BE_SYNC.push('getCookies');
1118
- CookieJar.prototype.getCookies = function(url, options, cb) {
1119
- var context = getCookieContext(url);
1120
- if (options instanceof Function) {
1121
- cb = options;
1122
- options = {};
1123
- }
1335
+ let sameSiteLevel = 0;
1336
+ if (options.sameSiteContext) {
1337
+ const sameSiteContext = checkSameSiteContext(options.sameSiteContext);
1338
+ sameSiteLevel = Cookie.sameSiteLevel[sameSiteContext];
1339
+ if (!sameSiteLevel) {
1340
+ return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR));
1341
+ }
1342
+ }
1124
1343
 
1125
- var host = canonicalDomain(context.hostname);
1126
- var path = context.pathname || '/';
1344
+ let http = options.http;
1345
+ if (http == null) {
1346
+ http = true;
1347
+ }
1127
1348
 
1128
- var secure = options.secure;
1129
- if (secure == null && context.protocol &&
1130
- (context.protocol == 'https:' || context.protocol == 'wss:'))
1131
- {
1132
- secure = true;
1133
- }
1349
+ const now = options.now || Date.now();
1350
+ const expireCheck = options.expire !== false;
1351
+ const allPaths = !!options.allPaths;
1352
+ const store = this.store;
1353
+
1354
+ function matchingCookie(c) {
1355
+ // "Either:
1356
+ // The cookie's host-only-flag is true and the canonicalized
1357
+ // request-host is identical to the cookie's domain.
1358
+ // Or:
1359
+ // The cookie's host-only-flag is false and the canonicalized
1360
+ // request-host domain-matches the cookie's domain."
1361
+ if (c.hostOnly) {
1362
+ if (c.domain != host) {
1363
+ return false;
1364
+ }
1365
+ } else {
1366
+ if (!domainMatch(host, c.domain, false)) {
1367
+ return false;
1368
+ }
1369
+ }
1134
1370
 
1135
- var http = options.http;
1136
- if (http == null) {
1137
- http = true;
1138
- }
1371
+ // "The request-uri's path path-matches the cookie's path."
1372
+ if (!allPaths && !pathMatch(path, c.path)) {
1373
+ return false;
1374
+ }
1139
1375
 
1140
- var now = options.now || Date.now();
1141
- var expireCheck = options.expire !== false;
1142
- var allPaths = !!options.allPaths;
1143
- var store = this.store;
1376
+ // "If the cookie's secure-only-flag is true, then the request-uri's
1377
+ // scheme must denote a "secure" protocol"
1378
+ if (c.secure && !secure) {
1379
+ return false;
1380
+ }
1144
1381
 
1145
- function matchingCookie(c) {
1146
- // "Either:
1147
- // The cookie's host-only-flag is true and the canonicalized
1148
- // request-host is identical to the cookie's domain.
1149
- // Or:
1150
- // The cookie's host-only-flag is false and the canonicalized
1151
- // request-host domain-matches the cookie's domain."
1152
- if (c.hostOnly) {
1153
- if (c.domain != host) {
1382
+ // "If the cookie's http-only-flag is true, then exclude the cookie if the
1383
+ // cookie-string is being generated for a "non-HTTP" API"
1384
+ if (c.httpOnly && !http) {
1154
1385
  return false;
1155
1386
  }
1156
- } else {
1157
- if (!domainMatch(host, c.domain, false)) {
1387
+
1388
+ // RFC6265bis-02 S5.3.7
1389
+ if (sameSiteLevel) {
1390
+ const cookieLevel = Cookie.sameSiteLevel[c.sameSite || "none"];
1391
+ if (cookieLevel > sameSiteLevel) {
1392
+ // only allow cookies at or below the request level
1393
+ return false;
1394
+ }
1395
+ }
1396
+
1397
+ // deferred from S5.3
1398
+ // non-RFC: allow retention of expired cookies by choice
1399
+ if (expireCheck && c.expiryTime() <= now) {
1400
+ store.removeCookie(c.domain, c.path, c.key, () => {}); // result ignored
1158
1401
  return false;
1159
1402
  }
1160
- }
1161
1403
 
1162
- // "The request-uri's path path-matches the cookie's path."
1163
- if (!allPaths && !pathMatch(path, c.path)) {
1164
- return false;
1404
+ return true;
1165
1405
  }
1166
1406
 
1167
- // "If the cookie's secure-only-flag is true, then the request-uri's
1168
- // scheme must denote a "secure" protocol"
1169
- if (c.secure && !secure) {
1170
- return false;
1171
- }
1407
+ store.findCookies(
1408
+ host,
1409
+ allPaths ? null : path,
1410
+ this.allowSpecialUseDomain,
1411
+ (err, cookies) => {
1412
+ if (err) {
1413
+ return cb(err);
1414
+ }
1172
1415
 
1173
- // "If the cookie's http-only-flag is true, then exclude the cookie if the
1174
- // cookie-string is being generated for a "non-HTTP" API"
1175
- if (c.httpOnly && !http) {
1176
- return false;
1177
- }
1416
+ cookies = cookies.filter(matchingCookie);
1178
1417
 
1179
- // deferred from S5.3
1180
- // non-RFC: allow retention of expired cookies by choice
1181
- if (expireCheck && c.expiryTime() <= now) {
1182
- store.removeCookie(c.domain, c.path, c.key, function(){}); // result ignored
1183
- return false;
1184
- }
1418
+ // sorting of S5.4 part 2
1419
+ if (options.sort !== false) {
1420
+ cookies = cookies.sort(cookieCompare);
1421
+ }
1185
1422
 
1186
- return true;
1423
+ // S5.4 part 3
1424
+ const now = new Date();
1425
+ for (const cookie of cookies) {
1426
+ cookie.lastAccessed = now;
1427
+ }
1428
+ // TODO persist lastAccessed
1429
+
1430
+ cb(null, cookies);
1431
+ }
1432
+ );
1187
1433
  }
1188
1434
 
1189
- store.findCookies(host, allPaths ? null : path, function(err,cookies) {
1190
- if (err) {
1191
- return cb(err);
1192
- }
1435
+ getCookieString(...args) {
1436
+ const cb = args.pop();
1437
+ validators.validate(validators.isFunction(cb), cb);
1438
+ const next = function(err, cookies) {
1439
+ if (err) {
1440
+ cb(err);
1441
+ } else {
1442
+ cb(
1443
+ null,
1444
+ cookies
1445
+ .sort(cookieCompare)
1446
+ .map(c => c.cookieString())
1447
+ .join("; ")
1448
+ );
1449
+ }
1450
+ };
1451
+ args.push(next);
1452
+ this.getCookies.apply(this, args);
1453
+ }
1193
1454
 
1194
- cookies = cookies.filter(matchingCookie);
1455
+ getSetCookieStrings(...args) {
1456
+ const cb = args.pop();
1457
+ validators.validate(validators.isFunction(cb), cb);
1458
+ const next = function(err, cookies) {
1459
+ if (err) {
1460
+ cb(err);
1461
+ } else {
1462
+ cb(
1463
+ null,
1464
+ cookies.map(c => {
1465
+ return c.toString();
1466
+ })
1467
+ );
1468
+ }
1469
+ };
1470
+ args.push(next);
1471
+ this.getCookies.apply(this, args);
1472
+ }
1195
1473
 
1196
- // sorting of S5.4 part 2
1197
- if (options.sort !== false) {
1198
- cookies = cookies.sort(cookieCompare);
1474
+ serialize(cb) {
1475
+ validators.validate(validators.isFunction(cb), cb);
1476
+ let type = this.store.constructor.name;
1477
+ if (validators.isObject(type)) {
1478
+ type = null;
1199
1479
  }
1200
1480
 
1201
- // S5.4 part 3
1202
- var now = new Date();
1203
- cookies.forEach(function(c) {
1204
- c.lastAccessed = now;
1205
- });
1206
- // TODO persist lastAccessed
1481
+ // update README.md "Serialization Format" if you change this, please!
1482
+ const serialized = {
1483
+ // The version of tough-cookie that serialized this jar. Generally a good
1484
+ // practice since future versions can make data import decisions based on
1485
+ // known past behavior. When/if this matters, use `semver`.
1486
+ version: `tough-cookie@${VERSION}`,
1207
1487
 
1208
- cb(null,cookies);
1209
- });
1210
- };
1488
+ // add the store type, to make humans happy:
1489
+ storeType: type,
1211
1490
 
1212
- CAN_BE_SYNC.push('getCookieString');
1213
- CookieJar.prototype.getCookieString = function(/*..., cb*/) {
1214
- var args = Array.prototype.slice.call(arguments,0);
1215
- var cb = args.pop();
1216
- var next = function(err,cookies) {
1217
- if (err) {
1218
- cb(err);
1219
- } else {
1220
- cb(null, cookies
1221
- .sort(cookieCompare)
1222
- .map(function(c){
1223
- return c.cookieString();
1224
- })
1225
- .join('; '));
1226
- }
1227
- };
1228
- args.push(next);
1229
- this.getCookies.apply(this,args);
1230
- };
1491
+ // CookieJar configuration:
1492
+ rejectPublicSuffixes: !!this.rejectPublicSuffixes,
1493
+ enableLooseMode: !!this.enableLooseMode,
1494
+ allowSpecialUseDomain: !!this.allowSpecialUseDomain,
1495
+ prefixSecurity: getNormalizedPrefixSecurity(this.prefixSecurity),
1231
1496
 
1232
- CAN_BE_SYNC.push('getSetCookieStrings');
1233
- CookieJar.prototype.getSetCookieStrings = function(/*..., cb*/) {
1234
- var args = Array.prototype.slice.call(arguments,0);
1235
- var cb = args.pop();
1236
- var next = function(err,cookies) {
1237
- if (err) {
1238
- cb(err);
1239
- } else {
1240
- cb(null, cookies.map(function(c){
1241
- return c.toString();
1242
- }));
1497
+ // this gets filled from getAllCookies:
1498
+ cookies: []
1499
+ };
1500
+
1501
+ if (
1502
+ !(
1503
+ this.store.getAllCookies &&
1504
+ typeof this.store.getAllCookies === "function"
1505
+ )
1506
+ ) {
1507
+ return cb(
1508
+ new Error(
1509
+ "store does not support getAllCookies and cannot be serialized"
1510
+ )
1511
+ );
1243
1512
  }
1244
- };
1245
- args.push(next);
1246
- this.getCookies.apply(this,args);
1247
- };
1248
1513
 
1249
- CAN_BE_SYNC.push('serialize');
1250
- CookieJar.prototype.serialize = function(cb) {
1251
- var type = this.store.constructor.name;
1252
- if (type === 'Object') {
1253
- type = null;
1254
- }
1514
+ this.store.getAllCookies((err, cookies) => {
1515
+ if (err) {
1516
+ return cb(err);
1517
+ }
1255
1518
 
1256
- // update README.md "Serialization Format" if you change this, please!
1257
- var serialized = {
1258
- // The version of tough-cookie that serialized this jar. Generally a good
1259
- // practice since future versions can make data import decisions based on
1260
- // known past behavior. When/if this matters, use `semver`.
1261
- version: 'tough-cookie@'+VERSION,
1519
+ serialized.cookies = cookies.map(cookie => {
1520
+ // convert to serialized 'raw' cookies
1521
+ cookie = cookie instanceof Cookie ? cookie.toJSON() : cookie;
1262
1522
 
1263
- // add the store type, to make humans happy:
1264
- storeType: type,
1523
+ // Remove the index so new ones get assigned during deserialization
1524
+ delete cookie.creationIndex;
1265
1525
 
1266
- // CookieJar configuration:
1267
- rejectPublicSuffixes: !!this.rejectPublicSuffixes,
1526
+ return cookie;
1527
+ });
1268
1528
 
1269
- // this gets filled from getAllCookies:
1270
- cookies: []
1271
- };
1529
+ return cb(null, serialized);
1530
+ });
1531
+ }
1272
1532
 
1273
- if (!(this.store.getAllCookies &&
1274
- typeof this.store.getAllCookies === 'function'))
1275
- {
1276
- return cb(new Error('store does not support getAllCookies and cannot be serialized'));
1533
+ toJSON() {
1534
+ return this.serializeSync();
1277
1535
  }
1278
1536
 
1279
- this.store.getAllCookies(function(err,cookies) {
1280
- if (err) {
1281
- return cb(err);
1537
+ // use the class method CookieJar.deserialize instead of calling this directly
1538
+ _importCookies(serialized, cb) {
1539
+ let cookies = serialized.cookies;
1540
+ if (!cookies || !Array.isArray(cookies)) {
1541
+ return cb(new Error("serialized jar has no cookies array"));
1282
1542
  }
1543
+ cookies = cookies.slice(); // do not modify the original
1283
1544
 
1284
- serialized.cookies = cookies.map(function(cookie) {
1285
- // convert to serialized 'raw' cookies
1286
- cookie = (cookie instanceof Cookie) ? cookie.toJSON() : cookie;
1545
+ const putNext = err => {
1546
+ if (err) {
1547
+ return cb(err);
1548
+ }
1287
1549
 
1288
- // Remove the index so new ones get assigned during deserialization
1289
- delete cookie.creationIndex;
1550
+ if (!cookies.length) {
1551
+ return cb(err, this);
1552
+ }
1290
1553
 
1291
- return cookie;
1292
- });
1554
+ let cookie;
1555
+ try {
1556
+ cookie = fromJSON(cookies.shift());
1557
+ } catch (e) {
1558
+ return cb(e);
1559
+ }
1293
1560
 
1294
- return cb(null, serialized);
1295
- });
1296
- };
1561
+ if (cookie === null) {
1562
+ return putNext(null); // skip this cookie
1563
+ }
1297
1564
 
1298
- // well-known name that JSON.stringify calls
1299
- CookieJar.prototype.toJSON = function() {
1300
- return this.serializeSync();
1301
- };
1565
+ this.store.putCookie(cookie, putNext);
1566
+ };
1302
1567
 
1303
- // use the class method CookieJar.deserialize instead of calling this directly
1304
- CAN_BE_SYNC.push('_importCookies');
1305
- CookieJar.prototype._importCookies = function(serialized, cb) {
1306
- var jar = this;
1307
- var cookies = serialized.cookies;
1308
- if (!cookies || !Array.isArray(cookies)) {
1309
- return cb(new Error('serialized jar has no cookies array'));
1568
+ putNext();
1310
1569
  }
1311
- cookies = cookies.slice(); // do not modify the original
1312
1570
 
1313
- function putNext(err) {
1314
- if (err) {
1315
- return cb(err);
1571
+ clone(newStore, cb) {
1572
+ if (arguments.length === 1) {
1573
+ cb = newStore;
1574
+ newStore = null;
1316
1575
  }
1317
1576
 
1318
- if (!cookies.length) {
1319
- return cb(err, jar);
1320
- }
1577
+ this.serialize((err, serialized) => {
1578
+ if (err) {
1579
+ return cb(err);
1580
+ }
1581
+ CookieJar.deserialize(serialized, newStore, cb);
1582
+ });
1583
+ }
1321
1584
 
1322
- var cookie;
1323
- try {
1324
- cookie = fromJSON(cookies.shift());
1325
- } catch (e) {
1326
- return cb(e);
1585
+ cloneSync(newStore) {
1586
+ if (arguments.length === 0) {
1587
+ return this._cloneSync();
1327
1588
  }
1328
-
1329
- if (cookie === null) {
1330
- return putNext(null); // skip this cookie
1589
+ if (!newStore.synchronous) {
1590
+ throw new Error(
1591
+ "CookieJar clone destination store is not synchronous; use async API instead."
1592
+ );
1331
1593
  }
1332
-
1333
- jar.store.putCookie(cookie, putNext);
1334
- }
1335
-
1336
- putNext();
1337
- };
1338
-
1339
- CookieJar.deserialize = function(strOrObj, store, cb) {
1340
- if (arguments.length !== 3) {
1341
- // store is optional
1342
- cb = store;
1343
- store = null;
1594
+ return this._cloneSync(newStore);
1344
1595
  }
1345
1596
 
1346
- var serialized;
1347
- if (typeof strOrObj === 'string') {
1348
- serialized = jsonParse(strOrObj);
1349
- if (serialized instanceof Error) {
1350
- return cb(serialized);
1351
- }
1352
- } else {
1353
- serialized = strOrObj;
1354
- }
1597
+ removeAllCookies(cb) {
1598
+ validators.validate(validators.isFunction(cb), cb);
1599
+ const store = this.store;
1355
1600
 
1356
- var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
1357
- jar._importCookies(serialized, function(err) {
1358
- if (err) {
1359
- return cb(err);
1601
+ // Check that the store implements its own removeAllCookies(). The default
1602
+ // implementation in Store will immediately call the callback with a "not
1603
+ // implemented" Error.
1604
+ if (
1605
+ typeof store.removeAllCookies === "function" &&
1606
+ store.removeAllCookies !== Store.prototype.removeAllCookies
1607
+ ) {
1608
+ return store.removeAllCookies(cb);
1360
1609
  }
1361
- cb(null, jar);
1362
- });
1363
- };
1364
1610
 
1365
- CookieJar.deserializeSync = function(strOrObj, store) {
1366
- var serialized = typeof strOrObj === 'string' ?
1367
- JSON.parse(strOrObj) : strOrObj;
1368
- var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
1611
+ store.getAllCookies((err, cookies) => {
1612
+ if (err) {
1613
+ return cb(err);
1614
+ }
1369
1615
 
1370
- // catch this mistake early:
1371
- if (!jar.store.synchronous) {
1372
- throw new Error('CookieJar store is not synchronous; use async API instead.');
1373
- }
1616
+ if (cookies.length === 0) {
1617
+ return cb(null);
1618
+ }
1374
1619
 
1375
- jar._importCookiesSync(serialized);
1376
- return jar;
1377
- };
1378
- CookieJar.fromJSON = CookieJar.deserializeSync;
1620
+ let completedCount = 0;
1621
+ const removeErrors = [];
1379
1622
 
1380
- CookieJar.prototype.clone = function(newStore, cb) {
1381
- if (arguments.length === 1) {
1382
- cb = newStore;
1383
- newStore = null;
1384
- }
1623
+ function removeCookieCb(removeErr) {
1624
+ if (removeErr) {
1625
+ removeErrors.push(removeErr);
1626
+ }
1385
1627
 
1386
- this.serialize(function(err,serialized) {
1387
- if (err) {
1388
- return cb(err);
1389
- }
1390
- CookieJar.deserialize(serialized, newStore, cb);
1391
- });
1392
- };
1628
+ completedCount++;
1393
1629
 
1394
- CAN_BE_SYNC.push('removeAllCookies');
1395
- CookieJar.prototype.removeAllCookies = function(cb) {
1396
- var store = this.store;
1630
+ if (completedCount === cookies.length) {
1631
+ return cb(removeErrors.length ? removeErrors[0] : null);
1632
+ }
1633
+ }
1397
1634
 
1398
- // Check that the store implements its own removeAllCookies(). The default
1399
- // implementation in Store will immediately call the callback with a "not
1400
- // implemented" Error.
1401
- if (store.removeAllCookies instanceof Function &&
1402
- store.removeAllCookies !== Store.prototype.removeAllCookies)
1403
- {
1404
- return store.removeAllCookies(cb);
1635
+ cookies.forEach(cookie => {
1636
+ store.removeCookie(
1637
+ cookie.domain,
1638
+ cookie.path,
1639
+ cookie.key,
1640
+ removeCookieCb
1641
+ );
1642
+ });
1643
+ });
1405
1644
  }
1406
1645
 
1407
- store.getAllCookies(function(err, cookies) {
1408
- if (err) {
1409
- return cb(err);
1646
+ static deserialize(strOrObj, store, cb) {
1647
+ if (arguments.length !== 3) {
1648
+ // store is optional
1649
+ cb = store;
1650
+ store = null;
1410
1651
  }
1652
+ validators.validate(validators.isFunction(cb), cb);
1411
1653
 
1412
- if (cookies.length === 0) {
1413
- return cb(null);
1654
+ let serialized;
1655
+ if (typeof strOrObj === "string") {
1656
+ serialized = jsonParse(strOrObj);
1657
+ if (serialized instanceof Error) {
1658
+ return cb(serialized);
1659
+ }
1660
+ } else {
1661
+ serialized = strOrObj;
1414
1662
  }
1415
1663
 
1416
- var completedCount = 0;
1417
- var removeErrors = [];
1418
-
1419
- function removeCookieCb(removeErr) {
1420
- if (removeErr) {
1421
- removeErrors.push(removeErr);
1664
+ const jar = new CookieJar(store, {
1665
+ rejectPublicSuffixes: serialized.rejectPublicSuffixes,
1666
+ looseMode: serialized.enableLooseMode,
1667
+ allowSpecialUseDomain: serialized.allowSpecialUseDomain,
1668
+ prefixSecurity: serialized.prefixSecurity
1669
+ });
1670
+ jar._importCookies(serialized, err => {
1671
+ if (err) {
1672
+ return cb(err);
1422
1673
  }
1674
+ cb(null, jar);
1675
+ });
1676
+ }
1423
1677
 
1424
- completedCount++;
1678
+ static deserializeSync(strOrObj, store) {
1679
+ const serialized =
1680
+ typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj;
1681
+ const jar = new CookieJar(store, {
1682
+ rejectPublicSuffixes: serialized.rejectPublicSuffixes,
1683
+ looseMode: serialized.enableLooseMode
1684
+ });
1425
1685
 
1426
- if (completedCount === cookies.length) {
1427
- return cb(removeErrors.length ? removeErrors[0] : null);
1428
- }
1686
+ // catch this mistake early:
1687
+ if (!jar.store.synchronous) {
1688
+ throw new Error(
1689
+ "CookieJar store is not synchronous; use async API instead."
1690
+ );
1429
1691
  }
1430
1692
 
1431
- cookies.forEach(function(cookie) {
1432
- store.removeCookie(cookie.domain, cookie.path, cookie.key, removeCookieCb);
1433
- });
1434
- });
1435
- };
1436
-
1437
- CookieJar.prototype._cloneSync = syncWrap('clone');
1438
- CookieJar.prototype.cloneSync = function(newStore) {
1439
- if (!newStore.synchronous) {
1440
- throw new Error('CookieJar clone destination store is not synchronous; use async API instead.');
1693
+ jar._importCookiesSync(serialized);
1694
+ return jar;
1441
1695
  }
1442
- return this._cloneSync(newStore);
1443
- };
1696
+ }
1697
+ CookieJar.fromJSON = CookieJar.deserializeSync;
1698
+
1699
+ [
1700
+ "_importCookies",
1701
+ "clone",
1702
+ "getCookies",
1703
+ "getCookieString",
1704
+ "getSetCookieStrings",
1705
+ "removeAllCookies",
1706
+ "serialize",
1707
+ "setCookie"
1708
+ ].forEach(name => {
1709
+ CookieJar.prototype[name] = fromCallback(CookieJar.prototype[name]);
1710
+ });
1711
+ CookieJar.deserialize = fromCallback(CookieJar.deserialize);
1444
1712
 
1445
1713
  // Use a closure to provide a true imperative API for synchronous stores.
1446
1714
  function syncWrap(method) {
1447
- return function() {
1715
+ return function(...args) {
1448
1716
  if (!this.store.synchronous) {
1449
- throw new Error('CookieJar store is not synchronous; use async API instead.');
1717
+ throw new Error(
1718
+ "CookieJar store is not synchronous; use async API instead."
1719
+ );
1450
1720
  }
1451
1721
 
1452
- var args = Array.prototype.slice.call(arguments);
1453
- var syncErr, syncResult;
1454
- args.push(function syncCb(err, result) {
1722
+ let syncErr, syncResult;
1723
+ this[method](...args, (err, result) => {
1455
1724
  syncErr = err;
1456
1725
  syncResult = result;
1457
1726
  });
1458
- this[method].apply(this, args);
1459
1727
 
1460
1728
  if (syncErr) {
1461
1729
  throw syncErr;
@@ -1464,11 +1732,6 @@ function syncWrap(method) {
1464
1732
  };
1465
1733
  }
1466
1734
 
1467
- // wrap all declared CAN_BE_SYNC methods in the sync wrapper
1468
- CAN_BE_SYNC.forEach(function(method) {
1469
- CookieJar.prototype[method+'Sync'] = syncWrap(method);
1470
- });
1471
-
1472
1735
  exports.version = VERSION;
1473
1736
  exports.CookieJar = CookieJar;
1474
1737
  exports.Cookie = Cookie;
@@ -1483,6 +1746,8 @@ exports.defaultPath = defaultPath;
1483
1746
  exports.pathMatch = pathMatch;
1484
1747
  exports.getPublicSuffix = pubsuffix.getPublicSuffix;
1485
1748
  exports.cookieCompare = cookieCompare;
1486
- exports.permuteDomain = require('./permuteDomain').permuteDomain;
1749
+ exports.permuteDomain = require("./permuteDomain").permuteDomain;
1487
1750
  exports.permutePath = permutePath;
1488
1751
  exports.canonicalDomain = canonicalDomain;
1752
+ exports.PrefixSecurityEnum = PrefixSecurityEnum;
1753
+ exports.ParameterError = validators.ParameterError;