fix: handle Infinity/NaN maxAge in res.cookie() without crashing#7287
fix: handle Infinity/NaN maxAge in res.cookie() without crashing#7287Mohammad-Faiz-Cloud-Engineer wants to merge 2 commits into
Conversation
Ap-0007
left a comment
There was a problem hiding this comment.
Thanks for the contribution! Handling non-finite values like Infinity, -Infinity, and NaN in res.cookie is a great safety enhancement.
However, checking typeof opts.maxAge === 'number' introduces a regression for string representations of non-finite numbers (like 'Infinity').
The Bug
Since maxAge = opts.maxAge - 0 is coerced to a number before the check:
- If a user passes
res.cookie('name', 'val', { maxAge: 'Infinity' }),typeof opts.maxAgeis'string'. - This bypasses the new guard block (
typeof opts.maxAge === 'number' && !isFinite(maxAge)). - The code then falls into the
else if (!isNaN(maxAge))block because!isNaN(Infinity)istrue. - It sets
opts.expires = new Date(Date.now() + Infinity)(which isInvalid Date) andopts.maxAge = Infinity. - This is then passed to the
cookiepackage, causing it to throw aTypeError: option maxAge is invalidor produce an invalid date—exactly what the PR seeks to prevent.
Suggested Fix
We can check maxAge (which has already been coerced to a number) instead of typeof opts.maxAge:
if (maxAge === Infinity || maxAge === -Infinity || (typeof opts.maxAge === 'number' && isNaN(maxAge))) {
// strip non-finite numeric maxAge (Infinity, -Infinity, NaN)
delete opts.maxAge
} else if (!isNaN(maxAge)) {
opts.expires = new Date(Date.now() + maxAge)
opts.maxAge = Math.floor(maxAge / 1000)
}This correctly handles both numeric and string representations of Infinity/-Infinity, correctly ignores the number NaN, and still allows invalid string garbage like 'foobar' to flow through to the cookie package and throw as expected.
Infinity, -Infinity, and NaN maxAge values now gracefully produce a session cookie instead of throwing an unhandled TypeError from cookie.serialize(). Non-numeric invalid maxAge values (e.g. 'foobar') still throw as before.
f64acca to
344573f
Compare
|
Done Brother, Now Check |
notadev99
left a comment
There was a problem hiding this comment.
Nice catch on the root cause — !isNaN() passing for Infinity is a real gap, and the fix is appropriately minimal. Two things before this is ready:
Tests. This changes observable behavior (Infinity/-Infinity/NaN now produce a session cookie instead of throwing), but nothing covers it. Could you add cases to the res.cookie tests asserting that maxAge: Infinity, -Infinity, and NaN set a session cookie (no Max-Age) without throwing — and one pinning that maxAge: 'foobar' still throws, so the intentional "leave non-numeric strings alone" behavior is locked in?
String 'Infinity' vs 'foobar'. Since maxAge is already coerced (opts.maxAge - 0), the string 'Infinity' coerces to Infinity and gets silently dropped to a session cookie, while 'foobar' coerces to NaN, fails the typeof guard, and throws. Two non-numeric strings, two outcomes. If that's intended, a one-line comment would save the next maintainer some confusion; if not, worth deciding whether 'Infinity' should throw too.
Logic looks correct otherwise.
Add tests for Infinity, -Infinity, and NaN maxAge values producing session cookies (no Max-Age) as requested in PR review feedback. Expand the inline comment to explain why the typeof guard is needed: it distinguishes numeric NaN (stripped to session cookie) from non-numeric strings like 'foobar' (which pass through and throw). Also documents that string 'Infinity' coerces to numeric Infinity and is intentionally handled the same way.
Noticed that passing { maxAge: Infinity } to res.cookie() throws an unhandled TypeError: option maxAge is invalid from the cookie package. The old !isNaN() check passes for Infinity (since it's not NaN), so the code tries to compute Math.floor(Infinity / 1000) and passes Infinity straight to cookie.serialize(), which rightfully rejects it.
Same issue with NaN and -Infinity.
The fix adds an early guard: if maxAge is a number and non-finite (Infinity, -Infinity, NaN), just delete it from opts so the cookie falls back to a session cookie (no Max-Age). Non-numeric garbage like 'foobar' still throws as before that path is unchanged.
Checked all edge cases manually and ran the suite a few times 1249 passing, 0 failing.