Skip to content

fix: prevent ReDoS in like()/ilike() pattern matching - #1745

Open
ifeelBALANCED wants to merge 1 commit into
TanStack:mainfrom
ifeelBALANCED:fix/like-redos-linear-matcher
Open

fix: prevent ReDoS in like()/ilike() pattern matching#1745
ifeelBALANCED wants to merge 1 commit into
TanStack:mainfrom
ifeelBALANCED:fix/like-redos-linear-matcher

Conversation

@ifeelBALANCED

@ifeelBALANCED ifeelBALANCED commented Aug 18, 2026

Copy link
Copy Markdown

Fixes #1690.

The problem

evaluateLike() compiled SQL LIKE patterns to a RegExp by rewriting %.* and _.. A pattern with many % wildcards produces overlapping unbounded .* segments, and a near-miss value (one that matches most of the pattern but fails near the end) sends the regex engine into catastrophic backtracking — exponential time on attacker-controlled input reachable straight from the public query builder API (CWE-1333). See the PoC in #1690.

The fix

This takes the long-term remediation suggested in the issue: the regex is gone entirely. evaluateLike() now matches the pattern with the classic iterative two-pointer walk (greedy % with single-position backtracking), which is O(value.length × pattern.length) worst-case and allocation-free. No wildcard-count limit, no behavior cliff — pathological patterns just run in linear-ish time like any other.

Semantics are preserved exactly:

  • % matches any sequence of characters (including none, and across line breaks — the old regex used the s flag)
  • _ matches exactly one character
  • everything else is literal (regex metacharacters need no escaping anymore)
  • full-string anchoring, ilike case-folding via toLowerCase, and the non-string → false guard are unchanged

Testing

  • 7 new unit tests in evaluators.test.ts: mid-pattern %, _ must consume exactly one char, wildcard-only and empty patterns, matching across \n, and a ReDoS regression test — the issue's pathological pattern/near-miss pair for both like and ilike with a time bound (hung for minutes before the fix, runs in <1 ms now)
  • Differential fuzz against the old regex implementation: 200,000 random value/pattern pairs over an alphabet including %, _, regex metacharacters, \n, and non-ASCII — zero output mismatches
  • Full @tanstack/db test suite, build, and lint pass

Note

AI assisted: implemented with the help of an AI assistant (Claude); I have reviewed, fuzz-verified, and tested the change.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed LIKE and ILIKE matching for wildcard patterns, including % and _.
    • Prevented crafted patterns from causing excessive processing delays.
    • Improved handling of empty patterns, newlines, exact matches, and wildcard-only patterns.
  • Tests

    • Added coverage for wildcard matching and pathological patterns to ensure reliable, responsive evaluation.
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b71150b3-b0a4-460c-a22a-77078b6d44cc

📥 Commits

Reviewing files that changed from the base of the PR and between 4b9e8cd and f9e5556.

📒 Files selected for processing (3)
  • .changeset/like-redos-linear-matcher.md
  • packages/db/src/query/compiler/evaluators.ts
  • packages/db/tests/query/compiler/evaluators.test.ts

Included review availability: Your plan includes up to 8 reviews per rolling hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

like() and ilike() now use iterative wildcard matching instead of regular expressions. Tests cover wildcard semantics and near-miss patterns. A patch Changeset documents the ReDoS fix.

Changes

LIKE matcher fix

Layer / File(s) Summary
Iterative LIKE matching
packages/db/src/query/compiler/evaluators.ts
evaluateLike now traverses % and _ directly, retries mismatches from the latest %, and accepts only trailing % after input consumption.
Matcher tests and release entry
packages/db/tests/query/compiler/evaluators.test.ts, .changeset/like-redos-linear-matcher.md
Tests cover wildcard placement, single-character matching, empty and wildcard-only patterns, line breaks, and pathological near-miss patterns. The Changeset records the patch release entry.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to f9e55

The PR replaces vulnerable pattern matching with a bounded iterative matcher and adds focused regression coverage; no actionable merge-blocking risk remains after normal checks and review.

Suggested reviewers: kevin-dp

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: preventing ReDoS in LIKE and ILIKE pattern matching.
Description check ✅ Passed The description explains the problem, fix, preserved behavior, testing, and changeset impact, but it does not use the repository template headings.
Linked Issues check ✅ Passed The implementation replaces regex matching with an iterative matcher and adds regression tests, satisfying issue #1690's remediation objective.
Out of Scope Changes check ✅ Passed The changeset, matcher update, and evaluator tests directly support the ReDoS remediation in issue #1690.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

Labels

None yet

1 participant