@@ -81,18 +81,73 @@ export interface ValidationExpressionEvaluator {
8181/**
8282 * Simple expression evaluator (basic implementation)
8383 * In production, this should use a proper expression engine
84+ *
85+ * SECURITY NOTE: This implementation uses a sandboxed approach with limited
86+ * expression capabilities. For production use, consider:
87+ * - JSONLogic (jsonlogic.com)
88+ * - expr-eval with allowlist
89+ * - Custom AST-based evaluator
8490 */
8591class SimpleExpressionEvaluator implements ValidationExpressionEvaluator {
8692 evaluate ( expression : string , context : Record < string , any > ) : any {
8793 try {
88- // Create a safe evaluation context
89- const func = new Function ( ...Object . keys ( context ) , `return ${ expression } ` ) ;
90- return func ( ...Object . values ( context ) ) ;
94+ // Sanitize expression: only allow basic comparisons and logical operators
95+ // This is a basic safeguard - proper expression parsing should be used in production
96+ const sanitizedExpression = this . sanitizeExpression ( expression ) ;
97+
98+ // Create a safe evaluation context with read-only access
99+ const safeContext = this . createSafeContext ( context ) ;
100+ const contextKeys = Object . keys ( safeContext ) ;
101+ const contextValues = Object . values ( safeContext ) ;
102+
103+ // Use Function constructor with controlled input
104+ const func = new Function ( ...contextKeys , `'use strict'; return (${ sanitizedExpression } );` ) ;
105+ return func ( ...contextValues ) ;
91106 } catch ( error ) {
92107 console . error ( 'Expression evaluation error:' , error ) ;
93108 return false ;
94109 }
95110 }
111+
112+ /**
113+ * Sanitize expression to prevent code injection
114+ */
115+ private sanitizeExpression ( expression : string ) : string {
116+ // Remove potentially dangerous patterns
117+ const dangerous = [
118+ / r e q u i r e \s * \( / gi,
119+ / i m p o r t \s + / gi,
120+ / e v a l \s * \( / gi,
121+ / F u n c t i o n \s * \( / gi,
122+ / c o n s t r u c t o r / gi,
123+ / _ _ p r o t o _ _ / gi,
124+ / p r o t o t y p e / gi,
125+ ] ;
126+
127+ for ( const pattern of dangerous ) {
128+ if ( pattern . test ( expression ) ) {
129+ throw new Error ( 'Invalid expression: contains forbidden pattern' ) ;
130+ }
131+ }
132+
133+ return expression ;
134+ }
135+
136+ /**
137+ * Create a safe read-only context
138+ */
139+ private createSafeContext ( context : Record < string , any > ) : Record < string , any > {
140+ const safe : Record < string , any > = { } ;
141+ for ( const [ key , value ] of Object . entries ( context ) ) {
142+ // Deep clone primitive values and objects to prevent mutation
143+ if ( typeof value === 'object' && value !== null ) {
144+ safe [ key ] = JSON . parse ( JSON . stringify ( value ) ) ;
145+ } else {
146+ safe [ key ] = value ;
147+ }
148+ }
149+ return safe ;
150+ }
96151}
97152
98153/**
@@ -533,7 +588,7 @@ export class ObjectValidationEngine {
533588 private getPredefinedPattern ( format : string ) : RegExp {
534589 const patterns : Record < string , RegExp > = {
535590 email : / ^ [ ^ \s @ ] + @ [ ^ \s @ ] + \. [ ^ \s @ ] + $ / ,
536- url : / ^ h t t p s ? : \/ \/ ( w w w \. ) ? [ - a - z A - Z 0 - 9 @ : % . _ \+ ~ # = ] { 1 , 256 } \. [ a - z A - Z 0 - 9 ( ) ] { 1 , 6 } \b ( [ - a - z A - Z 0 - 9 ( ) @ : % _ \+ . ~ # ? & / / = ] * ) $ / ,
591+ url : / ^ h t t p s ? : \/ \/ ( w w w \. ) ? [ - a - z A - Z 0 - 9 @ : % . _ \+ ~ # = ] { 1 , 256 } \. [ a - z A - Z 0 - 9 ( ) ] { 1 , 6 } \b ( [ - a - z A - Z 0 - 9 ( ) @ : % _ \+ . ~ # ? & / = ] * ) $ / ,
537592 phone : / ^ [ \d \s \- + ( ) ] + $ / ,
538593 ipv4 : / ^ ( \d { 1 , 3 } \. ) { 3 } \d { 1 , 3 } $ / ,
539594 ipv6 : / ^ ( [ \d a - f ] { 1 , 4 } : ) { 7 } [ \d a - f ] { 1 , 4 } $ / i,
0 commit comments