Skip to content

Commit 2a4775c

Browse files
committed
Avoid unbounded Regexp parts in date parsing
Replaces a bunch of `[^\d]*$` with bounded `(?:[^\d]|$)` Double checked the RFC6265 spec: time cannot have non-digits beside the colons.
1 parent c9bd79d commit 2a4775c

File tree

2 files changed

+61
-12
lines changed

2 files changed

+61
-12
lines changed

lib/cookie.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ try {
4444
console.warn("cookie: can't load punycode; won't use punycode for domain normalization");
4545
}
4646

47-
var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/;
48-
4947
// From RFC6265 S4.1.1
5048
// note that it excludes \x3B ";"
5149
var COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/;
@@ -61,10 +59,17 @@ var TERMINATORS = ['\n', '\r', '\0'];
6159
// Note ';' is \x3B
6260
var PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/;
6361

64-
var DAY_OF_MONTH = /^(\d{1,2})[^\d]*$/;
65-
var TIME = /^(\d{1,2})[^\d]*:(\d{1,2})[^\d]*:(\d{1,2})[^\d]*$/;
66-
var MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i;
62+
// date-time parsing constants (RFC6265 S5.1.1)
6763

64+
var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/;
65+
var DAY_OF_MONTH = /^(\d{1,2})(?:[^\d]|$)/;
66+
67+
// S5.1.1 for "hms-time" -- is one or two digits each separated by :
68+
// Cannot have non-digits beside the numbers like in other parts of the
69+
// construction.
70+
var TIME = /^(\d{1,2}):(\d{1,2}):(\d{1,2})(?:[^\d]|$)/; // only anchor at start
71+
72+
var MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i;
6873
var MONTH_TO_NUM = {
6974
jan:0, feb:1, mar:2, apr:3, may:4, jun:5,
7075
jul:6, aug:7, sep:8, oct:9, nov:10, dec:11
@@ -76,7 +81,7 @@ var NUM_TO_DAY = [
7681
'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
7782
];
7883

79-
var YEAR = /^(\d{2}|\d{4})$/; // 2 to 4 digits
84+
var YEAR = /^(\d{2}|\d{4})(?:[^\d]|$)/; // 2 or 4 digits, anchored at start
8085

8186
var MAX_TIME = 2147483647000; // 31-bit max
8287
var MIN_TIME = 0; // 31-bit min
@@ -146,7 +151,7 @@ function parseDate(str) {
146151
if (day === null) {
147152
result = DAY_OF_MONTH.exec(token);
148153
if (result) {
149-
day = parseInt(result, 10);
154+
day = parseInt(result[1], 10);
150155
/* RFC6265 S5.1.1.5:
151156
* [fail if] the day-of-month-value is less than 1 or greater than 31
152157
*/

test/date_test.js

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,21 @@ function dateVows(table) {
3939
Object.keys(table).forEach(function (date) {
4040
var expect = table[date];
4141
theVows[date] = function () {
42-
var got = tough.parseDate(date) ? 'valid' : 'invalid';
43-
assert.equal(got, expect ? 'valid' : 'invalid');
42+
var got = tough.parseDate(date) ? true : false;
43+
if (expect && !got) {
44+
assert.ok(false, "expected valid date but was invalid");
45+
} else if (!expect && got) {
46+
assert.ok(false, "expected invalid date but was valid");
47+
} else {
48+
assert.ok(true);
49+
}
4450
};
4551
});
4652
return {"date parsing": theVows};
4753
}
4854

55+
var TOO_MANY_XS = 'x'.repeat(65535);
56+
4957
vows
5058
.describe('Date')
5159
.addBatch(dateVows({
@@ -55,6 +63,7 @@ vows
5563
"18 Oct 2011 07:42:42 GMT": true,
5664
"8 Oct 2011 7:42:42 GMT": true,
5765
"8 Oct 2011 7:2:42 GMT": true,
66+
"8 Oct 2011 7:2:2 GMT": true,
5867
"Oct 18 2011 07:42:42 GMT": true,
5968
"Tue Oct 18 2011 07:05:03 GMT+0000 (GMT)": true,
6069
"09 Jun 2021 10:18:14 GMT": true,
@@ -64,16 +73,51 @@ vows
6473
'01 Jan 1601 00:00:00 GMT': true,
6574
'10 Feb 81 13:00:00 GMT': true, // implicit year
6675
'Thu, 17-Apr-2014 02:12:29 GMT': true, // dashes
67-
'Thu, 17-Apr-2014 02:12:29 UTC': true // dashes and UTC
76+
'Thu, 17-Apr-2014 02:12:29 UTC': true, // dashes and UTC
77+
78+
// garbage after parts:
79+
"Wedxxx, 09 Jun 2021 10:18:14 GMT": true, // day of week doesn't matter
80+
"Wed, 09e9 Jun 2021 10:18:14 GMT": true, // garbage after day ignored
81+
"Wed, 09 Junxxx 2021 10:18:14 GMT": true, // prefix match on month
82+
"Wed, 09 Jun 2021e9 10:18:14 GMT": true, // garbage after year OK
83+
"Wed, 09 Jun 2021 10e9:18:14 GMT": false, // can't have garbage after HH
84+
"Wed, 09 Jun 2021 10:18e9:14 GMT": false, // can't have garbage after MM
85+
"Wed, 09 Jun 2021 10:18:14e9 GMT": true, // garbage after SS ignored
86+
87+
// extra digit in time parts:
88+
"Thu, 01 Jan 1970 000:00:01 GMT": false,
89+
"Thu, 01 Jan 1970 00:000:01 GMT": false,
90+
"Thu, 01 Jan 1970 00:00:010 GMT": false,
91+
92+
"": false
6893
}))
6994
.addBatch({
70-
"strict date parse of Thu, 01 Jan 1970 00:00:010 GMT": {
95+
"reDos hr": {
7196
topic: function () {
72-
return tough.parseDate('Thu, 01 Jan 1970 00:00:010 GMT', true) ? true : false;
97+
var str = "Wed, 09 Jun 2021 10" + TOO_MANY_XS + ":18:14 GMT";
98+
return tough.parseDate(str, true) ? true : false;
7399
},
74100
"invalid": function (date) {
75101
assert.equal(date, false);
76102
}
103+
},
104+
"reDos min": {
105+
topic: function () {
106+
var str = "Wed, 09 Jun 2021 10:18" + TOO_MANY_XS + ":14 GMT";
107+
return tough.parseDate(str, true) ? true : false;
108+
},
109+
"invalid": function (date) {
110+
assert.equal(date, false);
111+
}
112+
},
113+
"reDos sec": {
114+
topic: function () {
115+
var str = "Wed, 09 Jun 2021 10:18:14" + TOO_MANY_XS + " GMT";
116+
return tough.parseDate(str, true) ? true : false;
117+
},
118+
"valid": function (date) {
119+
assert.equal(date, true);
120+
}
77121
}
78122
})
79123
.export(module);

0 commit comments

Comments
 (0)