tough-cookie 3.0.1 → 4.1.1

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,949 @@ 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 =
1103
+ typeof options.allowSpecialUseDomain === "boolean"
1104
+ ? options.allowSpecialUseDomain
1105
+ : true;
1106
+ this.store = store || new MemoryCookieStore();
1107
+ this.prefixSecurity = getNormalizedPrefixSecurity(options.prefixSecurity);
1108
+ this._cloneSync = syncWrap("clone");
1109
+ this._importCookiesSync = syncWrap("_importCookies");
1110
+ this.getCookiesSync = syncWrap("getCookies");
1111
+ this.getCookieStringSync = syncWrap("getCookieString");
1112
+ this.getSetCookieStringsSync = syncWrap("getSetCookieStrings");
1113
+ this.removeAllCookiesSync = syncWrap("removeAllCookies");
1114
+ this.setCookieSync = syncWrap("setCookie");
1115
+ this.serializeSync = syncWrap("serialize");
1116
+ }
1117
+
1118
+ setCookie(cookie, url, options, cb) {
1119
+ validators.validate(validators.isNonEmptyString(url), cb, options);
1120
+ let err;
1121
+
1122
+ if (validators.isFunction(url)) {
1123
+ cb = url;
1124
+ return cb(new Error("No URL was specified"));
1125
+ }
1008
1126
 
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);
1127
+ const context = getCookieContext(url);
1128
+ if (validators.isFunction(options)) {
1129
+ cb = options;
1130
+ options = {};
1015
1131
  }
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
1132
 
1024
- // S5.3 step 2
1025
- var now = options.now || new Date(); // will assign later to save effort in the face of errors
1133
+ validators.validate(validators.isFunction(cb), cb);
1026
1134
 
1027
- // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
1135
+ if (
1136
+ !validators.isNonEmptyString(cookie) &&
1137
+ !validators.isObject(cookie) &&
1138
+ cookie instanceof String &&
1139
+ cookie.length == 0
1140
+ ) {
1141
+ return cb(null);
1142
+ }
1028
1143
 
1029
- // S5.3 step 4: NOOP; domain is null by default
1144
+ const host = canonicalDomain(context.hostname);
1145
+ const loose = options.loose || this.enableLooseMode;
1030
1146
 
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);
1147
+ let sameSiteContext = null;
1148
+ if (options.sameSiteContext) {
1149
+ sameSiteContext = checkSameSiteContext(options.sameSiteContext);
1150
+ if (!sameSiteContext) {
1151
+ return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR));
1152
+ }
1037
1153
  }
1038
- }
1039
1154
 
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);
1155
+ // S5.3 step 1
1156
+ if (typeof cookie === "string" || cookie instanceof String) {
1157
+ cookie = Cookie.parse(cookie, { loose: loose });
1158
+ if (!cookie) {
1159
+ err = new Error("Cookie failed to parse");
1160
+ return cb(options.ignoreError ? null : err);
1161
+ }
1162
+ } else if (!(cookie instanceof Cookie)) {
1163
+ // If you're seeing this error, and are passing in a Cookie object,
1164
+ // it *might* be a Cookie object from another loaded version of tough-cookie.
1165
+ err = new Error(
1166
+ "First argument to setCookie must be a Cookie object or string"
1167
+ );
1044
1168
  return cb(options.ignoreError ? null : err);
1045
1169
  }
1046
1170
 
1047
- if (cookie.hostOnly == null) { // don't reset if already set
1048
- cookie.hostOnly = false;
1171
+ // S5.3 step 2
1172
+ const now = options.now || new Date(); // will assign later to save effort in the face of errors
1173
+
1174
+ // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
1175
+
1176
+ // S5.3 step 4: NOOP; domain is null by default
1177
+
1178
+ // S5.3 step 5: public suffixes
1179
+ if (this.rejectPublicSuffixes && cookie.domain) {
1180
+ const suffix = pubsuffix.getPublicSuffix(cookie.cdomain(), {
1181
+ allowSpecialUseDomain: this.allowSpecialUseDomain,
1182
+ ignoreError: options.ignoreError
1183
+ });
1184
+ if (suffix == null && !IP_V6_REGEX_OBJECT.test(cookie.domain)) {
1185
+ // e.g. "com"
1186
+ err = new Error("Cookie has domain set to a public suffix");
1187
+ return cb(options.ignoreError ? null : err);
1188
+ }
1049
1189
  }
1050
1190
 
1051
- } else {
1052
- cookie.hostOnly = true;
1053
- cookie.domain = host;
1054
- }
1191
+ // S5.3 step 6:
1192
+ if (cookie.domain) {
1193
+ if (!domainMatch(host, cookie.cdomain(), false)) {
1194
+ err = new Error(
1195
+ `Cookie not in this host's domain. Cookie:${cookie.cdomain()} Request:${host}`
1196
+ );
1197
+ return cb(options.ignoreError ? null : err);
1198
+ }
1055
1199
 
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
- }
1200
+ if (cookie.hostOnly == null) {
1201
+ // don't reset if already set
1202
+ cookie.hostOnly = false;
1203
+ }
1204
+ } else {
1205
+ cookie.hostOnly = true;
1206
+ cookie.domain = host;
1207
+ }
1063
1208
 
1064
- // S5.3 step 8: NOOP; secure attribute
1065
- // S5.3 step 9: NOOP; httpOnly attribute
1209
+ //S5.2.4 If the attribute-value is empty or if the first character of the
1210
+ //attribute-value is not %x2F ("/"):
1211
+ //Let cookie-path be the default-path.
1212
+ if (!cookie.path || cookie.path[0] !== "/") {
1213
+ cookie.path = defaultPath(context.pathname);
1214
+ cookie.pathIsDefault = true;
1215
+ }
1066
1216
 
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
- }
1217
+ // S5.3 step 8: NOOP; secure attribute
1218
+ // S5.3 step 9: NOOP; httpOnly attribute
1219
+
1220
+ // S5.3 step 10
1221
+ if (options.http === false && cookie.httpOnly) {
1222
+ err = new Error("Cookie is HttpOnly and this isn't an HTTP API");
1223
+ return cb(options.ignoreError ? null : err);
1224
+ }
1072
1225
 
1073
- var store = this.store;
1226
+ // 6252bis-02 S5.4 Step 13 & 14:
1227
+ if (
1228
+ cookie.sameSite !== "none" &&
1229
+ cookie.sameSite !== undefined &&
1230
+ sameSiteContext
1231
+ ) {
1232
+ // "If the cookie's "same-site-flag" is not "None", and the cookie
1233
+ // is being set from a context whose "site for cookies" is not an
1234
+ // exact match for request-uri's host's registered domain, then
1235
+ // abort these steps and ignore the newly created cookie entirely."
1236
+ if (sameSiteContext === "none") {
1237
+ err = new Error(
1238
+ "Cookie is SameSite but this is a cross-origin request"
1239
+ );
1240
+ return cb(options.ignoreError ? null : err);
1241
+ }
1242
+ }
1074
1243
 
1075
- if (!store.updateCookie) {
1076
- store.updateCookie = function(oldCookie, newCookie, cb) {
1077
- this.putCookie(newCookie, cb);
1078
- };
1079
- }
1244
+ /* 6265bis-02 S5.4 Steps 15 & 16 */
1245
+ const ignoreErrorForPrefixSecurity =
1246
+ this.prefixSecurity === PrefixSecurityEnum.SILENT;
1247
+ const prefixSecurityDisabled =
1248
+ this.prefixSecurity === PrefixSecurityEnum.DISABLED;
1249
+ /* If prefix checking is not disabled ...*/
1250
+ if (!prefixSecurityDisabled) {
1251
+ let errorFound = false;
1252
+ let errorMsg;
1253
+ /* Check secure prefix condition */
1254
+ if (!isSecurePrefixConditionMet(cookie)) {
1255
+ errorFound = true;
1256
+ errorMsg = "Cookie has __Secure prefix but Secure attribute is not set";
1257
+ } else if (!isHostPrefixConditionMet(cookie)) {
1258
+ /* Check host prefix condition */
1259
+ errorFound = true;
1260
+ errorMsg =
1261
+ "Cookie has __Host prefix but either Secure or HostOnly attribute is not set or Path is not '/'";
1262
+ }
1263
+ if (errorFound) {
1264
+ return cb(
1265
+ options.ignoreError || ignoreErrorForPrefixSecurity
1266
+ ? null
1267
+ : new Error(errorMsg)
1268
+ );
1269
+ }
1270
+ }
1080
1271
 
1081
- function withCookie(err, oldCookie) {
1082
- if (err) {
1083
- return cb(err);
1272
+ const store = this.store;
1273
+
1274
+ if (!store.updateCookie) {
1275
+ store.updateCookie = function(oldCookie, newCookie, cb) {
1276
+ this.putCookie(newCookie, cb);
1277
+ };
1084
1278
  }
1085
1279
 
1086
- var next = function(err) {
1280
+ function withCookie(err, oldCookie) {
1087
1281
  if (err) {
1088
1282
  return cb(err);
1089
- } else {
1090
- cb(null, cookie);
1091
1283
  }
1092
- };
1093
1284
 
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);
1285
+ const next = function(err) {
1286
+ if (err) {
1287
+ return cb(err);
1288
+ } else {
1289
+ cb(null, cookie);
1290
+ }
1291
+ };
1292
+
1293
+ if (oldCookie) {
1294
+ // S5.3 step 11 - "If the cookie store contains a cookie with the same name,
1295
+ // domain, and path as the newly created cookie:"
1296
+ if (options.http === false && oldCookie.httpOnly) {
1297
+ // step 11.2
1298
+ err = new Error("old Cookie is HttpOnly and this isn't an HTTP API");
1299
+ return cb(options.ignoreError ? null : err);
1300
+ }
1301
+ cookie.creation = oldCookie.creation; // step 11.3
1302
+ cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker
1303
+ cookie.lastAccessed = now;
1304
+ // Step 11.4 (delete cookie) is implied by just setting the new one:
1305
+ store.updateCookie(oldCookie, cookie, next); // step 12
1306
+ } else {
1307
+ cookie.creation = cookie.lastAccessed = now;
1308
+ store.putCookie(cookie, next); // step 12
1100
1309
  }
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
1310
  }
1311
+
1312
+ store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
1111
1313
  }
1112
1314
 
1113
- store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
1114
- };
1315
+ // RFC6365 S5.4
1316
+ getCookies(url, options, cb) {
1317
+ validators.validate(validators.isNonEmptyString(url), cb, url);
1318
+ const context = getCookieContext(url);
1319
+ if (validators.isFunction(options)) {
1320
+ cb = options;
1321
+ options = {};
1322
+ }
1323
+ validators.validate(validators.isObject(options), cb, options);
1324
+ validators.validate(validators.isFunction(cb), cb);
1325
+
1326
+ const host = canonicalDomain(context.hostname);
1327
+ const path = context.pathname || "/";
1328
+
1329
+ let secure = options.secure;
1330
+ if (
1331
+ secure == null &&
1332
+ context.protocol &&
1333
+ (context.protocol == "https:" || context.protocol == "wss:")
1334
+ ) {
1335
+ secure = true;
1336
+ }
1115
1337
 
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
- }
1338
+ let sameSiteLevel = 0;
1339
+ if (options.sameSiteContext) {
1340
+ const sameSiteContext = checkSameSiteContext(options.sameSiteContext);
1341
+ sameSiteLevel = Cookie.sameSiteLevel[sameSiteContext];
1342
+ if (!sameSiteLevel) {
1343
+ return cb(new Error(SAME_SITE_CONTEXT_VAL_ERR));
1344
+ }
1345
+ }
1124
1346
 
1125
- var host = canonicalDomain(context.hostname);
1126
- var path = context.pathname || '/';
1347
+ let http = options.http;
1348
+ if (http == null) {
1349
+ http = true;
1350
+ }
1127
1351
 
1128
- var secure = options.secure;
1129
- if (secure == null && context.protocol &&
1130
- (context.protocol == 'https:' || context.protocol == 'wss:'))
1131
- {
1132
- secure = true;
1133
- }
1352
+ const now = options.now || Date.now();
1353
+ const expireCheck = options.expire !== false;
1354
+ const allPaths = !!options.allPaths;
1355
+ const store = this.store;
1356
+
1357
+ function matchingCookie(c) {
1358
+ // "Either:
1359
+ // The cookie's host-only-flag is true and the canonicalized
1360
+ // request-host is identical to the cookie's domain.
1361
+ // Or:
1362
+ // The cookie's host-only-flag is false and the canonicalized
1363
+ // request-host domain-matches the cookie's domain."
1364
+ if (c.hostOnly) {
1365
+ if (c.domain != host) {
1366
+ return false;
1367
+ }
1368
+ } else {
1369
+ if (!domainMatch(host, c.domain, false)) {
1370
+ return false;
1371
+ }
1372
+ }
1134
1373
 
1135
- var http = options.http;
1136
- if (http == null) {
1137
- http = true;
1138
- }
1374
+ // "The request-uri's path path-matches the cookie's path."
1375
+ if (!allPaths && !pathMatch(path, c.path)) {
1376
+ return false;
1377
+ }
1139
1378
 
1140
- var now = options.now || Date.now();
1141
- var expireCheck = options.expire !== false;
1142
- var allPaths = !!options.allPaths;
1143
- var store = this.store;
1379
+ // "If the cookie's secure-only-flag is true, then the request-uri's
1380
+ // scheme must denote a "secure" protocol"
1381
+ if (c.secure && !secure) {
1382
+ return false;
1383
+ }
1144
1384
 
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) {
1385
+ // "If the cookie's http-only-flag is true, then exclude the cookie if the
1386
+ // cookie-string is being generated for a "non-HTTP" API"
1387
+ if (c.httpOnly && !http) {
1154
1388
  return false;
1155
1389
  }
1156
- } else {
1157
- if (!domainMatch(host, c.domain, false)) {
1390
+
1391
+ // RFC6265bis-02 S5.3.7
1392
+ if (sameSiteLevel) {
1393
+ const cookieLevel = Cookie.sameSiteLevel[c.sameSite || "none"];
1394
+ if (cookieLevel > sameSiteLevel) {
1395
+ // only allow cookies at or below the request level
1396
+ return false;
1397
+ }
1398
+ }
1399
+
1400
+ // deferred from S5.3
1401
+ // non-RFC: allow retention of expired cookies by choice
1402
+ if (expireCheck && c.expiryTime() <= now) {
1403
+ store.removeCookie(c.domain, c.path, c.key, () => {}); // result ignored
1158
1404
  return false;
1159
1405
  }
1160
- }
1161
1406
 
1162
- // "The request-uri's path path-matches the cookie's path."
1163
- if (!allPaths && !pathMatch(path, c.path)) {
1164
- return false;
1407
+ return true;
1165
1408
  }
1166
1409
 
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
- }
1410
+ store.findCookies(
1411
+ host,
1412
+ allPaths ? null : path,
1413
+ this.allowSpecialUseDomain,
1414
+ (err, cookies) => {
1415
+ if (err) {
1416
+ return cb(err);
1417
+ }
1172
1418
 
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
- }
1419
+ cookies = cookies.filter(matchingCookie);
1178
1420
 
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
- }
1421
+ // sorting of S5.4 part 2
1422
+ if (options.sort !== false) {
1423
+ cookies = cookies.sort(cookieCompare);
1424
+ }
1185
1425
 
1186
- return true;
1426
+ // S5.4 part 3
1427
+ const now = new Date();
1428
+ for (const cookie of cookies) {
1429
+ cookie.lastAccessed = now;
1430
+ }
1431
+ // TODO persist lastAccessed
1432
+
1433
+ cb(null, cookies);
1434
+ }
1435
+ );
1187
1436
  }
1188
1437
 
1189
- store.findCookies(host, allPaths ? null : path, function(err,cookies) {
1190
- if (err) {
1191
- return cb(err);
1192
- }
1438
+ getCookieString(...args) {
1439
+ const cb = args.pop();
1440
+ validators.validate(validators.isFunction(cb), cb);
1441
+ const next = function(err, cookies) {
1442
+ if (err) {
1443
+ cb(err);
1444
+ } else {
1445
+ cb(
1446
+ null,
1447
+ cookies
1448
+ .sort(cookieCompare)
1449
+ .map(c => c.cookieString())
1450
+ .join("; ")
1451
+ );
1452
+ }
1453
+ };
1454
+ args.push(next);
1455
+ this.getCookies.apply(this, args);
1456
+ }
1193
1457
 
1194
- cookies = cookies.filter(matchingCookie);
1458
+ getSetCookieStrings(...args) {
1459
+ const cb = args.pop();
1460
+ validators.validate(validators.isFunction(cb), cb);
1461
+ const next = function(err, cookies) {
1462
+ if (err) {
1463
+ cb(err);
1464
+ } else {
1465
+ cb(
1466
+ null,
1467
+ cookies.map(c => {
1468
+ return c.toString();
1469
+ })
1470
+ );
1471
+ }
1472
+ };
1473
+ args.push(next);
1474
+ this.getCookies.apply(this, args);
1475
+ }
1195
1476
 
1196
- // sorting of S5.4 part 2
1197
- if (options.sort !== false) {
1198
- cookies = cookies.sort(cookieCompare);
1477
+ serialize(cb) {
1478
+ validators.validate(validators.isFunction(cb), cb);
1479
+ let type = this.store.constructor.name;
1480
+ if (validators.isObject(type)) {
1481
+ type = null;
1199
1482
  }
1200
1483
 
1201
- // S5.4 part 3
1202
- var now = new Date();
1203
- cookies.forEach(function(c) {
1204
- c.lastAccessed = now;
1205
- });
1206
- // TODO persist lastAccessed
1484
+ // update README.md "Serialization Format" if you change this, please!
1485
+ const serialized = {
1486
+ // The version of tough-cookie that serialized this jar. Generally a good
1487
+ // practice since future versions can make data import decisions based on
1488
+ // known past behavior. When/if this matters, use `semver`.
1489
+ version: `tough-cookie@${VERSION}`,
1207
1490
 
1208
- cb(null,cookies);
1209
- });
1210
- };
1491
+ // add the store type, to make humans happy:
1492
+ storeType: type,
1211
1493
 
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
- };
1494
+ // CookieJar configuration:
1495
+ rejectPublicSuffixes: !!this.rejectPublicSuffixes,
1496
+ enableLooseMode: !!this.enableLooseMode,
1497
+ allowSpecialUseDomain: !!this.allowSpecialUseDomain,
1498
+ prefixSecurity: getNormalizedPrefixSecurity(this.prefixSecurity),
1231
1499
 
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
- }));
1500
+ // this gets filled from getAllCookies:
1501
+ cookies: []
1502
+ };
1503
+
1504
+ if (
1505
+ !(
1506
+ this.store.getAllCookies &&
1507
+ typeof this.store.getAllCookies === "function"
1508
+ )
1509
+ ) {
1510
+ return cb(
1511
+ new Error(
1512
+ "store does not support getAllCookies and cannot be serialized"
1513
+ )
1514
+ );
1243
1515
  }
1244
- };
1245
- args.push(next);
1246
- this.getCookies.apply(this,args);
1247
- };
1248
1516
 
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
- }
1517
+ this.store.getAllCookies((err, cookies) => {
1518
+ if (err) {
1519
+ return cb(err);
1520
+ }
1255
1521
 
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,
1522
+ serialized.cookies = cookies.map(cookie => {
1523
+ // convert to serialized 'raw' cookies
1524
+ cookie = cookie instanceof Cookie ? cookie.toJSON() : cookie;
1262
1525
 
1263
- // add the store type, to make humans happy:
1264
- storeType: type,
1526
+ // Remove the index so new ones get assigned during deserialization
1527
+ delete cookie.creationIndex;
1265
1528
 
1266
- // CookieJar configuration:
1267
- rejectPublicSuffixes: !!this.rejectPublicSuffixes,
1529
+ return cookie;
1530
+ });
1268
1531
 
1269
- // this gets filled from getAllCookies:
1270
- cookies: []
1271
- };
1532
+ return cb(null, serialized);
1533
+ });
1534
+ }
1272
1535
 
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'));
1536
+ toJSON() {
1537
+ return this.serializeSync();
1277
1538
  }
1278
1539
 
1279
- this.store.getAllCookies(function(err,cookies) {
1280
- if (err) {
1281
- return cb(err);
1540
+ // use the class method CookieJar.deserialize instead of calling this directly
1541
+ _importCookies(serialized, cb) {
1542
+ let cookies = serialized.cookies;
1543
+ if (!cookies || !Array.isArray(cookies)) {
1544
+ return cb(new Error("serialized jar has no cookies array"));
1282
1545
  }
1546
+ cookies = cookies.slice(); // do not modify the original
1283
1547
 
1284
- serialized.cookies = cookies.map(function(cookie) {
1285
- // convert to serialized 'raw' cookies
1286
- cookie = (cookie instanceof Cookie) ? cookie.toJSON() : cookie;
1548
+ const putNext = err => {
1549
+ if (err) {
1550
+ return cb(err);
1551
+ }
1287
1552
 
1288
- // Remove the index so new ones get assigned during deserialization
1289
- delete cookie.creationIndex;
1553
+ if (!cookies.length) {
1554
+ return cb(err, this);
1555
+ }
1290
1556
 
1291
- return cookie;
1292
- });
1557
+ let cookie;
1558
+ try {
1559
+ cookie = fromJSON(cookies.shift());
1560
+ } catch (e) {
1561
+ return cb(e);
1562
+ }
1293
1563
 
1294
- return cb(null, serialized);
1295
- });
1296
- };
1564
+ if (cookie === null) {
1565
+ return putNext(null); // skip this cookie
1566
+ }
1297
1567
 
1298
- // well-known name that JSON.stringify calls
1299
- CookieJar.prototype.toJSON = function() {
1300
- return this.serializeSync();
1301
- };
1568
+ this.store.putCookie(cookie, putNext);
1569
+ };
1302
1570
 
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'));
1571
+ putNext();
1310
1572
  }
1311
- cookies = cookies.slice(); // do not modify the original
1312
1573
 
1313
- function putNext(err) {
1314
- if (err) {
1315
- return cb(err);
1574
+ clone(newStore, cb) {
1575
+ if (arguments.length === 1) {
1576
+ cb = newStore;
1577
+ newStore = null;
1316
1578
  }
1317
1579
 
1318
- if (!cookies.length) {
1319
- return cb(err, jar);
1320
- }
1580
+ this.serialize((err, serialized) => {
1581
+ if (err) {
1582
+ return cb(err);
1583
+ }
1584
+ CookieJar.deserialize(serialized, newStore, cb);
1585
+ });
1586
+ }
1321
1587
 
1322
- var cookie;
1323
- try {
1324
- cookie = fromJSON(cookies.shift());
1325
- } catch (e) {
1326
- return cb(e);
1588
+ cloneSync(newStore) {
1589
+ if (arguments.length === 0) {
1590
+ return this._cloneSync();
1327
1591
  }
1328
-
1329
- if (cookie === null) {
1330
- return putNext(null); // skip this cookie
1592
+ if (!newStore.synchronous) {
1593
+ throw new Error(
1594
+ "CookieJar clone destination store is not synchronous; use async API instead."
1595
+ );
1331
1596
  }
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;
1597
+ return this._cloneSync(newStore);
1344
1598
  }
1345
1599
 
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
- }
1600
+ removeAllCookies(cb) {
1601
+ validators.validate(validators.isFunction(cb), cb);
1602
+ const store = this.store;
1355
1603
 
1356
- var jar = new CookieJar(store, serialized.rejectPublicSuffixes);
1357
- jar._importCookies(serialized, function(err) {
1358
- if (err) {
1359
- return cb(err);
1604
+ // Check that the store implements its own removeAllCookies(). The default
1605
+ // implementation in Store will immediately call the callback with a "not
1606
+ // implemented" Error.
1607
+ if (
1608
+ typeof store.removeAllCookies === "function" &&
1609
+ store.removeAllCookies !== Store.prototype.removeAllCookies
1610
+ ) {
1611
+ return store.removeAllCookies(cb);
1360
1612
  }
1361
- cb(null, jar);
1362
- });
1363
- };
1364
1613
 
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);
1614
+ store.getAllCookies((err, cookies) => {
1615
+ if (err) {
1616
+ return cb(err);
1617
+ }
1369
1618
 
1370
- // catch this mistake early:
1371
- if (!jar.store.synchronous) {
1372
- throw new Error('CookieJar store is not synchronous; use async API instead.');
1373
- }
1619
+ if (cookies.length === 0) {
1620
+ return cb(null);
1621
+ }
1374
1622
 
1375
- jar._importCookiesSync(serialized);
1376
- return jar;
1377
- };
1378
- CookieJar.fromJSON = CookieJar.deserializeSync;
1623
+ let completedCount = 0;
1624
+ const removeErrors = [];
1379
1625
 
1380
- CookieJar.prototype.clone = function(newStore, cb) {
1381
- if (arguments.length === 1) {
1382
- cb = newStore;
1383
- newStore = null;
1384
- }
1626
+ function removeCookieCb(removeErr) {
1627
+ if (removeErr) {
1628
+ removeErrors.push(removeErr);
1629
+ }
1385
1630
 
1386
- this.serialize(function(err,serialized) {
1387
- if (err) {
1388
- return cb(err);
1389
- }
1390
- CookieJar.deserialize(serialized, newStore, cb);
1391
- });
1392
- };
1631
+ completedCount++;
1393
1632
 
1394
- CAN_BE_SYNC.push('removeAllCookies');
1395
- CookieJar.prototype.removeAllCookies = function(cb) {
1396
- var store = this.store;
1633
+ if (completedCount === cookies.length) {
1634
+ return cb(removeErrors.length ? removeErrors[0] : null);
1635
+ }
1636
+ }
1397
1637
 
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);
1638
+ cookies.forEach(cookie => {
1639
+ store.removeCookie(
1640
+ cookie.domain,
1641
+ cookie.path,
1642
+ cookie.key,
1643
+ removeCookieCb
1644
+ );
1645
+ });
1646
+ });
1405
1647
  }
1406
1648
 
1407
- store.getAllCookies(function(err, cookies) {
1408
- if (err) {
1409
- return cb(err);
1649
+ static deserialize(strOrObj, store, cb) {
1650
+ if (arguments.length !== 3) {
1651
+ // store is optional
1652
+ cb = store;
1653
+ store = null;
1410
1654
  }
1655
+ validators.validate(validators.isFunction(cb), cb);
1411
1656
 
1412
- if (cookies.length === 0) {
1413
- return cb(null);
1657
+ let serialized;
1658
+ if (typeof strOrObj === "string") {
1659
+ serialized = jsonParse(strOrObj);
1660
+ if (serialized instanceof Error) {
1661
+ return cb(serialized);
1662
+ }
1663
+ } else {
1664
+ serialized = strOrObj;
1414
1665
  }
1415
1666
 
1416
- var completedCount = 0;
1417
- var removeErrors = [];
1418
-
1419
- function removeCookieCb(removeErr) {
1420
- if (removeErr) {
1421
- removeErrors.push(removeErr);
1667
+ const jar = new CookieJar(store, {
1668
+ rejectPublicSuffixes: serialized.rejectPublicSuffixes,
1669
+ looseMode: serialized.enableLooseMode,
1670
+ allowSpecialUseDomain: serialized.allowSpecialUseDomain,
1671
+ prefixSecurity: serialized.prefixSecurity
1672
+ });
1673
+ jar._importCookies(serialized, err => {
1674
+ if (err) {
1675
+ return cb(err);
1422
1676
  }
1677
+ cb(null, jar);
1678
+ });
1679
+ }
1423
1680
 
1424
- completedCount++;
1681
+ static deserializeSync(strOrObj, store) {
1682
+ const serialized =
1683
+ typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj;
1684
+ const jar = new CookieJar(store, {
1685
+ rejectPublicSuffixes: serialized.rejectPublicSuffixes,
1686
+ looseMode: serialized.enableLooseMode
1687
+ });
1425
1688
 
1426
- if (completedCount === cookies.length) {
1427
- return cb(removeErrors.length ? removeErrors[0] : null);
1428
- }
1689
+ // catch this mistake early:
1690
+ if (!jar.store.synchronous) {
1691
+ throw new Error(
1692
+ "CookieJar store is not synchronous; use async API instead."
1693
+ );
1429
1694
  }
1430
1695
 
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.');
1696
+ jar._importCookiesSync(serialized);
1697
+ return jar;
1441
1698
  }
1442
- return this._cloneSync(newStore);
1443
- };
1699
+ }
1700
+ CookieJar.fromJSON = CookieJar.deserializeSync;
1701
+
1702
+ [
1703
+ "_importCookies",
1704
+ "clone",
1705
+ "getCookies",
1706
+ "getCookieString",
1707
+ "getSetCookieStrings",
1708
+ "removeAllCookies",
1709
+ "serialize",
1710
+ "setCookie"
1711
+ ].forEach(name => {
1712
+ CookieJar.prototype[name] = fromCallback(CookieJar.prototype[name]);
1713
+ });
1714
+ CookieJar.deserialize = fromCallback(CookieJar.deserialize);
1444
1715
 
1445
1716
  // Use a closure to provide a true imperative API for synchronous stores.
1446
1717
  function syncWrap(method) {
1447
- return function() {
1718
+ return function(...args) {
1448
1719
  if (!this.store.synchronous) {
1449
- throw new Error('CookieJar store is not synchronous; use async API instead.');
1720
+ throw new Error(
1721
+ "CookieJar store is not synchronous; use async API instead."
1722
+ );
1450
1723
  }
1451
1724
 
1452
- var args = Array.prototype.slice.call(arguments);
1453
- var syncErr, syncResult;
1454
- args.push(function syncCb(err, result) {
1725
+ let syncErr, syncResult;
1726
+ this[method](...args, (err, result) => {
1455
1727
  syncErr = err;
1456
1728
  syncResult = result;
1457
1729
  });
1458
- this[method].apply(this, args);
1459
1730
 
1460
1731
  if (syncErr) {
1461
1732
  throw syncErr;
@@ -1464,11 +1735,6 @@ function syncWrap(method) {
1464
1735
  };
1465
1736
  }
1466
1737
 
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
1738
  exports.version = VERSION;
1473
1739
  exports.CookieJar = CookieJar;
1474
1740
  exports.Cookie = Cookie;
@@ -1483,6 +1749,8 @@ exports.defaultPath = defaultPath;
1483
1749
  exports.pathMatch = pathMatch;
1484
1750
  exports.getPublicSuffix = pubsuffix.getPublicSuffix;
1485
1751
  exports.cookieCompare = cookieCompare;
1486
- exports.permuteDomain = require('./permuteDomain').permuteDomain;
1752
+ exports.permuteDomain = require("./permuteDomain").permuteDomain;
1487
1753
  exports.permutePath = permutePath;
1488
1754
  exports.canonicalDomain = canonicalDomain;
1755
+ exports.PrefixSecurityEnum = PrefixSecurityEnum;
1756
+ exports.ParameterError = validators.ParameterError;