5353 # Defines how ports are split across local workers.
5454 "DISTRIBUTION_STRATEGY" : "SLICE" , # "SLICE" or "MIRROR"
5555 "PORT_ORDER" : "SHUFFLE" , # "SHUFFLE" or "SEQUENTIAL"
56+ "EXCLUDED_FEATURES" : [],
5657
5758 'VALIDATION_RULES' : {
5859 ** BasePlugin .CONFIG ['VALIDATION_RULES' ],
@@ -304,6 +305,7 @@ def _launch_job(
304305 nr_local_workers = 4 ,
305306 exceptions = None ,
306307 port_order = None ,
308+ excluded_features = None ,
307309 ):
308310 """
309311 Launch local worker threads for a job by splitting the port range.
@@ -326,6 +328,8 @@ def _launch_job(
326328 Ports to exclude from scanning.
327329 port_order : str, optional
328330 Port scanning order: "SHUFFLE" or "SEQUENTIAL".
331+ excluded_features : list[str], optional
332+ List of feature names to exclude from scanning.
329333
330334 Returns
331335 -------
@@ -337,6 +341,8 @@ def _launch_job(
337341 ValueError
338342 When no ports are available or batches cannot be allocated.
339343 """
344+ if excluded_features is None :
345+ excluded_features = []
340346 local_jobs = {}
341347 ports = list (range (start_port , end_port + 1 ))
342348 batches = []
@@ -381,6 +387,7 @@ def _launch_job(
381387 initiator = network_worker_address ,
382388 exceptions = exceptions ,
383389 worker_target_ports = batch ,
390+ excluded_features = excluded_features ,
384391 )
385392 batch_job .start ()
386393 local_jobs [batch_job .local_worker_id ] = batch_job
@@ -424,6 +431,7 @@ def _maybe_launch_jobs(self, nr_local_workers=None):
424431 target = job_specs .get ("target" )
425432 job_id = job_specs .get ("job_id" , normalized_key )
426433 port_order = job_specs .get ("port_order" , self .cfg_port_order )
434+ excluded_features = job_specs .get ("excluded_features" , self .cfg_excluded_features )
427435 if job_id is None :
428436 continue
429437 worker_entry = self ._ensure_worker_entry (job_id , job_specs )
@@ -463,7 +471,8 @@ def _maybe_launch_jobs(self, nr_local_workers=None):
463471 network_worker_address = launcher ,
464472 nr_local_workers = workers_requested ,
465473 exceptions = exceptions ,
466- port_order = port_order
474+ port_order = port_order ,
475+ excluded_features = excluded_features ,
467476 )
468477 except ValueError as exc :
469478 self .P (f"Skipping job { job_id } : { exc } " , color = 'r' )
@@ -756,6 +765,7 @@ def launch_test(
756765 exceptions : str = "64297" ,
757766 distribution_strategy : str = "" ,
758767 port_order : str = "" ,
768+ excluded_features = None ,
759769 ):
760770 """
761771 Start a pentest on the specified target.
@@ -778,6 +788,8 @@ def launch_test(
778788 port_order: str, optional
779789 Defines port scanning order at worker-thread level:
780790 "SHUFFLE" to randomize port order; "SEQUENTIAL" for ordered scan.
791+ excluded_features: list[str], optional
792+ List of feature names to exclude from scanning.
781793
782794 Returns
783795 -------
@@ -792,6 +804,8 @@ def launch_test(
792804 # INFO: This method only announces the job to the network. It does not
793805 # execute the job itself - that part is handled by PentestJob
794806 # executed after periodical check from plugin process.
807+ if excluded_features is None :
808+ excluded_features = self .cfg_excluded_features or []
795809 if not target :
796810 raise ValueError ("No target specified." )
797811
@@ -801,13 +815,21 @@ def launch_test(
801815 if start_port > end_port :
802816 raise ValueError ("start_port must be less than end_port." )
803817
804-
805818 if len (exceptions ) > 0 :
806819 exceptions = [
807820 int (x ) for x in self .re .findall (r'\d+' , exceptions )
808821 if x .isdigit ()
809822 ]
810823
824+ # Validate excluded_features against known features and calculate enabled_features for audit
825+ all_features = self .__features
826+ if excluded_features :
827+ invalid = [f for f in excluded_features if f not in all_features ]
828+ if invalid :
829+ self .P (f"Warning: Unknown features in excluded_features (ignored): { self .json_dumps (invalid )} " )
830+ excluded_features = [f for f in excluded_features if f in all_features ]
831+ enabled_features = [f for f in all_features if f not in excluded_features ]
832+
811833 distribution_strategy = str (distribution_strategy ).upper ()
812834
813835 if not distribution_strategy or distribution_strategy not in ["MIRROR" , "SLICE" ]:
@@ -874,6 +896,8 @@ def launch_test(
874896 "workers" : workers ,
875897 "distribution_strategy" : distribution_strategy ,
876898 "port_order" : port_order ,
899+ "excluded_features" : excluded_features ,
900+ "enabled_features" : enabled_features ,
877901 }
878902 self .chainstore_hset (
879903 hkey = self .cfg_instance_id ,
0 commit comments