Skip to content

ReDoS heuristic misses non-identical alternation and delimiter-separated adjacent quantified groups #157

Description

@jeronimodeleon

Summary

Two catastrophic-backtracking (ReDoS) pattern shapes slip through _heuristic_unsafe() in libs/core/genblaze_core/providers/pattern_safety.py regardless of whether google-re2 is installed. These are pre-existing gaps in the heuristic itself, distinct from #148 (which made the heuristic always run) and #80 (the original ReDoS guard). Surfaced by the adversarial reviewer during the #148 fix (PR #154) and independently reproduced.

Bypassing shapes (verified)

Both pass assert_safe() today, then backtrack exponentially when matched by the stdlib re engine used at runtime (family.py ModelFamily.matches()):

  1. Non-identical alternation(a|aa)+$
    The duplicate-alternation-branch check only rejects identical branches; overlapping-but-not-identical branches (a vs aa) still produce exponential backtracking.
  2. Delimiter-separated adjacent quantified groups(a+)-?(a+)-?(a+)-?(a+)$
    The adjacent-unbounded-quantified-groups check looks for directly adjacent groups; inserting an optional delimiter (-?) between them evades the adjacency detection while preserving the exponential blowup.

Evidence

Reproduced independently by the reviewer: real exponential blowup, e.g. n=32 adversarial input → ~0.31s and climbing (vs. sub-millisecond for benign input), both accepted by assert_safe() on main after PR #154.

Impact

Same class/severity as #80/#148: the shipped connector catalog is clean, so no first-party exploit today. Exposure is a third-party/future connector pattern of one of these shapes passing the only guard, where step.model is attacker-influenceable at preflight → worker hang (DoS). Priority P2 — forward-looking guard-strength gap, no live exploit.

Suggested fix

Extend _heuristic_unsafe() to cover:

  • alternation branches with a shared prefix / overlapping language (not just byte-identical branches), and
  • adjacent unbounded-quantified groups separated by an optional/nullable connector (-?, \s*, (?:...)?, etc.), by treating a nullable separator as adjacency.

Add regression tests asserting both shapes are rejected by assert_safe() in both the _HAS_RE2=True and _HAS_RE2=False branches (mirroring the parametrization added in PR #154). Consider noting in the module docstring that the heuristic is best-effort and google-re2 at runtime would be the only complete guarantee (out of scope here).

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions