Skip to content

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

Merged
KyleAMathews merged 4 commits into
TanStack:mainfrom
ifeelBALANCED:fix/like-redos-linear-matcher
Sep 23, 2026
Merged

KyleAMathews merged 4 commits into
TanStack:mainfrom
ifeelBALANCED:fix/like-redos-linear-matcher

Conversation

@ifeelBALANCED

@ifeelBALANCED ifeelBALANCED commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #1690.

The problem

evaluateLike() compiled SQL LIKE patterns to a RegExp by rewriting % to .* and _ to .. A pattern with many % wildcards produces overlapping unbounded .* segments, and a near-miss value can trigger catastrophic backtracking through the public query-builder API (CWE-1333).

The fix

This removes the regex and matches with an iterative two-pointer walk. % uses bounded single-position retry, so hostile wildcard patterns no longer create exponential regex work.

The matcher preserves the existing public semantics:

  • % matches any sequence, including empty strings and line breaks
  • _ matches exactly one character
  • every other character is literal
  • matching remains full-string anchored
  • ilike retains the existing toLowerCase() case fold
  • non-string values still return false

Current main is merged into the branch. During the forward-port review, a wildcard-precedence regression was found: a % in the pattern could be treated as a literal when the input contained % at the same position. The matcher now recognizes the wildcard first, and focused witnesses cover inputs such as like('100% done', '100%done').

Oracle structure

The LIKE oracle now keeps its contract, independent dynamic-programming model, bounded string grammar, real compiled-expression driver, observations, replay interface, and declared limits together. It runs a stable 10,000-case campaign and a fresh 10,000-case campaign; TANSTACK_DB_LIKE_ORACLE_SEED replays a recorded seed and TANSTACK_DB_LIKE_ORACLE_RUNS controls the budget.

The repository guide and agent rules now say explicitly that literate structure is mandatory for every oracle and generated-history test. A focused non-oracle regression may remain short, but it cannot waive that structure or replace applicable oracle coverage.

Verification

  • The original bounded ReDoS regression remains green for both like and ilike.
  • The exact literal-before-wildcard source mutant fails the pinned witness and both generated campaigns. The recorded fixed campaign found 115 mismatches in 10,000 cases; the sampled random campaign found 104.
  • The restored implementation finds zero mismatches, and the deliberate wrong-result control remains killed.
  • Focused evaluator suite: 134/134 passed with no type errors.
  • Full @tanstack/db suite: 212 files and 6,306 tests passed with no type errors.
  • Targeted ESLint, Prettier, and diff checks passed.

Note

AI assisted: implemented with the help of AI assistants; the behavior was independently reproduced and verified against current main.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed LIKE and ILIKE matching for % and _ wildcards, including when input values contain those characters.
    • Prevented crafted patterns from causing excessive processing delays.
    • Improved matching coverage for empty patterns, newlines, exact matches, and wildcard-only patterns.
  • Tests

    • Added broad checks for wildcard behavior and demanding patterns to verify accurate, responsive matching across varied cases.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: ac490eb5-2f1e-4c18-854a-cf350c4ca674

📥 Commits

Reviewing files that changed from the base of the PR and between 13f294d and f01d901.

📒 Files selected for processing (4)
  • AGENTS.md
  • docs/contributing/oracle-coverage.md
  • docs/contributing/oracle-tests.md
  • packages/db/tests/query/compiler/evaluators.test.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

like() and ilike() now use iterative wildcard matching instead of regular expressions. Tests compare results with a reference evaluator and cover wildcard semantics and pathological near-miss patterns. A patch Changeset documents the change.

Changes

LIKE matcher fix

Layer / File(s) Summary
Iterative LIKE matching
packages/db/src/query/compiler/evaluators.ts, .changeset/like-redos-linear-matcher.md
evaluateLike traverses % and _ directly, retries mismatches from the latest %, and accepts only trailing % after input consumption. The Changeset records the patch entry.
LIKE oracle campaigns
packages/db/tests/query/compiler/evaluators.test.ts
The tests define a reference evaluator, seeded case generation, configurable campaigns, and mismatch reporting. Campaigns check category coverage and require zero mismatches.
Matcher regressions and oracle guidance
packages/db/tests/query/compiler/evaluators.test.ts, AGENTS.md, docs/contributing/oracle-coverage.md, docs/contributing/oracle-tests.md
Regression tests cover wildcard placement, single-character matching, empty patterns, line breaks, and a pathological near-miss. Contributor guidance requires oracle tests to show their contract, model, grammar, production path, and observations.

Priority: ⚪ Not assessed

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

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: kyleamathews

Merge Risk: ⚪ Minimal · up to f01d9

The LIKE and ILIKE implementation retains the established matching behavior while removing regex backtracking. No actionable merge-blocking issue remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. (3 skipped: 3… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #1690 requires a manual matcher with O(n × m) worst-case behavior. evaluateLike now uses an iterative two-pointer matcher with backtracking to the latest %; it does not compile a regular exp…
Out of Scope Changes check ✅ Passed The changes stay connected to issue #1690. The matcher and focused tests implement and verify the remediation. The changeset documents the fix. The oracle guidance and coverage-map updates support the…
Title check ✅ Passed The title clearly and concisely identifies the main change: preventing ReDoS in like() and ilike() pattern matching.
Description check ✅ Passed The description is detailed and relevant. It explains the problem, implementation, preserved behavior, regression coverage, oracle testing, verification results, and AI assistance. It does not reprodu…
Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. (3 skipped: 3 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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.

@pkg-pr-new

pkg-pr-new Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@1745

@tanstack/browser-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/browser-db-sqlite-persistence@1745

@tanstack/capacitor-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/capacitor-db-sqlite-persistence@1745

@tanstack/cloudflare-durable-objects-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/cloudflare-durable-objects-db-sqlite-persistence@1745

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@1745

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@1745

@tanstack/db-sqlite-persistence-core

npm i https://pkg.pr.new/@tanstack/db-sqlite-persistence-core@1745

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@1745

@tanstack/electron-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/electron-db-sqlite-persistence@1745

@tanstack/expo-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/expo-db-sqlite-persistence@1745

@tanstack/node-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/node-db-sqlite-persistence@1745

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@1745

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@1745

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@1745

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@1745

@tanstack/react-native-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/react-native-db-sqlite-persistence@1745

@tanstack/react-router-with-db

npm i https://pkg.pr.new/@tanstack/react-router-with-db@1745

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@1745

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@1745

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@1745

@tanstack/tauri-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/tauri-db-sqlite-persistence@1745

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@1745

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@1745

commit: f01d901

@KyleAMathews
KyleAMathews merged commit 1ca838b into TanStack:main Sep 23, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ReDoS (CWE-1333): Bad backtracking in like()/ilike() query LIKE pattern compilation

2 participants