Skip to content

fix(giskard-checks): bound RegexMatching with regex timeout to mitigate ReDoS#2381

Merged
kevinmessiaen merged 4 commits intomainfrom
advisory-fix-1-ghsa-rq2q-4r55-9877
Apr 10, 2026
Merged

fix(giskard-checks): bound RegexMatching with regex timeout to mitigate ReDoS#2381
kevinmessiaen merged 4 commits intomainfrom
advisory-fix-1-ghsa-rq2q-4r55-9877

Conversation

@kevinmessiaen
Copy link
Copy Markdown
Member

Description

Replace stdlib re.search with regex.search(..., timeout=...).
Add match_timeout_seconds and a ReDoS regression test.

RegexMatching no longer maps regex.search timeouts to CheckResult.error; the engine's TimeoutError is raised instead.

Update the ReDoS regression test to expect TimeoutError.

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the standard 're' module with the 'regex' package in the RegexMatching check to introduce a configurable timeout, effectively mitigating potential ReDoS vulnerabilities. The feedback identifies a typo in the 'regex' dependency version within 'pyproject.toml' and recommends explicitly catching 'TimeoutError' in the 'run' method to return a structured failure result instead of allowing an unhandled exception. Additionally, the test suite should be updated to verify this failure status rather than expecting a raised exception.

Comment thread pyproject.toml
"giskard-core>=1.0.0a1",
"giskard-agents>=1.0.0a1",
"giskard-checks>=1.0.0a1",
"regex>=2026.1.15",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The version 2026.1.15 for the regex package appears to be a typo, as that version does not exist yet. It should likely be a current version (e.g., 2024.11.6).

Suggested change
"regex>=2026.1.15",
"regex>=2024.11.6",

Comment on lines 460 to 466
try:
if re.search(pattern, text):
return CheckResult.success(
message=f"Text matches the regex pattern '{pattern}'.",
details=details,
)
else:
return CheckResult.failure(
message=f"Text does not match the regex pattern '{pattern}'.",
details=details,
)
except re.error as e:
matched = regex.search(pattern, text, timeout=self.match_timeout_seconds)
except regex.error as e:
return CheckResult.failure(
message=f"Invalid regex pattern '{pattern}': {str(e)}",
details=details,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regex.search function raises a TimeoutError when the specified timeout is exceeded. Currently, this exception is not handled, which will cause the check to fail with an unhandled exception. It is better to catch this error and return a CheckResult.failure to provide a cleaner error message to the user.

        try:
            matched = regex.search(pattern, text, timeout=self.match_timeout_seconds)
        except TimeoutError:
            return CheckResult.failure(
                message=f"Regex matching timed out after {self.match_timeout_seconds}s.",
                details=details,
            )
        except regex.error as e:
            return CheckResult.failure(
                message=f"Invalid regex pattern '{pattern}': {str(e)}",
                details=details,
            )

Comment on lines +371 to +372
with pytest.raises(TimeoutError):
_ = await check.run(Trace())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since the run method should ideally handle the TimeoutError and return a failure result (as suggested), this test should be updated to verify the result status and message instead of expecting an exception to be raised.

Suggested change
with pytest.raises(TimeoutError):
_ = await check.run(Trace())
result = await check.run(Trace())
assert result.status == CheckStatus.FAIL
assert "timed out" in result.message

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

1 participant