Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Oct 7, 2025

Summary by CodeRabbit

  • New Features

    • Rich, attribute-driven validations added for create/update: string helpers (length, startsWith, endsWith, contains, regex, email, url, datetime, trim, lower, upper), list checks (has/hasEvery/hasSome/isEmpty), and numeric comparisons including BigInt and Decimal.
    • Support for custom, expression-based validations with precise error path reporting.
  • Bug Fixes / Improvements

    • Validation error messages now include model context and accurate paths.
  • Tests

    • Added comprehensive end-to-end validation tests and a new test matcher to assert validation failures.
  • Chores

    • Minor import and internal consistency cleanups.

Copilot AI review requested due to automatic review settings October 7, 2025 02:37
@coderabbitai
Copy link

coderabbitai bot commented Oct 7, 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

Adds a runtime attribute-based validation engine and integrates it into CRUD validators, updates numeric validation signatures, refactors compound-unique filter flattening, makes exists delegate to readUnique, adds test-matchers/helpers, and introduces comprehensive E2E validation tests.

Changes

Cohort / File(s) Summary
Validator framework & custom validation engine
packages/runtime/src/client/crud/validator/index.ts, packages/runtime/src/client/crud/validator/utils.ts
Adds attribute-driven validators for strings, numbers, bigint, decimal; implements a custom @@Validate expression evaluator and runtime interpreter; integrates attribute-based validations into create/update/find schemas and improves validation error messages with model context.
CRUD exists refactor
packages/runtime/src/client/crud/operations/base.ts
Replaces manual select/where construction in exists with delegation to readUnique, returning the first matching id or null.
Query filter flattening
packages/runtime/src/client/query-utils.ts
Reworks flattenCompoundUniqueFilters to produce separate flattened and rest maps, handling overlaps by combining via AND or safe merge.
Language stdlib numeric validators
packages/language/res/stdlib.zmodel
Changes @gt/@gte/@lt/@LTe attribute signatures to accept Any and include BigIntField as a valid target.
Runtime contract import
packages/runtime/src/client/contract.ts
Switches Decimal to a default import from decimal.js (type import change only).
Test tools: matchers & helpers
packages/testtools/src/types.d.ts, packages/testtools/src/vitest-ext.ts
Adds toBeRejectedByValidation matcher and shared expectErrorMessages helper; updates imports to include InputValidationError.
E2E validation tests
tests/e2e/orm/validation/toplevel.test.ts, tests/e2e/orm/validation/nested.test.ts, tests/e2e/orm/validation/custom-validation.test.ts
Introduces comprehensive tests for top-level, nested, and custom validations (strings, numbers, bigint, decimal, lists, custom messages) across create/update/upsert/updateMany paths.
Import reorder in tests
tests/e2e/orm/client-api/compound-id.test.ts, tests/e2e/orm/client-api/type-coverage.test.ts
Reorders imports only; no functional changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Poem

I hopped through schemas, nibbling attributes bright,
I stitched Decimal and BigInt in the pale moonlight.
I flattened filters, then taught exists to read,
I chased validation rules with a twitch of my speed.
Tests bloom like clover — a rabbit’s small delight. 🐇✨

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 title concisely and accurately describes the primary feature introduced by the changeset, namely the implementation of comprehensive field validation across various data types and workflows. It uses a standard commit style prefix, remains focused on the main change, and avoids unnecessary detail. This allows team members to quickly understand the pull request’s intent without being misled by extraneous information.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ 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 feat/validation

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

claude bot commented Oct 7, 2025

Claude finished @ymc9's task —— View job


PR Review: Field Validation Implementation

I've completed my review of this field validation implementation. This is a comprehensive addition that adds Zod-based input validation to the ZenStack runtime. Here's my detailed feedback:

Todo List:

  • Read and understand the validation implementation
  • Review core validator changes
  • Analyze new utility functions
  • Review test coverage and quality
  • Check for potential bugs and security issues
  • Evaluate performance considerations
  • Provide comprehensive feedback

Strengths

Robust Architecture

  • Well-structured validation layer: The InputValidator class provides a clean abstraction over Zod schemas with proper caching
  • Comprehensive coverage: Validates all CRUD operations (create, update, upsert, delete, etc.) and query operations
  • Type-safe implementation: Strong TypeScript integration with generic schema types

Excellent Field Support

  • Built-in validators: Comprehensive string validators (@email, @url, @regex, @length, etc.) and number validators (@gt, @lt, etc.)
  • Custom validation: Support for @@validate with expression evaluation for complex business rules
  • Array/list validation: Proper handling of array fields with has, hasEvery, hasSome, isEmpty functions

Integration Quality

  • Seamless integration: Validation is properly integrated into the base operation handler at /packages/runtime/src/client/crud/operations/base.ts:46
  • Error handling: Consistent InputValidationError usage with proper error propagation
  • Schema caching: Smart caching strategy to avoid rebuilding Zod schemas repeatedly

⚠️ Issues & Recommendations

Code Quality Issues

  1. Duplicate Code in /packages/runtime/src/client/crud/validator/utils.ts:109-114:

    .with('@lt', () => {
        schema = schema.lt(val);
    })
    .with('@lte', () => {
        schema = schema.lte(val);
    })

    These cases are duplicated. Should be removed.

  2. Missing Error Handling in Expression Evaluation:

    • evalExpression() could throw for malformed expressions, but errors aren't caught
    • Consider wrapping in try-catch with more descriptive error messages
  3. Unsafe Type Assertions:

    // Line 191 in validator/index.ts
    const cacheKey = stableStringify({...});
    this.schemaCache.set(cacheKey!, schema);  // cacheKey could be undefined

Performance Concerns

  1. Expression Evaluation Overhead:

    • Custom validation expressions are evaluated on every validation
    • Consider pre-compiling or memoizing expression functions for better performance
  2. Schema Cache Key Generation:

    • Using stableStringify for cache keys could be expensive for complex schemas
    • Consider using object references or simpler hash functions

Security Considerations

  1. Regex Pattern Safety:

    // Line 61 in utils.ts
    schema = schema.regex(new RegExp(pattern));

    No validation of regex patterns - could allow ReDoS attacks with complex patterns

  2. Expression Injection:

    • Custom validation expressions allow arbitrary code execution
    • While this appears intentional, ensure proper sandboxing in production

Missing Features

  1. Internationalization: Error messages are hardcoded in English
  2. Async Validation: No support for async validators (e.g., unique field checks)
  3. Conditional Validation: Limited support for field-dependent validation rules

🧪 Test Coverage Assessment

Excellent Coverage

  • Comprehensive test suites: Well-structured tests covering string, number, and custom validations
  • Integration tests: Tests validate real CRUD operations, not just isolated validators
  • Edge cases: Good coverage of null/undefined handling, array validations, nested objects
  • Error validation: Proper testing of validation failures with toBeRejectedByValidation()

Test Quality Highlights

  • Multiple operation testing: Tests validate across create, update, upsert, updateMany operations
  • Custom matcher support: Added toBeRejectedByValidation() matcher for consistent testing
  • Real-world scenarios: Tests include nested relations and complex validation rules

🔧 Suggested Improvements

Immediate Fixes

  1. Remove duplicate @lt/@lte cases in utils.ts:109-114
  2. Add null check for cacheKey in validator/index.ts:191
  3. Add regex validation to prevent ReDoS attacks

Performance Optimizations

  1. Implement expression compilation for custom validators
  2. Use simpler cache keys or object-based memoization
  3. Add validation short-circuiting for null/undefined values early

Feature Enhancements

  1. Add async validation support for database-dependent checks
  2. Implement validation context for field interdependencies
  3. Add validation error aggregation for better user experience

📊 Overall Assessment

Score: 8.5/10

This is a high-quality implementation that significantly enhances ZenStack's data integrity capabilities. The validation system is comprehensive, well-tested, and properly integrated into the existing architecture.

Key Strengths:

  • Robust validation framework with excellent test coverage
  • Clean integration with existing CRUD operations
  • Comprehensive field type support
  • Strong TypeScript integration

Main Concerns:

  • Minor code quality issues (duplicates, unsafe assertions)
  • Performance considerations for complex schemas
  • Security implications of regex patterns

Recommendation:

Approve with minor fixes. The implementation is solid and ready for production with the suggested improvements. The duplicate code removal and null checks should be addressed before merging.


Copy link
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 comprehensive field validation functionality, introducing both built-in validators for primitive types and custom validation rules for models.

Key changes:

  • Added built-in validation attributes (@Length, @email, @url, etc.) for string and number fields
  • Implemented custom model-level validation using @@Validate attribute with expression evaluation
  • Enhanced test framework with new validation assertion methods

Reviewed Changes

Copilot reviewed 9 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/e2e/orm/validation/toplevel.test.ts Tests for field-level validation attributes on string and number types
tests/e2e/orm/validation/nested.test.ts Tests for validation in nested create/update operations
tests/e2e/orm/validation/custom-validation.test.ts Tests for custom validation rules using @@Validate attribute
tests/e2e/orm/client-api/compound-id.test.ts Fixed import order
packages/testtools/src/vitest-ext.ts Added validation test helpers and error message checking
packages/runtime/src/client/query-utils.ts Enhanced compound unique filter flattening logic
packages/runtime/src/client/crud/validator/utils.ts Core validation utility functions for attributes and expression evaluation
packages/runtime/src/client/crud/validator/index.ts Integrated validation into the input validator system
packages/runtime/src/client/crud/operations/base.ts Simplified exists check implementation
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link

@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: 4

🧹 Nitpick comments (1)
packages/runtime/src/client/query-utils.ts (1)

313-313: Consider validating that value is a plain object before Object.assign.

Object.assign(flattenedResult, value) assumes value is a plain object. If the input filter is malformed and value is null, undefined, or a primitive, Object.assign will silently produce unexpected results (e.g., spreading string characters as numbered keys). While the function's contract expects proper input, a type guard adds robustness.

Apply this diff to add a validation check:

         if (compoundUniques.some(({ name }) => name === key)) {
             // flatten the compound field
+            if (typeof value !== 'object' || value === null || Array.isArray(value)) {
+                throw new QueryError(`Invalid compound unique filter value for "${key}"`);
+            }
             Object.assign(flattenedResult, value);
📜 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 f1a8cef and 9f5e0a7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • packages/runtime/src/client/crud/operations/base.ts (1 hunks)
  • packages/runtime/src/client/crud/validator/index.ts (8 hunks)
  • packages/runtime/src/client/crud/validator/utils.ts (1 hunks)
  • packages/runtime/src/client/query-utils.ts (1 hunks)
  • packages/testtools/src/types.d.ts (1 hunks)
  • packages/testtools/src/vitest-ext.ts (4 hunks)
  • tests/e2e/orm/client-api/compound-id.test.ts (1 hunks)
  • tests/e2e/orm/validation/custom-validation.test.ts (1 hunks)
  • tests/e2e/orm/validation/nested.test.ts (1 hunks)
  • tests/e2e/orm/validation/toplevel.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/runtime/src/client/query-utils.ts
  • packages/runtime/src/client/crud/operations/base.ts
  • tests/e2e/orm/client-api/compound-id.test.ts
  • packages/testtools/src/types.d.ts
  • tests/e2e/orm/validation/nested.test.ts
  • tests/e2e/orm/validation/toplevel.test.ts
  • packages/runtime/src/client/crud/validator/index.ts
  • packages/testtools/src/vitest-ext.ts
  • tests/e2e/orm/validation/custom-validation.test.ts
  • packages/runtime/src/client/crud/validator/utils.ts
tests/e2e/**

📄 CodeRabbit inference engine (CLAUDE.md)

End-to-end tests must live under tests/e2e/

Files:

  • tests/e2e/orm/client-api/compound-id.test.ts
  • tests/e2e/orm/validation/nested.test.ts
  • tests/e2e/orm/validation/toplevel.test.ts
  • tests/e2e/orm/validation/custom-validation.test.ts
🧬 Code graph analysis (6)
tests/e2e/orm/validation/nested.test.ts (1)
packages/testtools/src/client.ts (1)
  • createTestClient (51-167)
tests/e2e/orm/validation/toplevel.test.ts (1)
packages/testtools/src/client.ts (1)
  • createTestClient (51-167)
packages/runtime/src/client/crud/validator/index.ts (4)
packages/runtime/src/client/errors.ts (1)
  • InputValidationError (9-13)
packages/runtime/src/utils/zod-utils.ts (1)
  • formatError (8-14)
packages/sdk/src/schema/schema.ts (1)
  • AttributeApplication (39-42)
packages/runtime/src/client/crud/validator/utils.ts (3)
  • addStringValidation (23-84)
  • addNumberValidation (86-117)
  • addCustomValidation (119-139)
packages/testtools/src/vitest-ext.ts (1)
packages/runtime/src/client/errors.ts (1)
  • InputValidationError (9-13)
tests/e2e/orm/validation/custom-validation.test.ts (1)
packages/testtools/src/client.ts (1)
  • createTestClient (51-167)
packages/runtime/src/client/crud/validator/utils.ts (5)
packages/runtime/src/schema/expression.ts (1)
  • ExpressionUtils (16-120)
packages/sdk/src/schema/schema.ts (1)
  • AttributeApplication (39-42)
packages/sdk/src/schema/expression.ts (5)
  • FieldExpression (22-25)
  • UnaryExpression (33-37)
  • BinaryExpression (39-44)
  • MemberExpression (27-31)
  • CallExpression (46-50)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
packages/runtime/src/client/errors.ts (1)
  • QueryError (18-22)
🪛 ast-grep (0.39.5)
packages/runtime/src/client/crud/validator/utils.ts

[warning] 60-60: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 282-282: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ 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: build-test (20.x, postgresql)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: claude-review
🔇 Additional comments (3)
tests/e2e/orm/client-api/compound-id.test.ts (1)

2-2: Import order change looks good.

Reads cleanly and keeps vitest imports grouped.

packages/runtime/src/client/crud/operations/base.ts (1)

134-137: LGTM! Good refactor to eliminate duplication.

Delegating to readUnique rather than manually constructing a query reduces code duplication and improves maintainability. The behavior remains equivalent—both approaches return the entity's id fields (or null/undefined when not found)—and all call sites already check for truthy/falsy values, so the change is safe.

packages/runtime/src/client/query-utils.ts (1)

307-337: LGTM! The refactored merge logic correctly handles edge cases.

The separation into flattenedResult and restFilter clarifies intent, and the four-way branching properly handles:

  • Empty flattening (return original)
  • Complete flattening (return flattened)
  • Key overlap (AND clause prevents conflicting conditions)
  • Safe merge (no overlap)

The AND clause generation for overlapping keys (lines 328-332) is particularly important—it ensures that impossible conditions like { id1: 1, id1: 3 } are represented as an explicit AND that returns no results, rather than silently overwriting values.

@claude
Copy link

claude bot commented Oct 8, 2025

Claude encountered an error —— View job


I'll analyze this and get back to you.

Copy link

@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: 4

♻️ Duplicate comments (2)
tests/e2e/orm/validation/custom-validation.test.ts (1)

94-95: Good fix: explicit assertion added.

expect(thrown).toBe(true); ensures the test fails if no error is thrown.

packages/runtime/src/client/crud/validator/index.ts (1)

254-270: Primitive validators applied consistently.

String/Int/Float/BigInt/Decimal now flow through attribute validators. Nice catch on Float.

🧹 Nitpick comments (3)
packages/runtime/src/client/crud/validator/index.ts (1)

1141-1157: Update operators for Decimal/BigInt accept only numbers.

set/increment/decrement/multiply/divide are typed as z.number(), which loses precision for Decimal and can’t represent large BigInt safely.

Consider widening per field type (sketch):

-                                set: this.nullableIf(z.number().optional(), !!fieldDef.optional),
-                                increment: z.number().optional(),
+                                set: this.nullableIf(
+                                  match(fieldDef.type)
+                                    .with('BigInt', () => z.bigint().or(z.number().int()))
+                                    .with('Decimal', () => z.union([z.instanceof(Decimal), z.string(), z.number()]))
+                                    .otherwise(() => z.number())
+                                    .optional(),
+                                  !!fieldDef.optional,
+                                ),
+                                increment: match(fieldDef.type)
+                                  .with('BigInt', () => z.bigint().or(z.number().int()))
+                                  .with('Decimal', () => z.union([z.instanceof(Decimal), z.string(), z.number()]))
+                                  .otherwise(() => z.number())
+                                  .optional(),
                                 decrement: z.number().optional(),
                                 multiply: z.number().optional(),
                                 divide: z.number().optional(),
packages/runtime/src/client/crud/validator/utils.ts (2)

121-141: BigInt thresholds parsed from number can be unsafe.

Use string-or-number for the argument and parse via BigInt(); numbers beyond 2^53-1 will be lossy.

-        const val = getArgValue<number>(attr.args?.[0]?.value);
-        if (val === undefined) {
+        const raw = attr.args?.[0]?.value;
+        const val = ExpressionUtils.isLiteral(raw) ? raw.value : undefined;
+        if (val === undefined || (typeof val !== 'number' && typeof val !== 'string')) {
             continue;
         }
-        const bigIntVal = BigInt(val);
+        const bigIntVal = BigInt(val as any);

61-64: Guard against ReDoS from untrusted regex patterns.

new RegExp(pattern) on user-provided patterns can be abused. Consider validating patterns (e.g., safe-regex) or limiting length/flags.

Also applies to: 368-371

📜 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 9f5e0a7 and c52b1df.

📒 Files selected for processing (12)
  • packages/language/res/stdlib.zmodel (1 hunks)
  • packages/runtime/src/client/crud/dialects/postgresql.ts (1 hunks)
  • packages/runtime/src/client/crud/dialects/sqlite.ts (1 hunks)
  • packages/runtime/src/client/crud/validator/index.ts (8 hunks)
  • packages/runtime/src/client/crud/validator/utils.ts (1 hunks)
  • packages/runtime/src/client/query-builder.ts (1 hunks)
  • packages/runtime/src/utils/type-utils.ts (1 hunks)
  • packages/sdk/src/schema/schema.ts (1 hunks)
  • tests/e2e/orm/client-api/type-coverage.test.ts (1 hunks)
  • tests/e2e/orm/validation/custom-validation.test.ts (1 hunks)
  • tests/e2e/orm/validation/nested.test.ts (1 hunks)
  • tests/e2e/orm/validation/toplevel.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/orm/validation/toplevel.test.ts
  • tests/e2e/orm/validation/nested.test.ts
🧰 Additional context used
📓 Path-based instructions (3)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/runtime/src/client/crud/dialects/sqlite.ts
  • tests/e2e/orm/client-api/type-coverage.test.ts
  • packages/language/res/stdlib.zmodel
  • packages/runtime/src/client/crud/dialects/postgresql.ts
  • tests/e2e/orm/validation/custom-validation.test.ts
  • packages/runtime/src/client/crud/validator/index.ts
  • packages/runtime/src/client/crud/validator/utils.ts
  • packages/sdk/src/schema/schema.ts
  • packages/runtime/src/client/query-builder.ts
  • packages/runtime/src/utils/type-utils.ts
tests/e2e/**

📄 CodeRabbit inference engine (CLAUDE.md)

End-to-end tests must live under tests/e2e/

Files:

  • tests/e2e/orm/client-api/type-coverage.test.ts
  • tests/e2e/orm/validation/custom-validation.test.ts
**/schema.ts

📄 CodeRabbit inference engine (CLAUDE.md)

The generated TypeScript schema should be named schema.ts

Files:

  • packages/sdk/src/schema/schema.ts
🧬 Code graph analysis (3)
tests/e2e/orm/validation/custom-validation.test.ts (1)
packages/testtools/src/client.ts (1)
  • createTestClient (51-167)
packages/runtime/src/client/crud/validator/index.ts (4)
packages/runtime/src/client/errors.ts (1)
  • InputValidationError (9-13)
packages/runtime/src/utils/zod-utils.ts (1)
  • formatError (8-14)
packages/sdk/src/schema/schema.ts (1)
  • AttributeApplication (39-42)
packages/runtime/src/client/crud/validator/utils.ts (5)
  • addStringValidation (24-86)
  • addNumberValidation (88-114)
  • addBigIntValidation (116-143)
  • addDecimalValidation (145-203)
  • addCustomValidation (205-226)
packages/runtime/src/client/crud/validator/utils.ts (5)
packages/runtime/src/schema/expression.ts (1)
  • ExpressionUtils (16-120)
packages/sdk/src/schema/schema.ts (1)
  • AttributeApplication (39-42)
packages/sdk/src/schema/expression.ts (5)
  • FieldExpression (22-25)
  • UnaryExpression (33-37)
  • BinaryExpression (39-44)
  • MemberExpression (27-31)
  • CallExpression (46-50)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
packages/runtime/src/client/errors.ts (1)
  • QueryError (18-22)
🪛 ast-grep (0.39.5)
packages/runtime/src/client/crud/validator/utils.ts

[warning] 62-62: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 369-369: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ 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). (2)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: build-test (20.x, postgresql)
🔇 Additional comments (1)
packages/runtime/src/utils/type-utils.ts (1)

1-1: No change needed for Decimal import
decimal.js v10.x supports both named and default imports, so import type { Decimal } from 'decimal.js' is valid.

Likely an incorrect or invalid review comment.

@claude
Copy link

claude bot commented Oct 8, 2025

Claude encountered an error —— View job


I'll analyze this and get back to you.

Copy link

@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: 1

♻️ Duplicate comments (6)
packages/runtime/src/client/crud/validator/utils.ts (6)

99-111: ts-pattern match never executes: add .exhaustive() or .otherwise().

Same issue as lines 31-83. None of the numeric validation handlers run without a terminal call.

Apply this fix:

         match(attr.name)
             .with('@gt', () => {
                 result = result.gt(val);
             })
             .with('@gte', () => {
                 result = result.gte(val);
             })
             .with('@lt', () => {
                 result = result.lt(val);
             })
-            .with('@lte', () => {
+            .with('@lte', () => {
                 result = result.lte(val);
-            });
+            })
+            .otherwise(() => undefined);

128-140: ts-pattern match never executes: add .exhaustive() or .otherwise().

Same issue as lines 31-83. None of the BigInt validation handlers run without a terminal call.

Apply this fix:

         match(attr.name)
             .with('@gt', () => {
                 result = result.gt(bigIntVal);
             })
             .with('@gte', () => {
                 result = result.gte(bigIntVal);
             })
             .with('@lt', () => {
                 result = result.lt(bigIntVal);
             })
-            .with('@lte', () => {
+            .with('@lte', () => {
                 result = result.lte(bigIntVal);
-            });
+            })
+            .otherwise(() => undefined);

186-198: ts-pattern match never executes: add .exhaustive() or .otherwise().

Same issue as lines 31-83. None of the Decimal validation handlers run without a terminal call.

Apply this fix:

             match(attr.name)
                 .with('@gt', () => {
                     result = refine(result, 'gt', val);
                 })
                 .with('@gte', () => {
                     result = refine(result, 'gte', val);
                 })
                 .with('@lt', () => {
                     result = refine(result, 'lt', val);
                 })
-                .with('@lte', () => {
+                .with('@lte', () => {
                     result = refine(result, 'lte', val);
-                });
+                })
+                .otherwise(() => undefined);

363-371: RegExp from user input: ReDoS risk.

Same ReDoS vulnerability as line 62. Pattern comes from user input without validation.

Consider validating pattern safety:

.with('regex', (f) => {
    if (fieldArg === undefined || fieldArg === null) {
        return false;
    }
    invariant(typeof fieldArg === 'string', `"${f}" first argument must be a string`);
    const pattern = getArgValue<string>(expr.args?.[1])!;
    invariant(pattern !== undefined, `"${f}" requires a pattern argument`);
    // TODO: validate pattern safety before constructing RegExp
    return new RegExp(pattern).test(fieldArg);
})

Based on static analysis hints: [CWE-1333].


169-177: Decimal comparison loses precision: use Decimal arithmetic.

Converting Decimal to number via .toNumber() discards precision for large values and exponents. Despite past review feedback, this has not been fixed.

Apply this fix to preserve precision:

-    function refine(schema: z.ZodSchema, op: 'gt' | 'gte' | 'lt' | 'lte', value: number) {
+    function refine(schema: z.ZodSchema, op: 'gt' | 'gte' | 'lt' | 'lte', value: number | string) {
+        const threshold = new Decimal(value);
         return schema.superRefine((v, ctx) => {
-            const base = z.number();
-            const { error } = base[op](value).safeParse((v as Decimal).toNumber());
-            error?.errors.forEach((e) => {
-                ctx.addIssue(e);
-            });
+            const dec = v as Decimal;
+            const cmp = dec.comparedTo(threshold);
+            const ok =
+                (op === 'gt' && cmp > 0) ||
+                (op === 'gte' && cmp >= 0) ||
+                (op === 'lt' && cmp < 0) ||
+                (op === 'lte' && cmp <= 0);
+            if (!ok) {
+                ctx.addIssue({
+                    code: z.ZodIssueCode.custom,
+                    message: `Expected ${op} ${threshold.toString()}`,
+                });
+            }
         });
     }

31-83: ts-pattern match never executes: add .exhaustive() or .otherwise().

The match chain is built but never finalized, so none of the validation handlers run. Despite a past review claiming this was fixed in c52b1df, the code still lacks a terminal call.

Apply this fix:

         match(attr.name)
             .with('@length', () => {
                 const min = getArgValue<number>(attr.args?.[0]?.value);
                 if (min !== undefined) {
                     result = result.min(min);
                 }
                 const max = getArgValue<number>(attr.args?.[1]?.value);
                 if (max !== undefined) {
                     result = result.max(max);
                 }
             })
             .with('@startsWith', () => {
                 const value = getArgValue<string>(attr.args?.[0]?.value);
                 if (value !== undefined) {
                     result = result.startsWith(value);
                 }
             })
             .with('@endsWith', () => {
                 const value = getArgValue<string>(attr.args?.[0]?.value);
                 if (value !== undefined) {
                     result = result.endsWith(value);
                 }
             })
             .with('@contains', () => {
                 const value = getArgValue<string>(attr.args?.[0]?.value);
                 if (value !== undefined) {
                     result = result.includes(value);
                 }
             })
             .with('@regex', () => {
                 const pattern = getArgValue<string>(attr.args?.[0]?.value);
                 if (pattern !== undefined) {
                     result = result.regex(new RegExp(pattern));
                 }
             })
             .with('@email', () => {
                 result = result.email();
             })
             .with('@datetime', () => {
                 result = result.datetime();
             })
             .with('@url', () => {
                 result = result.url();
             })
             .with('@trim', () => {
                 result = result.trim();
             })
             .with('@lower', () => {
                 result = result.toLowerCase();
             })
-            .with('@upper', () => {
+            .with('@upper', () => {
                 result = result.toUpperCase();
-            });
+            })
+            .otherwise(() => undefined);
     }
🧹 Nitpick comments (1)
tests/e2e/orm/validation/toplevel.test.ts (1)

117-142: Use BigInt literals consistently.

Lines 135 and 138 correctly use BigInt literals (1n, 5n), but line 141 uses regular numbers for BigInt fields. While runtime coercion may handle this, using BigInt literals consistently improves test clarity and explicitly matches the field types.

Apply this diff for consistency:

     // satisfies all
-    await expect(db.foo.create({ data: { int1: 3, int2: 4 } })).toResolveTruthy();
+    await expect(db.foo.create({ data: { int1: 3n, int2: 4n } })).toResolveTruthy();
📜 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 c52b1df and c618515.

📒 Files selected for processing (6)
  • packages/language/res/stdlib.zmodel (1 hunks)
  • packages/runtime/src/client/contract.ts (1 hunks)
  • packages/runtime/src/client/crud/validator/index.ts (8 hunks)
  • packages/runtime/src/client/crud/validator/utils.ts (1 hunks)
  • tests/e2e/orm/client-api/type-coverage.test.ts (1 hunks)
  • tests/e2e/orm/validation/toplevel.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/language/res/stdlib.zmodel
🧰 Additional context used
📓 Path-based instructions (2)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • tests/e2e/orm/validation/toplevel.test.ts
  • packages/runtime/src/client/crud/validator/utils.ts
  • packages/runtime/src/client/contract.ts
  • packages/runtime/src/client/crud/validator/index.ts
  • tests/e2e/orm/client-api/type-coverage.test.ts
tests/e2e/**

📄 CodeRabbit inference engine (CLAUDE.md)

End-to-end tests must live under tests/e2e/

Files:

  • tests/e2e/orm/validation/toplevel.test.ts
  • tests/e2e/orm/client-api/type-coverage.test.ts
🧬 Code graph analysis (3)
tests/e2e/orm/validation/toplevel.test.ts (1)
packages/testtools/src/client.ts (1)
  • createTestClient (51-167)
packages/runtime/src/client/crud/validator/utils.ts (5)
packages/runtime/src/schema/expression.ts (1)
  • ExpressionUtils (16-120)
packages/sdk/src/schema/schema.ts (1)
  • AttributeApplication (39-42)
packages/sdk/src/schema/expression.ts (5)
  • FieldExpression (22-25)
  • UnaryExpression (33-37)
  • BinaryExpression (39-44)
  • MemberExpression (27-31)
  • CallExpression (46-50)
packages/common-helpers/src/tiny-invariant.ts (1)
  • invariant (4-14)
packages/runtime/src/client/errors.ts (1)
  • QueryError (18-22)
packages/runtime/src/client/crud/validator/index.ts (4)
packages/runtime/src/client/errors.ts (1)
  • InputValidationError (9-13)
packages/runtime/src/utils/zod-utils.ts (1)
  • formatError (8-14)
packages/sdk/src/schema/schema.ts (1)
  • AttributeApplication (39-42)
packages/runtime/src/client/crud/validator/utils.ts (5)
  • addStringValidation (24-86)
  • addNumberValidation (88-114)
  • addBigIntValidation (116-143)
  • addDecimalValidation (145-203)
  • addCustomValidation (205-226)
🪛 ast-grep (0.39.5)
packages/runtime/src/client/crud/validator/utils.ts

[warning] 62-62: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 369-369: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(pattern)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ 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). (2)
  • GitHub Check: build-test (20.x, postgresql)
  • GitHub Check: build-test (20.x, sqlite)
🔇 Additional comments (11)
tests/e2e/orm/client-api/type-coverage.test.ts (1)

1-1: LGTM!

The import reordering improves code organization by grouping project-internal imports first.

tests/e2e/orm/validation/toplevel.test.ts (4)

1-4: LGTM!

Imports are appropriate for validation testing, and the file location complies with the coding guidelines for e2e tests.


6-88: Excellent string validation coverage!

The test comprehensively exercises all string validators across multiple CRUD actions with proper boundary testing and transformation verification. The distinction between updateMany's count response and other operations' object responses is correctly handled.


90-115: LGTM!

Numeric validator boundary conditions are correctly tested with proper violation and acceptance cases.


144-172: LGTM!

Decimal validation correctly tests numeric comparisons with diverse input formats (numbers, strings, Decimal objects) and properly validates both boundary violations and invalid decimal strings.

packages/runtime/src/client/contract.ts (1)

1-1: LGTM: Correct import style for decimal.js.

The change from a named type import to a default type import is correct. Based on learnings, decimal.js exports Decimal as a default export, not a named export.

packages/runtime/src/client/crud/validator/utils.ts (1)

244-256: Good use of exhaustive ts-pattern matching.

The expression evaluator correctly uses .exhaustive() to ensure all cases are handled and the match chain executes.

packages/runtime/src/client/crud/validator/index.ts (4)

2-2: LGTM: Correct default import for Decimal.

The default import style is correct for decimal.js, consistent with the fix in contract.ts.


249-275: Good integration of attribute-based validation.

The primitive schema builder now correctly accepts attributes and pipes them through validation helpers (addStringValidation, addNumberValidation, addBigIntValidation, addDecimalValidation). This addresses the past review concern that Float fields weren't validated.


202-209: Improved error messaging.

The validation error now includes the model name, making debugging easier. The switch to destructured { error, data } from safeParse also improves clarity.


885-885: Good integration of field-level and model-level validation.

Field attributes are correctly passed through makePrimitiveSchema, and model-level custom validations are properly wrapped with addCustomValidation for both checked and unchecked schema variants.

Also applies to: 914-926

@claude
Copy link

claude bot commented Oct 8, 2025

Claude encountered an error —— View job


I'll analyze this and get back to you.

Copy link

@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: 1

🧹 Nitpick comments (1)
tests/e2e/orm/validation/toplevel.test.ts (1)

23-87: Consider extracting action test helper to reduce duplication.

The action loop pattern (lines 23-87) is repeated across test cases and creates a somewhat complex nested structure. While functional, extracting this into a reusable test helper function would improve maintainability and readability, especially if similar patterns appear in other validation test files.

Example refactor:

// Helper function to test across all actions
async function testActions(db: any, testCases: Array<{ data: any; shouldPass: boolean; errorMsgs?: string[] }>) {
  for (const action of ['create', 'update', 'upsert', 'updateMany']) {
    const _t = action === 'create'
      ? (data: any) => db.foo.create({ data })
      : action === 'update'
        ? (data: any) => db.foo.update({ where: { id: 100 }, data })
        : action === 'upsert'
          ? (data: any) => db.foo.upsert({ where: { id: 100 }, create: { id: 101, ...data }, update: data })
          : (data: any) => db.foo.updateMany({ where: { id: 100 }, data });

    for (const { data, shouldPass, errorMsgs } of testCases) {
      if (shouldPass) {
        await expect(_t(data)).toResolveTruthy();
      } else {
        await expect(_t(data)).toBeRejectedByValidation(errorMsgs);
      }
    }
  }
}

// Usage:
await testActions(db, [
  { data: { str1: 'a' }, shouldPass: false }, // violates @length min
  { data: { str1: 'abcde' }, shouldPass: false }, // violates @length max
  { data: { str1: 'ambb' }, shouldPass: true }, // satisfies all
  // ...
]);
📜 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 c618515 and 9639a77.

📒 Files selected for processing (1)
  • tests/e2e/orm/validation/toplevel.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • tests/e2e/orm/validation/toplevel.test.ts
tests/e2e/**

📄 CodeRabbit inference engine (CLAUDE.md)

End-to-end tests must live under tests/e2e/

Files:

  • tests/e2e/orm/validation/toplevel.test.ts
🧬 Code graph analysis (1)
tests/e2e/orm/validation/toplevel.test.ts (1)
packages/testtools/src/client.ts (1)
  • createTestClient (51-167)
⏰ 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). (2)
  • GitHub Check: build-test (20.x, sqlite)
  • GitHub Check: build-test (20.x, postgresql)
🔇 Additional comments (1)
tests/e2e/orm/validation/toplevel.test.ts (1)

1-209: Comprehensive validation test coverage looks good!

The test file provides thorough coverage of top-level field validators across multiple data types (String, Int, BigInt, Decimal) and CRUD operations. The test structure is well-organized, and the use of custom matchers (toBeRejectedByValidation, toResolveTruthy) enhances readability. The relation field rejection tests correctly verify that validation expressions cannot access relation fields.

@ymc9 ymc9 merged commit 8fbe27d into dev Oct 8, 2025
6 of 7 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.

2 participants