Conversation
1ee7987 to
c13b796
Compare
Since 6.10 (GH#61, "do not set discard if max-age is set") the max-age branch in extract_cookies incremented the expiry counter but never pushed the value onto @Cur, so set_cookie always saw an undef max-age. Max-Age was completely ignored: cookies got no expiry and Max-Age=0 failed to delete. Push the value through, and while here: - ignore a non-integer Max-Age per RFC 6265 5.2.2 (previously such a value would coerce to 0 and wrongly delete the cookie) - cap an absurdly large value at ~10 years, the same approximation the expires branch already uses, so time() + maxage cannot overflow to a float that serializes to garbage Add t/issue69.t covering a future expiry, Max-Age=0 deletion, the non-integer case and the cap. Update the existing max-age test in t/cookies.t, which had asserted the buggy no-expiry output, to a date-independent pattern. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both the expires and max-age branches pushed a "Max-Age" entry onto @Cur, and the downstream %hash dedup kept the first one seen. So whichever attribute appeared first in the header won, e.g. "expires=...; Max-Age=0" kept the cookie instead of deleting it. Collect the Max-Age and Expires values separately during parsing (last occurrence of each wins) and resolve once at the end, letting Max-Age take precedence regardless of order, per RFC 6265 5.3 (and 6265bis, which formalises last-value-wins). A malformed Max-Age now correctly falls back to Expires rather than being treated as a present-but-zero value. Extend t/issue69.t with order-independent precedence and repeated-attribute cases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Use a snake_case name for the Max-Age value collected while parsing, matching the reviewer's preference and the $expires_age sibling. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
t/issue69.t uses subtest(), added in Test::More 0.95_01. macOS Perl 5.12 ships core Test::More 0.94, where the subtests aborted with "no plan declared". Declaring the version on the use line makes AutoPrereqs emit a test-phase floor of 0.96 (the first stable release with subtest, already the develop-phase floor) and fails early with a clear message on too-old toolchains (GH#69). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 10*365*24*60*60 literal appeared in both the Expires fallback and the Max-Age cap. Hoist it to a single $ten_years lexical so the cap is defined once and the two branches share it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The issue69.t test was added before the repo standardised on perltidy (via the new precious lint job on master). Tidy it so the branch passes CI after rebasing onto the perltidy'd master. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Member
Author
|
@wolfsage, how does this look? |
wolfsage
approved these changes
Jun 30, 2026
wolfsage
left a comment
There was a problem hiding this comment.
lgtm, one minor change maybe needed
|
|
||
| # RFC 6265 5.2.2: a non-integer Max-Age is ignored, not | ||
| # treated as 0 (which would wrongly delete the cookie). | ||
| if ( defined $v && $v =~ /\A-?\d+\z/ ) { |
There was a problem hiding this comment.
\d will allow other digits besides [0-9] because of Unicode I think.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #69
Problem
Since 6.10 (GH#61, "do not set discard if max-age is set"), the
max-agebranch in
extract_cookiesincremented the expiry counter but never pushedthe value onto
@cur. As a resultset_cookiealways received anundefmax-age, so Max-Age was completely ignored: cookies received no expiry,
and
Max-Age=0no longer deleted a cookie (a server-side logout/expirysignal silently failed — CWE-613, Insufficient Session Expiration).
While fixing that, a second defect was found: both the
expiresandmax-agebranches collapsed into the same"Max-Age"entry on@cur, and adownstream first-wins dedup meant header order decided which applied. RFC
6265 §5.3 requires Max-Age to take precedence over Expires.
Changes
lib/HTTP/Cookies.pm:set_cookie.coerced to
0and wrongly deleted the cookie). Our regex also rejects abare
-, matching the stricter forthcoming 6265bis wording.sibling
expiresbranch already uses — sotime() + maxagecannot becomea float that serializes to garbage.
Expires regardless of order, and last-value-wins for a repeated
attribute (RFC 6265 §5.3 / 6265bis §5.7). A malformed Max-Age now falls
back to Expires instead of being treated as present-but-zero.
Changes: changelog entries.Testing
t/issue69.t: future expiry,Max-Age=0deletion, non-integerignored, large-value cap, Max-Age-over-Expires precedence in both
orders, finite-Max-Age-over-far-future-Expires, and repeated-Max-Age
last-wins. Each verified red→green (fails with the relevant fix reverted,
passes with it).
t/cookies.t, which had asserted thebuggy no-expiry output, to a date-independent pattern.
Tests=128, Result: PASS.RFC backing (verbatim)
-character, ignore the cookie-av. If the remainder … contains anon-DIGIT character, ignore the cookie-av." … "If delta-seconds is less
than or equal to zero (0), let expiry-time be the earliest representable
date and time."
Max-Age attribute has precedence and controls the expiration date."
Note: the ~10-year cap is a pragmatic anti-overflow measure, not mandated by
the RFC.