souleyez 2.43.29__py3-none-any.whl → 2.43.34__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.

Potentially problematic release.


This version of souleyez might be problematic. Click here for more details.

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 +9526 -2879
  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 +563 -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 +408 -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 +371 -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 +854 -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 +173 -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 +223 -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 +22827 -10678
  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-2.43.34.dist-info}/METADATA +1 -1
  353. souleyez-2.43.34.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
@@ -18,119 +18,142 @@ def show_correlation_view(engagement_id: int):
18
18
  console = Console()
19
19
  analyzer = CorrelationAnalyzer()
20
20
  detector = GapDetector()
21
-
21
+
22
22
  while True:
23
23
  DesignSystem.clear_screen()
24
-
24
+
25
25
  # Header
26
26
  width = DesignSystem.get_terminal_width()
27
27
  click.echo("\n┌" + "─" * (width - 2) + "┐")
28
- click.echo("│" + click.style(" ATTACK CORRELATION & GAP ANALYSIS ".center(width - 2), bold=True, fg='cyan') + "│")
28
+ click.echo(
29
+ "│"
30
+ + click.style(
31
+ " ATTACK CORRELATION & GAP ANALYSIS ".center(width - 2),
32
+ bold=True,
33
+ fg="cyan",
34
+ )
35
+ + "│"
36
+ )
29
37
  click.echo("└" + "─" * (width - 2) + "┘")
30
38
  click.echo()
31
-
39
+
32
40
  # Run analysis
33
41
  try:
34
42
  analysis = analyzer.analyze_engagement(engagement_id)
35
43
  except Exception as e:
36
44
  import traceback
37
- click.echo(click.style(f"Error analyzing engagement: {e}", fg='red'))
45
+
46
+ click.echo(click.style(f"Error analyzing engagement: {e}", fg="red"))
38
47
  click.echo()
39
- click.echo(click.style("Full traceback:", fg='yellow'))
48
+ click.echo(click.style("Full traceback:", fg="yellow"))
40
49
  click.echo(traceback.format_exc())
41
50
  click.pause()
42
51
  return
43
-
44
- summary = analysis['summary']
45
-
52
+
53
+ summary = analysis["summary"]
54
+
46
55
  # Exploitation Summary
47
56
  console.print("[bold cyan]📊 EXPLOITATION SUMMARY[/bold cyan]")
48
57
  console.print(f" ├─ Total Services: {summary['total_services']}")
49
-
58
+
50
59
  # Calculate percentages
51
- total = summary['total_services']
60
+ total = summary["total_services"]
52
61
  if total > 0:
53
- exploited_pct = int((summary['exploited_services'] / total) * 100)
54
- attempted_pct = int((summary['attempted_services'] / total) * 100)
55
- not_attempted_pct = int((summary['not_attempted_services'] / total) * 100)
62
+ exploited_pct = int((summary["exploited_services"] / total) * 100)
63
+ attempted_pct = int((summary["attempted_services"] / total) * 100)
64
+ not_attempted_pct = int((summary["not_attempted_services"] / total) * 100)
56
65
  else:
57
66
  exploited_pct = attempted_pct = not_attempted_pct = 0
58
-
59
- console.print(f" ├─ [green]✅ Exploited: {summary['exploited_services']} ({exploited_pct}%)[/green]")
60
- console.print(f" ├─ [yellow]🔄 Attempted: {summary['attempted_services']} ({attempted_pct}%)[/yellow]")
61
- console.print(f" └─ [red]⚠️ Not Attempted: {summary['not_attempted_services']} ({not_attempted_pct}%)[/red]")
67
+
68
+ console.print(
69
+ f" ├─ [green] Exploited: {summary['exploited_services']} ({exploited_pct}%)[/green]"
70
+ )
71
+ console.print(
72
+ f" ├─ [yellow]🔄 Attempted: {summary['attempted_services']} ({attempted_pct}%)[/yellow]"
73
+ )
74
+ console.print(
75
+ f" └─ [red]⚠️ Not Attempted: {summary['not_attempted_services']} ({not_attempted_pct}%)[/red]"
76
+ )
62
77
  console.print()
63
-
78
+
64
79
  # Compromised Hosts
65
- if summary['compromised_hosts'] > 0:
80
+ if summary["compromised_hosts"] > 0:
66
81
  console.print("[bold green]🎯 COMPROMISED HOSTS[/bold green]")
67
-
68
- for host_analysis in analysis['hosts']:
69
- host_summary = host_analysis['summary']
70
- if host_summary['access_level'] != 'none':
71
- host = host_analysis['host']
72
- access_emoji = {
73
- 'user': '👤',
74
- 'root': '👑',
75
- 'admin': '🔑'
76
- }.get(host_summary['access_level'], '❓')
77
-
78
- console.print(f" ├─ {access_emoji} {host['ip_address']} [{host_summary['access_level'].upper()} ACCESS] - {host_summary['exploited']} services exploited")
79
-
82
+
83
+ for host_analysis in analysis["hosts"]:
84
+ host_summary = host_analysis["summary"]
85
+ if host_summary["access_level"] != "none":
86
+ host = host_analysis["host"]
87
+ access_emoji = {"user": "👤", "root": "👑", "admin": "🔑"}.get(
88
+ host_summary["access_level"], "❓"
89
+ )
90
+
91
+ console.print(
92
+ f" ├─ {access_emoji} {host['ip_address']} [{host_summary['access_level'].upper()} ACCESS] - {host_summary['exploited']} services exploited"
93
+ )
94
+
80
95
  console.print()
81
-
96
+
82
97
  # Exploitation Gaps
83
- gaps = analysis['gaps']
84
-
98
+ gaps = analysis["gaps"]
99
+
85
100
  if gaps:
86
101
  # Prioritize gaps
87
102
  prioritized_gaps = detector.prioritize_gaps(gaps)
88
-
103
+
89
104
  gap_summary = detector.get_gap_summary(engagement_id)
90
-
91
- console.print(f"[bold yellow]⚠️ EXPLOITATION GAPS ({len(gaps)} total)[/bold yellow]")
105
+
106
+ console.print(
107
+ f"[bold yellow]⚠️ EXPLOITATION GAPS ({len(gaps)} total)[/bold yellow]"
108
+ )
92
109
  console.print(f" ├─ 🔴 Critical: {gap_summary['by_severity']['critical']}")
93
110
  console.print(f" ├─ 🟠 High: {gap_summary['by_severity']['high']}")
94
111
  console.print(f" ├─ 🟡 Medium: {gap_summary['by_severity']['medium']}")
95
112
  console.print(f" └─ ⚪ Low: {gap_summary['by_severity']['low']}")
96
113
  console.print()
97
-
114
+
98
115
  # Show top 5 gaps
99
116
  console.print("[bold]🎯 TOP PRIORITY GAPS:[/bold]")
100
-
117
+
101
118
  for idx, gap in enumerate(prioritized_gaps[:5], 1):
102
119
  severity_emoji = {
103
- 'critical': '🔴',
104
- 'high': '🟠',
105
- 'medium': '🟡',
106
- 'low': ''
107
- }.get(gap['severity'], '')
108
-
109
- service_display = gap['service']
110
- if gap.get('version'):
120
+ "critical": "🔴",
121
+ "high": "🟠",
122
+ "medium": "🟡",
123
+ "low": "",
124
+ }.get(gap["severity"], "")
125
+
126
+ service_display = gap["service"]
127
+ if gap.get("version"):
111
128
  service_display += f" ({gap['version']})"
112
-
113
- console.print(f"\n {severity_emoji} [bold]#{idx} {gap['host']}:{gap['port']}[/bold] ({service_display})")
129
+
130
+ console.print(
131
+ f"\n {severity_emoji} [bold]#{idx} {gap['host']}:{gap['port']}[/bold] ({service_display})"
132
+ )
114
133
  console.print(f" Priority Score: {gap['priority_score']}/100")
115
134
  console.print(f" Reason: {gap['reason']}")
116
-
135
+
117
136
  # Show top 2 suggested actions
118
- if gap.get('suggested_actions'):
137
+ if gap.get("suggested_actions"):
119
138
  console.print(" 💡 Suggested:")
120
- for action in gap['suggested_actions'][:2]:
139
+ for action in gap["suggested_actions"][:2]:
121
140
  console.print(f" • {action}")
122
141
  else:
123
- console.print("[bold green]✅ NO GAPS - All discovered services have been attempted![/bold green]")
142
+ console.print(
143
+ "[bold green]✅ NO GAPS - All discovered services have been attempted![/bold green]"
144
+ )
124
145
  console.print()
125
-
146
+
126
147
  # Credentials Summary
127
- if summary['total_credentials'] > 0:
128
- console.print(f"[bold green]🔑 {summary['total_credentials']} credential(s) discovered[/bold green]")
148
+ if summary["total_credentials"] > 0:
149
+ console.print(
150
+ f"[bold green]🔑 {summary['total_credentials']} credential(s) discovered[/bold green]"
151
+ )
129
152
  console.print()
130
-
153
+
131
154
  # Menu
132
155
  click.echo(DesignSystem.separator())
133
- click.echo(click.style("ACTIONS", bold=True, fg='yellow'))
156
+ click.echo(click.style("ACTIONS", bold=True, fg="yellow"))
134
157
  click.echo("─" * 170)
135
158
  click.echo()
136
159
  click.echo(" [1] View Detailed Host Breakdown - See per-host gap analysis")
@@ -144,23 +167,25 @@ def show_correlation_view(engagement_id: int):
144
167
  click.echo()
145
168
  click.echo(" [q] ← Back")
146
169
  click.echo()
147
-
148
- choice = click.prompt("Select option", type=str, default='q', show_default=False)
149
-
150
- if choice == 'q':
170
+
171
+ choice = click.prompt(
172
+ "Select option", type=str, default="q", show_default=False
173
+ )
174
+
175
+ if choice == "q":
151
176
  return
152
- elif choice == '1':
177
+ elif choice == "1":
153
178
  show_host_breakdown(engagement_id, analysis)
154
- elif choice == '2':
179
+ elif choice == "2":
155
180
  show_all_gaps(engagement_id, detector)
156
- elif choice == '3':
181
+ elif choice == "3":
157
182
  show_service_timeline_menu(engagement_id, analysis)
158
- elif choice == '4':
183
+ elif choice == "4":
159
184
  export_gap_report(engagement_id, gaps)
160
- elif choice == '5':
185
+ elif choice == "5":
161
186
  continue
162
187
  else:
163
- click.echo(click.style("Invalid choice", fg='red'))
188
+ click.echo(click.style("Invalid choice", fg="red"))
164
189
  click.pause()
165
190
 
166
191
 
@@ -176,15 +201,23 @@ def show_host_breakdown(engagement_id: int, analysis: dict):
176
201
  while True:
177
202
  # Filter hosts based on show_empty_hosts setting
178
203
  if show_empty_hosts:
179
- filtered_hosts = analysis['hosts']
204
+ filtered_hosts = analysis["hosts"]
180
205
  else:
181
- filtered_hosts = [h for h in analysis['hosts'] if h['summary']['total_services'] > 0]
206
+ filtered_hosts = [
207
+ h for h in analysis["hosts"] if h["summary"]["total_services"] > 0
208
+ ]
182
209
 
183
210
  if not filtered_hosts:
184
211
  DesignSystem.clear_screen()
185
212
  width = DesignSystem.get_terminal_width()
186
213
  click.echo("\n┌" + "─" * (width - 2) + "┐")
187
- click.echo("│" + click.style(" HOST BREAKDOWN ".center(width - 2), bold=True, fg='cyan') + "│")
214
+ click.echo(
215
+ "│"
216
+ + click.style(
217
+ " HOST BREAKDOWN ".center(width - 2), bold=True, fg="cyan"
218
+ )
219
+ + "│"
220
+ )
188
221
  click.echo("└" + "─" * (width - 2) + "┘")
189
222
  click.echo()
190
223
  console.print("[yellow]No hosts with services found.[/yellow]")
@@ -203,7 +236,11 @@ def show_host_breakdown(engagement_id: int, analysis: dict):
203
236
  DesignSystem.clear_screen()
204
237
  width = DesignSystem.get_terminal_width()
205
238
  click.echo("\n┌" + "─" * (width - 2) + "┐")
206
- click.echo("│" + click.style(" HOST BREAKDOWN ".center(width - 2), bold=True, fg='cyan') + "│")
239
+ click.echo(
240
+ "│"
241
+ + click.style(" HOST BREAKDOWN ".center(width - 2), bold=True, fg="cyan")
242
+ + "│"
243
+ )
207
244
  click.echo("└" + "─" * (width - 2) + "┘")
208
245
  click.echo()
209
246
 
@@ -214,61 +251,70 @@ def show_host_breakdown(engagement_id: int, analysis: dict):
214
251
 
215
252
  # Display hosts for current page
216
253
  for host_analysis in page_hosts:
217
- host = host_analysis['host']
218
- summary = host_analysis['summary']
254
+ host = host_analysis["host"]
255
+ summary = host_analysis["summary"]
219
256
 
220
257
  console.print(f"\n[bold cyan]{'═' * 80}[/bold cyan]")
221
- console.print(f"[bold]HOST: {host['ip_address']}[/bold]", end='')
222
- if host.get('hostname'):
223
- console.print(f" ({host['hostname']})", end='')
258
+ console.print(f"[bold]HOST: {host['ip_address']}[/bold]", end="")
259
+ if host.get("hostname"):
260
+ console.print(f" ({host['hostname']})", end="")
224
261
  console.print()
225
- console.print(f"Access Level: [{_get_access_color(summary['access_level'])}]{summary['access_level'].upper()}[/{_get_access_color(summary['access_level'])}]")
262
+ console.print(
263
+ f"Access Level: [{_get_access_color(summary['access_level'])}]{summary['access_level'].upper()}[/{_get_access_color(summary['access_level'])}]"
264
+ )
226
265
  console.print()
227
266
 
228
267
  # Service summary
229
- console.print(f"Services: {summary['total_services']} total | "
230
- f"[green]{summary['exploited']} exploited[/green] | "
231
- f"[yellow]{summary['attempted']} attempted[/yellow] | "
232
- f"[red]{summary['not_attempted']} not attempted[/red]")
268
+ console.print(
269
+ f"Services: {summary['total_services']} total | "
270
+ f"[green]{summary['exploited']} exploited[/green] | "
271
+ f"[yellow]{summary['attempted']} attempted[/yellow] | "
272
+ f"[red]{summary['not_attempted']} not attempted[/red]"
273
+ )
233
274
  console.print(f"Credentials: {summary['credentials_found']}")
234
275
  console.print()
235
276
 
236
277
  # Show services
237
- for svc_analysis in host_analysis['services']:
238
- service = svc_analysis['service']
239
- status = svc_analysis['exploitation_status']
278
+ for svc_analysis in host_analysis["services"]:
279
+ service = svc_analysis["service"]
280
+ status = svc_analysis["exploitation_status"]
240
281
 
241
282
  status_emoji = {
242
- 'EXPLOITED': '',
243
- 'ATTEMPTED': '🔄',
244
- 'NOT_ATTEMPTED': '⚠️'
283
+ "EXPLOITED": "",
284
+ "ATTEMPTED": "🔄",
285
+ "NOT_ATTEMPTED": "⚠️",
245
286
  }[status]
246
287
 
247
288
  service_display = f"{service['port']}/{service['protocol']} ({service.get('service_name') or 'unknown'})"
248
- if service.get('version'):
289
+ if service.get("version"):
249
290
  service_display += f" - {service['version']}"
250
291
 
251
292
  console.print(f" {status_emoji} {service_display}")
252
293
 
253
294
  # Show job count
254
- job_count = len(svc_analysis['jobs'])
255
- cred_count = len(svc_analysis['credentials'])
295
+ job_count = len(svc_analysis["jobs"])
296
+ cred_count = len(svc_analysis["credentials"])
256
297
 
257
298
  if job_count > 0:
258
- success_rate = int(svc_analysis['success_rate'] * 100)
259
- console.print(f" Jobs: {job_count} | Success Rate: {success_rate}% | Credentials: {cred_count}")
299
+ success_rate = int(svc_analysis["success_rate"] * 100)
300
+ console.print(
301
+ f" Jobs: {job_count} | Success Rate: {success_rate}% | Credentials: {cred_count}"
302
+ )
260
303
 
261
304
  # Show top recommendation
262
- if svc_analysis['recommendations']:
305
+ if svc_analysis["recommendations"]:
263
306
  console.print(f" 💡 {svc_analysis['recommendations'][0]}")
264
307
 
265
308
  console.print(f"\n[bold cyan]{'═' * 80}[/bold cyan]")
266
309
  click.echo()
267
310
 
268
311
  # Pagination info
269
- click.echo(click.style(f"Page {current_page} of {total_pages}", fg='cyan', bold=True) +
270
- f" (Showing {start_idx + 1}-{end_idx} of {total_hosts} hosts" +
271
- ("" if show_empty_hosts else " with services") + ")")
312
+ click.echo(
313
+ click.style(f"Page {current_page} of {total_pages}", fg="cyan", bold=True)
314
+ + f" (Showing {start_idx + 1}-{end_idx} of {total_hosts} hosts"
315
+ + ("" if show_empty_hosts else " with services")
316
+ + ")"
317
+ )
272
318
  click.echo()
273
319
 
274
320
  # Navigation options
@@ -286,15 +332,17 @@ def show_host_breakdown(engagement_id: int, analysis: dict):
286
332
  click.echo(" " + " | ".join(nav_options))
287
333
  click.echo()
288
334
 
289
- choice = click.prompt("Select option", type=str, default='q', show_default=False).lower()
335
+ choice = click.prompt(
336
+ "Select option", type=str, default="q", show_default=False
337
+ ).lower()
290
338
 
291
- if choice == 'q':
339
+ if choice == "q":
292
340
  return
293
- elif choice == 'n' and current_page < total_pages:
341
+ elif choice == "n" and current_page < total_pages:
294
342
  current_page += 1
295
- elif choice == 'p' and current_page > 1:
343
+ elif choice == "p" and current_page > 1:
296
344
  current_page -= 1
297
- elif choice == 'a':
345
+ elif choice == "a":
298
346
  show_empty_hosts = not show_empty_hosts
299
347
  current_page = 1 # Reset to first page when toggling filter
300
348
  elif choice.isdigit():
@@ -302,10 +350,10 @@ def show_host_breakdown(engagement_id: int, analysis: dict):
302
350
  if 1 <= page_num <= total_pages:
303
351
  current_page = page_num
304
352
  else:
305
- click.echo(click.style("Invalid page number", fg='red'))
353
+ click.echo(click.style("Invalid page number", fg="red"))
306
354
  click.pause()
307
355
  else:
308
- click.echo(click.style("Invalid option", fg='red'))
356
+ click.echo(click.style("Invalid option", fg="red"))
309
357
  click.pause()
310
358
 
311
359
 
@@ -320,24 +368,44 @@ def show_all_gaps(engagement_id: int, detector: GapDetector):
320
368
  DesignSystem.clear_screen()
321
369
  width = DesignSystem.get_terminal_width()
322
370
  click.echo("\n┌" + "─" * (width - 2) + "┐")
323
- click.echo("│" + click.style(" EXPLOITATION GAPS - DETAILED VIEW ".center(width - 2), bold=True, fg='yellow') + "│")
371
+ click.echo(
372
+ "│"
373
+ + click.style(
374
+ " EXPLOITATION GAPS - DETAILED VIEW ".center(width - 2),
375
+ bold=True,
376
+ fg="yellow",
377
+ )
378
+ + "│"
379
+ )
324
380
  click.echo("└" + "─" * (width - 2) + "┘")
325
381
  click.echo()
326
- console.print("[bold green]✅ No gaps found! All services have been attempted.[/bold green]")
382
+ console.print(
383
+ "[bold green]✅ No gaps found! All services have been attempted.[/bold green]"
384
+ )
327
385
  click.pause()
328
386
  return
329
387
 
330
388
  # Pagination settings
331
389
  items_per_page = 20
332
390
  total_items = len(prioritized)
333
- total_pages = (total_items + items_per_page - 1) // items_per_page # Ceiling division
391
+ total_pages = (
392
+ total_items + items_per_page - 1
393
+ ) // items_per_page # Ceiling division
334
394
  current_page = 1
335
395
 
336
396
  while True:
337
397
  DesignSystem.clear_screen()
338
398
  width = DesignSystem.get_terminal_width()
339
399
  click.echo("\n┌" + "─" * (width - 2) + "┐")
340
- click.echo("│" + click.style(" EXPLOITATION GAPS - DETAILED VIEW ".center(width - 2), bold=True, fg='yellow') + "│")
400
+ click.echo(
401
+ "│"
402
+ + click.style(
403
+ " EXPLOITATION GAPS - DETAILED VIEW ".center(width - 2),
404
+ bold=True,
405
+ fg="yellow",
406
+ )
407
+ + "│"
408
+ )
341
409
  click.echo("└" + "─" * (width - 2) + "┘")
342
410
  click.echo()
343
411
 
@@ -347,7 +415,12 @@ def show_all_gaps(engagement_id: int, detector: GapDetector):
347
415
  page_items = prioritized[start_idx:end_idx]
348
416
 
349
417
  # Create table
350
- table = Table(show_header=True, header_style="bold", box=DesignSystem.TABLE_BOX, expand=True)
418
+ table = Table(
419
+ show_header=True,
420
+ header_style="bold",
421
+ box=DesignSystem.TABLE_BOX,
422
+ expand=True,
423
+ )
351
424
  table.add_column("#", width=8)
352
425
  table.add_column("Host", width=22)
353
426
  table.add_column("Port", width=10)
@@ -359,31 +432,37 @@ def show_all_gaps(engagement_id: int, detector: GapDetector):
359
432
  for idx in range(start_idx, end_idx):
360
433
  gap = prioritized[idx]
361
434
  severity_emoji = {
362
- 'critical': '🔴',
363
- 'high': '🟠',
364
- 'medium': '🟡',
365
- 'low': ''
366
- }[gap['severity']]
435
+ "critical": "🔴",
436
+ "high": "🟠",
437
+ "medium": "🟡",
438
+ "low": "",
439
+ }[gap["severity"]]
367
440
 
368
441
  # Get first suggested action
369
- first_action = gap['suggested_actions'][0] if gap['suggested_actions'] else 'No suggestions'
442
+ first_action = (
443
+ gap["suggested_actions"][0]
444
+ if gap["suggested_actions"]
445
+ else "No suggestions"
446
+ )
370
447
 
371
448
  table.add_row(
372
449
  str(idx + 1),
373
- gap['host'],
374
- str(gap['port']),
375
- gap['service'],
376
- (gap.get('version') or 'Unknown')[:26],
450
+ gap["host"],
451
+ str(gap["port"]),
452
+ gap["service"],
453
+ (gap.get("version") or "Unknown")[:26],
377
454
  f"{severity_emoji} {gap['priority_score']}",
378
- first_action[:40]
455
+ first_action[:40],
379
456
  )
380
457
 
381
458
  console.print(table)
382
459
  click.echo()
383
460
 
384
461
  # Pagination info
385
- click.echo(click.style(f"Page {current_page} of {total_pages}", fg='cyan', bold=True) +
386
- f" (Showing {start_idx + 1}-{end_idx} of {total_items} gaps)")
462
+ click.echo(
463
+ click.style(f"Page {current_page} of {total_pages}", fg="cyan", bold=True)
464
+ + f" (Showing {start_idx + 1}-{end_idx} of {total_items} gaps)"
465
+ )
387
466
  click.echo()
388
467
 
389
468
  # Navigation options
@@ -397,155 +476,167 @@ def show_all_gaps(engagement_id: int, detector: GapDetector):
397
476
  click.echo(" " + " | ".join(nav_options))
398
477
  click.echo()
399
478
 
400
- choice = click.prompt("Select option", type=str, default='q', show_default=False).lower()
479
+ choice = click.prompt(
480
+ "Select option", type=str, default="q", show_default=False
481
+ ).lower()
401
482
 
402
- if choice == 'q':
483
+ if choice == "q":
403
484
  return
404
- elif choice == 'n' and current_page < total_pages:
485
+ elif choice == "n" and current_page < total_pages:
405
486
  current_page += 1
406
- elif choice == 'p' and current_page > 1:
487
+ elif choice == "p" and current_page > 1:
407
488
  current_page -= 1
408
489
  elif choice.isdigit():
409
490
  page_num = int(choice)
410
491
  if 1 <= page_num <= total_pages:
411
492
  current_page = page_num
412
493
  else:
413
- click.echo(click.style("Invalid page number", fg='red'))
494
+ click.echo(click.style("Invalid page number", fg="red"))
414
495
  click.pause()
415
496
  else:
416
- click.echo(click.style("Invalid option", fg='red'))
497
+ click.echo(click.style("Invalid option", fg="red"))
417
498
  click.pause()
418
499
 
419
500
 
420
501
  def show_service_timeline_menu(engagement_id: int, analysis: dict):
421
502
  """Select a service to view timeline."""
422
503
  console = Console()
423
-
504
+
424
505
  DesignSystem.clear_screen()
425
506
  width = DesignSystem.get_terminal_width()
426
507
  click.echo("\n┌" + "─" * (width - 2) + "┐")
427
- click.echo("│" + click.style(" SELECT SERVICE FOR TIMELINE ".center(width - 2), bold=True, fg='cyan') + "│")
508
+ click.echo(
509
+ "│"
510
+ + click.style(
511
+ " SELECT SERVICE FOR TIMELINE ".center(width - 2), bold=True, fg="cyan"
512
+ )
513
+ + "│"
514
+ )
428
515
  click.echo("└" + "─" * (width - 2) + "┘")
429
516
  click.echo()
430
-
517
+
431
518
  # Build service list
432
519
  services = []
433
- for host_analysis in analysis['hosts']:
434
- host = host_analysis['host']
435
- for svc_analysis in host_analysis['services']:
436
- if len(svc_analysis['jobs']) > 0: # Only show services with jobs
437
- services.append({
438
- 'host': host,
439
- 'service': svc_analysis['service'],
440
- 'analysis': svc_analysis
441
- })
442
-
520
+ for host_analysis in analysis["hosts"]:
521
+ host = host_analysis["host"]
522
+ for svc_analysis in host_analysis["services"]:
523
+ if len(svc_analysis["jobs"]) > 0: # Only show services with jobs
524
+ services.append(
525
+ {
526
+ "host": host,
527
+ "service": svc_analysis["service"],
528
+ "analysis": svc_analysis,
529
+ }
530
+ )
531
+
443
532
  if not services:
444
533
  console.print("[yellow]No services with exploitation attempts found.[/yellow]")
445
534
  click.pause()
446
535
  return
447
-
536
+
448
537
  # Display services
449
538
  for idx, svc_data in enumerate(services, 1):
450
- host = svc_data['host']
451
- service = svc_data['service']
452
- analysis = svc_data['analysis']
453
-
454
- status_emoji = {
455
- 'EXPLOITED': '✅',
456
- 'ATTEMPTED': '🔄',
457
- 'NOT_ATTEMPTED': '⚠️'
458
- }[analysis['exploitation_status']]
459
-
460
- console.print(f"[{idx}] {status_emoji} {host['ip_address']}:{service['port']} ({service['service_name']}) - {len(analysis['jobs'])} attempts")
461
-
539
+ host = svc_data["host"]
540
+ service = svc_data["service"]
541
+ analysis = svc_data["analysis"]
542
+
543
+ status_emoji = {"EXPLOITED": "✅", "ATTEMPTED": "🔄", "NOT_ATTEMPTED": "⚠️"}[
544
+ analysis["exploitation_status"]
545
+ ]
546
+
547
+ console.print(
548
+ f"[{idx}] {status_emoji} {host['ip_address']}:{service['port']} ({service['service_name']}) - {len(analysis['jobs'])} attempts"
549
+ )
550
+
462
551
  click.echo()
463
552
  choice = click.prompt("Select option", type=int, default=0, show_default=False)
464
-
553
+
465
554
  if choice > 0 and choice <= len(services):
466
555
  selected = services[choice - 1]
467
556
  show_service_timeline(
468
- selected['host'],
469
- selected['service'],
470
- selected['analysis']
557
+ selected["host"], selected["service"], selected["analysis"]
471
558
  )
472
559
 
473
560
 
474
561
  def show_service_timeline(host: dict, service: dict, analysis: dict):
475
562
  """Show attack progression timeline for a service."""
476
563
  console = Console()
477
-
564
+
478
565
  DesignSystem.clear_screen()
479
566
  width = DesignSystem.get_terminal_width()
480
567
  click.echo("\n┌" + "─" * (width - 2) + "┐")
481
568
  service_title = f" ATTACK TIMELINE: {host['ip_address']}:{service['port']} ({service['service_name']}) "
482
- click.echo("│" + click.style(service_title.center(width - 2), bold=True, fg='cyan') + "│")
569
+ click.echo(
570
+ "│" + click.style(service_title.center(width - 2), bold=True, fg="cyan") + "│"
571
+ )
483
572
  click.echo("└" + "─" * (width - 2) + "┘")
484
573
  click.echo()
485
-
574
+
486
575
  # Service details
487
576
  console.print(f"[bold]Service:[/bold] {service['service_name']}")
488
- if service.get('version'):
577
+ if service.get("version"):
489
578
  console.print(f"[bold]Version:[/bold] {service['version']}")
490
- console.print(f"[bold]Status:[/bold] [{_get_status_color(analysis['exploitation_status'])}]{analysis['exploitation_status']}[/{_get_status_color(analysis['exploitation_status'])}]")
579
+ console.print(
580
+ f"[bold]Status:[/bold] [{_get_status_color(analysis['exploitation_status'])}]{analysis['exploitation_status']}[/{_get_status_color(analysis['exploitation_status'])}]"
581
+ )
491
582
  console.print()
492
-
583
+
493
584
  # Timeline
494
585
  console.print("[bold cyan]📅 TIMELINE[/bold cyan]")
495
586
  console.print()
496
-
587
+
497
588
  # Show jobs chronologically
498
- for idx, job in enumerate(analysis['jobs'], 1):
499
- timestamp = job.get('created_at', 'Unknown')[:19].replace('T', ' ')
500
-
589
+ for idx, job in enumerate(analysis["jobs"], 1):
590
+ timestamp = job.get("created_at", "Unknown")[:19].replace("T", " ")
591
+
501
592
  # Job status emoji
502
- if job.get('success'):
503
- emoji = ''
504
- color = 'green'
505
- elif job['status'] == 'failed':
506
- emoji = ''
507
- color = 'red'
508
- elif job['status'] == 'killed':
509
- emoji = '🛑'
510
- color = 'yellow'
593
+ if job.get("success"):
594
+ emoji = ""
595
+ color = "green"
596
+ elif job["status"] == "failed":
597
+ emoji = ""
598
+ color = "red"
599
+ elif job["status"] == "killed":
600
+ emoji = "🛑"
601
+ color = "yellow"
511
602
  else:
512
- emoji = '🔄'
513
- color = 'blue'
514
-
603
+ emoji = "🔄"
604
+ color = "blue"
605
+
515
606
  console.print(f"[{color}][{timestamp}] {emoji} Attempt #{idx}[/{color}]")
516
607
  console.print(f" ├─ Tool: {job['tool']}")
517
-
518
- if job.get('label'):
608
+
609
+ if job.get("label"):
519
610
  console.print(f" ├─ Label: {job['label']}")
520
-
611
+
521
612
  console.print(f" ├─ Status: {job['status'].upper()}")
522
-
523
- if job.get('success'):
613
+
614
+ if job.get("success"):
524
615
  console.print(" └─ Result: [bold green]SUCCESS ✅[/bold green]")
525
616
  else:
526
617
  console.print(" └─ Result: [yellow]No credentials found[/yellow]")
527
-
618
+
528
619
  console.print()
529
-
620
+
530
621
  # Show credentials found
531
- if analysis['credentials']:
622
+ if analysis["credentials"]:
532
623
  console.print("[bold green]🔐 CREDENTIALS OBTAINED[/bold green]")
533
- for cred in analysis['credentials']:
534
- username = cred.get('username', 'N/A')
535
- password = cred.get('password', 'N/A')
536
- status = cred.get('status', 'unknown')
537
-
538
- status_emoji = '' if status == 'valid' else ''
624
+ for cred in analysis["credentials"]:
625
+ username = cred.get("username", "N/A")
626
+ password = cred.get("password", "N/A")
627
+ status = cred.get("status", "unknown")
628
+
629
+ status_emoji = "" if status == "valid" else ""
539
630
  console.print(f" {status_emoji} {username}:{password} [{status}]")
540
631
  console.print()
541
-
632
+
542
633
  # Show recommendations
543
- if analysis['recommendations']:
634
+ if analysis["recommendations"]:
544
635
  console.print("[bold yellow]💡 RECOMMENDATIONS[/bold yellow]")
545
- for rec in analysis['recommendations']:
636
+ for rec in analysis["recommendations"]:
546
637
  console.print(f" • {rec}")
547
638
  console.print()
548
-
639
+
549
640
  click.pause()
550
641
 
551
642
 
@@ -553,78 +644,69 @@ def export_gap_report(engagement_id: int, gaps: list):
553
644
  """Export gaps to a text report."""
554
645
  from datetime import datetime
555
646
  from pathlib import Path
556
-
647
+
557
648
  eng_mgr = EngagementManager()
558
649
  engagement = eng_mgr.get_by_id(engagement_id)
559
650
 
560
651
  if not engagement:
561
- click.echo(click.style("Engagement not found", fg='red'))
652
+ click.echo(click.style("Engagement not found", fg="red"))
562
653
  click.pause()
563
654
  return
564
-
655
+
565
656
  # Create reports directory in user home
566
657
  reports_dir = Path.home() / ".souleyez" / "data" / "reports"
567
658
  reports_dir.mkdir(parents=True, exist_ok=True)
568
-
659
+
569
660
  # Generate filename
570
661
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
571
662
  filename = f"gap_report_{engagement['name']}_{timestamp}.txt"
572
663
  filepath = reports_dir / filename
573
-
664
+
574
665
  # Write report
575
- with open(filepath, 'w') as f:
666
+ with open(filepath, "w") as f:
576
667
  f.write("=" * 80 + "\n")
577
668
  f.write(f"EXPLOITATION GAP REPORT\n")
578
669
  f.write(f"Engagement: {engagement['name']}\n")
579
670
  f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
580
671
  f.write("=" * 80 + "\n\n")
581
-
672
+
582
673
  f.write(f"Total Gaps: {len(gaps)}\n\n")
583
-
674
+
584
675
  for idx, gap in enumerate(gaps, 1):
585
676
  f.write(f"GAP #{idx}\n")
586
677
  f.write("-" * 80 + "\n")
587
678
  f.write(f"Host: {gap['host']}\n")
588
- if gap.get('hostname'):
679
+ if gap.get("hostname"):
589
680
  f.write(f"Hostname: {gap['hostname']}\n")
590
681
  f.write(f"Port: {gap['port']}\n")
591
682
  f.write(f"Service: {gap['service']}\n")
592
- if gap.get('version'):
683
+ if gap.get("version"):
593
684
  f.write(f"Version: {gap['version']}\n")
594
685
  f.write(f"Severity: {gap['severity'].upper()}\n")
595
686
  f.write(f"Priority Score: {gap['priority_score']}/100\n")
596
687
  f.write(f"Reason: {gap['reason']}\n")
597
688
  f.write("\nSuggested Actions:\n")
598
- for action in gap['suggested_actions']:
689
+ for action in gap["suggested_actions"]:
599
690
  f.write(f" - {action}\n")
600
-
601
- if gap.get('msf_modules'):
691
+
692
+ if gap.get("msf_modules"):
602
693
  f.write("\nMetasploit Modules:\n")
603
- for module in gap['msf_modules']:
694
+ for module in gap["msf_modules"]:
604
695
  f.write(f" - {module}\n")
605
-
696
+
606
697
  f.write("\n")
607
-
608
- click.echo(click.style(f"✅ Gap report exported to: {filepath}", fg='green'))
698
+
699
+ click.echo(click.style(f"✅ Gap report exported to: {filepath}", fg="green"))
609
700
  click.pause()
610
701
 
611
702
 
612
703
  def _get_access_color(access_level: str) -> str:
613
704
  """Get color for access level."""
614
- colors = {
615
- 'root': 'red',
616
- 'admin': 'red',
617
- 'user': 'yellow',
618
- 'none': 'dim'
619
- }
620
- return colors.get(access_level, 'white')
705
+ colors = {"root": "red", "admin": "red", "user": "yellow", "none": "dim"}
706
+ return colors.get(access_level, "white")
621
707
 
622
708
 
623
709
  def _get_status_color(status: str) -> str:
624
710
  """Get color for exploitation status."""
625
- colors = {
626
- 'EXPLOITED': 'green',
627
- 'ATTEMPTED': 'yellow',
628
- 'NOT_ATTEMPTED': 'red'
629
- }
630
- return colors.get(status, 'white')
711
+ colors = {"EXPLOITED": "green", "ATTEMPTED": "yellow", "NOT_ATTEMPTED": "red"}
712
+ return colors.get(status, "white")