1
- import demistomock as demisto
2
- from CommonServerPython import *
3
- import urllib3
4
- from typing import Any
5
1
import itertools
2
+ from typing import Any
3
+
4
+ import urllib3
6
5
from dateutil import parser
7
6
7
+ import demistomock as demisto
8
+ from CommonServerPython import *
9
+
8
10
# Disable insecure warnings
9
11
urllib3 .disable_warnings ()
10
12
11
13
EVENT_TYPE_ALERTS = "alerts"
12
14
EVENT_TYPE_ACTIVITIES = "activity"
13
15
EVENT_TYPE_DEVICES = "devices"
16
+ MAX_PAGINATION_DURATION_SECONDS = 180
14
17
15
18
16
19
class EVENT_TYPE :
@@ -72,7 +75,9 @@ def __init__(self, base_url, api_key, access_token, verify=False, proxy=False):
72
75
self ._api_key = api_key
73
76
super ().__init__ (base_url = base_url , verify = verify , proxy = proxy )
74
77
if not access_token or not self .is_valid_access_token (access_token ):
78
+ demisto .debug ("Invalid access token was used, attempting to get new access token." )
75
79
access_token = self .get_access_token ()
80
+ demisto .debug ("New access token was successfully generated." )
76
81
self .update_access_token (access_token )
77
82
78
83
def update_access_token (self , access_token = None ):
@@ -89,13 +94,14 @@ def perform_fetch(self, params):
89
94
)
90
95
except Exception as e :
91
96
if "Invalid access token" in str (e ):
92
- demisto .debug ("debug-log: Invalid access token" )
97
+ demisto .debug ("Expired or invalid access token, attempting to update access token. " )
93
98
self .update_access_token ()
99
+ demisto .debug ("Access token successfully updated." )
94
100
raw_response = self ._http_request (
95
101
url_suffix = "/search/" , method = "GET" , params = params , headers = self ._headers , timeout = API_TIMEOUT
96
102
)
97
103
else :
98
- demisto .debug (f"debug-log: Error occurred while fetching events: { e } " )
104
+ demisto .debug (f"Error occurred while fetching events: { e } " )
99
105
raise e
100
106
return raw_response
101
107
@@ -121,6 +127,7 @@ def fetch_by_aql_query(
121
127
order_by : str = "time" ,
122
128
from_param : None | int = None ,
123
129
before : Optional [datetime ] = None ,
130
+ event_type : str = "" ,
124
131
):
125
132
"""Fetches events using AQL query.
126
133
@@ -137,7 +144,7 @@ def fetch_by_aql_query(
137
144
aql_query = f"{ aql_query } after:{ after .strftime (DATE_FORMAT )} " # noqa: E231
138
145
if before :
139
146
aql_query = f"{ aql_query } before:{ before .strftime (DATE_FORMAT )} " # noqa: E231
140
- demisto .info (f"info-log: Fetching events until { before } ." )
147
+ demisto .info (f"Fetching events until { before } ." )
141
148
params : dict [str , Any ] = {"aql" : aql_query , "includeTotal" : "false" , "length" : max_fetch , "orderBy" : order_by }
142
149
if from_param :
143
150
params ["from" ] = from_param
@@ -147,6 +154,7 @@ def fetch_by_aql_query(
147
154
# perform pagination if needed (until max_fetch limit), cycle through all pages and add results to results list.
148
155
# The response's 'next' attribute carries the index to start the next request in the
149
156
# pagination (using the 'from' request parameter), or null if there are no more pages left.
157
+ start_time = datetime .now ()
150
158
try :
151
159
while next and (len (results ) < max_fetch ):
152
160
if len (results ) < max_fetch :
@@ -156,9 +164,19 @@ def fetch_by_aql_query(
156
164
next = raw_response .get ("data" , {}).get ("next" ) or 0
157
165
current_results = raw_response .get ("data" , {}).get ("results" , [])
158
166
results .extend (current_results )
159
- demisto .info (f"info-log: fetched { len (current_results )} results, total is { len (results )} , and { next = } ." )
167
+ demisto .info (f"fetched { len (current_results )} results, total is { len (results )} , and { next = } ." )
168
+
169
+ total_seconds = (datetime .now () - start_time ).total_seconds ()
170
+ demisto .debug (f"total { total_seconds } seconds so far" )
171
+ if next and total_seconds >= MAX_PAGINATION_DURATION_SECONDS :
172
+ demisto .debug (
173
+ f"Reached pagination time limit of { MAX_PAGINATION_DURATION_SECONDS } s for { event_type } , "
174
+ f"breaking early with { next = } to avoid timeout. Pagination will resume in the next fetch cycle."
175
+ )
176
+ break
177
+
160
178
except Exception as e :
161
- demisto .info (f"info-log: caught an exception during pagination:\n { str (e )} " ) # noqa: E231
179
+ demisto .info (f"caught an exception during pagination:\n { str (e )} " ) # noqa: E231
162
180
163
181
return results , next
164
182
@@ -251,7 +269,7 @@ def calculate_fetch_start_time(
251
269
# case 1
252
270
if last_fetch_time :
253
271
if isinstance (last_fetch_time , str ):
254
- demisto .info (f"info-log: calculating_fetch_time for { last_fetch_time = } " )
272
+ demisto .info (f"calculating_fetch_time for { last_fetch_time = } " )
255
273
last_fetch_datetime = arg_to_datetime (last_fetch_time )
256
274
else :
257
275
last_fetch_datetime = last_fetch_time
@@ -264,7 +282,7 @@ def calculate_fetch_start_time(
264
282
if after_time :
265
283
after_time = after_time .replace (tzinfo = None )
266
284
if not after_time or after_time >= before_time :
267
- demisto .info ("info-log: last run time is later than before time, overwriting after time." )
285
+ demisto .info ("last run time is later than before time, overwriting after time." )
268
286
after_time = before_time - timedelta (minutes = 1 )
269
287
return after_time , before_time
270
288
@@ -323,7 +341,7 @@ def dedup_events(events: list[dict], events_last_fetch_ids: list[str], unique_id
323
341
"""
324
342
# case 1
325
343
if not events :
326
- demisto .debug ("debug-log: Dedup case 1 - Empty event list (no new events received from API response)." )
344
+ demisto .debug ("Dedup case 1 - Empty event list (no new events received from API response)." )
327
345
return [], events_last_fetch_ids
328
346
329
347
new_events : list [dict ] = [event for event in events if event .get (unique_id_key ) not in events_last_fetch_ids ]
@@ -337,7 +355,7 @@ def dedup_events(events: list[dict], events_last_fetch_ids: list[str], unique_id
337
355
and latest_event_datetime
338
356
and are_two_datetime_equal_by_second (latest_event_datetime , earliest_event_datetime )
339
357
):
340
- demisto .debug ("debug-log: Dedup case 2 - All events from the current fetch cycle have the same timestamp." )
358
+ demisto .debug ("Dedup case 2 - All events from the current fetch cycle have the same timestamp." )
341
359
new_ids = [event .get (unique_id_key , "" ) for event in new_events ]
342
360
events_last_fetch_ids .extend (new_ids )
343
361
return new_events , events_last_fetch_ids
@@ -346,7 +364,7 @@ def dedup_events(events: list[dict], events_last_fetch_ids: list[str], unique_id
346
364
else :
347
365
# Note that the following timestamps comparison are made between strings and assume
348
366
# the following timestamp format from the response: "YYYY-MM-DDTHH:MM:SS.fffff+Z"
349
- demisto .debug ("debug-log: Dedup case 3 - Most recent event has later timestamp then other events in the response." )
367
+ demisto .debug ("Dedup case 3 - Most recent event has later timestamp then other events in the response." )
350
368
351
369
latest_event_timestamp = events [- 1 ].get (event_order_by , "" )[:19 ]
352
370
# itertools.takewhile is used to iterate over the list of events (from latest time to earliest)
@@ -385,11 +403,11 @@ def fetch_by_event_type(
385
403
last_fetch_time_field = f"{ event_type .type } _last_fetch_time"
386
404
last_fetch_next_field = f"{ event_type .type } _last_fetch_next_field"
387
405
388
- demisto .debug (f"debug-log: handling event-type: { event_type .type } " )
406
+ demisto .debug (f"handling event-type: { event_type .type } " )
389
407
if last_fetch_time := last_run .get (last_fetch_time_field ):
390
- demisto .debug (f"debug-log: last run of type: { event_type .type } time is: { last_fetch_time } " )
408
+ demisto .debug (f"last run of type: { event_type .type } time is: { last_fetch_time } " )
391
409
last_fetch_next = last_run .get (last_fetch_next_field , 0 )
392
- demisto .debug (f"debug-log: last run of type: { event_type .type } next is: { last_fetch_next } " )
410
+ demisto .debug (f"last run of type: { event_type .type } next is: { last_fetch_next } " )
393
411
event_type_fetch_start_time , before_time = calculate_fetch_start_time (last_fetch_time , fetch_start_time , fetch_delay )
394
412
response , next = client .fetch_by_aql_query (
395
413
aql_query = event_type .aql_query ,
@@ -398,16 +416,17 @@ def fetch_by_event_type(
398
416
order_by = event_type .order_by ,
399
417
from_param = last_fetch_next ,
400
418
before = before_time ,
419
+ event_type = event_type .type ,
401
420
)
402
421
new_events : list [dict ] = []
403
- demisto .debug (f"debug-log: fetched { len (response )} { event_type .type } from API" )
422
+ demisto .debug (f"fetched { len (response )} { event_type .type } from API" )
404
423
if response :
405
424
new_events , next_run [last_fetch_ids ] = dedup_events (
406
425
response , last_run .get (last_fetch_ids , []), event_type .unique_id_key , event_type .order_by
407
426
)
408
427
events .setdefault (event_type .dataset_name , []).extend (new_events )
409
- demisto .debug (f"debug-log: overall { len (new_events )} { event_type .dataset_name } (after dedup)" )
410
- demisto .debug (f"debug-log: last { event_type .dataset_name } in list: { new_events [- 1 ] if new_events else {}} " )
428
+ demisto .debug (f"overall { len (new_events )} { event_type .dataset_name } (after dedup)" )
429
+ demisto .debug (f"last { event_type .dataset_name } in list: { new_events [- 1 ] if new_events else {}} " )
411
430
412
431
if not next : # we wish to update the time only in case the next is 0 because the next is relative to the time.
413
432
event_type_fetch_start_time = new_events [- 1 ].get (event_type .order_by ) if new_events else last_fetch_time
@@ -416,7 +435,7 @@ def fetch_by_event_type(
416
435
if isinstance (event_type_fetch_start_time , datetime ):
417
436
event_type_fetch_start_time = event_type_fetch_start_time .strftime (DATE_FORMAT )
418
437
next_run [last_fetch_time_field ] = event_type_fetch_start_time
419
- demisto .debug (f"debug-log: updated next_run for event type { event_type .type } with { next = } and { event_type_fetch_start_time = } " )
438
+ demisto .debug (f"updated next_run for event type { event_type .type } with { next = } and { event_type_fetch_start_time = } " )
420
439
421
440
422
441
def fetch_events_for_specific_alert_ids (client : Client , alert , aql_alert_id ):
@@ -431,14 +450,14 @@ def fetch_events_for_specific_alert_ids(client: Client, alert, aql_alert_id):
431
450
None: Alert dict is updated in-place with activitiesData and devicesData.
432
451
433
452
"""
434
- demisto .debug (f"debug-log: Fetching Activities and Devices for specific alert IDs: { aql_alert_id } " )
453
+ demisto .debug (f"Fetching Activities and Devices for specific alert IDs: { aql_alert_id } " )
435
454
activities_aql_query = f'{ EVENT_TYPES ["Activities" ].aql_query } { aql_alert_id } '
436
455
devices_aql_query = f'{ EVENT_TYPES ["Devices" ].aql_query } { aql_alert_id } '
437
456
activities_response = client .fetch_by_ids_in_aql_query (
438
457
aql_query = activities_aql_query , order_by = EVENT_TYPES ["Activities" ].order_by
439
458
)
440
459
devices_response = client .fetch_by_ids_in_aql_query (aql_query = devices_aql_query , order_by = EVENT_TYPES ["Devices" ].order_by )
441
- demisto .debug (f"debug-log: fetch by alert ids\
460
+ demisto .debug (f"fetch by alert ids\
442
461
fetched { len (activities_response )} Activities and { len (devices_response )} Devices" )
443
462
alert ["activitiesData" ] = activities_response if activities_response else {}
444
463
alert ["devicesData" ] = devices_response if devices_response else {}
@@ -471,7 +490,7 @@ def fetch_events(
471
490
events : dict [str , list [dict ]] = {}
472
491
next_run : dict [str , list | str ] = {}
473
492
if "Devices" in event_types_to_fetch and not should_run_device_fetch (last_run , device_fetch_interval , datetime .now ()):
474
- demisto .debug ("debug-log: skipping Devices fetch as it is not yet reached the device interval." )
493
+ demisto .debug ("skipping Devices fetch as it is not yet reached the device interval." )
475
494
event_types_to_fetch .remove ("Devices" )
476
495
477
496
if "Alerts" in event_types_to_fetch :
@@ -500,7 +519,7 @@ def fetch_events(
500
519
501
520
next_run ["access_token" ] = client ._access_token
502
521
503
- demisto .debug (f"debug-log: events: { events } " )
522
+ demisto .debug (f"events: { events } " )
504
523
return events , next_run
505
524
506
525
@@ -547,18 +566,18 @@ def handle_fetched_events(events: dict[str, list[dict[str, Any]]], next_run: dic
547
566
if events :
548
567
for event_type , events_list in events .items ():
549
568
if not events_list :
550
- demisto .debug (f"debug-log: No events of type: { event_type } fetched from API." )
569
+ demisto .debug (f"No events of type: { event_type } fetched from API." )
551
570
else :
552
571
add_time_to_events (events_list , event_type )
553
- demisto .debug (f"debug-log: { len (events_list )} events of type: { event_type } are about to be sent to XSIAM." )
572
+ demisto .debug (f"{ len (events_list )} events of type: { event_type } are about to be sent to XSIAM." )
554
573
product = f"{ PRODUCT } _{ event_type } " if event_type != EVENT_TYPE_ALERTS else PRODUCT
555
574
send_events_to_xsiam (events_list , vendor = VENDOR , product = product )
556
- demisto .debug (f"debug-log: { len (events )} events were sent to XSIAM." )
575
+ demisto .debug (f"{ len (events )} events were sent to XSIAM." )
557
576
else :
558
- demisto .debug ("debug-log: No new events fetched. Sending 0 to XSIAM." )
577
+ demisto .debug ("No new events fetched. Sending 0 to XSIAM." )
559
578
send_events_to_xsiam (events = [], vendor = VENDOR , product = PRODUCT )
560
579
561
- demisto .debug (f"debug-log: setting { next_run = } " )
580
+ demisto .debug (f"setting { next_run = } " )
562
581
demisto .setLastRun (next_run )
563
582
564
583
@@ -651,7 +670,7 @@ def main(): # pragma: no cover
651
670
652
671
if not last_run : # initial fetch - update last fetch time values to current time
653
672
set_last_run_for_last_minute (last_run )
654
- demisto .debug ("debug-log: Initial fetch - updating last fetch time value to current time for each event type." )
673
+ demisto .debug ("Initial fetch - updating last fetch time value to current time for each event type." )
655
674
656
675
if command == "armis-get-events" :
657
676
event_type_name = args .get ("event_type" )
@@ -676,7 +695,7 @@ def main(): # pragma: no cover
676
695
fetch_delay = fetch_delay ,
677
696
)
678
697
for key , value in events .items ():
679
- demisto .debug (f"debug-log: { len (value )} events of type: { key } fetched from armis api" )
698
+ demisto .debug (f"{ len (value )} events of type: { key } fetched from armis api" )
680
699
681
700
if should_push_events :
682
701
handle_fetched_events (events , next_run )
0 commit comments