From 2113c1b4777d9bdefef11cdca9d7ab73885ec20c Mon Sep 17 00:00:00 2001 From: mohammed arib Date: Mon, 6 Jul 2026 12:41:16 +0530 Subject: [PATCH] fix quadratic backtracking in dehumanize unit search --- arrow/arrow.py | 2 +- tests/test_arrow.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/arrow/arrow.py b/arrow/arrow.py index eecf23266..f43de682a 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -1411,7 +1411,7 @@ def dehumanize(self, input_string: str, locale: str = "en_us") -> "Arrow": search_string = search_string.format(r"\d+") # Create search pattern and find within string - pattern = re.compile(rf"(^|\b|\d){search_string}") + pattern = re.compile(rf"(^|\b){search_string}") match = pattern.search(input_string) # If there is no match continue to next iteration diff --git a/tests/test_arrow.py b/tests/test_arrow.py index b595e4e21..a80822f3e 100644 --- a/tests/test_arrow.py +++ b/tests/test_arrow.py @@ -2930,6 +2930,17 @@ def test_no_units_modified(self, locale_list_no_weeks: List[str]): with pytest.raises(ValueError): arw.dehumanize(empty_future_string, locale=lang) + def test_long_malformed_input(self): + arw = arrow.Arrow(2000, 6, 18, 5, 55, 0) + + # A long run of digits with no trailing time unit used to make the + # per-unit search regex backtrack quadratically (ReDoS). It must be + # rejected quickly, not hang. + start = time.time() + with pytest.raises(ValueError): + arw.dehumanize("9" * 50000) + assert time.time() - start < 1.0 + def test_slavic_locales(self, slavic_locales: List[str]): # Relevant units for Slavic locale plural logic units = [