@@ -364,11 +364,57 @@ def run(self, args=None) -> Dict[str, Any]:
364
364
365
365
start_time = time .time ()
366
366
367
+ # Calculate global timeout: (timeout + retry_delay) * max_retries * estimated_requests
368
+ # Estimated requests: 6 engines + 9 detectors = ~15 components, each making ~2-3 requests
369
+ estimated_requests = 45 # Conservative estimate
370
+ timeout_per_request = self .config .get ('timeout' , 10 )
371
+ max_retries = self .config .get ('max_retries' , 3 )
372
+ retry_delay = min (0.5 , timeout_per_request / 2 )
373
+
374
+ # Global timeout: (timeout + retry_delay) * (max_retries + 1) * estimated_requests
375
+ global_timeout = (timeout_per_request + retry_delay ) * (max_retries + 1 ) * estimated_requests
376
+
377
+ # Add a reasonable cap to prevent extremely long scans
378
+ global_timeout = min (global_timeout , 300 ) # Max 5 minutes
379
+
367
380
summary_only = self .config .get ('summary_only' , False )
368
381
verbose_mode = self .config .get ('verbose' , False )
369
382
370
383
if not summary_only and hasattr (self .presenter , 'print_status' ):
371
384
self .presenter .print_status (f"Starting analysis of { self .request_manager .target_url } " )
385
+ if verbose_mode :
386
+ self .presenter .print_status (f"Global timeout set to { global_timeout :.1f} seconds" )
387
+
388
+ # Early connectivity check
389
+ if not summary_only and hasattr (self .presenter , 'print_status' ):
390
+ self .presenter .print_status (" Performing connectivity check..." )
391
+
392
+ connectivity_response = self .request_manager .make_request ()
393
+ if not connectivity_response :
394
+ if not summary_only and hasattr (self .presenter , 'print_status' ):
395
+ self .presenter .print_status (" Target is unreachable, stopping scan" )
396
+
397
+ # Return early with minimal results
398
+ elapsed = time .time () - start_time
399
+ results = {
400
+ 'target_url' : self .request_manager .target_url ,
401
+ 'scan_time' : elapsed ,
402
+ 'frameworks' : [],
403
+ 'security_headers' : [],
404
+ 'server_info' : {},
405
+ 'error' : 'Target is unreachable'
406
+ }
407
+
408
+ # Register the scan in the database
409
+ self ._record_scan (results , elapsed )
410
+
411
+ if not summary_only :
412
+ self .presenter .print_results (results )
413
+
414
+ return results
415
+
416
+ if not summary_only and hasattr (self .presenter , 'print_status' ):
417
+ self .presenter .print_status (" Target is reachable, proceeding with analysis..." )
372
418
373
419
try :
374
420
from rich .progress import Progress , SpinnerColumn , TextColumn , BarColumn , TimeElapsedColumn
@@ -378,12 +424,26 @@ def run(self, args=None) -> Dict[str, Any]:
378
424
engine_name = engine .__class__ .__name__
379
425
if not summary_only and hasattr (self .presenter , 'print_status' ):
380
426
self .presenter .print_status (f" Running { engine_name } " )
427
+
428
+ # Check global timeout before each engine
429
+ if time .time () - start_time > global_timeout :
430
+ if not summary_only and hasattr (self .presenter , 'print_status' ):
431
+ self .presenter .print_status (f" Global timeout reached, stopping scan" )
432
+ break
433
+
381
434
engine .analyze ()
382
435
383
436
for detector in self .detectors :
384
437
detector_name = detector .__class__ .__name__
385
438
if not summary_only and hasattr (self .presenter , 'print_status' ):
386
439
self .presenter .print_status (f" Running { detector_name } " )
440
+
441
+ # Check global timeout before each detector
442
+ if time .time () - start_time > global_timeout :
443
+ if not summary_only and hasattr (self .presenter , 'print_status' ):
444
+ self .presenter .print_status (f" Global timeout reached, stopping scan" )
445
+ break
446
+
387
447
detector .detect ()
388
448
else :
389
449
progress = Progress (
@@ -399,6 +459,11 @@ def run(self, args=None) -> Dict[str, Any]:
399
459
engine_task = progress .add_task (f"Running engines" , total = len (self .engines ))
400
460
401
461
for i , engine in enumerate (self .engines ):
462
+ # Check global timeout
463
+ if time .time () - start_time > global_timeout :
464
+ progress .update (engine_task , description = "Global timeout reached" )
465
+ break
466
+
402
467
engine_name = engine .__class__ .__name__
403
468
progress .update (engine_task , description = f"Running engine: { engine_name } " )
404
469
engine .analyze ()
@@ -409,6 +474,11 @@ def run(self, args=None) -> Dict[str, Any]:
409
474
detector_task = progress .add_task (f"Running detectors" , total = len (self .detectors ))
410
475
411
476
for i , detector in enumerate (self .detectors ):
477
+ # Check global timeout
478
+ if time .time () - start_time > global_timeout :
479
+ progress .update (detector_task , description = "Global timeout reached" )
480
+ break
481
+
412
482
detector_name = detector .__class__ .__name__
413
483
framework_name = detector .FRAMEWORK if hasattr (detector , 'FRAMEWORK' ) else "Unknown"
414
484
progress .update (detector_task , description = f"Running { framework_name } Detector" )
@@ -419,12 +489,24 @@ def run(self, args=None) -> Dict[str, Any]:
419
489
420
490
except ImportError :
421
491
for i , engine in enumerate (self .engines , 1 ):
492
+ # Check global timeout
493
+ if time .time () - start_time > global_timeout :
494
+ if not summary_only :
495
+ print ("Global timeout reached, stopping scan" )
496
+ break
497
+
422
498
engine_name = engine .__class__ .__name__
423
499
if not summary_only :
424
500
print (f"Running engine: { engine_name } ({ i } /{ len (self .engines )} )" )
425
501
engine .analyze ()
426
502
427
503
for i , detector in enumerate (self .detectors , 1 ):
504
+ # Check global timeout
505
+ if time .time () - start_time > global_timeout :
506
+ if not summary_only :
507
+ print ("Global timeout reached, stopping scan" )
508
+ break
509
+
428
510
print (detector )
429
511
detector_name = detector .__class__ .__name__
430
512
framework_name = detector .FRAMEWORK if hasattr (detector , 'FRAMEWORK' ) else "Unknown"
0 commit comments