tough-cookie 3.0.1 → 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 +56 -1
- package/lib/cookie.js +996 -813
- package/lib/memstore.js +140 -131
- package/lib/pathMatch.js +2 -2
- package/lib/permuteDomain.js +22 -8
- package/lib/pubsuffix-psl.js +2 -2
- package/lib/store.js +34 -33
- package/lib/version.js +1 -1
- package/package.json +46 -16
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 (ipRegex.test(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,783 +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
|
-
|
|
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
|
+
);
|
|
1014
1110
|
return cb(options.ignoreError ? null : err);
|
|
1015
1111
|
}
|
|
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
1112
|
|
|
1024
|
-
|
|
1025
|
-
|
|
1113
|
+
// S5.3 step 2
|
|
1114
|
+
const now = options.now || new Date(); // will assign later to save effort in the face of errors
|
|
1026
1115
|
|
|
1027
|
-
|
|
1116
|
+
// S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie()
|
|
1028
1117
|
|
|
1029
|
-
|
|
1118
|
+
// S5.3 step 4: NOOP; domain is null by default
|
|
1030
1119
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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
|
+
}
|
|
1037
1128
|
}
|
|
1038
|
-
}
|
|
1039
1129
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
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
|
+
}
|
|
1046
1138
|
|
|
1047
|
-
|
|
1048
|
-
|
|
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;
|
|
1049
1146
|
}
|
|
1050
1147
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
cookie
|
|
1054
|
-
|
|
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
|
+
}
|
|
1055
1155
|
|
|
1056
|
-
|
|
1057
|
-
|
|
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
|
-
}
|
|
1156
|
+
// S5.3 step 8: NOOP; secure attribute
|
|
1157
|
+
// S5.3 step 9: NOOP; httpOnly attribute
|
|
1063
1158
|
|
|
1064
|
-
|
|
1065
|
-
|
|
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
|
+
}
|
|
1066
1164
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
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
|
+
}
|
|
1072
1178
|
|
|
1073
|
-
|
|
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
|
+
}
|
|
1074
1206
|
|
|
1075
|
-
|
|
1076
|
-
store.updateCookie = function(oldCookie, newCookie, cb) {
|
|
1077
|
-
this.putCookie(newCookie, cb);
|
|
1078
|
-
};
|
|
1079
|
-
}
|
|
1207
|
+
const store = this.store;
|
|
1080
1208
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1209
|
+
if (!store.updateCookie) {
|
|
1210
|
+
store.updateCookie = function(oldCookie, newCookie, cb) {
|
|
1211
|
+
this.putCookie(newCookie, cb);
|
|
1212
|
+
};
|
|
1084
1213
|
}
|
|
1085
1214
|
|
|
1086
|
-
|
|
1215
|
+
function withCookie(err, oldCookie) {
|
|
1087
1216
|
if (err) {
|
|
1088
1217
|
return cb(err);
|
|
1089
|
-
} else {
|
|
1090
|
-
cb(null, cookie);
|
|
1091
1218
|
}
|
|
1092
|
-
};
|
|
1093
1219
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
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
|
|
1100
1244
|
}
|
|
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
1245
|
}
|
|
1246
|
+
|
|
1247
|
+
store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie);
|
|
1111
1248
|
}
|
|
1112
1249
|
|
|
1113
|
-
|
|
1114
|
-
|
|
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
|
+
}
|
|
1115
1257
|
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
CookieJar.prototype.getCookies = function(url, options, cb) {
|
|
1119
|
-
var context = getCookieContext(url);
|
|
1120
|
-
if (options instanceof Function) {
|
|
1121
|
-
cb = options;
|
|
1122
|
-
options = {};
|
|
1123
|
-
}
|
|
1258
|
+
const host = canonicalDomain(context.hostname);
|
|
1259
|
+
const path = context.pathname || "/";
|
|
1124
1260
|
|
|
1125
|
-
|
|
1126
|
-
|
|
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
|
+
}
|
|
1127
1269
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
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
|
+
}
|
|
1134
1278
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1279
|
+
let http = options.http;
|
|
1280
|
+
if (http == null) {
|
|
1281
|
+
http = true;
|
|
1282
|
+
}
|
|
1139
1283
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
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
|
+
}
|
|
1144
1305
|
|
|
1145
|
-
|
|
1146
|
-
|
|
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) {
|
|
1306
|
+
// "The request-uri's path path-matches the cookie's path."
|
|
1307
|
+
if (!allPaths && !pathMatch(path, c.path)) {
|
|
1154
1308
|
return false;
|
|
1155
1309
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
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) {
|
|
1158
1314
|
return false;
|
|
1159
1315
|
}
|
|
1160
|
-
}
|
|
1161
1316
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
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
|
-
}
|
|
1172
|
-
|
|
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
|
-
}
|
|
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
|
+
}
|
|
1178
1322
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
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
|
+
}
|
|
1185
1331
|
|
|
1186
|
-
|
|
1187
|
-
|
|
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
|
+
}
|
|
1188
1338
|
|
|
1189
|
-
|
|
1190
|
-
if (err) {
|
|
1191
|
-
return cb(err);
|
|
1339
|
+
return true;
|
|
1192
1340
|
}
|
|
1193
1341
|
|
|
1194
|
-
|
|
1342
|
+
store.findCookies(
|
|
1343
|
+
host,
|
|
1344
|
+
allPaths ? null : path,
|
|
1345
|
+
this.allowSpecialUseDomain,
|
|
1346
|
+
(err, cookies) => {
|
|
1347
|
+
if (err) {
|
|
1348
|
+
return cb(err);
|
|
1349
|
+
}
|
|
1195
1350
|
|
|
1196
|
-
|
|
1197
|
-
if (options.sort !== false) {
|
|
1198
|
-
cookies = cookies.sort(cookieCompare);
|
|
1199
|
-
}
|
|
1351
|
+
cookies = cookies.filter(matchingCookie);
|
|
1200
1352
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
});
|
|
1206
|
-
// TODO persist lastAccessed
|
|
1353
|
+
// sorting of S5.4 part 2
|
|
1354
|
+
if (options.sort !== false) {
|
|
1355
|
+
cookies = cookies.sort(cookieCompare);
|
|
1356
|
+
}
|
|
1207
1357
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
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
|
|
1211
1364
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
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
|
-
};
|
|
1365
|
+
cb(null, cookies);
|
|
1366
|
+
}
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1231
1369
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
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
|
+
}
|
|
1248
1388
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
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);
|
|
1254
1405
|
}
|
|
1255
1406
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
version: 'tough-cookie@'+VERSION,
|
|
1407
|
+
serialize(cb) {
|
|
1408
|
+
let type = this.store.constructor.name;
|
|
1409
|
+
if (type === "Object") {
|
|
1410
|
+
type = null;
|
|
1411
|
+
}
|
|
1262
1412
|
|
|
1263
|
-
//
|
|
1264
|
-
|
|
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}`,
|
|
1265
1419
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1420
|
+
// add the store type, to make humans happy:
|
|
1421
|
+
storeType: type,
|
|
1268
1422
|
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
};
|
|
1423
|
+
// CookieJar configuration:
|
|
1424
|
+
rejectPublicSuffixes: !!this.rejectPublicSuffixes,
|
|
1272
1425
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
return cb(new Error('store does not support getAllCookies and cannot be serialized'));
|
|
1277
|
-
}
|
|
1426
|
+
// this gets filled from getAllCookies:
|
|
1427
|
+
cookies: []
|
|
1428
|
+
};
|
|
1278
1429
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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
|
+
);
|
|
1282
1441
|
}
|
|
1283
1442
|
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1443
|
+
this.store.getAllCookies((err, cookies) => {
|
|
1444
|
+
if (err) {
|
|
1445
|
+
return cb(err);
|
|
1446
|
+
}
|
|
1287
1447
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1448
|
+
serialized.cookies = cookies.map(cookie => {
|
|
1449
|
+
// convert to serialized 'raw' cookies
|
|
1450
|
+
cookie = cookie instanceof Cookie ? cookie.toJSON() : cookie;
|
|
1290
1451
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1452
|
+
// Remove the index so new ones get assigned during deserialization
|
|
1453
|
+
delete cookie.creationIndex;
|
|
1293
1454
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
};
|
|
1455
|
+
return cookie;
|
|
1456
|
+
});
|
|
1297
1457
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
};
|
|
1458
|
+
return cb(null, serialized);
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1302
1461
|
|
|
1303
|
-
|
|
1304
|
-
|
|
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'));
|
|
1462
|
+
toJSON() {
|
|
1463
|
+
return this.serializeSync();
|
|
1310
1464
|
}
|
|
1311
|
-
cookies = cookies.slice(); // do not modify the original
|
|
1312
1465
|
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
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"));
|
|
1316
1471
|
}
|
|
1472
|
+
cookies = cookies.slice(); // do not modify the original
|
|
1317
1473
|
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1474
|
+
const putNext = err => {
|
|
1475
|
+
if (err) {
|
|
1476
|
+
return cb(err);
|
|
1477
|
+
}
|
|
1321
1478
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
} catch (e) {
|
|
1326
|
-
return cb(e);
|
|
1327
|
-
}
|
|
1479
|
+
if (!cookies.length) {
|
|
1480
|
+
return cb(err, this);
|
|
1481
|
+
}
|
|
1328
1482
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1483
|
+
let cookie;
|
|
1484
|
+
try {
|
|
1485
|
+
cookie = fromJSON(cookies.shift());
|
|
1486
|
+
} catch (e) {
|
|
1487
|
+
return cb(e);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
if (cookie === null) {
|
|
1491
|
+
return putNext(null); // skip this cookie
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
this.store.putCookie(cookie, putNext);
|
|
1495
|
+
};
|
|
1332
1496
|
|
|
1333
|
-
|
|
1497
|
+
putNext();
|
|
1334
1498
|
}
|
|
1335
1499
|
|
|
1336
|
-
|
|
1337
|
-
|
|
1500
|
+
clone(newStore, cb) {
|
|
1501
|
+
if (arguments.length === 1) {
|
|
1502
|
+
cb = newStore;
|
|
1503
|
+
newStore = null;
|
|
1504
|
+
}
|
|
1338
1505
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1506
|
+
this.serialize((err, serialized) => {
|
|
1507
|
+
if (err) {
|
|
1508
|
+
return cb(err);
|
|
1509
|
+
}
|
|
1510
|
+
CookieJar.deserialize(serialized, newStore, cb);
|
|
1511
|
+
});
|
|
1344
1512
|
}
|
|
1345
1513
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
if (serialized instanceof Error) {
|
|
1350
|
-
return cb(serialized);
|
|
1514
|
+
cloneSync(newStore) {
|
|
1515
|
+
if (arguments.length === 0) {
|
|
1516
|
+
return this._cloneSync();
|
|
1351
1517
|
}
|
|
1352
|
-
|
|
1353
|
-
|
|
1518
|
+
if (!newStore.synchronous) {
|
|
1519
|
+
throw new Error(
|
|
1520
|
+
"CookieJar clone destination store is not synchronous; use async API instead."
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
return this._cloneSync(newStore);
|
|
1354
1524
|
}
|
|
1355
1525
|
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1526
|
+
removeAllCookies(cb) {
|
|
1527
|
+
const store = this.store;
|
|
1528
|
+
|
|
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);
|
|
1360
1537
|
}
|
|
1361
|
-
cb(null, jar);
|
|
1362
|
-
});
|
|
1363
|
-
};
|
|
1364
1538
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1539
|
+
store.getAllCookies((err, cookies) => {
|
|
1540
|
+
if (err) {
|
|
1541
|
+
return cb(err);
|
|
1542
|
+
}
|
|
1369
1543
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
}
|
|
1544
|
+
if (cookies.length === 0) {
|
|
1545
|
+
return cb(null);
|
|
1546
|
+
}
|
|
1374
1547
|
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
};
|
|
1378
|
-
CookieJar.fromJSON = CookieJar.deserializeSync;
|
|
1548
|
+
let completedCount = 0;
|
|
1549
|
+
const removeErrors = [];
|
|
1379
1550
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
}
|
|
1551
|
+
function removeCookieCb(removeErr) {
|
|
1552
|
+
if (removeErr) {
|
|
1553
|
+
removeErrors.push(removeErr);
|
|
1554
|
+
}
|
|
1385
1555
|
|
|
1386
|
-
|
|
1387
|
-
if (err) {
|
|
1388
|
-
return cb(err);
|
|
1389
|
-
}
|
|
1390
|
-
CookieJar.deserialize(serialized, newStore, cb);
|
|
1391
|
-
});
|
|
1392
|
-
};
|
|
1556
|
+
completedCount++;
|
|
1393
1557
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1558
|
+
if (completedCount === cookies.length) {
|
|
1559
|
+
return cb(removeErrors.length ? removeErrors[0] : null);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1397
1562
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1563
|
+
cookies.forEach(cookie => {
|
|
1564
|
+
store.removeCookie(
|
|
1565
|
+
cookie.domain,
|
|
1566
|
+
cookie.path,
|
|
1567
|
+
cookie.key,
|
|
1568
|
+
removeCookieCb
|
|
1569
|
+
);
|
|
1570
|
+
});
|
|
1571
|
+
});
|
|
1405
1572
|
}
|
|
1406
1573
|
|
|
1407
|
-
|
|
1408
|
-
if (
|
|
1409
|
-
|
|
1574
|
+
static deserialize(strOrObj, store, cb) {
|
|
1575
|
+
if (arguments.length !== 3) {
|
|
1576
|
+
// store is optional
|
|
1577
|
+
cb = store;
|
|
1578
|
+
store = null;
|
|
1410
1579
|
}
|
|
1411
1580
|
|
|
1412
|
-
|
|
1413
|
-
|
|
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;
|
|
1414
1589
|
}
|
|
1415
1590
|
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
if (removeErr) {
|
|
1421
|
-
removeErrors.push(removeErr);
|
|
1591
|
+
const jar = new CookieJar(store, serialized.rejectPublicSuffixes);
|
|
1592
|
+
jar._importCookies(serialized, err => {
|
|
1593
|
+
if (err) {
|
|
1594
|
+
return cb(err);
|
|
1422
1595
|
}
|
|
1596
|
+
cb(null, jar);
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1423
1599
|
|
|
1424
|
-
|
|
1600
|
+
static deserializeSync(strOrObj, store) {
|
|
1601
|
+
const serialized =
|
|
1602
|
+
typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj;
|
|
1603
|
+
const jar = new CookieJar(store, serialized.rejectPublicSuffixes);
|
|
1425
1604
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
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
|
+
);
|
|
1429
1610
|
}
|
|
1430
1611
|
|
|
1431
|
-
|
|
1432
|
-
|
|
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.');
|
|
1612
|
+
jar._importCookiesSync(serialized);
|
|
1613
|
+
return jar;
|
|
1441
1614
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1615
|
+
}
|
|
1616
|
+
CookieJar.fromJSON = CookieJar.deserializeSync;
|
|
1617
|
+
|
|
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);
|
|
1444
1631
|
|
|
1445
1632
|
// Use a closure to provide a true imperative API for synchronous stores.
|
|
1446
1633
|
function syncWrap(method) {
|
|
1447
|
-
return function() {
|
|
1634
|
+
return function(...args) {
|
|
1448
1635
|
if (!this.store.synchronous) {
|
|
1449
|
-
throw new Error(
|
|
1636
|
+
throw new Error(
|
|
1637
|
+
"CookieJar store is not synchronous; use async API instead."
|
|
1638
|
+
);
|
|
1450
1639
|
}
|
|
1451
1640
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
args.push(function syncCb(err, result) {
|
|
1641
|
+
let syncErr, syncResult;
|
|
1642
|
+
this[method](...args, (err, result) => {
|
|
1455
1643
|
syncErr = err;
|
|
1456
1644
|
syncResult = result;
|
|
1457
1645
|
});
|
|
1458
|
-
this[method].apply(this, args);
|
|
1459
1646
|
|
|
1460
1647
|
if (syncErr) {
|
|
1461
1648
|
throw syncErr;
|
|
@@ -1464,11 +1651,6 @@ function syncWrap(method) {
|
|
|
1464
1651
|
};
|
|
1465
1652
|
}
|
|
1466
1653
|
|
|
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
1654
|
exports.version = VERSION;
|
|
1473
1655
|
exports.CookieJar = CookieJar;
|
|
1474
1656
|
exports.Cookie = Cookie;
|
|
@@ -1483,6 +1665,7 @@ exports.defaultPath = defaultPath;
|
|
|
1483
1665
|
exports.pathMatch = pathMatch;
|
|
1484
1666
|
exports.getPublicSuffix = pubsuffix.getPublicSuffix;
|
|
1485
1667
|
exports.cookieCompare = cookieCompare;
|
|
1486
|
-
exports.permuteDomain = require(
|
|
1668
|
+
exports.permuteDomain = require("./permuteDomain").permuteDomain;
|
|
1487
1669
|
exports.permutePath = permutePath;
|
|
1488
1670
|
exports.canonicalDomain = canonicalDomain;
|
|
1671
|
+
exports.PrefixSecurityEnum = PrefixSecurityEnum;
|