diff --git a/CHANGELOG.md b/CHANGELOG.md index 94424f8a3..783f0a67f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - 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) ## [3.3.0] - 2026-05-20 diff --git a/src/NumberLiteralHelper.ts b/src/NumberLiteralHelper.ts index 8d7a526ce..bf03e7796 100644 --- a/src/NumberLiteralHelper.ts +++ b/src/NumberLiteralHelper.ts @@ -16,7 +16,11 @@ export class NumberLiteralHelper { const thousandSeparator = this.config.thousandSeparator === '.' ? `\\${this.config.thousandSeparator}` : this.config.thousandSeparator const decimalSeparator = this.config.decimalSeparator === '.' ? `\\${this.config.decimalSeparator}` : this.config.decimalSeparator - this.numberPattern = new RegExp(`^([+-]?((${decimalSeparator}\\d+)|(\\d+(${thousandSeparator}\\d{3,})*(${decimalSeparator}\\d*)?)))([eE][+-]?\\d+)?$`) + // When thousandSeparator is empty (the default), the group `(sep\d{3,})*` degenerates to + // `(\d{3,})*` adjacent to `\d+`, which causes catastrophic backtracking (ReDoS) on a long + // run of digits that ultimately fails to match. Omit the group entirely in that case. (DEV-2120) + const thousandSeparatorGroup = this.config.thousandSeparator === '' ? '' : `(${thousandSeparator}\\d{3,})*` + this.numberPattern = new RegExp(`^([+-]?((${decimalSeparator}\\d+)|(\\d+${thousandSeparatorGroup}(${decimalSeparator}\\d*)?)))([eE][+-]?\\d+)?$`) this.allThousandSeparatorsRegex = new RegExp(`${thousandSeparator}`, 'g') }