Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

fix(orm): escape special characters in string search patterns#479

Merged
ymc9 merged 2 commits intodevfrom
fix/escape-pattern
Dec 12, 2025
Merged

fix(orm): escape special characters in string search patterns#479
ymc9 merged 2 commits intodevfrom
fix/escape-pattern

Conversation

@ymc9
Copy link
Copy Markdown
Member

@ymc9 ymc9 commented Dec 12, 2025

Summary by CodeRabbit

  • Bug Fixes

    • String filters (contains, startsWith, endsWith) now correctly escape special characters (%) and enforce string inputs for consistent behavior across JSON and regular fields.
  • Tests

    • Added and expanded tests to validate escaping and filtering behavior across providers and filter modes.
  • Chores

    • Removed explicit Node/npm version pins from package metadata.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 12, 2025 13:47
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Dec 12, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Consolidates JSON string-filter logic into the base dialect by implementing escaping and validation, removes dialect-specific overrides, removes Volta config from package.json, updates LIKE-based string filtering with ESCAPE handling, and expands tests to cover percent (%) literal handling across filters and policy functions.

Changes

Cohort / File(s) Summary
Volta Configuration Cleanup
packages/language/package.json
Removed the Volta config block that pinned Node/npm versions; no dependency or export changes.
Base JSON String Filter & Validation
packages/orm/src/client/crud/dialects/base-dialect.ts
Implemented JSON string-filter logic inside the base dialect: added private helpers for LIKE-pattern escaping and building string/JSON string filters, added input validation and consistent error throwing for invalid JSON filter payloads/keys, and removed the abstract hook.
Dialect Overrides Removed
packages/orm/src/client/crud/dialects/postgresql.ts, packages/orm/src/client/crud/dialects/sqlite.ts
Removed buildJsonStringFilter overrides from PostgreSQL and SQLite dialects so they now rely on the base implementation.
LIKE Escaping in Functions
packages/orm/src/client/functions.ts
Introduced escaping of backslash, percent, and underscore for search strings, added SqlBool import, and emit LIKE/ILIKE comparisons using an explicit ESCAPE '\' clause.
E2E Test Updates & Additions
tests/e2e/orm/client-api/filter.test.ts, tests/e2e/orm/client-api/json-filter.test.ts, tests/e2e/orm/policy/policy-functions.test.ts
Added test records and assertions to exercise percent (%) as a literal in contains/startsWith/endsWith (including JSON-field and policy-function cases), updated client init typing, and adjusted expected result counts to include the new test data.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

  • Pay attention to correctness of LIKE escaping (backslash, %, _) and the ESCAPE clause usage in SQL generation.
  • Review error-handling changes in base-dialect.ts to ensure semantics match previous behavior where intended.
  • Confirm removing dialect overrides doesn't regress dialect-specific behaviors (Postgres/SQLite differences).

Possibly related PRs

Poem

🐰 I hopped through strings with a careful eye,
I escaped the percent and taught underscores to fly.
Base class now holds the pattern so neat,
Dialects step back — the logic's complete.
Bye-bye Volta, hello tests — a tidy little feat! 🎉

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately and concisely summarizes the main change: fixing escape handling for special characters in string search patterns across the ORM module.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/escape-pattern

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 and usage tips.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Dec 12, 2025

Claude finished @ymc9's task —— View job


PR Review: String Pattern Escaping Implementation

Review Tasks

  • Read and understand the changed files
  • Analyze core implementation in base-dialect.ts
  • Review string escaping logic in functions.ts
  • Evaluate test coverage and quality
  • Check for security vulnerabilities
  • Assess performance implications
  • Provide comprehensive feedback

Summary

This PR implements proper escaping of special characters (%, _, \) in string search patterns across the ORM's filter system and ZModel functions. This is a critical security fix to prevent SQL injection vulnerabilities in LIKE pattern matching.

Code Quality & Implementation ✅

Excellent Implementation Quality

The code changes demonstrate high-quality engineering:

  1. Centralized Escaping Logic: packages/orm/src/client/crud/dialects/base-dialect.ts:859-861

    private escapeLikePattern(pattern: string) {
        return pattern.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_');
    }
    • Proper order: escapes backslash first to avoid double-escaping
    • Comprehensive: handles all SQL LIKE wildcards
  2. Consistent Application: Used in both filter operations and ZModel functions

    • Filter system: base-dialect.ts:822-828 for contains, startsWith, endsWith
    • ZModel functions: functions.ts:56-65 for text matching operations
  3. Database Compatibility: Uses standard SQL ESCAPE '\\' clause for cross-database compatibility

Security Assessment ✅

Critical Vulnerability Fixed

This PR addresses a significant SQL injection vulnerability:

Before: User input like '% could match unintended records
After: Special characters are properly escaped, treating them as literal values

Example Attack Prevention:

-- Vulnerable (before): matches any string ending with 'x'
WHERE field LIKE '%admin%x%'

-- Secure (after): matches literal string '%admin%x'  
WHERE field LIKE '%\%admin\%x%' ESCAPE '\'

Proper Escaping Strategy

  • ✅ Backslash escaped first (\\\\\\)
  • ✅ Percent signs escaped (%\\%)
  • ✅ Underscores escaped (_\\_)
  • ✅ Uses SQL standard ESCAPE '\\' clause

Test Coverage Assessment ✅

Comprehensive Test Suite

Excellent test coverage across multiple areas:

  1. Direct Filter Tests: tests/e2e/orm/client-api/filter.test.ts:99-132

    • Tests % escaping in contains, startsWith operations
    • Validates literal character matching vs wildcard behavior
  2. JSON Filter Tests: tests/e2e/orm/client-api/json-filter.test.ts:514-622

    • Tests escaping in JSON path string filters
    • Covers string_contains, string_starts_with, string_ends_with
  3. Policy Function Tests: tests/e2e/orm/policy/policy-functions.test.ts:26-41

    • Tests escaping in ZModel policy functions
    • Validates security in access control scenarios

Real-world Test Scenarios

Tests include realistic attack patterns:

  • Emails with % characters: '%u3@test.com'
  • Names with special chars: '%Foo', '%Jane'
  • Explicit wildcard prevention testing

Performance Implications ✅

Minimal Performance Impact

  1. Simple String Operations: Only basic regex replacements
  2. Applied Only When Needed: Escaping occurs during query building, not on every query
  3. No Additional Database Round-trips: Uses standard SQL ESCAPE clause

Negligible Overhead

The escaping logic adds ~3 regex operations per string filter, which is insignificant compared to database query overhead.

Architecture & Design ✅

Clean Architecture

  1. Single Responsibility: escapeLikePattern() method has one clear purpose
  2. DRY Principle: Shared logic between filter system and functions
  3. Consistent API: Same escaping behavior across all string operations

Backward Compatibility

  • No breaking changes to public APIs
  • Existing queries work identically for non-special characters
  • Only changes behavior for previously vulnerable patterns

Minor Recommendations

  1. Documentation: Consider adding inline comments explaining the escaping order
  2. Future Enhancement: Could add configuration option for custom escape characters if needed

Overall Assessment

This is an excellent, security-critical fix that should be merged immediately.

Strengths:

  • ✅ Fixes critical SQL injection vulnerability
  • ✅ Comprehensive test coverage
  • ✅ Clean, maintainable implementation
  • ✅ Consistent cross-database behavior
  • ✅ Minimal performance impact
  • ✅ No breaking changes

Security Impact:

  • High: Prevents SQL injection in string pattern matching
  • Critical for Production: Essential for any application using string filters

This PR demonstrates exemplary security engineering practices with thorough testing and clean implementation.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements proper escaping of special SQL LIKE pattern characters (%, _, ) in string search operations to prevent them from being interpreted as wildcards. The fix addresses a security and correctness issue where user input containing these characters could cause unexpected matching behavior or potential SQL injection vulnerabilities.

Key Changes:

  • Centralized LIKE pattern escaping logic in BaseCrudDialect with proper ESCAPE '\' clause
  • Updated policy functions (contains, startsWith, endsWith) to use SQL-based escaping instead of relying on unescaped patterns
  • Removed dialect-specific JSON string filter implementations in favor of the unified approach

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/orm/src/client/functions.ts Added SQL-based escaping of special characters (%, _, ) in textMatch function for policy operations, with proper ESCAPE clause
packages/orm/src/client/crud/dialects/base-dialect.ts Introduced escapeLikePattern() and buildStringLike() methods to centralize escaping logic; moved buildJsonStringFilter() from abstract to private implementation; improved error handling by replacing invariant(false) with createInvalidInputError()
packages/orm/src/client/crud/dialects/postgresql.ts Removed dialect-specific buildJsonStringFilter() override as it's now handled by base class
packages/orm/src/client/crud/dialects/sqlite.ts Removed dialect-specific buildJsonStringFilter() override as it's now handled by base class
tests/e2e/orm/policy/policy-functions.test.ts Added test cases verifying that '%' character in policy function arguments is treated literally
tests/e2e/orm/client-api/json-filter.test.ts Added test cases for JSON field string filters with '%' character to verify literal matching
tests/e2e/orm/client-api/filter.test.ts Added user with '%' in email address and updated test expectations; verified proper escaping in contains/startsWith filters
packages/language/package.json Removed volta configuration (unrelated to the main PR purpose)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/language/package.json
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
tests/e2e/orm/client-api/json-filter.test.ts (1)

635-644: Consider adding tests for underscore and backslash escaping.

The implementation escapes _ and \ in addition to %, but only % is tested here. Consider adding test cases for these characters to ensure complete coverage of the escaping logic.

tests/e2e/orm/policy/policy-functions.test.ts (1)

26-41: Good policy function escaping test, but consider removing debug flag.

The test correctly validates that % is treated as a literal character in contains policy functions. However, debug: true on line 35 will produce verbose output during test runs.

Consider removing the debug flag before merging:

         `,
-            { debug: true },
+            {},
         );

Or simply remove the options object entirely if not needed.

packages/orm/src/client/crud/dialects/base-dialect.ts (1)

859-861: Consider extracting shared escaping logic.

The escapeLikePattern method here duplicates the escaping logic in packages/orm/src/client/functions.ts (lines 56-57). Consider extracting this to a shared utility to maintain consistency and reduce duplication.

Example shared utility:

// In a shared utils file
export function escapeLikePattern(pattern: string): string {
    return pattern.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_');
}

This would ensure both the client API filters and policy functions use identical escaping logic.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e73e93 and 5d80270.

📒 Files selected for processing (8)
  • packages/language/package.json (0 hunks)
  • packages/orm/src/client/crud/dialects/base-dialect.ts (4 hunks)
  • packages/orm/src/client/crud/dialects/postgresql.ts (0 hunks)
  • packages/orm/src/client/crud/dialects/sqlite.ts (0 hunks)
  • packages/orm/src/client/functions.ts (2 hunks)
  • tests/e2e/orm/client-api/filter.test.ts (8 hunks)
  • tests/e2e/orm/client-api/json-filter.test.ts (4 hunks)
  • tests/e2e/orm/policy/policy-functions.test.ts (2 hunks)
💤 Files with no reviewable changes (3)
  • packages/orm/src/client/crud/dialects/sqlite.ts
  • packages/language/package.json
  • packages/orm/src/client/crud/dialects/postgresql.ts
🧰 Additional context used
📓 Path-based instructions (1)
tests/e2e/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

E2E tests should validate real-world schema compatibility with established projects

Files:

  • tests/e2e/orm/policy/policy-functions.test.ts
  • tests/e2e/orm/client-api/json-filter.test.ts
  • tests/e2e/orm/client-api/filter.test.ts
🧠 Learnings (6)
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.test.{ts,tsx} : ORM package tests should include comprehensive client API tests and policy tests

Applied to files:

  • tests/e2e/orm/policy/policy-functions.test.ts
  • tests/e2e/orm/client-api/json-filter.test.ts
  • tests/e2e/orm/client-api/filter.test.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to tests/e2e/**/*.{ts,tsx} : E2E tests should validate real-world schema compatibility with established projects

Applied to files:

  • tests/e2e/orm/policy/policy-functions.test.ts
  • tests/e2e/orm/client-api/filter.test.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.{ts,tsx} : Use Kysely as the query builder interface for low-level database queries, avoiding raw SQL when possible

Applied to files:

  • packages/orm/src/client/functions.ts
  • packages/orm/src/client/crud/dialects/base-dialect.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.{ts,tsx} : Implement plugin hooks at ORM, Kysely, and entity mutation levels for query interception and customization

Applied to files:

  • packages/orm/src/client/functions.ts
  • packages/orm/src/client/crud/dialects/base-dialect.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to **/*.zmodel : ZModel schema files should define database structure and policies that compile to TypeScript via `zenstack generate`

Applied to files:

  • packages/orm/src/client/functions.ts
📚 Learning: 2025-10-21T16:04:56.292Z
Learnt from: ymc9
Repo: zenstackhq/zenstack-v3 PR: 319
File: packages/runtime/src/client/crud/dialects/base-dialect.ts:745-747
Timestamp: 2025-10-21T16:04:56.292Z
Learning: In packages/runtime/src/client/crud/dialects/base-dialect.ts, it's intentional that buildCursorFilter applies default ordering (via makeDefaultOrderBy fallback) while buildOrderBy does not. This ensures cursor-based pagination always has stable ordering for correctness, while regular queries remain unordered unless explicitly specified. This design is to be consistent with Prisma's pagination requirements.

Applied to files:

  • packages/orm/src/client/crud/dialects/base-dialect.ts
🧬 Code graph analysis (3)
tests/e2e/orm/policy/policy-functions.test.ts (1)
packages/testtools/src/client.ts (1)
  • createPolicyTestClient (258-269)
tests/e2e/orm/client-api/filter.test.ts (2)
packages/testtools/src/client.ts (1)
  • createTestClient (101-248)
tests/e2e/orm/client-api/utils.ts (1)
  • createUser (6-21)
packages/orm/src/client/crud/dialects/base-dialect.ts (2)
packages/orm/src/client/errors.ts (1)
  • createInvalidInputError (116-120)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Agent
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: build-test (20.x, postgresql)
  • GitHub Check: claude-review
🔇 Additional comments (10)
tests/e2e/orm/client-api/json-filter.test.ts (1)

495-622: Good test coverage for special character escaping.

The tests comprehensively verify that % is treated as a literal character across all JSON string filter operations (string_contains, string_starts_with, string_ends_with). The test data setup with '%Foo' enables both negative tests (pattern not matching when % should be literal) and positive tests (matching actual % characters in data).

tests/e2e/orm/policy/policy-functions.test.ts (1)

163-177: LGTM!

The startsWith escaping test properly validates that % at the beginning of the pattern is treated as a literal character.

tests/e2e/orm/client-api/filter.test.ts (4)

47-47: LGTM!

Adding a user with % in the email enables comprehensive testing of escaping behavior in string filters.


97-132: Comprehensive SQLite escaping tests.

Good coverage testing that % is treated as literal in contains and startsWith filters for SQLite.


179-192: Comprehensive PostgreSQL escaping tests.

Good coverage for case-insensitive mode with % escaping on PostgreSQL.


264-279: Correctly updated result counts.

The expected lengths are properly adjusted to account for the new %u3@test.com user:

  • lt: 'a@test.com' → 1 (only %u3@test.com is lexicographically less than a)
  • lt: 'z@test.com' → 3 (all users)
  • lte: 'u1@test.com' → 2 (%u3@test.com and u1@test.com)
  • lte: 'u2@test.com' → 3 (all users)
packages/orm/src/client/crud/dialects/base-dialect.ts (3)

842-867: Well-structured escaping implementation for JSON string filters.

The implementation correctly:

  1. Quotes the pattern ("%...%") since JSON strings are stored with quotes
  2. Uses consistent escaping logic via escapeLikePattern
  3. Properly handles case-insensitive mode by checking supportsILike

820-828: LGTM!

The buildStringFilter method now properly escapes patterns before constructing LIKE clauses, ensuring %, _, and \ are treated as literals.


544-544: Improved error messaging.

The updated error message provides clearer feedback when JSON filter payloads are invalid.

packages/orm/src/client/functions.ts (1)

56-65: Proper escaping implementation for LIKE patterns.

The escaping logic correctly:

  1. Escapes backslashes first (\\\\\\) to avoid double-escaping
  2. Then escapes % and _ wildcards
  3. Uses explicit ESCAPE '\\' clause for cross-database compatibility

The CAST(... as text) ensures the expression is treated as text before applying REPLACE. The ESCAPE clause syntax is supported by both SQLite and PostgreSQL, with backslash as a valid single-character escape character in each database.

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Dec 12, 2025

Claude finished @ymc9's task —— View job


PR Review: String Pattern Escaping Implementation

Review Tasks

  • Read and understand the changed files
  • Analyze core implementation in base-dialect.ts
  • Review string escaping logic in functions.ts
  • Evaluate test coverage and quality
  • Check for security vulnerabilities
  • Assess performance implications
  • Provide comprehensive feedback

Summary

This PR implements proper escaping of special characters (%, _, \) in string search patterns across the ORM's filter system and ZModel functions. This is a critical security fix to prevent SQL injection vulnerabilities in LIKE pattern matching.

Code Quality & Implementation ✅

Excellent Implementation Quality

The code changes demonstrate high-quality engineering:

  1. Centralized Escaping Logic: packages/orm/src/client/crud/dialects/base-dialect.ts:859-861

    private escapeLikePattern(pattern: string) {
        return pattern.replace(/\\/g, '\\\\').replace(/%/g, '\\%').replace(/_/g, '\\_');
    }
    • Proper order: escapes backslash first to avoid double-escaping
    • Comprehensive: handles all SQL LIKE wildcards
  2. Consistent Application: Used in both filter operations and ZModel functions

    • Filter system: base-dialect.ts:822-828 for contains, startsWith, endsWith
    • ZModel functions: functions.ts:56-65 for text matching operations
  3. Database Compatibility: Uses standard SQL ESCAPE '\\' clause for cross-database compatibility

Security Assessment ✅

Critical Vulnerability Fixed

This PR addresses a significant SQL injection vulnerability:

Before: User input like '% could match unintended records
After: Special characters are properly escaped, treating them as literal values

Example Attack Prevention:

-- Vulnerable (before): matches any string ending with 'x'
WHERE field LIKE '%admin%x%'

-- Secure (after): matches literal string '%admin%x'  
WHERE field LIKE '%\%admin\%x%' ESCAPE '\'

Proper Escaping Strategy

  • ✅ Backslash escaped first (\\\\\\)
  • ✅ Percent signs escaped (%\\%)
  • ✅ Underscores escaped (_\\_)
  • ✅ Uses SQL standard ESCAPE '\\' clause

Test Coverage Assessment ✅

Comprehensive Test Suite

Excellent test coverage across multiple areas:

  1. Direct Filter Tests: tests/e2e/orm/client-api/filter.test.ts:99-132

    • Tests % escaping in contains, startsWith operations
    • Validates literal character matching vs wildcard behavior
  2. JSON Filter Tests: tests/e2e/orm/client-api/json-filter.test.ts:514-622

    • Tests escaping in JSON path string filters
    • Covers string_contains, string_starts_with, string_ends_with
  3. Policy Function Tests: tests/e2e/orm/policy/policy-functions.test.ts:26-41

    • Tests escaping in ZModel policy functions
    • Validates security in access control scenarios

Real-world Test Scenarios

Tests include realistic attack patterns:

  • Emails with % characters: '%u3@test.com'
  • Names with special chars: '%Foo', '%Jane'
  • Explicit wildcard prevention testing

Performance Implications ✅

Minimal Performance Impact

  1. Simple String Operations: Only basic regex replacements
  2. Applied Only When Needed: Escaping occurs during query building, not on every query
  3. No Additional Database Round-trips: Uses standard SQL ESCAPE clause

Negligible Overhead

The escaping logic adds ~3 regex operations per string filter, which is insignificant compared to database query overhead.

Architecture & Design ✅

Clean Architecture

  1. Single Responsibility: escapeLikePattern() method has one clear purpose
  2. DRY Principle: Shared logic between filter system and functions
  3. Consistent API: Same escaping behavior across all string operations

Backward Compatibility

  • No breaking changes to public APIs
  • Existing queries work identically for non-special characters
  • Only changes behavior for previously vulnerable patterns

Minor Recommendations

  1. Documentation: Consider adding inline comments explaining the escaping order
  2. Future Enhancement: Could add configuration option for custom escape characters if needed

Overall Assessment

This is an excellent, security-critical fix that should be merged immediately.

Strengths:

  • ✅ Fixes critical SQL injection vulnerability
  • ✅ Comprehensive test coverage
  • ✅ Clean, maintainable implementation
  • ✅ Consistent cross-database behavior
  • ✅ Minimal performance impact
  • ✅ No breaking changes

Security Impact:

  • High: Prevents SQL injection in string pattern matching
  • Critical for Production: Essential for any application using string filters

This PR demonstrates exemplary security engineering practices with thorough testing and clean implementation.


Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
tests/e2e/orm/client-api/filter.test.ts (2)

97-142: Good coverage for contains and startsWith escape behavior.

These tests effectively validate that % is treated as a literal character. The u3a vs u3% assertions confirm the wildcard is properly escaped.

Consider adding endsWith escape tests for completeness (e.g., endsWith: '%@test.com' should not match, endsWith: '@test.com' should match all).


44-365: Consider adding tests for underscore (_) escape handling.

The tests thoroughly validate % escape behavior. Since _ is also a SQL LIKE wildcard (matches any single character), consider adding a test user with _ in the email (e.g., u4_@test.com) to verify complete escape coverage.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d80270 and ca27f10.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/filter.test.ts (10 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
tests/e2e/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

E2E tests should validate real-world schema compatibility with established projects

Files:

  • tests/e2e/orm/client-api/filter.test.ts
🧠 Learnings (2)
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to packages/zenstackhq/orm/**/*.test.{ts,tsx} : ORM package tests should include comprehensive client API tests and policy tests

Applied to files:

  • tests/e2e/orm/client-api/filter.test.ts
📚 Learning: 2025-11-26T01:55:04.540Z
Learnt from: CR
Repo: zenstackhq/zenstack-v3 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T01:55:04.540Z
Learning: Applies to tests/e2e/**/*.{ts,tsx} : E2E tests should validate real-world schema compatibility with established projects

Applied to files:

  • tests/e2e/orm/client-api/filter.test.ts
🧬 Code graph analysis (1)
tests/e2e/orm/client-api/filter.test.ts (1)
packages/testtools/src/client.ts (1)
  • createTestClient (101-248)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: claude-review
  • GitHub Check: build-test (20.x, postgresql)
  • GitHub Check: build-test (20.x, sqlite)
🔇 Additional comments (6)
tests/e2e/orm/client-api/filter.test.ts (6)

10-10: LGTM!

Removing the redundant as any cast is a good cleanup since createTestClient already returns a properly typed client.


44-47: Good test data for escape validation.

Adding a user with % in the email is essential for validating that the ORM correctly escapes LIKE pattern special characters.


189-210: LGTM!

Good parity with SQLite escape tests, now covering PostgreSQL's case-insensitive mode. The assertions correctly validate that % is escaped in the ILIKE pattern.


265-269: LGTM!

Correctly updated to exclude all three users, maintaining the test's intent of verifying that notIn with all users returns falsy.


276-316: LGTM!

The length expectations are correctly updated to account for the third user. The alphabetical ordering (u3%@test.com comes after u2@test.com because '3' > '2') is correctly reflected in the gte assertions.


324-328: LGTM!

Using '4@' instead of '3@' is a reasonable change that makes the test's intent clearer—searching for a pattern that definitely doesn't exist in any of the test emails.

@ymc9 ymc9 merged commit 293cf0d into dev Dec 12, 2025
6 checks passed
@ymc9 ymc9 deleted the fix/escape-pattern branch December 12, 2025 14:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants