fix: mitigate ReDoS in to_duration via max input length check (closes #494) (#523)

This commit is contained in:
Christian Heinrich Hohlfeld 2025-04-22 00:16:33 +02:00 committed by GitHub
parent 63962343d9
commit 3c12e99970
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 39 additions and 0 deletions

View File

@ -34,6 +34,7 @@ from timezonefinder import TimezoneFinder
from akkudoktoreos.core.logging import get_logger
logger = get_logger(__name__)
MAX_DURATION_STRING_LENGTH = 350
@overload
@ -287,6 +288,11 @@ def to_duration(
"second": 1,
}
# Mitigate ReDoS vulnerability (#494) by checking input string length.
if len(input_value) > MAX_DURATION_STRING_LENGTH:
raise ValueError(
f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH})."
)
# Regular expression to match time components like '2 days', '5 hours', etc.
matches = re.findall(r"(\d+)\s*(days?|hours?|minutes?|seconds?)", input_value)

View File

@ -1,10 +1,13 @@
"""Test Module for pendulum.datetimeutil Module."""
import re
import pendulum
import pytest
from pendulum.tz.timezone import Timezone
from akkudoktoreos.utils.datetimeutil import (
MAX_DURATION_STRING_LENGTH,
compare_datetimes,
hours_in_day,
to_datetime,
@ -620,3 +623,33 @@ def test_compare_datetimes_gt(dt1, dt2):
assert compare_datetimes(dt1, dt2).gt
assert compare_datetimes(dt1, dt2).le == False
assert compare_datetimes(dt1, dt2).lt == False
def test_to_duration_excessive_length_raises_valueerror():
"""Test that to_duration raises ValueError for strings exceeding max length.
This test covers the fix for the ReDoS vulnerability.
Related to: #494
"""
# String exceeds limits
long_string = "a" * (MAX_DURATION_STRING_LENGTH + 50)
# Expected Errormessage ESCAPED für Regex
expected_error_message = re.escape(
f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH})."
)
# Check if error was raised
with pytest.raises(ValueError, match=expected_error_message):
to_duration(long_string)
# Optional: String exactly at the limit should NOT trigger the length check.
at_limit_string = "b" * MAX_DURATION_STRING_LENGTH
try:
to_duration(at_limit_string)
except ValueError as e:
if str(e) == f"Input string exceeds maximum allowed length ({MAX_DURATION_STRING_LENGTH}).":
pytest.fail(
f"to_duration raised length ValueError unexpectedly for string at limit: {at_limit_string}"
)
pass