Skip to content

Commit ebdc224

Browse files
Copilothotlong
andcommitted
fix: Address code review feedback
- Fix severity handling: treat 'info' as non-blocking like warnings - Add try/catch for regex pattern validation to handle invalid patterns - Fix conditional rule validation: validate rules array and use validateRule for nested rules - Remove function-based conditions for security (now deprecated with warning) - Add default case for unhandled validation rule types with warning - Fix field name propagation in validateFields() - Fix buildCondition to properly map all filter operators (not just '=') - Remove unused imports from data-protocol.ts - Fix ObjectPermission union type (remove redundant boolean shape) - Rename FieldValidationFunction to avoid conflict with data-protocol ValidationFunction - Export ValidationFunction from data-protocol for AdvancedValidationRule - Update ValidationFunction doc comment to clarify it's specific to data-protocol - Make validateBuiltInRule async to support conditional rules with async validators Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent b05c82d commit ebdc224

5 files changed

Lines changed: 113 additions & 25 deletions

File tree

packages/core/src/query/query-ast.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,64 @@ export class QueryASTBuilder {
118118

119119
private buildCondition(condition: AdvancedFilterCondition): OperatorNode {
120120
const field = this.buildField(condition.field);
121-
const value = this.buildLiteral(condition.value);
122121

122+
// Map filter operators to comparison operators
123+
const operatorMap: Record<string, string> = {
124+
'equals': '=',
125+
'not_equals': '!=',
126+
'greater_than': '>',
127+
'greater_than_or_equal': '>=',
128+
'less_than': '<',
129+
'less_than_or_equal': '<=',
130+
'contains': 'contains',
131+
'not_contains': 'contains',
132+
'starts_with': 'starts_with',
133+
'ends_with': 'ends_with',
134+
'like': 'like',
135+
'ilike': 'ilike',
136+
'in': 'in',
137+
'not_in': 'not_in',
138+
'is_null': 'is_null',
139+
'is_not_null': 'is_not_null',
140+
'between': 'between',
141+
};
142+
143+
const operator = operatorMap[condition.operator] || '=';
144+
145+
// Handle special operators
146+
if (operator === 'between' && condition.values && condition.values.length === 2) {
147+
return {
148+
type: 'operator',
149+
operator: 'between' as any,
150+
operands: [
151+
field,
152+
this.buildLiteral(condition.values[0]),
153+
this.buildLiteral(condition.values[1])
154+
],
155+
};
156+
}
157+
158+
if ((operator === 'in' || operator === 'not_in') && condition.values) {
159+
return {
160+
type: 'operator',
161+
operator: operator as any,
162+
operands: [field, ...condition.values.map(v => this.buildLiteral(v))],
163+
};
164+
}
165+
166+
if (operator === 'is_null' || operator === 'is_not_null') {
167+
return {
168+
type: 'operator',
169+
operator: operator as any,
170+
operands: [field],
171+
};
172+
}
173+
174+
// Standard binary operator
175+
const value = this.buildLiteral(condition.value);
123176
return {
124177
type: 'operator',
125-
operator: '=',
178+
operator: operator as any,
126179
operands: [field, value],
127180
};
128181
}

packages/core/src/validation/validation-engine.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class ValidationEngine {
5757
severity: rule.severity || 'error',
5858
};
5959

60-
if (rule.severity === 'warning') {
60+
if (rule.severity === 'warning' || rule.severity === 'info') {
6161
warnings.push(error);
6262
} else {
6363
errors.push(error);
@@ -111,11 +111,11 @@ export class ValidationEngine {
111111
/**
112112
* Validate built-in rules
113113
*/
114-
private validateBuiltInRule(
114+
private async validateBuiltInRule(
115115
value: any,
116116
rule: AdvancedValidationRule,
117117
context?: ValidationContext
118-
): string | null {
118+
): Promise<string | null> {
119119
const { type, params, message } = rule;
120120

121121
switch (type) {
@@ -139,9 +139,13 @@ export class ValidationEngine {
139139

140140
case 'pattern':
141141
if (typeof value === 'string') {
142-
const regex = typeof params === 'string' ? new RegExp(params) : params;
143-
if (!regex.test(value)) {
144-
return message || 'Invalid format';
142+
try {
143+
const regex = typeof params === 'string' ? new RegExp(params) : params;
144+
if (!regex.test(value)) {
145+
return message || 'Invalid format';
146+
}
147+
} catch (error) {
148+
return message || 'Invalid pattern configuration';
145149
}
146150
}
147151
break;
@@ -305,29 +309,41 @@ export class ValidationEngine {
305309
case 'conditional':
306310
if (context?.values && params) {
307311
const { condition, rules } = params;
312+
313+
if (!Array.isArray(rules) || rules.length === 0) {
314+
break;
315+
}
316+
308317
const conditionMet = this.evaluateCondition(condition, context.values);
309318

310319
if (conditionMet) {
311320
for (const conditionalRule of rules) {
312-
const result = this.validateBuiltInRule(value, conditionalRule, context);
321+
const result = await this.validateRule(value, conditionalRule, context);
313322
if (result) {
314323
return result;
315324
}
316325
}
317326
}
318327
}
319328
break;
329+
330+
default:
331+
// Unhandled validation rule type
332+
console.warn(`Unsupported validation rule type: ${type}`);
333+
return null;
320334
}
321335

322336
return null;
323337
}
324338

325339
/**
326340
* Evaluate a condition
341+
* Note: Conditions must be declarative objects, not functions, for security.
327342
*/
328343
private evaluateCondition(condition: any, values: Record<string, any>): boolean {
329344
if (typeof condition === 'function') {
330-
return condition(values);
345+
console.warn('Function-based conditions are deprecated and will be removed. Use declarative conditions instead.');
346+
return false; // Security: reject function-based conditions
331347
}
332348

333349
if (typeof condition === 'object' && condition.field) {
@@ -372,7 +388,11 @@ export class ValidationEngine {
372388

373389
for (const [field, schema] of Object.entries(schemas)) {
374390
const value = values[field];
375-
results[field] = await this.validate(value, schema, context);
391+
const schemaWithField: AdvancedValidationSchema = {
392+
...schema,
393+
field: schema.field ?? field,
394+
};
395+
results[field] = await this.validate(value, schemaWithField, context);
376396
}
377397

378398
return results;

packages/types/src/data-protocol.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@
1717
*/
1818

1919
// Import existing base types to avoid duplication
20-
import type { QueryParams } from './data';
21-
import type { FilterOperator as BaseFilterOperator, FilterCondition as BaseFilterCondition } from './complex';
2220
import type { SortConfig as BaseSortConfig } from './objectql';
23-
import type { ValidationRule as BaseValidationRule } from './field-types';
24-
import type { ValidationError as BaseValidationError } from './data';
21+
import type { FilterOperator as BaseFilterOperator } from './complex';
2522

2623
/**
2724
* =============================================================================
@@ -623,7 +620,14 @@ export type ValidationRuleType =
623620
| 'exists_check';
624621

625622
/**
626-
* Validation function type from base
623+
* Validation function signature used by AdvancedValidationRule in the data protocol.
624+
*
625+
* This type is defined in this module and may differ from similarly named
626+
* validation function types in other packages (e.g., in `field-types`).
627+
*
628+
* @param value - The value to validate
629+
* @param context - Optional validation context with access to other field values
630+
* @returns true if valid, false or error message string if invalid
627631
*/
628632
export type ValidationFunction = (value: any, context?: ValidationContext) => boolean | string;
629633

@@ -681,9 +685,24 @@ export interface AdvancedValidationResult {
681685
}
682686

683687
/**
684-
* Validation error (Phase 3.5.5: Improved error messages) - Extended
688+
* Validation error (Phase 3.5.5: Improved error messages)
685689
*/
686-
export interface AdvancedValidationError extends BaseValidationError {
690+
export interface AdvancedValidationError {
691+
/**
692+
* Field path
693+
*/
694+
field: string;
695+
696+
/**
697+
* Error message
698+
*/
699+
message: string;
700+
701+
/**
702+
* Error code
703+
*/
704+
code?: string;
705+
687706
/**
688707
* Rule type that failed
689708
*/

packages/types/src/field-types.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -731,12 +731,7 @@ export interface ObjectSchemaMetadata {
731731
/**
732732
* Permissions (Phase 3.1.4: Enhanced permissions)
733733
*/
734-
permissions?: ObjectPermission | {
735-
create?: boolean;
736-
read?: boolean;
737-
update?: boolean;
738-
delete?: boolean;
739-
};
734+
permissions?: ObjectPermission;
740735

741736
/**
742737
* Parent object to inherit from (Phase 3.1.2)

packages/types/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ export type {
300300
export type {
301301
BaseFieldMetadata,
302302
VisibilityCondition,
303-
ValidationFunction,
303+
ValidationFunction as FieldValidationFunction,
304304
TextFieldMetadata,
305305
TextareaFieldMetadata,
306306
MarkdownFieldMetadata,
@@ -390,6 +390,7 @@ export type {
390390
AdvancedValidationSchema,
391391
AdvancedValidationRule,
392392
ValidationRuleType,
393+
ValidationFunction,
393394
AsyncValidationFunction,
394395
ValidationContext,
395396
AdvancedValidationResult,

0 commit comments

Comments
 (0)