souleyez 2.43.29__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9564 -2881
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1239 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2200 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +564 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +409 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +417 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +292 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +913 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +219 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +237 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +23034 -10679
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
  353. souleyez-3.0.0.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
@@ -17,13 +17,16 @@ from souleyez.ui.design_system import DesignSystem
17
17
  def get_terminal_width() -> int:
18
18
  """Get terminal width, defaulting to 120 if not available."""
19
19
  import shutil
20
+
20
21
  try:
21
22
  return shutil.get_terminal_size().columns
22
23
  except Exception:
23
24
  return 120
24
25
 
25
26
 
26
- def get_target_services(target_ip: str, engagement_id: int) -> Tuple[List[Dict], Set[str], Set[int]]:
27
+ def get_target_services(
28
+ target_ip: str, engagement_id: int
29
+ ) -> Tuple[List[Dict], Set[str], Set[int]]:
27
30
  """
28
31
  Get services running on target host.
29
32
 
@@ -38,14 +41,14 @@ def get_target_services(target_ip: str, engagement_id: int) -> Tuple[List[Dict],
38
41
  if not host:
39
42
  return [], set(), set()
40
43
 
41
- services = hm.get_host_services(host['id'])
44
+ services = hm.get_host_services(host["id"])
42
45
 
43
46
  service_names = set()
44
47
  ports = set()
45
48
 
46
49
  for svc in services:
47
- svc_name = (svc.get('service_name') or '').lower()
48
- port = svc.get('port')
50
+ svc_name = (svc.get("service_name") or "").lower()
51
+ port = svc.get("port")
49
52
 
50
53
  if svc_name:
51
54
  service_names.add(svc_name)
@@ -68,44 +71,46 @@ def get_msf_job_status(target_ip: str, engagement_id: int) -> Dict[str, Dict]:
68
71
 
69
72
  # Filter to MSF jobs for this target and engagement
70
73
  msf_jobs = [
71
- j for j in all_jobs
72
- if j.get('tool') == 'msf_auxiliary'
73
- and j.get('engagement_id') == engagement_id
74
- and target_ip in (j.get('target') or '')
74
+ j
75
+ for j in all_jobs
76
+ if j.get("tool") == "msf_auxiliary"
77
+ and j.get("engagement_id") == engagement_id
78
+ and target_ip in (j.get("target") or "")
75
79
  ]
76
80
 
77
81
  status_map = {}
78
82
 
79
83
  for job in msf_jobs:
80
- args = job.get('args', [])
84
+ args = job.get("args", [])
81
85
  if args:
82
86
  module_path = args[0]
83
87
  current_status = status_map.get(module_path, {})
84
- job_status = job.get('status', 'unknown')
88
+ job_status = job.get("status", "unknown")
85
89
 
86
90
  # Keep most recent / highest priority status
87
- if job_status == 'running':
88
- status_map[module_path] = {
89
- 'status': 'running',
90
- 'job_id': job.get('id'),
91
- 'started_at': job.get('queued_at')
92
- }
93
- elif job_status == 'done' and current_status.get('status') != 'running':
91
+ if job_status == "running":
94
92
  status_map[module_path] = {
95
- 'status': 'done',
96
- 'job_id': job.get('id'),
97
- 'completed_at': job.get('completed_at')
93
+ "status": "running",
94
+ "job_id": job.get("id"),
95
+ "started_at": job.get("queued_at"),
98
96
  }
99
- elif job_status in ('pending', 'queued') and current_status.get('status') not in ('running', 'done'):
97
+ elif job_status == "done" and current_status.get("status") != "running":
100
98
  status_map[module_path] = {
101
- 'status': 'pending',
102
- 'job_id': job.get('id')
99
+ "status": "done",
100
+ "job_id": job.get("id"),
101
+ "completed_at": job.get("completed_at"),
103
102
  }
103
+ elif job_status in ("pending", "queued") and current_status.get(
104
+ "status"
105
+ ) not in ("running", "done"):
106
+ status_map[module_path] = {"status": "pending", "job_id": job.get("id")}
104
107
 
105
108
  return status_map
106
109
 
107
110
 
108
- def get_recommendations(target_ip: str, engagement_id: int, services: List[Dict]) -> List[Dict]:
111
+ def get_recommendations(
112
+ target_ip: str, engagement_id: int, services: List[Dict]
113
+ ) -> List[Dict]:
109
114
  """
110
115
  Generate smart recommendations based on discovered services and findings.
111
116
 
@@ -121,65 +126,75 @@ def get_recommendations(target_ip: str, engagement_id: int, services: List[Dict]
121
126
  service_names = set()
122
127
  ports = set()
123
128
  for svc in services:
124
- svc_name = (svc.get('service_name') or '').lower()
129
+ svc_name = (svc.get("service_name") or "").lower()
125
130
  if svc_name:
126
131
  service_names.add(svc_name)
127
- port = svc.get('port')
132
+ port = svc.get("port")
128
133
  if port:
129
134
  ports.add(port)
130
135
 
131
136
  # Check for SMBv1 or SMB - recommend MS17-010 check
132
- if 'smb' in service_names or 'microsoft-ds' in service_names or 445 in ports:
133
- recommendations.append({
134
- 'module': 'auxiliary/scanner/smb/smb_ms17_010',
135
- 'name': 'MS17-010 Check',
136
- 'reason': 'SMB detected - check for EternalBlue',
137
- 'priority': 'high'
138
- })
137
+ if "smb" in service_names or "microsoft-ds" in service_names or 445 in ports:
138
+ recommendations.append(
139
+ {
140
+ "module": "auxiliary/scanner/smb/smb_ms17_010",
141
+ "name": "MS17-010 Check",
142
+ "reason": "SMB detected - check for EternalBlue",
143
+ "priority": "high",
144
+ }
145
+ )
139
146
 
140
147
  # Check for anonymous FTP
141
- if 'ftp' in service_names or 21 in ports:
142
- recommendations.append({
143
- 'module': 'auxiliary/scanner/ftp/anonymous',
144
- 'name': 'FTP Anonymous Check',
145
- 'reason': 'FTP detected - check anonymous access',
146
- 'priority': 'high'
147
- })
148
+ if "ftp" in service_names or 21 in ports:
149
+ recommendations.append(
150
+ {
151
+ "module": "auxiliary/scanner/ftp/anonymous",
152
+ "name": "FTP Anonymous Check",
153
+ "reason": "FTP detected - check anonymous access",
154
+ "priority": "high",
155
+ }
156
+ )
148
157
 
149
158
  # Check for VNC without auth
150
- if 'vnc' in service_names or 5900 in ports or 5901 in ports:
151
- recommendations.append({
152
- 'module': 'auxiliary/scanner/vnc/vnc_none_auth',
153
- 'name': 'VNC None Auth',
154
- 'reason': 'VNC detected - check for no-auth',
155
- 'priority': 'high'
156
- })
159
+ if "vnc" in service_names or 5900 in ports or 5901 in ports:
160
+ recommendations.append(
161
+ {
162
+ "module": "auxiliary/scanner/vnc/vnc_none_auth",
163
+ "name": "VNC None Auth",
164
+ "reason": "VNC detected - check for no-auth",
165
+ "priority": "high",
166
+ }
167
+ )
157
168
 
158
169
  # Check for RDP NLA
159
- if 'rdp' in service_names or 'ms-wbt-server' in service_names or 3389 in ports:
160
- recommendations.append({
161
- 'module': 'auxiliary/scanner/rdp/rdp_scanner',
162
- 'name': 'RDP Scanner',
163
- 'reason': 'RDP detected - check NLA settings',
164
- 'priority': 'medium'
165
- })
170
+ if "rdp" in service_names or "ms-wbt-server" in service_names or 3389 in ports:
171
+ recommendations.append(
172
+ {
173
+ "module": "auxiliary/scanner/rdp/rdp_scanner",
174
+ "name": "RDP Scanner",
175
+ "reason": "RDP detected - check NLA settings",
176
+ "priority": "medium",
177
+ }
178
+ )
166
179
 
167
180
  # HTTP services - recommend directory scan
168
- if any(s in service_names for s in ['http', 'https']) or any(p in ports for p in [80, 443, 8080]):
169
- recommendations.append({
170
- 'module': 'auxiliary/scanner/http/dir_scanner',
171
- 'name': 'HTTP Dir Scanner',
172
- 'reason': 'Web server detected - discover directories',
173
- 'priority': 'medium'
174
- })
181
+ if any(s in service_names for s in ["http", "https"]) or any(
182
+ p in ports for p in [80, 443, 8080]
183
+ ):
184
+ recommendations.append(
185
+ {
186
+ "module": "auxiliary/scanner/http/dir_scanner",
187
+ "name": "HTTP Dir Scanner",
188
+ "reason": "Web server detected - discover directories",
189
+ "priority": "medium",
190
+ }
191
+ )
175
192
 
176
193
  return recommendations
177
194
 
178
195
 
179
196
  def filter_presets_by_services(
180
- preset_categories: Dict[str, List[Dict]],
181
- service_names: Set[str],
182
- ports: Set[int]
197
+ preset_categories: Dict[str, List[Dict]], service_names: Set[str], ports: Set[int]
183
198
  ) -> Tuple[Dict[str, List[Dict]], int]:
184
199
  """
185
200
  Filter presets to only show those relevant to target's services.
@@ -197,8 +212,8 @@ def filter_presets_by_services(
197
212
  for category, presets in preset_categories.items():
198
213
  relevant_presets = []
199
214
  for preset in presets:
200
- preset_services = set(s.lower() for s in preset.get('services', []))
201
- preset_ports = set(preset.get('ports', []))
215
+ preset_services = set(s.lower() for s in preset.get("services", []))
216
+ preset_ports = set(preset.get("ports", []))
202
217
 
203
218
  # Check if any of target's services/ports match
204
219
  service_match = bool(preset_services & service_names)
@@ -219,7 +234,7 @@ def render_msf_auxiliary_menu(
219
234
  target: str,
220
235
  preset_categories: Dict[str, List[Dict]],
221
236
  engagement_id: int,
222
- show_all: bool = False
237
+ show_all: bool = False,
223
238
  ) -> Optional[Dict]:
224
239
  """
225
240
  Render enhanced MSF Auxiliary preset menu.
@@ -262,25 +277,36 @@ def render_msf_auxiliary_menu(
262
277
 
263
278
  # Header
264
279
  click.echo()
265
- click.echo(click.style("=" * width, fg='cyan'))
266
- click.echo(click.style(" MSF Auxiliary (Metasploit)".center(width), fg='cyan', bold=True))
267
- click.echo(click.style("=" * width, fg='cyan'))
280
+ click.echo(click.style("=" * width, fg="cyan"))
281
+ click.echo(
282
+ click.style(
283
+ " MSF Auxiliary (Metasploit)".center(width), fg="cyan", bold=True
284
+ )
285
+ )
286
+ click.echo(click.style("=" * width, fg="cyan"))
268
287
  click.echo()
269
288
 
270
289
  # Target info with detected services
271
- click.echo(click.style(f" Target: {target}", fg='green', bold=True))
290
+ click.echo(click.style(f" Target: {target}", fg="green", bold=True))
272
291
  if services:
273
292
  svc_summary = ", ".join(sorted(service_names)[:8])
274
293
  if len(service_names) > 8:
275
294
  svc_summary += f" (+{len(service_names) - 8} more)"
276
- click.echo(click.style(f" Services: {svc_summary}", fg='cyan'))
277
- click.echo(click.style(f" Ports: {', '.join(str(p) for p in sorted(ports)[:12])}", fg='cyan'))
295
+ click.echo(click.style(f" Services: {svc_summary}", fg="cyan"))
296
+ click.echo(
297
+ click.style(
298
+ f" Ports: {', '.join(str(p) for p in sorted(ports)[:12])}",
299
+ fg="cyan",
300
+ )
301
+ )
278
302
  else:
279
- click.echo(click.style(" Services: No service data (run nmap first)", fg='yellow'))
303
+ click.echo(
304
+ click.style(" Services: No service data (run nmap first)", fg="yellow")
305
+ )
280
306
  click.echo()
281
307
 
282
308
  # Quick actions
283
- click.echo(click.style(" QUICK ACTIONS", bold=True, fg='yellow'))
309
+ click.echo(click.style(" QUICK ACTIONS", bold=True, fg="yellow"))
284
310
  click.echo(" [e] Run ALL enumeration modules for detected services")
285
311
  click.echo(" [v] Run ALL vulnerability scans for detected services")
286
312
  click.echo(" [/] Search modules by keyword")
@@ -290,11 +316,11 @@ def render_msf_auxiliary_menu(
290
316
 
291
317
  # Recommendations (if any)
292
318
  if recommendations and not show_all:
293
- click.echo(click.style(" RECOMMENDED", bold=True, fg='red'))
319
+ click.echo(click.style(" RECOMMENDED", bold=True, fg="red"))
294
320
  for i, rec in enumerate(recommendations[:3], 1):
295
- priority_color = 'red' if rec['priority'] == 'high' else 'yellow'
296
- status = job_status_map.get(rec['module'], {})
297
- status_icon = _get_status_icon(status.get('status'))
321
+ priority_color = "red" if rec["priority"] == "high" else "yellow"
322
+ status = job_status_map.get(rec["module"], {})
323
+ status_icon = _get_status_icon(status.get("status"))
298
324
  click.echo(f" [r{i}] {status_icon} {rec['name']} - {rec['reason']}")
299
325
  click.echo()
300
326
 
@@ -303,24 +329,28 @@ def render_msf_auxiliary_menu(
303
329
  preset_map = {} # num -> preset
304
330
 
305
331
  for category_name, category_presets in filtered_categories.items():
306
- display_name = category_name.replace('_', ' ').title()
307
- click.echo(click.style(f" {display_name}:", bold=True, fg='cyan'))
332
+ display_name = category_name.replace("_", " ").title()
333
+ click.echo(click.style(f" {display_name}:", bold=True, fg="cyan"))
308
334
 
309
335
  for preset in category_presets:
310
336
  # Get job status for this module
311
- module_path = preset['args'][0] if preset.get('args') else ''
337
+ module_path = preset["args"][0] if preset.get("args") else ""
312
338
  status = job_status_map.get(module_path, {})
313
- status_icon = _get_status_icon(status.get('status'))
339
+ status_icon = _get_status_icon(status.get("status"))
314
340
  status_info = _get_status_info(status)
315
341
 
316
342
  # Format name with status
317
- name = preset['name']
318
- desc = preset['desc']
343
+ name = preset["name"]
344
+ desc = preset["desc"]
319
345
 
320
346
  if status_info:
321
- click.echo(f" {preset_num:2d}. {status_icon} {name:<24} - {desc} {status_info}")
347
+ click.echo(
348
+ f" {preset_num:2d}. {status_icon} {name:<24} - {desc} {status_info}"
349
+ )
322
350
  else:
323
- click.echo(f" {preset_num:2d}. {status_icon} {name:<24} - {desc}")
351
+ click.echo(
352
+ f" {preset_num:2d}. {status_icon} {name:<24} - {desc}"
353
+ )
324
354
 
325
355
  preset_map[preset_num] = preset
326
356
  preset_num += 1
@@ -334,41 +364,60 @@ def render_msf_auxiliary_menu(
334
364
 
335
365
  # Hidden modules notice
336
366
  if hidden_count > 0 and not show_all:
337
- click.echo(click.style(f" ({hidden_count} modules hidden - no matching services)", fg='yellow', dim=True))
367
+ click.echo(
368
+ click.style(
369
+ f" ({hidden_count} modules hidden - no matching services)",
370
+ fg="yellow",
371
+ dim=True,
372
+ )
373
+ )
338
374
  click.echo()
339
375
 
340
376
  try:
341
- choice = click.prompt(
342
- click.style(" Select option", fg='green', bold=True),
343
- type=str,
344
- default="1",
345
- show_default=False
346
- ).strip().lower()
377
+ choice = (
378
+ click.prompt(
379
+ click.style(" Select option", fg="green", bold=True),
380
+ type=str,
381
+ default="1",
382
+ show_default=False,
383
+ )
384
+ .strip()
385
+ .lower()
386
+ )
347
387
 
348
388
  # Handle special commands
349
- if choice == 'q':
389
+ if choice == "q":
350
390
  return None
351
391
 
352
- elif choice == 'e':
392
+ elif choice == "e":
353
393
  # Batch enumeration
354
- return _batch_select(filtered_categories.get('enumeration', []), 'enumeration')
394
+ return _batch_select(
395
+ filtered_categories.get("enumeration", []), "enumeration"
396
+ )
355
397
 
356
- elif choice == 'v':
398
+ elif choice == "v":
357
399
  # Batch vulnerability scan
358
- return _batch_select(filtered_categories.get('vulnerability_scanning', []), 'vulnerability')
400
+ return _batch_select(
401
+ filtered_categories.get("vulnerability_scanning", []),
402
+ "vulnerability",
403
+ )
359
404
 
360
- elif choice == '/':
405
+ elif choice == "/":
361
406
  # Search
362
- search_term = click.prompt(" Search", type=str, default="").strip().lower()
407
+ search_term = (
408
+ click.prompt(" Search", type=str, default="").strip().lower()
409
+ )
363
410
  if search_term:
364
411
  return _search_presets(preset_categories, search_term)
365
412
  continue
366
413
 
367
- elif choice == '*':
414
+ elif choice == "*":
368
415
  # Show all - recursive call with show_all=True
369
- return render_msf_auxiliary_menu(target, preset_categories, engagement_id, show_all=True)
416
+ return render_msf_auxiliary_menu(
417
+ target, preset_categories, engagement_id, show_all=True
418
+ )
370
419
 
371
- elif choice.startswith('r') and len(choice) == 2:
420
+ elif choice.startswith("r") and len(choice) == 2:
372
421
  # Recommendation selection
373
422
  try:
374
423
  rec_num = int(choice[1])
@@ -377,7 +426,7 @@ def render_msf_auxiliary_menu(
377
426
  # Find matching preset
378
427
  for presets in preset_categories.values():
379
428
  for preset in presets:
380
- if preset.get('args', [''])[0] == rec['module']:
429
+ if preset.get("args", [""])[0] == rec["module"]:
381
430
  return preset
382
431
  except ValueError:
383
432
  pass
@@ -390,9 +439,9 @@ def render_msf_auxiliary_menu(
390
439
  return preset_map[num]
391
440
  elif num == preset_num:
392
441
  # Custom args
393
- return {'custom': True}
442
+ return {"custom": True}
394
443
  except ValueError:
395
- click.echo(click.style(" Invalid selection", fg='red'))
444
+ click.echo(click.style(" Invalid selection", fg="red"))
396
445
  click.pause()
397
446
 
398
447
  except (KeyboardInterrupt, click.Abort):
@@ -401,14 +450,14 @@ def render_msf_auxiliary_menu(
401
450
 
402
451
  def _get_status_icon(status: Optional[str]) -> str:
403
452
  """Get status icon for a module."""
404
- if status == 'done':
405
- return click.style("", fg='green')
406
- elif status == 'running':
407
- return click.style("", fg='yellow')
408
- elif status == 'pending':
409
- return click.style("", fg='blue')
453
+ if status == "done":
454
+ return click.style("", fg="green")
455
+ elif status == "running":
456
+ return click.style("", fg="yellow")
457
+ elif status == "pending":
458
+ return click.style("", fg="blue")
410
459
  else:
411
- return click.style("", fg='white', dim=True)
460
+ return click.style("", fg="white", dim=True)
412
461
 
413
462
 
414
463
  def _get_status_info(status: Dict) -> str:
@@ -416,28 +465,28 @@ def _get_status_info(status: Dict) -> str:
416
465
  if not status:
417
466
  return ""
418
467
 
419
- s = status.get('status')
420
- if s == 'done':
421
- return click.style(f"(Job #{status.get('job_id')})", fg='green', dim=True)
422
- elif s == 'running':
423
- return click.style(f"(Running #{status.get('job_id')})", fg='yellow')
424
- elif s == 'pending':
425
- return click.style(f"(Queued #{status.get('job_id')})", fg='blue', dim=True)
468
+ s = status.get("status")
469
+ if s == "done":
470
+ return click.style(f"(Job #{status.get('job_id')})", fg="green", dim=True)
471
+ elif s == "running":
472
+ return click.style(f"(Running #{status.get('job_id')})", fg="yellow")
473
+ elif s == "pending":
474
+ return click.style(f"(Queued #{status.get('job_id')})", fg="blue", dim=True)
426
475
  return ""
427
476
 
428
477
 
429
478
  def _batch_select(presets: List[Dict], batch_type: str) -> Optional[Dict]:
430
479
  """Handle batch selection of multiple presets."""
431
480
  if not presets:
432
- click.echo(click.style(f" No {batch_type} modules available for this target", fg='yellow'))
481
+ click.echo(
482
+ click.style(
483
+ f" No {batch_type} modules available for this target", fg="yellow"
484
+ )
485
+ )
433
486
  click.pause()
434
487
  return None
435
488
 
436
- return {
437
- 'batch': True,
438
- 'batch_type': batch_type,
439
- 'presets': presets
440
- }
489
+ return {"batch": True, "batch_type": batch_type, "presets": presets}
441
490
 
442
491
 
443
492
  def _search_presets(preset_categories: Dict, search_term: str) -> Optional[Dict]:
@@ -446,15 +495,15 @@ def _search_presets(preset_categories: Dict, search_term: str) -> Optional[Dict]
446
495
 
447
496
  for category, presets in preset_categories.items():
448
497
  for preset in presets:
449
- name = preset.get('name', '').lower()
450
- desc = preset.get('desc', '').lower()
451
- module = preset.get('args', [''])[0].lower()
498
+ name = preset.get("name", "").lower()
499
+ desc = preset.get("desc", "").lower()
500
+ module = preset.get("args", [""])[0].lower()
452
501
 
453
502
  if search_term in name or search_term in desc or search_term in module:
454
503
  matches.append(preset)
455
504
 
456
505
  if not matches:
457
- click.echo(click.style(f" No modules matching '{search_term}'", fg='yellow'))
506
+ click.echo(click.style(f" No modules matching '{search_term}'", fg="yellow"))
458
507
  click.pause()
459
508
  return None
460
509
 
@@ -463,13 +512,15 @@ def _search_presets(preset_categories: Dict, search_term: str) -> Optional[Dict]
463
512
 
464
513
  # Show matches
465
514
  click.echo()
466
- click.echo(click.style(f" Found {len(matches)} matching modules:", fg='cyan'))
515
+ click.echo(click.style(f" Found {len(matches)} matching modules:", fg="cyan"))
467
516
  for i, preset in enumerate(matches, 1):
468
517
  click.echo(f" {i}. {preset['name']} - {preset['desc']}")
469
518
  click.echo()
470
519
 
471
520
  try:
472
- choice = click.prompt(" Select option", type=int, default=1, show_default=False)
521
+ choice = click.prompt(
522
+ " Select option", type=int, default=1, show_default=False
523
+ )
473
524
  if 1 <= choice <= len(matches):
474
525
  return matches[choice - 1]
475
526
  except (ValueError, KeyboardInterrupt, click.Abort):