Skip to content

fix(regex): bound regex matching to prevent ReDoS - #2271

Open
feiiiiii5 wants to merge 1 commit into
NVIDIA-NeMo:developfrom
feiiiiii5:fix/regex-redos-2203
Open

fix(regex): bound regex matching to prevent ReDoS#2271
feiiiiii5 wants to merge 1 commit into
NVIDIA-NeMo:developfrom
feiiiiii5:fix/regex-redos-2203

Conversation

@feiiiiii5

Copy link
Copy Markdown

Root Cause

RegexDetectionOptions.compile_patterns pre-compiles configured patterns with the stdlib backtracking re engine, and detect_regex_pattern evaluates them with compiled.search(text) — no time limit, no input bound. A pathological configured pattern (e.g. nested quantifiers) combined with crafted input, model output, or retrieval text can cause catastrophic backtracking and pin a worker indefinitely (ReDoS).

Fix

  • Compile patterns with the timeout-capable regex module (already an existing transitive dependency; now declared directly in pyproject.toml).
  • Bound each match with REGEX_MATCH_TIMEOUT_SECONDS = 0.5 (nemoguardrails/library/regex/actions.py).
  • Fail closed on timeout: if a configured pattern cannot be evaluated within the budget, the text is treated as matching (blocked) and a warning is logged — forbidden content cannot slip through because the matcher was too slow.
  • Preserve case_insensitive behavior and invalid-pattern error reporting at config load (regex.error and re.error are both caught and surfaced as the existing ValueError).

Test

  • test_regex_action_bounds_catastrophic_backtracking: the pathological pattern (a|aa)+$ against "a"*40 + "b" returns a blocked outcome within 2s instead of hanging (previously effectively unbounded under re).
  • test_regex_action_valid_pattern_still_matches_with_timeout: ordinary case-insensitive patterns still match within the budget.
  • Existing suite tests/test_regex_detection.py: 18 passed (including invalid-pattern-at-config-load, case-insensitive, multiple-pattern, input/output/retrieval flows).
  • ruff==0.14.6 check + format clean on changed files.

Diff scope

4 files, +87/-6: nemoguardrails/library/regex/rail_config.py, nemoguardrails/library/regex/actions.py, tests/test_regex_detection.py, pyproject.toml.

AI Disclosure

AI-assisted implementation and tests; human review of the fail-closed semantics, the regex-module compatibility (superset of re, error-type handling), timeout budget, and final diff before submission.

Closes #2203

Configured regex patterns were compiled with the stdlib backtracking
engine and evaluated without a time limit against user input, model
output, and retrieval text, so a pathological configured pattern plus
crafted text could pin a worker indefinitely.

Compile patterns with the timeout-capable third-party regex module (an
existing transitive dependency, now declared directly) and bound each
match to 0.5s. If a pattern times out, the text is treated as matching
(fail-closed) with a warning, so forbidden content cannot slip through
because the matcher was too slow. Invalid-pattern errors and
case-insensitive behavior are preserved.

Signed-off-by: fei <204683769+feiiiiii5@users.noreply.github.com>
@github-actions github-actions Bot added status: needs triage New issues that have not yet been reviewed or categorized. size: M labels Aug 9, 2026
@feiiiiii5

Copy link
Copy Markdown
Author

@Pouyanpi gentle ping for review when you have a chance. This bounds regex matching to fail closed on catastrophic-backtracking timeouts, with regression tests; CI is green. Happy to adjust anything.

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

Labels

size: M status: needs triage New issues that have not yet been reviewed or categorized.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: bound regex evaluation to prevent ReDoS in regex detection rails

1 participant