@@ -77,6 +77,13 @@ export interface SecurityConfig {
7777 * @default true
7878 */
7979 enableInputSanitization ?: boolean
80+
81+ /**
82+ * Maximum query complexity score
83+ * PERFORMANCE: Limits expensive queries to protect cluster performance
84+ * @default 100
85+ */
86+ maxQueryComplexity ?: number
8087}
8188
8289/**
@@ -92,7 +99,8 @@ export const DEFAULT_SECURITY_CONFIG: Required<SecurityConfig> = {
9299 allowedRawMethods : [ ] ,
93100 searchableFields : [ ] ,
94101 enableDetailedErrors : process . env . NODE_ENV !== 'production' ,
95- enableInputSanitization : true
102+ enableInputSanitization : true ,
103+ maxQueryComplexity : 100
96104}
97105
98106/**
@@ -343,6 +351,7 @@ export function sanitizeError(
343351/**
344352 * Calculates the complexity score of a query
345353 * Used for rate limiting or rejection of overly complex queries
354+ * PERFORMANCE: Enhanced complexity calculation with costs for expensive operations
346355 *
347356 * @param query - Query object
348357 * @returns Complexity score (higher = more complex)
@@ -357,17 +366,33 @@ export function calculateQueryComplexity(query: unknown): number {
357366 for ( const key of Object . keys ( query as object ) ) {
358367 const value = ( query as Record < string , unknown > ) [ key ]
359368
360- // Each operator adds to complexity
369+ // Base cost for each operator
361370 complexity += 1
362371
372+ // Expensive operators (wildcards, regex, fuzzy) have higher costs
373+ if ( key === '$wildcard' ) {
374+ complexity += 5
375+ } else if ( key === '$regexp' ) {
376+ complexity += 8
377+ } else if ( key === '$fuzzy' ) {
378+ complexity += 6
379+ } else if ( key === '$prefix' ) {
380+ complexity += 3
381+ } else if ( key === '$script' ) {
382+ complexity += 15 // Scripts are very expensive
383+ }
363384 // Nested operators are more expensive
364- if ( key === '$or' || key === '$and' ) {
385+ else if ( key === '$or' || key === '$and' ) {
365386 if ( Array . isArray ( value ) ) {
366387 for ( const item of value ) {
367388 complexity += calculateQueryComplexity ( item ) * 2
368389 }
369390 }
370- } else if ( key === '$nested' || key === '$child' || key === '$parent' ) {
391+ } else if ( key === '$nested' ) {
392+ if ( typeof value === 'object' ) {
393+ complexity += calculateQueryComplexity ( value ) * 10 // Nested queries are very expensive
394+ }
395+ } else if ( key === '$child' || key === '$parent' ) {
371396 if ( typeof value === 'object' ) {
372397 complexity += calculateQueryComplexity ( value ) * 3
373398 }
@@ -381,3 +406,22 @@ export function calculateQueryComplexity(query: unknown): number {
381406
382407 return complexity
383408}
409+
410+ /**
411+ * Validates query complexity against budget
412+ * PERFORMANCE: Rejects overly complex queries to protect cluster performance
413+ *
414+ * @param query - Query object to validate
415+ * @param maxComplexity - Maximum allowed complexity score
416+ * @throws BadRequest if query exceeds complexity budget
417+ */
418+ export function validateQueryComplexity ( query : unknown , maxComplexity : number ) : void {
419+ const complexity = calculateQueryComplexity ( query )
420+
421+ if ( complexity > maxComplexity ) {
422+ throw new errors . BadRequest (
423+ `Query complexity (${ complexity } ) exceeds maximum allowed (${ maxComplexity } ). ` +
424+ `Simplify your query by reducing nested conditions, wildcard searches, or array sizes.`
425+ )
426+ }
427+ }
0 commit comments