Skip to content

Fix ReDoS in parse_date#33

Merged
oalders merged 1 commit into
masterfrom
http-date-parse-date
Jul 9, 2026
Merged

Fix ReDoS in parse_date#33
oalders merged 1 commit into
masterfrom
http-date-parse-date

Conversation

@oalders

@oalders oalders commented Jul 4, 2026

Copy link
Copy Markdown
Member

parse_date() -- and str2time(), which delegates to it -- used regexes with
ambiguous adjacent greedy runs (year/clock/timezone digits and
timezone/ASCII-timezone letters) before a trailing anchor. A valid-looking
date prefix followed by a long interior run of digits, letters, or whitespace
and a trailing junk character forced O(N^2) backtracking, so a ~100 KB string
burned tens of seconds of CPU: a denial of service.

Guard parse_date() with an up-front length check ($HTTP::Date::MAX_LENGTH,
128 by default) so hostile input is rejected before any regex runs. The check
is on the untrimmed string, so no work happens on padded input, and the limit
is read defensively -- a nonsensical value (undef/zero/negative) falls back to
the default rather than silently rejecting every date. Every format the module
recognizes is far shorter than the limit, which is configurable for unusual
needs. The cap also bounds the substring later handed to Time::Zone.

Add t/redos.t covering the guard, the exact length boundary, and the digit,
letter, and whitespace DoS vectors.

Fixes CVE-2026-14741.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.30%. Comparing base (cf9cf93) to head (624f773).
⚠️ Report is 4 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master      #33      +/-   ##
==========================================
+ Coverage   89.70%   91.30%   +1.59%     
==========================================
  Files           1        1              
  Lines          68       69       +1     
  Branches       29       30       +1     
==========================================
+ Hits           61       63       +2     
+ Misses          1        0       -1     
  Partials        6        6              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@oalders oalders force-pushed the http-date-parse-date branch 2 times, most recently from 2fbfb1e to 0173966 Compare July 4, 2026 19:14
@robrwo

robrwo commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

That seems like a much better fix than the regex changes. But is 128 bytes too long? I would think 64 is probably enough.

@oalders

oalders commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

Agreed — lowered the cap to 64. The longest format the module recognizes is around Wednesday, 03-Feb-94 00:00:00 GMT (~33 chars), so 64 still leaves comfortable headroom, and both values bound the worst-case backtracking to instant. Also fixed the Changes entry, which incorrectly claimed the limit was configurable via $HTTP::Date::MAX_LENGTH (it's hardcoded).

parse_date() -- and str2time(), which delegates to it -- used regexes with
ambiguous adjacent greedy runs (year/clock/timezone digits and
timezone/ASCII-timezone letters) before a trailing anchor. A valid-looking
date prefix followed by a long interior run of digits, letters, or whitespace
and a trailing junk character forced O(N^2) backtracking, so a ~100 KB string
burned tens of seconds of CPU: a denial of service.

Guard parse_date() with an up-front length check (reject input longer than
64 characters) so hostile strings are rejected before any regex runs. The
check is on the untrimmed string, so no work happens on padded input. The cap
is a hardcoded security limit, not a tunable: every format the module
recognizes is far shorter, and the parsing regexes still contain adjacent
unbounded quantifiers, so raising it would demand re-benchmarking against
hostile input. The cap also bounds the substring later handed to Time::Zone.

Add t/redos.t covering the guard, the exact length boundary, and the digit,
letter, and whitespace DoS vectors.

Fixes CVE-2026-14741.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@oalders oalders force-pushed the http-date-parse-date branch from 0173966 to 624f773 Compare July 9, 2026 01:52
@oalders oalders merged commit 78c2095 into master Jul 9, 2026
51 checks passed
@oalders oalders deleted the http-date-parse-date branch July 9, 2026 02:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants