Skip to content

Commit fb89db3

Browse files
committed
feat: add enabled and excluded features lists
1 parent 6b2af54 commit fb89db3

File tree

2 files changed

+38
-7
lines changed

2 files changed

+38
-7
lines changed

extensions/business/cybersec/red_mesh/pentester_api_01.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
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,

extensions/business/cybersec/red_mesh/redmesh_utils.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def __init__(
5555
local_id_prefix : str,
5656
worker_target_ports=COMMON_PORTS,
5757
exceptions=None,
58+
excluded_features=None,
5859
):
5960
"""
6061
Initialize a pentest worker with target ports and exclusions.
@@ -75,12 +76,16 @@ def __init__(
7576
Ports assigned to this worker; defaults to common ports.
7677
exceptions : list[int], optional
7778
Ports to exclude from scanning.
79+
excluded_features: list[str], optional
80+
List of feature method names to exclude.
7881
7982
Raises
8083
------
8184
ValueError
8285
If no ports remain after applying exceptions.
8386
"""
87+
if excluded_features is None:
88+
excluded_features = []
8489
if exceptions is None:
8590
exceptions = []
8691
self.target = target
@@ -127,7 +132,9 @@ def __init__(
127132
"done": False,
128133
"canceled": False,
129134
}
130-
self.__features = self._get_all_features()
135+
self.excluded_features = excluded_features or []
136+
self.__all_features = self._get_all_features()
137+
self.__enabled_features = [f for f in self.__all_features if f not in self.excluded_features]
131138
self.P("Initialized worker {} on {} ports [{}-{}]...".format(
132139
self.local_worker_id,
133140
len(worker_target_ports),
@@ -197,8 +204,8 @@ def get_status(self, for_aggregations=False):
197204
Worker status including progress and findings.
198205
"""
199206
completed_tests = self.state.get("completed_tests", [])
200-
max_features = len(self.__features) + 3 # +1 port scan, +1 service_info_completed, +1 web_tests_completed
201-
progress = f"{(len(completed_tests) / max_features) * 100 if self.__features else 0:.1f}%"
207+
max_features = len(self.__enabled_features) + 3 # +1 port scan, +1 service_info_completed, +1 web_tests_completed
208+
progress = f"{(len(completed_tests) / max_features) * 100 if self.__enabled_features else 0:.1f}%"
202209

203210
dct_status = {
204211
# same data for all workers below
@@ -416,7 +423,7 @@ def _gather_service_info(self):
416423
return
417424
self.P(f"Gathering service info for {len(open_ports)} open ports.")
418425
target = self.target
419-
service_info_methods = [method for method in dir(self) if method.startswith("_service_info_")]
426+
service_info_methods = [m for m in self.__enabled_features if m.startswith("_service_info_")]
420427
aggregated_info = []
421428
for method in service_info_methods:
422429
func = getattr(self, method)
@@ -463,7 +470,7 @@ def _run_web_tests(self):
463470
self.state["web_tested"] = True
464471
return
465472
result = []
466-
web_tests_methods = [method for method in dir(self) if method.startswith("_web_test_")]
473+
web_tests_methods = [m for m in self.__enabled_features if m.startswith("_web_test_")]
467474
for method in web_tests_methods:
468475
func = getattr(self, method)
469476
for port in ports_to_test:

0 commit comments

Comments
 (0)