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