tough-cookie
Advanced tools
Comparing version 2.3.3 to 2.3.4
@@ -47,26 +47,13 @@ /*! | ||
var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/; | ||
// From RFC6265 S4.1.1 | ||
// note that it excludes \x3B ";" | ||
var COOKIE_OCTET = /[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/; | ||
var COOKIE_OCTETS = new RegExp('^'+COOKIE_OCTET.source+'+$'); | ||
var COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/; | ||
var CONTROL_CHARS = /[\x00-\x1F]/; | ||
// For COOKIE_PAIR and LOOSE_COOKIE_PAIR below, the number of spaces has been | ||
// restricted to 256 to side-step a ReDoS issue reported here: | ||
// https://github.com/salesforce/tough-cookie/issues/92 | ||
// From Chromium // '\r', '\n' and '\0' should be treated as a terminator in | ||
// the "relaxed" mode, see: | ||
// https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60 | ||
var TERMINATORS = ['\n', '\r', '\0']; | ||
// Double quotes are part of the value (see: S4.1.1). | ||
// '\r', '\n' and '\0' should be treated as a terminator in the "relaxed" mode | ||
// (see: https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60) | ||
// '=' and ';' are attribute/values separators | ||
// (see: https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L64) | ||
var COOKIE_PAIR = /^(([^=;]+))\s{0,256}=\s*([^\n\r\0]*)/; | ||
// Used to parse non-RFC-compliant cookies like '=abc' when given the `loose` | ||
// option in Cookie.parse: | ||
var LOOSE_COOKIE_PAIR = /^((?:=)?([^=;]*)\s{0,256}=\s*)?([^\n\r\0]*)/; | ||
// RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"' | ||
@@ -76,6 +63,6 @@ // Note ';' is \x3B | ||
var DAY_OF_MONTH = /^(\d{1,2})[^\d]*$/; | ||
var TIME = /^(\d{1,2})[^\d]*:(\d{1,2})[^\d]*:(\d{1,2})[^\d]*$/; | ||
var MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i; | ||
// date-time parsing constants (RFC6265 S5.1.1) | ||
var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/; | ||
var MONTH_TO_NUM = { | ||
@@ -92,9 +79,76 @@ jan:0, feb:1, mar:2, apr:3, may:4, jun:5, | ||
var YEAR = /^(\d{2}|\d{4})$/; // 2 to 4 digits | ||
var MAX_TIME = 2147483647000; // 31-bit max | ||
var MIN_TIME = 0; // 31-bit min | ||
/* | ||
* Parses a Natural number (i.e., non-negative integer) with either the | ||
* <min>*<max>DIGIT ( non-digit *OCTET ) | ||
* or | ||
* <min>*<max>DIGIT | ||
* grammar (RFC6265 S5.1.1). | ||
* | ||
* The "trailingOK" boolean controls if the grammar accepts a | ||
* "( non-digit *OCTET )" trailer. | ||
*/ | ||
function parseDigits(token, minDigits, maxDigits, trailingOK) { | ||
var count = 0; | ||
while (count < token.length) { | ||
var c = token.charCodeAt(count); | ||
// "non-digit = %x00-2F / %x3A-FF" | ||
if (c <= 0x2F || c >= 0x3A) { | ||
break; | ||
} | ||
count++; | ||
} | ||
// RFC6265 S5.1.1 date parser: | ||
// constrain to a minimum and maximum number of digits. | ||
if (count < minDigits || count > maxDigits) { | ||
return null; | ||
} | ||
if (!trailingOK && count != token.length) { | ||
return null; | ||
} | ||
return parseInt(token.substr(0,count), 10); | ||
} | ||
function parseTime(token) { | ||
var parts = token.split(':'); | ||
var result = [0,0,0]; | ||
/* RF6256 S5.1.1: | ||
* time = hms-time ( non-digit *OCTET ) | ||
* hms-time = time-field ":" time-field ":" time-field | ||
* time-field = 1*2DIGIT | ||
*/ | ||
if (parts.length !== 3) { | ||
return null; | ||
} | ||
for (var i = 0; i < 3; i++) { | ||
// "time-field" must be strictly "1*2DIGIT", HOWEVER, "hms-time" can be | ||
// followed by "( non-digit *OCTET )" so therefore the last time-field can | ||
// have a trailer | ||
var trailingOK = (i == 2); | ||
var num = parseDigits(parts[i], 1, 2, trailingOK); | ||
if (num === null) { | ||
return null; | ||
} | ||
result[i] = num; | ||
} | ||
return result; | ||
} | ||
function parseMonth(token) { | ||
token = String(token).substr(0,3).toLowerCase(); | ||
var num = MONTH_TO_NUM[token]; | ||
return num >= 0 ? num : null; | ||
} | ||
/* | ||
* RFC6265 S5.1.1 date parser (see RFC for full grammar) | ||
*/ | ||
function parseDate(str) { | ||
@@ -115,5 +169,5 @@ if (!str) { | ||
var hour = null; | ||
var minutes = null; | ||
var seconds = null; | ||
var day = null; | ||
var minute = null; | ||
var second = null; | ||
var dayOfMonth = null; | ||
var month = null; | ||
@@ -136,18 +190,8 @@ var year = null; | ||
*/ | ||
if (seconds === null) { | ||
result = TIME.exec(token); | ||
if (second === null) { | ||
result = parseTime(token); | ||
if (result) { | ||
hour = parseInt(result[1], 10); | ||
minutes = parseInt(result[2], 10); | ||
seconds = parseInt(result[3], 10); | ||
/* RFC6265 S5.1.1.5: | ||
* [fail if] | ||
* * the hour-value is greater than 23, | ||
* * the minute-value is greater than 59, or | ||
* * the second-value is greater than 59. | ||
*/ | ||
if(hour > 23 || minutes > 59 || seconds > 59) { | ||
return; | ||
} | ||
hour = result[0]; | ||
minute = result[1]; | ||
second = result[2]; | ||
continue; | ||
@@ -162,12 +206,7 @@ } | ||
*/ | ||
if (day === null) { | ||
result = DAY_OF_MONTH.exec(token); | ||
if (result) { | ||
day = parseInt(result, 10); | ||
/* RFC6265 S5.1.1.5: | ||
* [fail if] the day-of-month-value is less than 1 or greater than 31 | ||
*/ | ||
if(day < 1 || day > 31) { | ||
return; | ||
} | ||
if (dayOfMonth === null) { | ||
// "day-of-month = 1*2DIGIT ( non-digit *OCTET )" | ||
result = parseDigits(token, 1, 2, true); | ||
if (result !== null) { | ||
dayOfMonth = result; | ||
continue; | ||
@@ -183,5 +222,5 @@ } | ||
if (month === null) { | ||
result = MONTH.exec(token); | ||
if (result) { | ||
month = MONTH_TO_NUM[result[1].toLowerCase()]; | ||
result = parseMonth(token); | ||
if (result !== null) { | ||
month = result; | ||
continue; | ||
@@ -191,11 +230,12 @@ } | ||
/* 2.4. If the found-year flag is not set and the date-token matches the year | ||
* production, set the found-year flag and set the year-value to the number | ||
* denoted by the date-token. Skip the remaining sub-steps and continue to | ||
* the next date-token. | ||
/* 2.4. If the found-year flag is not set and the date-token matches the | ||
* year production, set the found-year flag and set the year-value to the | ||
* number denoted by the date-token. Skip the remaining sub-steps and | ||
* continue to the next date-token. | ||
*/ | ||
if (year === null) { | ||
result = YEAR.exec(token); | ||
if (result) { | ||
year = parseInt(result[0], 10); | ||
// "year = 2*4DIGIT ( non-digit *OCTET )" | ||
result = parseDigits(token, 2, 4, true); | ||
if (result !== null) { | ||
year = result; | ||
/* From S5.1.1: | ||
@@ -207,11 +247,7 @@ * 3. If the year-value is greater than or equal to 70 and less | ||
*/ | ||
if (70 <= year && year <= 99) { | ||
if (year >= 70 && year <= 99) { | ||
year += 1900; | ||
} else if (0 <= year && year <= 69) { | ||
} else if (year >= 0 && year <= 69) { | ||
year += 2000; | ||
} | ||
if (year < 1601) { | ||
return; // 5. ... the year-value is less than 1601 | ||
} | ||
} | ||
@@ -221,8 +257,27 @@ } | ||
if (seconds === null || day === null || month === null || year === null) { | ||
return; // 5. ... at least one of the found-day-of-month, found-month, found- | ||
// year, or found-time flags is not set, | ||
/* RFC 6265 S5.1.1 | ||
* "5. Abort these steps and fail to parse the cookie-date if: | ||
* * at least one of the found-day-of-month, found-month, found- | ||
* year, or found-time flags is not set, | ||
* * the day-of-month-value is less than 1 or greater than 31, | ||
* * the year-value is less than 1601, | ||
* * the hour-value is greater than 23, | ||
* * the minute-value is greater than 59, or | ||
* * the second-value is greater than 59. | ||
* (Note that leap seconds cannot be represented in this syntax.)" | ||
* | ||
* So, in order as above: | ||
*/ | ||
if ( | ||
dayOfMonth === null || month === null || year === null || second === null || | ||
dayOfMonth < 1 || dayOfMonth > 31 || | ||
year < 1601 || | ||
hour > 23 || | ||
minute > 59 || | ||
second > 59 | ||
) { | ||
return; | ||
} | ||
return new Date(Date.UTC(year, month, day, hour, minutes, seconds)); | ||
return new Date(Date.UTC(year, month, dayOfMonth, hour, minute, second)); | ||
} | ||
@@ -334,3 +389,47 @@ | ||
function trimTerminator(str) { | ||
for (var t = 0; t < TERMINATORS.length; t++) { | ||
var terminatorIdx = str.indexOf(TERMINATORS[t]); | ||
if (terminatorIdx !== -1) { | ||
str = str.substr(0,terminatorIdx); | ||
} | ||
} | ||
return str; | ||
} | ||
function parseCookiePair(cookiePair, looseMode) { | ||
cookiePair = trimTerminator(cookiePair); | ||
var firstEq = cookiePair.indexOf('='); | ||
if (looseMode) { | ||
if (firstEq === 0) { // '=' is immediately at start | ||
cookiePair = cookiePair.substr(1); | ||
firstEq = cookiePair.indexOf('='); // might still need to split on '=' | ||
} | ||
} else { // non-loose mode | ||
if (firstEq <= 0) { // no '=' or is at start | ||
return; // needs to have non-empty "cookie-name" | ||
} | ||
} | ||
var cookieName, cookieValue; | ||
if (firstEq <= 0) { | ||
cookieName = ""; | ||
cookieValue = cookiePair.trim(); | ||
} else { | ||
cookieName = cookiePair.substr(0, firstEq).trim(); | ||
cookieValue = cookiePair.substr(firstEq+1).trim(); | ||
} | ||
if (CONTROL_CHARS.test(cookieName) || CONTROL_CHARS.test(cookieValue)) { | ||
return; | ||
} | ||
var c = new Cookie(); | ||
c.key = cookieName; | ||
c.value = cookieValue; | ||
return c; | ||
} | ||
function parse(str, options) { | ||
@@ -344,22 +443,8 @@ if (!options || typeof options !== 'object') { | ||
var firstSemi = str.indexOf(';'); // S5.2 step 1 | ||
var pairRe = options.loose ? LOOSE_COOKIE_PAIR : COOKIE_PAIR; | ||
var result = pairRe.exec(firstSemi === -1 ? str : str.substr(0,firstSemi)); | ||
// Rx satisfies the "the name string is empty" and "lacks a %x3D ("=")" | ||
// constraints as well as trimming any whitespace. | ||
if (!result) { | ||
var cookiePair = (firstSemi === -1) ? str : str.substr(0, firstSemi); | ||
var c = parseCookiePair(cookiePair, !!options.loose); | ||
if (!c) { | ||
return; | ||
} | ||
var c = new Cookie(); | ||
if (result[1]) { | ||
c.key = result[2].trim(); | ||
} else { | ||
c.key = ''; | ||
} | ||
c.value = result[3].trim(); | ||
if (CONTROL_CHARS.test(c.key) || CONTROL_CHARS.test(c.value)) { | ||
return; | ||
} | ||
if (firstSemi === -1) { | ||
@@ -366,0 +451,0 @@ return c; |
@@ -46,3 +46,3 @@ { | ||
], | ||
"version": "2.3.3", | ||
"version": "2.3.4", | ||
"homepage": "https://github.com/salesforce/tough-cookie", | ||
@@ -49,0 +49,0 @@ "repository": { |
@@ -137,3 +137,3 @@ [RFC6265](https://tools.ietf.org/html/rfc6265) Cookies and CookieJar for Node.js | ||
_Potentially non-standard behavior:_ currently, tough-cookie will limit the number of spaces before the `=` to 256 characters. | ||
_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) | ||
@@ -140,0 +140,0 @@ |
Sorry, the diff of this file is too big to display
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
245484
2263