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