tough-cookie
Advanced tools
Comparing version 4.0.0 to 4.1.0
/*! | ||
* Copyright (c) 2015, Salesforce.com, Inc. | ||
* Copyright (c) 2015-2020, Salesforce.com, Inc. | ||
* All rights reserved. | ||
@@ -32,5 +32,4 @@ * | ||
"use strict"; | ||
const punycode = require("punycode"); | ||
const urlParse = require("url").parse; | ||
const util = require("util"); | ||
const punycode = require("punycode/"); | ||
const urlParse = require("url-parse"); | ||
const pubsuffix = require("./pubsuffix-psl"); | ||
@@ -40,4 +39,6 @@ const Store = require("./store").Store; | ||
const pathMatch = require("./pathMatch").pathMatch; | ||
const validators = require("./validators.js"); | ||
const VERSION = require("./version"); | ||
const { fromCallback } = require("universalify"); | ||
const { getCustomInspectSymbol } = require("./utilHelper"); | ||
@@ -84,2 +85,3 @@ // From RFC6265 S4.1.1 | ||
function checkSameSiteContext(value) { | ||
validators.validate(validators.isNonEmptyString(value), value); | ||
const context = String(value).toLowerCase(); | ||
@@ -103,3 +105,19 @@ if (context === "none" || context === "lax" || context === "strict") { | ||
// * lowercase hexadecimal only | ||
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}|:)))$)/; | ||
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}|:)))$)/; | ||
const IP_V6_REGEX = ` | ||
\\[?(?: | ||
(?:[a-fA-F\\d]{1,4}:){7}(?:[a-fA-F\\d]{1,4}|:)| | ||
(?:[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}|:)| | ||
(?:[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}|:)| | ||
(?:[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}|:)| | ||
(?:[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}|:)| | ||
(?:[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}|:)| | ||
(?:[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}|:)| | ||
(?::(?:(?::[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}|:)) | ||
)(?:%[0-9a-zA-Z]{1,})?\\]? | ||
` | ||
.replace(/\s*\/\/.*$/gm, "") | ||
.replace(/\n/g, "") | ||
.trim(); | ||
const IP_V6_REGEX_OBJECT = new RegExp(`^${IP_V6_REGEX}$`); | ||
@@ -308,2 +326,3 @@ /* | ||
function formatDate(date) { | ||
validators.validate(validators.isDate(date), date); | ||
return date.toUTCString(); | ||
@@ -319,2 +338,6 @@ } | ||
if (IP_V6_REGEX_OBJECT.test(str)) { | ||
str = str.replace("[", "").replace("]", ""); | ||
} | ||
// convert to IDN if any non-ASCII characters | ||
@@ -354,3 +377,3 @@ if (punycode && /[^\u0001-\u007f]/.test(str)) { | ||
/* "* The domain string is a suffix of the string" */ | ||
const idx = str.indexOf(domStr); | ||
const idx = str.lastIndexOf(domStr); | ||
if (idx <= 0) { | ||
@@ -369,3 +392,3 @@ return false; // it's a non-match (-1) or prefix (0) | ||
* domain string is a %x2E (".") character." */ | ||
if (str.substr(idx-1,1) !== '.') { | ||
if (str.substr(idx - 1, 1) !== ".") { | ||
return false; // doesn't align on "." | ||
@@ -414,2 +437,3 @@ } | ||
function trimTerminator(str) { | ||
if (validators.isEmptyString(str)) return str; | ||
for (let t = 0; t < TERMINATORS.length; t++) { | ||
@@ -427,2 +451,3 @@ const terminatorIdx = str.indexOf(TERMINATORS[t]); | ||
cookiePair = trimTerminator(cookiePair); | ||
validators.validate(validators.isString(cookiePair), cookiePair); | ||
@@ -467,2 +492,7 @@ let firstEq = cookiePair.indexOf("="); | ||
} | ||
if (validators.isEmptyString(str) || !validators.isString(str)) { | ||
return null; | ||
} | ||
str = str.trim(); | ||
@@ -603,7 +633,7 @@ | ||
break; | ||
case "none": | ||
c.sameSite = "none"; | ||
break; | ||
default: | ||
// RFC6265bis-02 S5.3.7 step 1: | ||
// "If cookie-av's attribute-value is not a case-insensitive match | ||
// for "Strict" or "Lax", ignore the "cookie-av"." | ||
// This effectively sets it to 'none' from the prototype. | ||
c.sameSite = undefined; | ||
break; | ||
@@ -631,2 +661,3 @@ } | ||
function isSecurePrefixConditionMet(cookie) { | ||
validators.validate(validators.isObject(cookie), cookie); | ||
return !cookie.key.startsWith("__Secure-") || cookie.secure; | ||
@@ -647,2 +678,3 @@ } | ||
function isHostPrefixConditionMet(cookie) { | ||
validators.validate(validators.isObject(cookie)); | ||
return ( | ||
@@ -669,3 +701,3 @@ !cookie.key.startsWith("__Host-") || | ||
function fromJSON(str) { | ||
if (!str) { | ||
if (!str || validators.isEmptyString(str)) { | ||
return null; | ||
@@ -716,2 +748,4 @@ } | ||
function cookieCompare(a, b) { | ||
validators.validate(validators.isObject(a), a); | ||
validators.validate(validators.isObject(b), b); | ||
let cmp = 0; | ||
@@ -744,2 +778,3 @@ | ||
function permutePath(path) { | ||
validators.validate(validators.isString(path)); | ||
if (path === "/") { | ||
@@ -792,3 +827,3 @@ return ["/"]; | ||
lastAccessed: null, | ||
sameSite: "none" | ||
sameSite: undefined | ||
}; | ||
@@ -798,4 +833,5 @@ | ||
constructor(options = {}) { | ||
if (util.inspect.custom) { | ||
this[util.inspect.custom] = this.inspect; | ||
const customInspectSymbol = getCustomInspectSymbol(); | ||
if (customInspectSymbol) { | ||
this[customInspectSymbol] = this.inspect; | ||
} | ||
@@ -1082,2 +1118,3 @@ | ||
} | ||
validators.validate(validators.isObject(options), options); | ||
this.rejectPublicSuffixes = options.rejectPublicSuffixes; | ||
@@ -1099,5 +1136,12 @@ this.enableLooseMode = !!options.looseMode; | ||
setCookie(cookie, url, options, cb) { | ||
validators.validate(validators.isNonEmptyString(url), cb, options); | ||
let err; | ||
if (validators.isFunction(url)) { | ||
cb = url; | ||
return cb(new Error("No URL was specified")); | ||
} | ||
const context = getCookieContext(url); | ||
if (typeof options === "function") { | ||
if (validators.isFunction(options)) { | ||
cb = options; | ||
@@ -1107,2 +1151,13 @@ options = {}; | ||
validators.validate(validators.isFunction(cb), cb); | ||
if ( | ||
!validators.isNonEmptyString(cookie) && | ||
!validators.isObject(cookie) && | ||
cookie instanceof String && | ||
cookie.length == 0 | ||
) { | ||
return cb(null); | ||
} | ||
const host = canonicalDomain(context.hostname); | ||
@@ -1144,4 +1199,7 @@ const loose = options.loose || this.enableLooseMode; | ||
if (this.rejectPublicSuffixes && cookie.domain) { | ||
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain()); | ||
if (suffix == null) { | ||
const suffix = pubsuffix.getPublicSuffix(cookie.cdomain(), { | ||
allowSpecialUseDomain: this.allowSpecialUseDomain, | ||
ignoreError: options.ignoreError | ||
}); | ||
if (suffix == null && !IP_V6_REGEX_OBJECT.test(cookie.domain)) { | ||
// e.g. "com" | ||
@@ -1189,3 +1247,7 @@ err = new Error("Cookie has domain set to a public suffix"); | ||
// 6252bis-02 S5.4 Step 13 & 14: | ||
if (cookie.sameSite !== "none" && sameSiteContext) { | ||
if ( | ||
cookie.sameSite !== "none" && | ||
cookie.sameSite !== undefined && | ||
sameSiteContext | ||
) { | ||
// "If the cookie's "same-site-flag" is not "None", and the cookie | ||
@@ -1276,7 +1338,10 @@ // is being set from a context whose "site for cookies" is not an | ||
getCookies(url, options, cb) { | ||
validators.validate(validators.isNonEmptyString(url), cb, url); | ||
const context = getCookieContext(url); | ||
if (typeof options === "function") { | ||
if (validators.isFunction(options)) { | ||
cb = options; | ||
options = {}; | ||
} | ||
validators.validate(validators.isObject(options), cb, options); | ||
validators.validate(validators.isFunction(cb), cb); | ||
@@ -1397,2 +1462,3 @@ const host = canonicalDomain(context.hostname); | ||
const cb = args.pop(); | ||
validators.validate(validators.isFunction(cb), cb); | ||
const next = function(err, cookies) { | ||
@@ -1417,2 +1483,3 @@ if (err) { | ||
const cb = args.pop(); | ||
validators.validate(validators.isFunction(cb), cb); | ||
const next = function(err, cookies) { | ||
@@ -1435,4 +1502,5 @@ if (err) { | ||
serialize(cb) { | ||
validators.validate(validators.isFunction(cb), cb); | ||
let type = this.store.constructor.name; | ||
if (type === "Object") { | ||
if (validators.isObject(type)) { | ||
type = null; | ||
@@ -1453,2 +1521,5 @@ } | ||
rejectPublicSuffixes: !!this.rejectPublicSuffixes, | ||
enableLooseMode: !!this.enableLooseMode, | ||
allowSpecialUseDomain: !!this.allowSpecialUseDomain, | ||
prefixSecurity: getNormalizedPrefixSecurity(this.prefixSecurity), | ||
@@ -1556,2 +1627,3 @@ // this gets filled from getAllCookies: | ||
removeAllCookies(cb) { | ||
validators.validate(validators.isFunction(cb), cb); | ||
const store = this.store; | ||
@@ -1610,2 +1682,3 @@ | ||
} | ||
validators.validate(validators.isFunction(cb), cb); | ||
@@ -1622,3 +1695,8 @@ let serialized; | ||
const jar = new CookieJar(store, serialized.rejectPublicSuffixes); | ||
const jar = new CookieJar(store, { | ||
rejectPublicSuffixes: serialized.rejectPublicSuffixes, | ||
looseMode: serialized.enableLooseMode, | ||
allowSpecialUseDomain: serialized.allowSpecialUseDomain, | ||
prefixSecurity: serialized.prefixSecurity | ||
}); | ||
jar._importCookies(serialized, err => { | ||
@@ -1635,3 +1713,6 @@ if (err) { | ||
typeof strOrObj === "string" ? JSON.parse(strOrObj) : strOrObj; | ||
const jar = new CookieJar(store, serialized.rejectPublicSuffixes); | ||
const jar = new CookieJar(store, { | ||
rejectPublicSuffixes: serialized.rejectPublicSuffixes, | ||
looseMode: serialized.enableLooseMode | ||
}); | ||
@@ -1705,1 +1786,2 @@ // catch this mistake early: | ||
exports.PrefixSecurityEnum = PrefixSecurityEnum; | ||
exports.ParameterError = validators.ParameterError; |
@@ -36,3 +36,3 @@ /*! | ||
const pathMatch = require("./pathMatch").pathMatch; | ||
const util = require("util"); | ||
const { getCustomInspectSymbol, getUtilInspect } = require("./utilHelper"); | ||
@@ -44,4 +44,5 @@ class MemoryCookieStore extends Store { | ||
this.idx = {}; | ||
if (util.inspect.custom) { | ||
this[util.inspect.custom] = this.inspect; | ||
const customInspectSymbol = getCustomInspectSymbol(); | ||
if (customInspectSymbol) { | ||
this[customInspectSymbol] = this.inspect; | ||
} | ||
@@ -51,2 +52,3 @@ } | ||
inspect() { | ||
const util = { inspect: getUtilInspect(inspectFallback) }; | ||
return `{ idx: ${util.inspect(this.idx, false, 2)} }`; | ||
@@ -190,5 +192,55 @@ } | ||
].forEach(name => { | ||
MemoryCookieStore[name] = fromCallback(MemoryCookieStore.prototype[name]); | ||
MemoryCookieStore.prototype[name] = fromCallback( | ||
MemoryCookieStore.prototype[name] | ||
); | ||
}); | ||
exports.MemoryCookieStore = MemoryCookieStore; | ||
function inspectFallback(val) { | ||
const domains = Object.keys(val); | ||
if (domains.length === 0) { | ||
return "{}"; | ||
} | ||
let result = "{\n"; | ||
Object.keys(val).forEach((domain, i) => { | ||
result += formatDomain(domain, val[domain]); | ||
if (i < domains.length - 1) { | ||
result += ","; | ||
} | ||
result += "\n"; | ||
}); | ||
result += "}"; | ||
return result; | ||
} | ||
function formatDomain(domainName, domainValue) { | ||
const indent = " "; | ||
let result = `${indent}'${domainName}': {\n`; | ||
Object.keys(domainValue).forEach((path, i, paths) => { | ||
result += formatPath(path, domainValue[path]); | ||
if (i < paths.length - 1) { | ||
result += ","; | ||
} | ||
result += "\n"; | ||
}); | ||
result += `${indent}}`; | ||
return result; | ||
} | ||
function formatPath(pathName, pathValue) { | ||
const indent = " "; | ||
let result = `${indent}'${pathName}': {\n`; | ||
Object.keys(pathValue).forEach((cookieName, i, cookieNames) => { | ||
const cookie = pathValue[cookieName]; | ||
result += ` ${cookieName}: ${cookie.inspect()}`; | ||
if (i < cookieNames.length - 1) { | ||
result += ","; | ||
} | ||
result += "\n"; | ||
}); | ||
result += `${indent}}`; | ||
return result; | ||
} | ||
exports.inspectFallback = inspectFallback; |
@@ -36,17 +36,7 @@ /*! | ||
// array is in shortest-to-longest order. Handy for indexing. | ||
const SPECIAL_USE_DOMAINS = ["local"]; // RFC 6761 | ||
function permuteDomain(domain, allowSpecialUseDomain) { | ||
let pubSuf = null; | ||
if (allowSpecialUseDomain) { | ||
const domainParts = domain.split("."); | ||
if (SPECIAL_USE_DOMAINS.includes(domainParts[domainParts.length - 1])) { | ||
pubSuf = `${domainParts[domainParts.length - 2]}.${ | ||
domainParts[domainParts.length - 1] | ||
}`; | ||
} else { | ||
pubSuf = pubsuffix.getPublicSuffix(domain); | ||
} | ||
} else { | ||
pubSuf = pubsuffix.getPublicSuffix(domain); | ||
} | ||
const pubSuf = pubsuffix.getPublicSuffix(domain, { | ||
allowSpecialUseDomain: allowSpecialUseDomain | ||
}); | ||
@@ -60,2 +50,7 @@ if (!pubSuf) { | ||
// Nuke trailing dot | ||
if (domain.slice(-1) == ".") { | ||
domain = domain.slice(0, -1); | ||
} | ||
const prefix = domain.slice(0, -(pubSuf.length + 1)); // ".example.com" | ||
@@ -62,0 +57,0 @@ const parts = prefix.split(".").reverse(); |
@@ -34,3 +34,35 @@ /*! | ||
function getPublicSuffix(domain) { | ||
// RFC 6761 | ||
const SPECIAL_USE_DOMAINS = [ | ||
"local", | ||
"example", | ||
"invalid", | ||
"localhost", | ||
"test" | ||
]; | ||
function getPublicSuffix(domain, options = {}) { | ||
const domainParts = domain.split("."); | ||
const topLevelDomain = domainParts[domainParts.length - 1]; | ||
const allowSpecialUseDomain = !!options.allowSpecialUseDomain; | ||
const ignoreError = !!options.ignoreError; | ||
if ( | ||
allowSpecialUseDomain && | ||
domainParts.length > 1 && | ||
SPECIAL_USE_DOMAINS.includes(topLevelDomain) | ||
) { | ||
// If the right-most label in the name is a special-use domain (e.g. bananas.apple.localhost), | ||
// then don't use PSL. This is because most special-use domains are not listed on PSL. | ||
const secondLevelDomain = domainParts[domainParts.length - 2]; | ||
// In aforementioned example, the eTLD/pubSuf will be apple.localhost | ||
return `${secondLevelDomain}.${topLevelDomain}`; | ||
} | ||
if (!ignoreError && SPECIAL_USE_DOMAINS.includes(topLevelDomain)) { | ||
throw new Error( | ||
`Cookie has domain set to the public suffix "${topLevelDomain}" which is a special use domain. To allow this, configure your CookieJar with {allowSpecialUseDomain:true, rejectPublicSuffixes: false}.` | ||
); | ||
} | ||
return psl.get(domain); | ||
@@ -37,0 +69,0 @@ } |
// generated by genversion | ||
module.exports = '4.0.0' | ||
module.exports = '4.1.0' |
@@ -70,3 +70,3 @@ { | ||
], | ||
"version": "4.0.0", | ||
"version": "4.1.0", | ||
"homepage": "https://github.com/salesforce/tough-cookie", | ||
@@ -86,3 +86,3 @@ "repository": { | ||
"version": "genversion lib/version.js && git add lib/version.js", | ||
"test": "vows test/*_test.js", | ||
"test": "vows test/*_test.js && npm run eslint", | ||
"cover": "nyc --reporter=lcov --reporter=html vows test/*_test.js", | ||
@@ -109,4 +109,5 @@ "eslint": "eslint --env node --ext .js .", | ||
"punycode": "^2.1.1", | ||
"universalify": "^0.1.2" | ||
"universalify": "^0.2.0", | ||
"url-parse": "^1.5.3" | ||
} | ||
} |
492
README.md
@@ -1,3 +0,5 @@ | ||
[RFC6265](https://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js | ||
# tough-cookie | ||
[RFC 6265](https://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js | ||
[![npm package](https://nodei.co/npm/tough-cookie.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/tough-cookie/) | ||
@@ -7,185 +9,196 @@ | ||
# Synopsis | ||
## Synopsis | ||
``` javascript | ||
var tough = require('tough-cookie'); | ||
```javascript | ||
var tough = require("tough-cookie"); | ||
var Cookie = tough.Cookie; | ||
var cookie = Cookie.parse(header); | ||
cookie.value = 'somethingdifferent'; | ||
cookie.value = "somethingdifferent"; | ||
header = cookie.toString(); | ||
var cookiejar = new tough.CookieJar(); | ||
var cookiejar = new tough.CookieJar(); | ||
cookiejar.setCookie(cookie, 'http://currentdomain.example.com/path', cb); | ||
// ... | ||
cookiejar.getCookies('http://example.com/otherpath',function(err,cookies) { | ||
res.headers['cookie'] = cookies.join('; '); | ||
// Asynchronous! | ||
var cookie = await cookiejar.setCookie( | ||
cookie, | ||
"https://currentdomain.example.com/path" | ||
); | ||
var cookies = await cookiejar.getCookies("https://example.com/otherpath"); | ||
// Or with callbacks! | ||
cookiejar.setCookie( | ||
cookie, | ||
"https://currentdomain.example.com/path", | ||
function (err, cookie) { | ||
/* ... */ | ||
} | ||
); | ||
cookiejar.getCookies("http://example.com/otherpath", function (err, cookies) { | ||
/* ... */ | ||
}); | ||
``` | ||
# Installation | ||
Why the name? NPM modules `cookie`, `cookies` and `cookiejar` were already taken. | ||
It's _so_ easy! | ||
## Installation | ||
`npm install tough-cookie` | ||
It's _so_ easy! Install with `npm` or your preferred package manager. | ||
Why the name? NPM modules `cookie`, `cookies` and `cookiejar` were already taken. | ||
```sh | ||
npm install tough-cookie | ||
``` | ||
## Version Support | ||
## Node.js Version Support | ||
Support for versions of node.js will follow that of the [request](https://www.npmjs.com/package/request) module. | ||
We follow the [node.js release schedule](https://github.com/nodejs/Release#release-schedule) and support all versions that are in Active LTS or Maintenance. We will always do a major release when dropping support for older versions of node, and we will do so in consultation with our community. | ||
# API | ||
## API | ||
## tough | ||
### tough | ||
Functions on the module you get from `require('tough-cookie')`. All can be used as pure functions and don't need to be "bound". | ||
The top-level exports from `require('tough-cookie')` can all be used as pure functions and don't need to be bound. | ||
**Note**: prior to 1.0.x, several of these functions took a `strict` parameter. This has since been removed from the API as it was no longer necessary. | ||
#### `parseDate(string)` | ||
### `parseDate(string)` | ||
Parse a cookie date string into a `Date`. Parses according to [RFC 6265 Section 5.1.1](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.1), not `Date.parse()`. | ||
Parse a cookie date string into a `Date`. Parses according to RFC6265 Section 5.1.1, not `Date.parse()`. | ||
#### `formatDate(date)` | ||
### `formatDate(date)` | ||
Format a `Date` into an [RFC 822](https://datatracker.ietf.org/doc/html/rfc822#section-5) string (the RFC 6265 recommended format). | ||
Format a Date into a RFC1123 string (the RFC6265-recommended format). | ||
#### `canonicalDomain(str)` | ||
### `canonicalDomain(str)` | ||
Transforms a domain name into a canonical domain name. The canonical domain name is a domain name that has been trimmed, lowercased, stripped of leading dot, and optionally punycode-encoded ([Section 5.1.2 of RFC 6265](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.2)). For the most part, this function is idempotent (calling the function with the output from a previous call returns the same output). | ||
Transforms a domain-name into a canonical domain-name. The canonical domain-name is a trimmed, lowercased, stripped-of-leading-dot and optionally punycode-encoded domain-name (Section 5.1.2 of RFC6265). For the most part, this function is idempotent (can be run again on its output without ill effects). | ||
#### `domainMatch(str, domStr[, canonicalize=true])` | ||
### `domainMatch(str,domStr[,canonicalize=true])` | ||
Answers "does this real domain match the domain in a cookie?". The `str` is the "current" domain name and the `domStr` is the "cookie" domain name. Matches according to [RFC 6265 Section 5.1.3](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3), but it helps to think of it as a "suffix match". | ||
Answers "does this real domain match the domain in a cookie?". The `str` is the "current" domain-name and the `domStr` is the "cookie" domain-name. Matches according to RFC6265 Section 5.1.3, but it helps to think of it as a "suffix match". | ||
The `canonicalize` parameter toggles whether the domain parameters get normalized with `canonicalDomain` or not. | ||
The `canonicalize` parameter will run the other two parameters through `canonicalDomain` or not. | ||
#### `defaultPath(path)` | ||
### `defaultPath(path)` | ||
Given a current request/response path, gives the path appropriate for storing in a cookie. This is basically the "directory" of a "file" in the path, but is specified by [Section 5.1.4 of the RFC](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4). | ||
Given a current request/response path, gives the Path apropriate for storing in a cookie. This is basically the "directory" of a "file" in the path, but is specified by Section 5.1.4 of the RFC. | ||
The `path` parameter MUST be _only_ the pathname part of a URI (excluding the hostname, query, fragment, and so on). This is the `.pathname` property of node's `uri.parse()` output. | ||
The `path` parameter MUST be _only_ the pathname part of a URI (i.e. excludes the hostname, query, fragment, etc.). This is the `.pathname` property of node's `uri.parse()` output. | ||
#### `pathMatch(reqPath, cookiePath)` | ||
### `pathMatch(reqPath,cookiePath)` | ||
Answers "does the request-path path-match a given cookie-path?" as per [RFC 6265 Section 5.1.4](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4). Returns a boolean. | ||
Answers "does the request-path path-match a given cookie-path?" as per RFC6265 Section 5.1.4. Returns a boolean. | ||
This is essentially a prefix-match where `cookiePath` is a prefix of `reqPath`. | ||
### `parse(cookieString[, options])` | ||
#### `parse(cookieString[, options])` | ||
alias for `Cookie.parse(cookieString[, options])` | ||
Alias for [`Cookie.parse(cookieString[, options])`](#cookieparsecookiestring-options). | ||
### `fromJSON(string)` | ||
#### `fromJSON(string)` | ||
alias for `Cookie.fromJSON(string)` | ||
Alias for [`Cookie.fromJSON(string)`](#cookiefromjsonstrorobj). | ||
### `getPublicSuffix(hostname)` | ||
#### `getPublicSuffix(hostname)` | ||
Returns the public suffix of this hostname. The public suffix is the shortest domain-name upon which a cookie can be set. Returns `null` if the hostname cannot have cookies set for it. | ||
Returns the public suffix of this hostname. The public suffix is the shortest domain name upon which a cookie can be set. Returns `null` if the hostname cannot have cookies set for it. | ||
For example: `www.example.com` and `www.subdomain.example.com` both have public suffix `example.com`. | ||
For further information, see http://publicsuffix.org/. This module derives its list from that site. This call is currently a wrapper around [`psl`](https://www.npmjs.com/package/psl)'s [get() method](https://www.npmjs.com/package/psl#pslgetdomain). | ||
For further information, see the [Public Suffix List](http://publicsuffix.org/). This module derives its list from that site. This call is a wrapper around [`psl`](https://www.npmjs.com/package/psl)'s [`get` method](https://www.npmjs.com/package/psl##pslgetdomain). | ||
### `cookieCompare(a,b)` | ||
#### `cookieCompare(a, b)` | ||
For use with `.sort()`, sorts a list of cookies into the recommended order given in the RFC (Section 5.4 step 2). The sort algorithm is, in order of precedence: | ||
For use with `.sort()`, sorts a list of cookies into the recommended order given in step 2 of ([RFC 6265 Section 5.4](https://datatracker.ietf.org/doc/html/rfc6265#section-5.4)). The sort algorithm is, in order of precedence: | ||
* Longest `.path` | ||
* oldest `.creation` (which has a 1ms precision, same as `Date`) | ||
* lowest `.creationIndex` (to get beyond the 1ms precision) | ||
- Longest `.path` | ||
- oldest `.creation` (which has a 1-ms precision, same as `Date`) | ||
- lowest `.creationIndex` (to get beyond the 1-ms precision) | ||
``` javascript | ||
var cookies = [ /* unsorted array of Cookie objects */ ]; | ||
```javascript | ||
var cookies = [ | ||
/* unsorted array of Cookie objects */ | ||
]; | ||
cookies = cookies.sort(cookieCompare); | ||
``` | ||
**Note**: Since JavaScript's `Date` is limited to a 1ms precision, cookies within the same milisecond are entirely possible. This is especially true when using the `now` option to `.setCookie()`. The `.creationIndex` property is a per-process global counter, assigned during construction with `new Cookie()`. This preserves the spirit of the RFC sorting: older cookies go first. This works great for `MemoryCookieStore`, since `Set-Cookie` headers are parsed in order, but may not be so great for distributed systems. Sophisticated `Store`s may wish to set this to some other _logical clock_ such that if cookies A and B are created in the same millisecond, but cookie A is created before cookie B, then `A.creationIndex < B.creationIndex`. If you want to alter the global counter, which you probably _shouldn't_ do, it's stored in `Cookie.cookiesCreated`. | ||
> **Note**: Since the JavaScript `Date` is limited to a 1-ms precision, cookies within the same millisecond are entirely possible. This is especially true when using the `now` option to `.setCookie()`. The `.creationIndex` property is a per-process global counter, assigned during construction with `new Cookie()`, which preserves the spirit of the RFC sorting: older cookies go first. This works great for `MemoryCookieStore` since `Set-Cookie` headers are parsed in order, but is not so great for distributed systems. Sophisticated `Store`s may wish to set this to some other _logical clock_ so that if cookies A and B are created in the same millisecond, but cookie A is created before cookie B, then `A.creationIndex < B.creationIndex`. If you want to alter the global counter, which you probably _shouldn't_ do, it's stored in `Cookie.cookiesCreated`. | ||
### `permuteDomain(domain)` | ||
#### `permuteDomain(domain)` | ||
Generates a list of all possible domains that `domainMatch()` the parameter. May be handy for implementing cookie stores. | ||
Generates a list of all possible domains that `domainMatch()` the parameter. Can be handy for implementing cookie stores. | ||
### `permutePath(path)` | ||
#### `permutePath(path)` | ||
Generates a list of all possible paths that `pathMatch()` the parameter. May be handy for implementing cookie stores. | ||
Generates a list of all possible paths that `pathMatch()` the parameter. Can be handy for implementing cookie stores. | ||
### Cookie | ||
## Cookie | ||
Exported via `tough.Cookie`. | ||
### `Cookie.parse(cookieString[, options])` | ||
#### `Cookie.parse(cookieString[, options])` | ||
Parses a single Cookie or Set-Cookie HTTP header into a `Cookie` object. Returns `undefined` if the string can't be parsed. | ||
Parses a single Cookie or Set-Cookie HTTP header into a `Cookie` object. Returns `undefined` if the string can't be parsed. | ||
The options parameter is not required and currently has only one property: | ||
* _loose_ - boolean - if `true` enable parsing of key-less cookies like `=abc` and `=`, which are not RFC-compliant. | ||
- _loose_ - boolean - if `true` enable parsing of keyless cookies like `=abc` and `=`, which are not RFC-compliant. | ||
If options is not an object, it is ignored, which means you can use `Array#map` with it. | ||
If options is not an object it is ignored, which means it can be used with [`Array#map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). | ||
Here's how to process the Set-Cookie header(s) on a node HTTP/HTTPS response: | ||
To process the Set-Cookie header(s) on a node HTTP/HTTPS response: | ||
``` javascript | ||
if (res.headers['set-cookie'] instanceof Array) | ||
cookies = res.headers['set-cookie'].map(Cookie.parse); | ||
else | ||
cookies = [Cookie.parse(res.headers['set-cookie'])]; | ||
```javascript | ||
if (Array.isArray(res.headers["set-cookie"])) | ||
cookies = res.headers["set-cookie"].map(Cookie.parse); | ||
else cookies = [Cookie.parse(res.headers["set-cookie"])]; | ||
``` | ||
_Note:_ in version 2.3.3, tough-cookie limited the number of spaces before the `=` to 256 characters. This limitation has since been removed. | ||
See [Issue 92](https://github.com/salesforce/tough-cookie/issues/92) | ||
_Note:_ In version 2.3.3, tough-cookie limited the number of spaces before the `=` to 256 characters. This limitation was removed in version 2.3.4. | ||
For more details, see [issue #92](https://github.com/salesforce/tough-cookie/issues/92). | ||
### Properties | ||
#### Properties | ||
Cookie object properties: | ||
* _key_ - string - the name or key of the cookie (default "") | ||
* _value_ - string - the value of the cookie (default "") | ||
* _expires_ - `Date` - if set, the `Expires=` attribute of the cookie (defaults to the string `"Infinity"`). See `setExpires()` | ||
* _maxAge_ - seconds - if set, the `Max-Age=` attribute _in seconds_ of the cookie. May also be set to strings `"Infinity"` and `"-Infinity"` for non-expiry and immediate-expiry, respectively. See `setMaxAge()` | ||
* _domain_ - string - the `Domain=` attribute of the cookie | ||
* _path_ - string - the `Path=` of the cookie | ||
* _secure_ - boolean - the `Secure` cookie flag | ||
* _httpOnly_ - boolean - the `HttpOnly` cookie flag | ||
* _sameSite_ - string - the `SameSite` cookie attribute (from [RFC6265bis]); must be one of `none`, `lax`, or `strict` | ||
* _extensions_ - `Array` - any unrecognized cookie attributes as strings (even if equal-signs inside) | ||
* _creation_ - `Date` - when this cookie was constructed | ||
* _creationIndex_ - number - set at construction, used to provide greater sort precision (please see `cookieCompare(a,b)` for a full explanation) | ||
- _key_ - string - the name or key of the cookie (default `""`) | ||
- _value_ - string - the value of the cookie (default `""`) | ||
- _expires_ - `Date` - if set, the `Expires=` attribute of the cookie (defaults to the string `"Infinity"`). See `setExpires()` | ||
- _maxAge_ - seconds - if set, the `Max-Age=` attribute _in seconds_ of the cookie. Can also be set to strings `"Infinity"` and `"-Infinity"` for non-expiry and immediate-expiry, respectively. See `setMaxAge()` | ||
- _domain_ - string - the `Domain=` attribute of the cookie | ||
- _path_ - string - the `Path=` of the cookie | ||
- _secure_ - boolean - the `Secure` cookie flag | ||
- _httpOnly_ - boolean - the `HttpOnly` cookie flag | ||
- _sameSite_ - string - the `SameSite` cookie attribute (from [RFC 6265bis](#rfc-6265bis)); must be one of `none`, `lax`, or `strict` | ||
- _extensions_ - `Array` - any unrecognized cookie attributes as strings (even if equal-signs inside) | ||
- _creation_ - `Date` - when this cookie was constructed | ||
- _creationIndex_ - number - set at construction, used to provide greater sort precision (see `cookieCompare(a,b)` for a full explanation) | ||
After a cookie has been passed through `CookieJar.setCookie()` it will have the following additional attributes: | ||
After a cookie has been passed through `CookieJar.setCookie()` it has the following additional attributes: | ||
* _hostOnly_ - boolean - is this a host-only cookie (i.e. no Domain field was set, but was instead implied) | ||
* _pathIsDefault_ - boolean - if true, there was no Path field on the cookie and `defaultPath()` was used to derive one. | ||
* _creation_ - `Date` - **modified** from construction to when the cookie was added to the jar | ||
* _lastAccessed_ - `Date` - last time the cookie got accessed. Will affect cookie cleaning once implemented. Using `cookiejar.getCookies(...)` will update this attribute. | ||
- _hostOnly_ - boolean - is this a host-only cookie (that is, no Domain field was set, but was instead implied). | ||
- _pathIsDefault_ - boolean - if true, there was no Path field on the cookie and `defaultPath()` was used to derive one. | ||
- _creation_ - `Date` - **modified** from construction to when the cookie was added to the jar. | ||
- _lastAccessed_ - `Date` - last time the cookie got accessed. Affects cookie cleaning after it is implemented. Using `cookiejar.getCookies(...)` updates this attribute. | ||
### `Cookie([{properties}])` | ||
#### `new Cookie([properties])` | ||
Receives an options object that can contain any of the above Cookie properties, uses the default for unspecified properties. | ||
Receives an options object that can contain any of the above Cookie properties. Uses the default for unspecified properties. | ||
### `.toString()` | ||
#### `.toString()` | ||
encode to a Set-Cookie header value. The Expires cookie field is set using `formatDate()`, but is omitted entirely if `.expires` is `Infinity`. | ||
Encodes to a Set-Cookie header value. The Expires cookie field is set using `formatDate()`, but is omitted entirely if `.expires` is `Infinity`. | ||
### `.cookieString()` | ||
#### `.cookieString()` | ||
encode to a Cookie header value (i.e. the `.key` and `.value` properties joined with '='). | ||
Encodes to a Cookie header value (specifically, the `.key` and `.value` properties joined with `"="`). | ||
### `.setExpires(String)` | ||
#### `.setExpires(string)` | ||
sets the expiry based on a date-string passed through `parseDate()`. If parseDate returns `null` (i.e. can't parse this date string), `.expires` is set to `"Infinity"` (a string) is set. | ||
Sets the expiry based on a date-string passed through `parseDate()`. If parseDate returns `null` (that is, can't parse this date string), `.expires` is set to `"Infinity"` (a string). | ||
### `.setMaxAge(number)` | ||
#### `.setMaxAge(number)` | ||
sets the maxAge in seconds. Coerces `-Infinity` to `"-Infinity"` and `Infinity` to `"Infinity"` so it JSON serializes correctly. | ||
Sets the maxAge in seconds. Coerces `-Infinity` to `"-Infinity"` and `Infinity` to `"Infinity"` so it correctly serializes to JSON. | ||
### `.expiryTime([now=Date.now()])` | ||
#### `.expiryDate([now=Date.now()])` | ||
### `.expiryDate([now=Date.now()])` | ||
`expiryTime()` computes the absolute unix-epoch milliseconds that this cookie expires. `expiryDate()` works similarly, except it returns a `Date` object. Note that in both cases the `now` parameter should be milliseconds. | ||
expiryTime() Computes the absolute unix-epoch milliseconds that this cookie expires. expiryDate() works similarly, except it returns a `Date` object. Note that in both cases the `now` parameter should be milliseconds. | ||
Max-Age takes precedence over Expires (as per the RFC). The `.creation` attribute -- or, by default, the `now` parameter -- is used to offset the `.maxAge` attribute. | ||
@@ -197,41 +210,41 @@ | ||
### `.TTL([now=Date.now()])` | ||
#### `.TTL([now=Date.now()])` | ||
compute the TTL relative to `now` (milliseconds). The same precedence rules as for `expiryTime`/`expiryDate` apply. | ||
Computes the TTL relative to `now` (milliseconds). The same precedence rules as for `expiryTime`/`expiryDate` apply. | ||
The "number" `Infinity` is returned for cookies without an explicit expiry and `0` is returned if the cookie is expired. Otherwise a time-to-live in milliseconds is returned. | ||
`Infinity` is returned for cookies without an explicit expiry and `0` is returned if the cookie is expired. Otherwise a time-to-live in milliseconds is returned. | ||
### `.canonicalizedDomain()` | ||
#### `.canonicalizedDomain()` | ||
### `.cdomain()` | ||
#### `.cdomain()` | ||
return the canonicalized `.domain` field. This is lower-cased and punycode (RFC3490) encoded if the domain has any non-ASCII characters. | ||
Returns the canonicalized `.domain` field. This is lower-cased and punycode ([RFC 3490](https://datatracker.ietf.org/doc/html/rfc3490)) encoded if the domain has any non-ASCII characters. | ||
### `.toJSON()` | ||
#### `.toJSON()` | ||
For convenience in using `JSON.serialize(cookie)`. Returns a plain-old `Object` that can be JSON-serialized. | ||
Any `Date` properties (i.e., `.expires`, `.creation`, and `.lastAccessed`) are exported in ISO format (`.toISOString()`). | ||
Any `Date` properties (such as `.expires`, `.creation`, and `.lastAccessed`) are exported in ISO format (`.toISOString()`). | ||
**NOTE**: Custom `Cookie` properties will be discarded. In tough-cookie 1.x, since there was no `.toJSON` method explicitly defined, all enumerable properties were captured. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array. | ||
> **NOTE**: Custom `Cookie` properties are discarded. In tough-cookie 1.x, since there was no `.toJSON` method explicitly defined, all enumerable properties were captured. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array. | ||
### `Cookie.fromJSON(strOrObj)` | ||
#### `Cookie.fromJSON(strOrObj)` | ||
Does the reverse of `cookie.toJSON()`. If passed a string, will `JSON.parse()` that first. | ||
Any `Date` properties (i.e., `.expires`, `.creation`, and `.lastAccessed`) are parsed via `Date.parse()`, not the tough-cookie `parseDate`, since it's JavaScript/JSON-y timestamps being handled at this layer. | ||
Any `Date` properties (such as `.expires`, `.creation`, and `.lastAccessed`) are parsed via [`Date.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse), not tough-cookie's `parseDate`, since ISO timestamps are being handled at this layer. | ||
Returns `null` upon JSON parsing error. | ||
Returns `null` upon a JSON parsing error. | ||
### `.clone()` | ||
#### `.clone()` | ||
Does a deep clone of this cookie, exactly implemented as `Cookie.fromJSON(cookie.toJSON())`. | ||
Does a deep clone of this cookie, implemented exactly as `Cookie.fromJSON(cookie.toJSON())`. | ||
### `.validate()` | ||
#### `.validate()` | ||
Status: *IN PROGRESS*. Works for a few things, but is by no means comprehensive. | ||
Status: _IN PROGRESS_. Works for a few things, but is by no means comprehensive. | ||
validates cookie attributes for semantic correctness. Useful for "lint" checking any Set-Cookie headers you generate. For now, it returns a boolean, but eventually could return a reason string -- you can future-proof with this construct: | ||
Validates cookie attributes for semantic correctness. Useful for "lint" checking any Set-Cookie headers you generate. For now, it returns a boolean, but eventually could return a reason string. Future-proof with this construct: | ||
``` javascript | ||
```javascript | ||
if (cookie.validate() === true) { | ||
@@ -244,117 +257,119 @@ // it's tasty | ||
### CookieJar | ||
## CookieJar | ||
Exported via `tough.CookieJar`. | ||
### `CookieJar([store],[options])` | ||
#### `CookieJar([store][, options])` | ||
Simply use `new CookieJar()`. If you'd like to use a custom store, pass that to the constructor otherwise a `MemoryCookieStore` will be created and used. | ||
Simply use `new CookieJar()`. If a custom store is not passed to the constructor, a [`MemoryCookieStore`](#memorycookiestore) is created and used. | ||
The `options` object can be omitted and can have the following properties: | ||
* _rejectPublicSuffixes_ - boolean - default `true` - reject cookies with domains like "com" and "co.uk" | ||
* _looseMode_ - boolean - default `false` - accept malformed cookies like `bar` and `=bar`, which have an implied empty name. | ||
* _prefixSecurity_ - string - default `silent` - set to `'unsafe-disabled'`, `'silent'`, or `'strict'`. See [Cookie Prefixes] below. | ||
* _allowSpecialUseDomain_ - boolean - default `false` - accepts special-use domain suffixes, such as `local`. Useful for testing purposes. | ||
This is not in the standard, but is used sometimes on the web and is accepted by (most) browsers. | ||
- _rejectPublicSuffixes_ - boolean - default `true` - reject cookies with domains like "com" and "co.uk" | ||
- _looseMode_ - boolean - default `false` - accept malformed cookies like `bar` and `=bar`, which have an implied empty name. | ||
- _prefixSecurity_ - string - default `silent` - set to `'unsafe-disabled'`, `'silent'`, or `'strict'`. See [Cookie Prefixes](#cookie-prefixes) below. | ||
- _allowSpecialUseDomain_ - boolean - default `false` - accepts special-use domain suffixes, such as `local`. Useful for testing purposes. | ||
This is not in the standard, but is used sometimes on the web and is accepted by most browsers. | ||
Since eventually this module would like to support database/remote/etc. CookieJars, continuation passing style is used for CookieJar methods. | ||
#### `.setCookie(cookieOrString, currentUrl[, options][, callback(err, cookie)])` | ||
### `.setCookie(cookieOrString, currentUrl, [{options},] cb(err,cookie))` | ||
Attempt to set the cookie in the cookie jar. The cookie has updated `.creation`, `.lastAccessed` and `.hostOnly` properties. And returns a promise if a callback is not provided. | ||
Attempt to set the cookie in the cookie jar. If the operation fails, an error will be given to the callback `cb`, otherwise the cookie is passed through. The cookie will have updated `.creation`, `.lastAccessed` and `.hostOnly` properties. | ||
The `options` object can be omitted and can have the following properties: | ||
* _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects HttpOnly cookies. | ||
* _secure_ - boolean - autodetect from url - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`. | ||
* _now_ - Date - default `new Date()` - what to use for the creation/access time of cookies | ||
* _ignoreError_ - boolean - default `false` - silently ignore things like parse errors and invalid domains. `Store` errors aren't ignored by this option. | ||
* _sameSiteContext_ - string - default unset - set to `'none'`, `'lax'`, or `'strict'` See [SameSite Cookies] below. | ||
- _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects `HttpOnly` cookies. | ||
- _secure_ - boolean - autodetect from URL - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` this defaults to `true`, otherwise `false`. | ||
- _now_ - Date - default `new Date()` - what to use for the creation or access time of cookies. | ||
- _ignoreError_ - boolean - default `false` - silently ignore things like parse errors and invalid domains. `Store` errors aren't ignored by this option. | ||
- _sameSiteContext_ - string - default unset - set to `'none'`, `'lax'`, or `'strict'` See [SameSite Cookies](#samesite-cookies) below. | ||
As per the RFC, the `.hostOnly` property is set if there was no "Domain=" parameter in the cookie string (or `.domain` was null on the Cookie object). The `.domain` property is set to the fully-qualified hostname of `currentUrl` in this case. Matching this cookie requires an exact hostname match (not a `domainMatch` as per usual). | ||
As per the RFC, the `.hostOnly` property is set if there was no "Domain=" parameter in the cookie string (or `.domain` was null on the Cookie object). The `.domain` property is set to the fully-qualified hostname of `currentUrl` in this case. Matching this cookie requires an exact hostname match (not a `domainMatch` as per usual). | ||
### `.setCookieSync(cookieOrString, currentUrl, [{options}])` | ||
#### `.setCookieSync(cookieOrString, currentUrl[, options])` | ||
Synchronous version of `setCookie`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
Synchronous version of [`setCookie`](#setcookiecookieorstring-currenturl-options-callbackerr-cookie); only works with synchronous stores (that is, the default `MemoryCookieStore`). | ||
### `.getCookies(currentUrl, [{options},] cb(err,cookies))` | ||
#### `.getCookies(currentUrl[, options][, callback(err, cookies)])` | ||
Retrieve the list of cookies that can be sent in a Cookie header for the current url. | ||
Retrieve the list of cookies that can be sent in a Cookie header for the current URL. Returns a promise if a callback is not provided. | ||
If an error is encountered, that's passed as `err` to the callback, otherwise an `Array` of `Cookie` objects is passed. The array is sorted with `cookieCompare()` unless the `{sort:false}` option is given. | ||
Returns an array of `Cookie` objects, sorted by default using [`cookieCompare`](#cookiecomparea-b). | ||
If an error is encountered it's passed as `err` to the callback, otherwise an array of `Cookie` objects is passed. The array is sorted with `cookieCompare()` unless the `{sort:false}` option is given. | ||
The `options` object can be omitted and can have the following properties: | ||
* _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects HttpOnly cookies. | ||
* _secure_ - boolean - autodetect from url - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`. | ||
* _now_ - Date - default `new Date()` - what to use for the creation/access time of cookies | ||
* _expire_ - boolean - default `true` - perform expiry-time checking of cookies and asynchronously remove expired cookies from the store. Using `false` will return expired cookies and **not** remove them from the store (which is useful for replaying Set-Cookie headers, potentially). | ||
* _allPaths_ - boolean - default `false` - if `true`, do not scope cookies by path. The default uses RFC-compliant path scoping. **Note**: may not be supported by the underlying store (the default `MemoryCookieStore` supports it). | ||
* _sameSiteContext_ - string - default unset - Set this to `'none'`, `'lax'` or `'strict'` to enforce SameSite cookies upon retrival. See [SameSite Cookies] below. | ||
- _http_ - boolean - default `true` - indicates if this is an HTTP or non-HTTP API. Affects `HttpOnly` cookies. | ||
- _secure_ - boolean - autodetect from URL - indicates if this is a "Secure" API. If the currentUrl starts with `https:` or `wss:` then this is defaulted to `true`, otherwise `false`. | ||
- _now_ - Date - default `new Date()` - what to use for the creation or access time of cookies | ||
- _expire_ - boolean - default `true` - perform expiry-time checking of cookies and asynchronously remove expired cookies from the store. Using `false` returns expired cookies and does **not** remove them from the store (which is potentially useful for replaying Set-Cookie headers). | ||
- _allPaths_ - boolean - default `false` - if `true`, do not scope cookies by path. The default uses RFC-compliant path scoping. **Note**: may not be supported by the underlying store (the default `MemoryCookieStore` supports it). | ||
- _sameSiteContext_ - string - default unset - Set this to `'none'`, `'lax'`, or `'strict'` to enforce SameSite cookies upon retrieval. See [SameSite Cookies](#samesite-cookies) below. | ||
- _sort_ - boolean - whether to sort the list of cookies. | ||
The `.lastAccessed` property of the returned cookies will have been updated. | ||
### `.getCookiesSync(currentUrl, [{options}])` | ||
#### `.getCookiesSync(currentUrl, [{options}])` | ||
Synchronous version of `getCookies`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
Synchronous version of [`getCookies`](#getcookiescurrenturl-options-callbackerr-cookies); only works with synchronous stores (for example, the default `MemoryCookieStore`). | ||
### `.getCookieString(...)` | ||
#### `.getCookieString(...)` | ||
Accepts the same options as `.getCookies()` but passes a string suitable for a Cookie header rather than an array to the callback. Simply maps the `Cookie` array via `.cookieString()`. | ||
Accepts the same options as [`.getCookies()`](#getcookiescurrenturl-options-callbackerr-cookies) but returns a string suitable for a Cookie header rather than an Array. | ||
### `.getCookieStringSync(...)` | ||
#### `.getCookieStringSync(...)` | ||
Synchronous version of `getCookieString`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
Synchronous version of [`getCookieString`](#getcookiestring); only works with synchronous stores (for example, the default `MemoryCookieStore`). | ||
### `.getSetCookieStrings(...)` | ||
#### `.getSetCookieStrings(...)` | ||
Returns an array of strings suitable for **Set-Cookie** headers. Accepts the same options as `.getCookies()`. Simply maps the cookie array via `.toString()`. | ||
Returns an array of strings suitable for **Set-Cookie** headers. Accepts the same options as [`.getCookies()`](#getcookiescurrenturl-options-callbackerr-cookies). Simply maps the cookie array via `.toString()`. | ||
### `.getSetCookieStringsSync(...)` | ||
#### `.getSetCookieStringsSync(...)` | ||
Synchronous version of `getSetCookieStrings`; only works with synchronous stores (e.g. the default `MemoryCookieStore`). | ||
Synchronous version of [`getSetCookieStrings`](#getsetcookiestrings); only works with synchronous stores (for example, the default `MemoryCookieStore`). | ||
### `.serialize(cb(err,serializedObject))` | ||
#### `.serialize([callback(err, serializedObject)])` | ||
Returns a promise if a callback is not provided. | ||
Serialize the Jar if the underlying store supports `.getAllCookies`. | ||
**NOTE**: Custom `Cookie` properties will be discarded. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array. | ||
> **NOTE**: Custom `Cookie` properties are discarded. If you want a property to be serialized, add the property name to the `Cookie.serializableProperties` Array. | ||
See [Serialization Format]. | ||
See [Serialization Format](#serialization-format). | ||
### `.serializeSync()` | ||
#### `.serializeSync()` | ||
Sync version of .serialize | ||
Synchronous version of [`serialize`](#serializecallbackerr-serializedobject); only works with synchronous stores (for example, the default `MemoryCookieStore`). | ||
### `.toJSON()` | ||
#### `.toJSON()` | ||
Alias of .serializeSync() for the convenience of `JSON.stringify(cookiejar)`. | ||
Alias of [`.serializeSync()`](#serializesync) for the convenience of `JSON.stringify(cookiejar)`. | ||
### `CookieJar.deserialize(serialized, [store], cb(err,object))` | ||
#### `CookieJar.deserialize(serialized[, store][, callback(err, object)])` | ||
A new Jar is created and the serialized Cookies are added to the underlying store. Each `Cookie` is added via `store.putCookie` in the order in which they appear in the serialization. | ||
A new Jar is created and the serialized Cookies are added to the underlying store. Each `Cookie` is added via `store.putCookie` in the order in which they appear in the serialization. A promise is returned if a callback is not provided. | ||
The `store` argument is optional, but should be an instance of `Store`. By default, a new instance of `MemoryCookieStore` is created. | ||
As a convenience, if `serialized` is a string, it is passed through `JSON.parse` first. If that throws an error, this is passed to the callback. | ||
As a convenience, if `serialized` is a string, it is passed through `JSON.parse` first. | ||
### `CookieJar.deserializeSync(serialized, [store])` | ||
#### `CookieJar.deserializeSync(serialized[, store])` | ||
Sync version of `.deserialize`. _Note_ that the `store` must be synchronous for this to work. | ||
Sync version of [`.deserialize`](#cookiejardeserializeserialized-store-callbackerr-object); only works with synchronous stores (for example, the default `MemoryCookieStore`). | ||
### `CookieJar.fromJSON(string)` | ||
#### `CookieJar.fromJSON(string)` | ||
Alias of `.deserializeSync` to provide consistency with `Cookie.fromJSON()`. | ||
Alias of [`.deserializeSync`](#cookiejardeserializesyncserialized-store) to provide consistency with [`Cookie.fromJSON()`](#cookiefromjsonstrorobj). | ||
### `.clone([store,]cb(err,newJar))` | ||
#### `.clone([store][, callback(err, cloned))` | ||
Produces a deep clone of this jar. Modifications to the original won't affect the clone, and vice versa. | ||
Produces a deep clone of this jar. Modifications to the original do not affect the clone, and vice versa. Returns a promise if a callback is not provided. | ||
The `store` argument is optional, but should be an instance of `Store`. By default, a new instance of `MemoryCookieStore` is created. Transferring between store types is supported so long as the source implements `.getAllCookies()` and the destination implements `.putCookie()`. | ||
### `.cloneSync([store])` | ||
#### `.cloneSync([store])` | ||
Synchronous version of `.clone`, returning a new `CookieJar` instance. | ||
Synchronous version of [`.clone`](#clonestore-callbackerr-cloned), returning a new `CookieJar` instance. | ||
@@ -365,59 +380,59 @@ The `store` argument is optional, but must be a _synchronous_ `Store` instance if specified. If not passed, a new instance of `MemoryCookieStore` is used. | ||
### `.removeAllCookies(cb(err))` | ||
#### `.removeAllCookies([callback(err)])` | ||
Removes all cookies from the jar. | ||
Removes all cookies from the jar. Returns a promise if a callback is not provided. | ||
This is a new backwards-compatible feature of `tough-cookie` version 2.5, so not all Stores will implement it efficiently. For Stores that do not implement `removeAllCookies`, the fallback is to call `removeCookie` after `getAllCookies`. If `getAllCookies` fails or isn't implemented in the Store, that error is returned. If one or more of the `removeCookie` calls fail, only the first error is returned. | ||
### `.removeAllCookiesSync()` | ||
#### `.removeAllCookiesSync()` | ||
Sync version of `.removeAllCookies()` | ||
Sync version of [`.removeAllCookies()`](#removeallcookiescallbackerr); only works with synchronous stores (for example, the default `MemoryCookieStore`). | ||
## Store | ||
### Store | ||
Base class for CookieJar stores. Available as `tough.Store`. | ||
## Store API | ||
### Store API | ||
The storage model for each `CookieJar` instance can be replaced with a custom implementation. The default is `MemoryCookieStore` which can be found in the `lib/memstore.js` file. The API uses continuation-passing-style to allow for asynchronous stores. | ||
The storage model for each `CookieJar` instance can be replaced with a custom implementation. The default is `MemoryCookieStore` which can be found in [`lib/memstore.js`](https://github.com/salesforce/tough-cookie/blob/master/lib/memstore.js). The API uses continuation-passing-style to allow for asynchronous stores. | ||
Stores should inherit from the base `Store` class, which is available as `require('tough-cookie').Store`. | ||
Stores should inherit from the base `Store` class, which is available as a top-level export. | ||
Stores are asynchronous by default, but if `store.synchronous` is set to `true`, then the `*Sync` methods on the of the containing `CookieJar` can be used (however, the continuation-passing style | ||
Stores are asynchronous by default, but if `store.synchronous` is set to `true`, then the `*Sync` methods of the containing `CookieJar` can be used. | ||
All `domain` parameters will have been normalized before calling. | ||
All `domain` parameters are normalized before calling. | ||
The Cookie store must have all of the following methods. | ||
The Cookie store must have all of the following methods. Note that asynchronous implementations **must** support callback parameters. | ||
### `store.findCookie(domain, path, key, cb(err,cookie))` | ||
#### `store.findCookie(domain, path, key, callback(err, cookie))` | ||
Retrieve a cookie with the given domain, path and key (a.k.a. name). The RFC maintains that exactly one of these cookies should exist in a store. If the store is using versioning, this means that the latest/newest such cookie should be returned. | ||
Retrieve a cookie with the given domain, path, and key (name). The RFC maintains that exactly one of these cookies should exist in a store. If the store is using versioning, this means that the latest or newest such cookie should be returned. | ||
Callback takes an error and the resulting `Cookie` object. If no cookie is found then `null` MUST be passed instead (i.e. not an error). | ||
Callback takes an error and the resulting `Cookie` object. If no cookie is found then `null` MUST be passed instead (that is, not an error). | ||
### `store.findCookies(domain, path, cb(err,cookies))` | ||
#### `store.findCookies(domain, path, callback(err, cookies))` | ||
Locates cookies matching the given domain and path. This is most often called in the context of `cookiejar.getCookies()` above. | ||
Locates cookies matching the given domain and path. This is most often called in the context of [`cookiejar.getCookies()`](#getcookiescurrenturl-options-callbackerr-cookies). | ||
If no cookies are found, the callback MUST be passed an empty array. | ||
The resulting list will be checked for applicability to the current request according to the RFC (domain-match, path-match, http-only-flag, secure-flag, expiry, etc.), so it's OK to use an optimistic search algorithm when implementing this method. However, the search algorithm used SHOULD try to find cookies that `domainMatch()` the domain and `pathMatch()` the path in order to limit the amount of checking that needs to be done. | ||
The resulting list is checked for applicability to the current request according to the RFC (domain-match, path-match, http-only-flag, secure-flag, expiry, and so on), so it's OK to use an optimistic search algorithm when implementing this method. However, the search algorithm used SHOULD try to find cookies that `domainMatch()` the domain and `pathMatch()` the path in order to limit the amount of checking that needs to be done. | ||
As of version 0.9.12, the `allPaths` option to `cookiejar.getCookies()` above will cause the path here to be `null`. If the path is `null`, path-matching MUST NOT be performed (i.e. domain-matching only). | ||
As of version 0.9.12, the `allPaths` option to `cookiejar.getCookies()` above causes the path here to be `null`. If the path is `null`, path-matching MUST NOT be performed (that is, domain-matching only). | ||
### `store.putCookie(cookie, cb(err))` | ||
#### `store.putCookie(cookie, callback(err))` | ||
Adds a new cookie to the store. The implementation SHOULD replace any existing cookie with the same `.domain`, `.path`, and `.key` properties -- depending on the nature of the implementation, it's possible that between the call to `fetchCookie` and `putCookie` that a duplicate `putCookie` can occur. | ||
Adds a new cookie to the store. The implementation SHOULD replace any existing cookie with the same `.domain`, `.path`, and `.key` properties. Depending on the nature of the implementation, it's possible that between the call to `fetchCookie` and `putCookie` that a duplicate `putCookie` can occur. | ||
The `cookie` object MUST NOT be modified; the caller will have already updated the `.creation` and `.lastAccessed` properties. | ||
The `cookie` object MUST NOT be modified; as the caller has already updated the `.creation` and `.lastAccessed` properties. | ||
Pass an error if the cookie cannot be stored. | ||
### `store.updateCookie(oldCookie, newCookie, cb(err))` | ||
#### `store.updateCookie(oldCookie, newCookie, callback(err))` | ||
Update an existing cookie. The implementation MUST update the `.value` for a cookie with the same `domain`, `.path` and `.key`. The implementation SHOULD check that the old value in the store is equivalent to `oldCookie` - how the conflict is resolved is up to the store. | ||
Update an existing cookie. The implementation MUST update the `.value` for a cookie with the same `domain`, `.path`, and `.key`. The implementation SHOULD check that the old value in the store is equivalent to `oldCookie` - how the conflict is resolved is up to the store. | ||
The `.lastAccessed` property will always be different between the two objects (to the precision possible via JavaScript's clock). Both `.creation` and `.creationIndex` are guaranteed to be the same. Stores MAY ignore or defer the `.lastAccessed` change at the cost of affecting how cookies are selected for automatic deletion (e.g., least-recently-used, which is up to the store to implement). | ||
The `.lastAccessed` property is always different between the two objects (to the precision possible via JavaScript's clock). Both `.creation` and `.creationIndex` are guaranteed to be the same. Stores MAY ignore or defer the `.lastAccessed` change at the cost of affecting how cookies are selected for automatic deletion (for example, least-recently-used, which is up to the store to implement). | ||
Stores may wish to optimize changing the `.value` of the cookie in the store versus storing a new cookie. If the implementation doesn't define this method a stub that calls `putCookie(newCookie,cb)` will be added to the store object. | ||
Stores may wish to optimize changing the `.value` of the cookie in the store versus storing a new cookie. If the implementation doesn't define this method, a stub that calls [`putCookie`](#storeputcookiecookie-callbackerr) is added to the store object. | ||
@@ -428,15 +443,15 @@ The `newCookie` and `oldCookie` objects MUST NOT be modified. | ||
### `store.removeCookie(domain, path, key, cb(err))` | ||
#### `store.removeCookie(domain, path, key, callback(err))` | ||
Remove a cookie from the store (see notes on `findCookie` about the uniqueness constraint). | ||
Remove a cookie from the store (see notes on [`findCookie`](#storefindcookiedomain-path-key-callbackerr-cookie) about the uniqueness constraint). | ||
The implementation MUST NOT pass an error if the cookie doesn't exist; only pass an error due to the failure to remove an existing cookie. | ||
The implementation MUST NOT pass an error if the cookie doesn't exist, and only pass an error due to the failure to remove an existing cookie. | ||
### `store.removeCookies(domain, path, cb(err))` | ||
#### `store.removeCookies(domain, path, callback(err))` | ||
Removes matching cookies from the store. The `path` parameter is optional, and if missing means all paths in a domain should be removed. | ||
Removes matching cookies from the store. The `path` parameter is optional and if missing, means all paths in a domain should be removed. | ||
Pass an error ONLY if removing any existing cookies failed. | ||
### `store.removeAllCookies(cb(err))` | ||
#### `store.removeAllCookies(callback(err))` | ||
@@ -447,15 +462,13 @@ _Optional_. Removes all cookies from the store. | ||
**Note**: New method as of `tough-cookie` version 2.5, so not all Stores will implement this, plus some stores may choose not to implement this. | ||
#### `store.getAllCookies(callback(err, cookies))` | ||
### `store.getAllCookies(cb(err, cookies))` | ||
_Optional_. Produces an `Array` of all cookies during [`jar.serialize()`](#serializecallbackerr-serializedobject). The items in the array can be true `Cookie` objects or generic `Object`s with the [Serialization Format](#serialization-format) data structure. | ||
_Optional_. Produces an `Array` of all cookies during `jar.serialize()`. The items in the array can be true `Cookie` objects or generic `Object`s with the [Serialization Format] data structure. | ||
Cookies SHOULD be returned in creation order to preserve sorting via [`compareCookie()`](#cookiecomparea-b). For reference, `MemoryCookieStore` sorts by `.creationIndex` since it uses true `Cookie` objects internally. If you don't return the cookies in creation order, they'll still be sorted by creation time, but this only has a precision of 1-ms. See `cookieCompare` for more detail. | ||
Cookies SHOULD be returned in creation order to preserve sorting via `compareCookies()`. For reference, `MemoryCookieStore` will sort by `.creationIndex` since it uses true `Cookie` objects internally. If you don't return the cookies in creation order, they'll still be sorted by creation time, but this only has a precision of 1ms. See `compareCookies` for more detail. | ||
Pass an error if retrieval fails. | ||
**Note**: not all Stores can implement this due to technical limitations, so it is optional. | ||
**Note**: Not all Stores can implement this due to technical limitations, so it is optional. | ||
## MemoryCookieStore | ||
### MemoryCookieStore | ||
@@ -466,3 +479,3 @@ Inherits from `Store`. | ||
## Community Cookie Stores | ||
### Community Cookie Stores | ||
@@ -477,7 +490,6 @@ These are some Store implementations authored and maintained by the community. They aren't official and we don't vouch for them but you may be interested to have a look: | ||
## Serialization Format | ||
# Serialization Format | ||
**NOTE**: If you want to have custom `Cookie` properties serialized, add the property name to `Cookie.serializableProperties`. | ||
**NOTE**: if you want to have custom `Cookie` properties serialized, add the property name to `Cookie.serializableProperties`. | ||
```js | ||
@@ -507,7 +519,7 @@ { | ||
# RFC6265bis | ||
## RFC 6265bis | ||
Support for RFC6265bis revision 02 is being developed. Since this is a bit of an omnibus revision to the RFC6252, support is broken up into the functional areas. | ||
Support for RFC 6265bis revision 02 is being developed. Since this is a bit of an omnibus revision to the RFC 6252, support is broken up into the functional areas. | ||
## Leave Secure Cookies Alone | ||
### Leave Secure Cookies Alone | ||
@@ -518,3 +530,3 @@ Not yet supported. | ||
## SameSite Cookies | ||
### SameSite Cookies | ||
@@ -525,15 +537,16 @@ Supported. | ||
On the Cookie object itself, you can get/set the `.sameSite` attribute, which will be serialized into the `SameSite=` cookie attribute. When unset or `undefined`, no `SameSite=` attribute will be serialized. The valid values of this attribute are `'none'`, `'lax'`, or `'strict'`. Other values will be serialized as-is. | ||
On the Cookie object itself, you can get or set the `.sameSite` attribute, which is serialized into the `SameSite=` cookie attribute. When unset or `undefined`, no `SameSite=` attribute is serialized. The valid values of this attribute are `'none'`, `'lax'`, or `'strict'`. Other values are serialized as-is. | ||
When parsing cookies with a `SameSite` cookie attribute, values other than `'lax'` or `'strict'` are parsed as `'none'`. For example, `SomeCookie=SomeValue; SameSite=garbage` will parse so that `cookie.sameSite === 'none'`. | ||
When parsing cookies with a `SameSite` cookie attribute, values other than `'lax'` or `'strict'` are parsed as `'none'`. For example, `SomeCookie=SomeValue; SameSite=garbage` parses so that `cookie.sameSite === 'none'`. | ||
In order to support SameSite cookies, you must provide a `sameSiteContext` option to _both_ `setCookie` and `getCookies`. Valid values for this option are just like for the Cookie object, but have particular meanings: | ||
1. `'strict'` mode - If the request is on the same "site for cookies" (see the RFC draft for what this means), pass this option to add a layer of defense against CSRF. | ||
2. `'lax'` mode - If the request is from another site, _but_ is directly because of navigation by the user, e.g., `<link type=prefetch>` or `<a href="...">`, pass `sameSiteContext: 'lax'`. | ||
1. `'strict'` mode - If the request is on the same "site for cookies" (see the RFC draft for more information), pass this option to add a layer of defense against CSRF. | ||
2. `'lax'` mode - If the request is from another site, _but_ is directly because of navigation by the user, such as, `<link type=prefetch>` or `<a href="...">`, pass `sameSiteContext: 'lax'`. | ||
3. `'none'` - Otherwise, pass `sameSiteContext: 'none'` (this indicates a cross-origin request). | ||
4. unset/`undefined` - SameSite **will not** be enforced! This can be a valid use-case for when CSRF isn't in the threat model of the system being built. | ||
4. unset/`undefined` - SameSite **is not** be enforced! This can be a valid use-case for when CSRF isn't in the threat model of the system being built. | ||
It is highly recommended that you read RFC 6265bis for fine details on SameSite cookies. In particular [Section 8.8](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02#section-8.8) discusses security considerations and defense in depth. | ||
It is highly recommended that you read RFC 6265bis for fine details on SameSite cookies. In particular [Section 8.8](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02##section-8.8) discusses security considerations and defense in depth. | ||
## Cookie Prefixes | ||
### Cookie Prefixes | ||
@@ -544,20 +557,21 @@ Supported. | ||
Cookie prefixes are defined in [Section 4.1.3 of 6265bis](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.3). Two prefixes are defined: | ||
Cookie prefixes are defined in [Section 4.1.3 of 6265bis](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03##section-4.1.3). | ||
1. `"__Secure-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "__Secure-", then the cookie will have been set with a "Secure" attribute. | ||
2. `"__Host-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "__Host-", then the cookie will have been set with a "Secure" attribute, a "Path" attribute with a value of "/", and no "Domain" attribute. | ||
Two prefixes are defined: | ||
If `prefixSecurity` is enabled for `CookieJar`, then cookies that match the prefixes defined above but do not obey the attribute restrictions will not be added. | ||
1. `"__Secure-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "\_\_Secure-", then the cookie was set with a "Secure" attribute. | ||
2. `"__Host-" Prefix`: If a cookie's name begins with a case-sensitive match for the string "\_\_Host-", then the cookie was set with a "Secure" attribute, a "Path" attribute with a value of "/", and no "Domain" attribute. | ||
You can define this functionality by passing in `prefixSecurity` option to `CookieJar`. It can be one of 3 values: | ||
If `prefixSecurity` is enabled for `CookieJar`, then cookies that match the prefixes defined above but do not obey the attribute restrictions are not added. | ||
1. `silent`: Enable cookie prefix checking but silently fail to add the cookie if conditions not met. Default. | ||
2. `strict`: Enable cookie prefix checking and error out if conditions not met. | ||
You can define this functionality by passing in the `prefixSecurity` option to `CookieJar`. It can be one of 3 values: | ||
1. `silent`: Enable cookie prefix checking but silently fail to add the cookie if conditions are not met. Default. | ||
2. `strict`: Enable cookie prefix checking and error out if conditions are not met. | ||
3. `unsafe-disabled`: Disable cookie prefix checking. | ||
Note that if `ignoreError` is passed in as `true` then the error will be silent regardless of `prefixSecurity` option (assuming it's enabled). | ||
Note that if `ignoreError` is passed in as `true` then the error is silent regardless of the `prefixSecurity` option (assuming it's enabled). | ||
## Copyright and License | ||
# Copyright and License | ||
BSD-3-Clause: | ||
@@ -564,0 +578,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
110945
12
2127
597
0
4
+ Addedurl-parse@^1.5.3
+ Addedquerystringify@2.2.0(transitive)
+ Addedrequires-port@1.0.0(transitive)
+ Addeduniversalify@0.2.0(transitive)
+ Addedurl-parse@1.5.10(transitive)
- Removeduniversalify@0.1.2(transitive)
Updateduniversalify@^0.2.0