@@ -170,9 +170,11 @@ def build_db_criteria(self, matchrule: dict) -> dict:
170170 else :
171171 regexps = matchrule ['value' ]
172172
173- for r in regexps :
173+ # Pre-compile regex patterns for efficiency
174+ compiled_regexps = [re .compile (r ) for r in regexps ]
175+ for compiled_re in compiled_regexps :
174176 for t in self .types :
175- if re .search (r , t [1 ]):
177+ if compiled_re .search (t [1 ]):
176178 criterias ['eventType' ].append (t [1 ])
177179
178180 if matchrule ['method' ] == 'exact' :
@@ -227,19 +229,21 @@ def enrich_event_sources(self, events: dict) -> None:
227229 if not isinstance (events , dict ):
228230 raise TypeError (f"events is { type (events )} ; expected dict()" )
229231
230- event_chunks = [list (events .keys ())[x :(x + 5000 )] for x in range (0 , len (list (events .keys ())), 5000 )]
232+ event_ids = list (events .keys ())
233+ event_chunks = [event_ids [x :(x + 5000 )] for x in range (0 , len (event_ids ), 5000 )]
231234
232235 for chunk in event_chunks :
233236 # Get sources
234237 self .log .debug (f"Getting sources for { len (chunk )} events" )
235238 source_data = self .dbh .scanElementSourcesDirect (self .scanId , chunk )
236239 for row in source_data :
240+ entity_type = self .type_entity_map .get (row [15 ])
237241 events [row [8 ]]['source' ].append ({
238242 'type' : row [15 ],
239243 'data' : row [2 ],
240244 'module' : row [16 ],
241245 'id' : row [9 ],
242- 'entity_type' : self . type_entity_map [ row [ 15 ]]
246+ 'entity_type' : entity_type
243247 })
244248
245249 def enrich_event_children (self , events : dict ) -> None :
@@ -254,7 +258,8 @@ def enrich_event_children(self, events: dict) -> None:
254258 if not isinstance (events , dict ):
255259 raise TypeError (f"events is { type (events )} ; expected dict()" )
256260
257- event_chunks = [list (events .keys ())[x :x + 5000 ] for x in range (0 , len (list (events .keys ())), 5000 )]
261+ event_ids = list (events .keys ())
262+ event_chunks = [event_ids [x :x + 5000 ] for x in range (0 , len (event_ids ), 5000 )]
258263
259264 for chunk in event_chunks :
260265 # Get children
@@ -306,20 +311,23 @@ def enrich_event_entities(self, events: dict) -> None:
306311 self .log .debug (f"{ len (entity_missing .keys ())} entities are missing, going deeper..." )
307312 new_missing = dict ()
308313 self .log .debug (f"Getting sources for { len (entity_missing .keys ())} items" )
309- if len (entity_missing .keys ()) > 5000 :
310- chunks = [list (entity_missing .keys ())[x :x + 5000 ] for x in range (0 , len (list (entity_missing .keys ())), 5000 )]
314+
315+ missing_ids = list (entity_missing .keys ())
316+ if len (missing_ids ) > 5000 :
317+ chunks = [missing_ids [x :x + 5000 ] for x in range (0 , len (missing_ids ), 5000 )]
311318 entity_data = list ()
312319 self .log .debug ("Fetching data in chunks" )
313320 for chunk in chunks :
314321 self .log .debug (f"chunk size: { len (chunk )} " )
315322 entity_data .extend (self .dbh .scanElementSourcesDirect (self .scanId , chunk ))
316323 else :
317324 self .log .debug (f"fetching sources for { len (entity_missing )} items" )
318- entity_data = self .dbh .scanElementSourcesDirect (self .scanId , list ( entity_missing . keys ()) )
325+ entity_data = self .dbh .scanElementSourcesDirect (self .scanId , missing_ids )
319326
320327 for entity_candidate in entity_data :
321328 event_id = entity_missing [entity_candidate [8 ]]
322- if self .type_entity_map [entity_candidate [15 ]] not in ['ENTITY' , 'INTERNAL' ]:
329+ entity_type = self .type_entity_map .get (entity_candidate [15 ])
330+ if entity_type not in ['ENTITY' , 'INTERNAL' ]:
323331 # key of this dictionary is the id we need to now get a source for,
324332 # and the value is the original ID of the item missing an entity
325333 new_missing [entity_candidate [9 ]] = event_id
@@ -329,13 +337,13 @@ def enrich_event_entities(self, events: dict) -> None:
329337 'data' : entity_candidate [2 ],
330338 'module' : entity_candidate [16 ],
331339 'id' : entity_candidate [9 ],
332- 'entity_type' : self . type_entity_map [ entity_candidate [ 15 ]]
340+ 'entity_type' : entity_type
333341 })
334342
335343 if len (new_missing ) == 0 :
336344 break
337345
338- entity_missing = deepcopy ( new_missing )
346+ entity_missing = new_missing
339347
340348 def collect_from_db (self , matchrule : dict , fetchChildren : bool , fetchSources : bool , fetchEntities : bool ) -> list :
341349 """Collect event values from database.
@@ -362,12 +370,13 @@ def collect_from_db(self, matchrule: dict, fetchChildren: bool, fetchSources: bo
362370 query_args ['instanceId' ] = self .scanId
363371 self .log .debug (f"db query: { query_args } " )
364372 for row in self .dbh .scanResultEvent (** query_args ):
373+ entity_type = self .type_entity_map .get (row [4 ])
365374 events [row [8 ]] = {
366375 'type' : row [4 ],
367376 'data' : row [1 ],
368377 'module' : row [3 ],
369378 'id' : row [8 ],
370- 'entity_type' : self . type_entity_map [ row [ 4 ]] ,
379+ 'entity_type' : entity_type ,
371380 'source' : [],
372381 'child' : [],
373382 'entity' : []
@@ -407,13 +416,13 @@ def event_extract(self, event: dict, field: str) -> list:
407416
408417 return [event [field ]]
409418
410- def event_keep (self , event : dict , field : str , patterns : str , patterntype : str ) -> bool :
419+ def event_keep (self , event : dict , field : str , patterns : list , patterntype : str ) -> bool :
411420 """Keep event field.
412421
413422 Args:
414423 event (dict): event
415424 field (str): TBD
416- patterns (str ): TBD
425+ patterns (list ): TBD
417426 patterntype (str): TBD
418427
419428 Returns:
@@ -431,7 +440,7 @@ def event_keep(self, event: dict, field: str, patterns: str, patterntype: str) -
431440 for pattern in patterns :
432441 if pattern .startswith ("not " ):
433442 ret = True
434- pattern = re . sub ( r"^not\s+" , "" , pattern )
443+ pattern = pattern [ 4 :]. lstrip () # Faster than re.sub
435444 if value == pattern :
436445 return False
437446 else :
@@ -447,7 +456,7 @@ def event_keep(self, event: dict, field: str, patterns: str, patterntype: str) -
447456 for pattern in patterns :
448457 if pattern .startswith ("not " ):
449458 ret = True
450- pattern = re . sub ( r"^not\s+" , "" , pattern )
459+ pattern = pattern [ 4 :]. lstrip () # Faster than re.sub
451460 if re .search (pattern , value , re .IGNORECASE ):
452461 return False
453462 else :
@@ -478,12 +487,8 @@ def refine_collection(self, matchrule: dict, events: list) -> None:
478487 field = matchrule ['field' ]
479488 self .log .debug (f"attempting to match { patterns } against the { field } field in { len (events )} events" )
480489
481- # Go through each event, remove it if we shouldn't keep it
482- # according to the match rule patterns.
483- for event in events [:]:
484- if not self .event_keep (event , field , patterns , matchrule ['method' ]):
485- self .log .debug (f"removing { event } because of { field } " )
486- events .remove (event )
490+ # Use list comprehension instead of .remove() in loop (O(n) instead of O(n²))
491+ events [:] = [e for e in events if self .event_keep (e , field , patterns , matchrule ['method' ])]
487492
488493 def collect_events (self , collection : dict , fetchChildren : bool , fetchSources : bool , fetchEntities : bool , collectIndex : int ) -> list :
489494 """Collect data for aggregation and analysis.
@@ -555,9 +560,8 @@ def event_strip(event: dict, field: str, value: str) -> None:
555560 """
556561 topfield , subfield = field .split ("." )
557562 if field .startswith (topfield + "." ):
558- for s in event [topfield ]:
559- if s [subfield ] != value :
560- event [topfield ].remove (s )
563+ # Use list comprehension to avoid O(n²) removal
564+ event [topfield ] = [s for s in event [topfield ] if s [subfield ] == value ]
561565
562566 ret = dict ()
563567 for e in events :
@@ -614,12 +618,12 @@ def analysis_match_all_to_first_collection(self, rule: dict, buckets: dict) -> N
614618 """
615619 self .log .debug (f"called with buckets { buckets } " )
616620
617- def check_event (events : list , reference : list ) -> bool :
621+ def check_event (events : list , reference : set ) -> bool :
618622 """Check event.
619623
620624 Args:
621625 events (list): TBD
622- reference (list ): TBD
626+ reference (set ): TBD
623627
624628 Returns:
625629 bool: TBD
@@ -660,14 +664,20 @@ def check_event(events: list, reference: list) -> bool:
660664
661665 for bucket in list (buckets .keys ()):
662666 pluszerocount = 0
663- for event in buckets [bucket ][:]:
667+ # Use list comprehension to filter events instead of .remove()
668+ filtered_events = []
669+ for event in buckets [bucket ]:
664670 if event ['_collection' ] == 0 :
671+ filtered_events .append (event )
665672 continue
666673 pluszerocount += 1
667674
668- if not check_event (self .event_extract (event , rule ['field' ]), reference ):
669- buckets [bucket ].remove (event )
675+ if check_event (self .event_extract (event , rule ['field' ]), reference ):
676+ filtered_events .append (event )
677+ else :
670678 pluszerocount -= 1
679+
680+ buckets [bucket ] = filtered_events
671681
672682 # delete the bucket if there are no events > collection 0
673683 if pluszerocount == 0 :
@@ -923,6 +933,7 @@ def build_correlation_title(self, rule: dict, data: list) -> str:
923933 v = self .event_extract (data [0 ], m )[0 ]
924934 except Exception :
925935 self .log .error (f"Field requested was not available: { m } " )
936+ continue
926937 title = title .replace ("{" + m + "}" , v .replace ("\r " , "" ).split ("\n " )[0 ])
927938 return title
928939
@@ -943,9 +954,7 @@ def create_correlation(self, rule: dict, data: list, readonly: bool = False) ->
943954 if readonly :
944955 return True
945956
946- eventIds = list ()
947- for e in data :
948- eventIds .append (e ['id' ])
957+ eventIds = [e ['id' ] for e in data ]
949958
950959 corrId = self .dbh .correlationResultCreate (self .scanId ,
951960 rule ['id' ],
0 commit comments