fix: avoid catastrophic backtracking in number parsing for empty thousand separator (DEV-2120) - #1713
fix: avoid catastrophic backtracking in number parsing for empty thousand separator (DEV-2120)#1713marcin-kordas-hoc wants to merge 1 commit into
Conversation
✅ Deploy Preview for hyperformula-dev-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Task linked: HF-154 Agent-friendly docs |
| if (!paramName) continue | ||
| // Optional when the name fragment contains `?` or a default `=` precedes `:`. | ||
| const beforeColon = clean.split(':')[0] ?? '' | ||
| const isOptional = beforeColon.includes('?') || beforeColon.includes('=') |
There was a problem hiding this comment.
Optional params marked required
Medium Severity
parseParams only treats a parameter as optional when ? or = appears before the :. Typical TypeScript defaults like name: Type = value put = after the type, so core factory methods such as buildFromArray and buildEmpty are exported with optional parameters classified as required.
Reviewed by Cursor Bugbot for commit 6637a91. Configure here.
6637a91 to
2f1ca3a
Compare
Performance comparison of head (8ec26f4) vs base (fd04f77) |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2f1ca3a. Configure here.
…sand separator (DEV-2120)
When thousandSeparator is empty (the default), the number-detection pattern's
group `(sep\d{3,})*` degenerated to `(\d{3,})*` adjacent to `\d+`, producing
catastrophic regex backtracking (ReDoS) on a long run of digits ending in a
non-digit character. Entering such a value into a cell froze the page.
Omit the thousand-separator group entirely when the separator is empty. Behavior
for non-empty separators is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2f1ca3a to
8ec26f4
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1713 +/- ##
========================================
Coverage 97.17% 97.17%
========================================
Files 176 176
Lines 15483 15484 +1
Branches 3429 3430 +1
========================================
+ Hits 15045 15046 +1
Misses 438 438
🚀 New features to boost your workflow:
|
|
|
||
| - Fixed the behavior of `MATCH`, `VLOOKUP`, `HLOOKUP`, and `XLOOKUP` functions when the search range contained empty cells. [#1697](https://github.com/handsontable/hyperformula/pull/1697) | ||
| - Fixed the `VLOOKUP`, `HLOOKUP`, and `XLOOKUP` functions to return `0` instead of an empty value when the matched cell in the result range is empty. [#1697](https://github.com/handsontable/hyperformula/pull/1697) | ||
| - Fixed the page freezing when entering a long string of digits ending in a non-digit character (e.g. `012...789a`) into a cell, caused by catastrophic regex backtracking in number parsing. [#1713](https://github.com/handsontable/hyperformula/pull/1713) |
There was a problem hiding this comment.
Reference this task in changelog


Summary
Entering a long run of digits ending in a non-digit character (e.g.
012345678901234567890123456789012345678901234567890123456789a) into a cell froze the page when formulas were enabled. DEV-2120 / HOT-9767.Root cause
NumberLiteralHelperbuilds its number-detection pattern by interpolating the configured separators. With the defaultthousandSeparator: '', the group(${thousandSeparator}\d{3,})*degenerates to(\d{3,})*placed immediately after\d+:For a long digit run that ultimately fails to match (trailing non-digit), the engine explores exponentially many ways to partition the digits between
\d+and the repeated\d{3,}group — classic catastrophic backtracking (ReDoS). Parse time roughly doubles every ~2 characters, so a 60-character input never returns.The same pattern is reached from raw cell input (
CellContentParser) and from string→number coercion during formula evaluation (ArithmeticHelper), so=VALUE("…")and arithmetic over such text hung too. Fixing the pattern builder covers all entry points.Fix
Omit the thousand-separator group entirely when the separator is empty. The emitted pattern for a non-empty separator (
,,,.) is byte-for-byte unchanged — a literal separator is a mandatory anchor between repetitions, so no ambiguous partition exists and those configs were never vulnerable.Testing
Paired tests in handsontable/hyperformula-tests (branch
fix/dev-2120-redos-number-parsing):setCellContents(raw, percent, currency) and formula coercion (=VALUE(...)).Reviewer notes
numberPattern.sourcehas no(\d{3,})*): a synchronous ReDoS cannot be caught by a Jest/Jasmine per-test timeout — the timer can't fire while the regex is stuck on the main thread — so asserting the emitted pattern shape is the one deterministic regression tripwire. The behavioral/e2e tests still cover actual behavior.Notes
Long-standing issue (reproduced on docs v17.1 and v18.0), not a v18 regression.
🤖 Generated with Claude Code