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
@@ -22,7 +22,11 @@ from souleyez.storage.hosts import HostManager
22
22
  from souleyez.storage.findings import FindingsManager
23
23
  from souleyez.storage.credentials import CredentialsManager
24
24
  from souleyez.storage.exploit_attempts import record_attempt
25
- from souleyez.storage.msf_sessions import add_msf_session, get_msf_sessions, close_msf_session
25
+ from souleyez.storage.msf_sessions import (
26
+ add_msf_session,
27
+ get_msf_sessions,
28
+ close_msf_session,
29
+ )
26
30
 
27
31
  logger = logging.getLogger(__name__)
28
32
 
@@ -38,7 +42,7 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
38
42
  """
39
43
  # Check user config first, then system-wide config (Kali uses system-wide)
40
44
  user_db_path = Path.home() / ".msf4" / "database.yml"
41
- system_db_path = Path('/usr/share/metasploit-framework/config/database.yml')
45
+ system_db_path = Path("/usr/share/metasploit-framework/config/database.yml")
42
46
 
43
47
  db_yml_path = None
44
48
  if user_db_path.exists():
@@ -53,13 +57,13 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
53
57
  try:
54
58
  import yaml
55
59
 
56
- with open(db_yml_path, 'r') as f:
60
+ with open(db_yml_path, "r") as f:
57
61
  config = yaml.safe_load(f)
58
62
 
59
63
  # MSF database.yml has environment sections (development, production, etc.)
60
64
  # Look for 'production' first, then 'development', then any section
61
65
  env_config = None
62
- for env in ['production', 'development']:
66
+ for env in ["production", "development"]:
63
67
  if env in config:
64
68
  env_config = config[env]
65
69
  break
@@ -67,7 +71,7 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
67
71
  if not env_config:
68
72
  # Take the first available section
69
73
  for key, val in config.items():
70
- if isinstance(val, dict) and 'database' in val:
74
+ if isinstance(val, dict) and "database" in val:
71
75
  env_config = val
72
76
  break
73
77
 
@@ -76,12 +80,12 @@ def get_msf_database_config() -> Optional[Dict[str, Any]]:
76
80
  return None
77
81
 
78
82
  return {
79
- 'host': env_config.get('host', 'localhost'),
80
- 'port': env_config.get('port', 5432),
81
- 'database': env_config.get('database', 'msf'),
82
- 'username': env_config.get('username', 'msf'),
83
- 'password': env_config.get('password', ''),
84
- 'workspace': 'default'
83
+ "host": env_config.get("host", "localhost"),
84
+ "port": env_config.get("port", 5432),
85
+ "database": env_config.get("database", "msf"),
86
+ "username": env_config.get("username", "msf"),
87
+ "password": env_config.get("password", ""),
88
+ "workspace": "default",
85
89
  }
86
90
  except ImportError:
87
91
  logger.debug("PyYAML not available, cannot parse database.yml")
@@ -106,6 +110,7 @@ def get_msf_active_sessions_count() -> Optional[int]:
106
110
  # Method 1: Try MSF RPC first (most reliable when msfrpcd is running)
107
111
  try:
108
112
  from souleyez.core.msf_rpc_client import MSFRPCClient, MSGPACK_AVAILABLE
113
+
109
114
  if MSGPACK_AVAILABLE:
110
115
  # Try connecting without password first (some setups don't require it)
111
116
  client = MSFRPCClient()
@@ -156,22 +161,22 @@ def _map_msf_state_to_status(msf_state: Optional[str]) -> str:
156
161
  SoulEyez status value ('up' or 'down')
157
162
  """
158
163
  if not msf_state:
159
- return 'up' # Default for null/empty
164
+ return "up" # Default for null/empty
160
165
 
161
166
  state_lower = msf_state.lower()
162
167
 
163
168
  # Map MSF state to SoulEyez status
164
- if state_lower == 'alive':
165
- return 'up'
166
- elif state_lower == 'dead':
167
- return 'down'
168
- elif state_lower in ('up', 'down'):
169
+ if state_lower == "alive":
170
+ return "up"
171
+ elif state_lower == "dead":
172
+ return "down"
173
+ elif state_lower in ("up", "down"):
169
174
  # Already in correct format
170
175
  return state_lower
171
176
  else:
172
177
  # Unknown state - log warning and default to up
173
178
  logger.warning(f"Unknown MSF host state '{msf_state}', defaulting to 'up'")
174
- return 'up'
179
+ return "up"
175
180
 
176
181
 
177
182
  class MSFSyncManager:
@@ -182,7 +187,7 @@ class MSFSyncManager:
182
187
  db_path: str,
183
188
  engagement_id: int,
184
189
  msf_db_config: Optional[Dict[str, Any]] = None,
185
- msf_rpc_config: Optional[Dict[str, Any]] = None
190
+ msf_rpc_config: Optional[Dict[str, Any]] = None,
186
191
  ):
187
192
  """
188
193
  Initialize MSF sync manager
@@ -196,19 +201,19 @@ class MSFSyncManager:
196
201
  self.db_path = db_path
197
202
  self.engagement_id = engagement_id
198
203
  self.msf_db_config = msf_db_config or {
199
- 'host': 'localhost',
200
- 'port': 5432,
201
- 'database': 'msf',
202
- 'username': 'msf',
203
- 'password': '',
204
- 'workspace': 'default'
204
+ "host": "localhost",
205
+ "port": 5432,
206
+ "database": "msf",
207
+ "username": "msf",
208
+ "password": "",
209
+ "workspace": "default",
205
210
  }
206
211
  self.msf_rpc_config = msf_rpc_config or {
207
- 'host': '127.0.0.1',
208
- 'port': 55553,
209
- 'username': 'msf',
210
- 'password': '',
211
- 'ssl': False
212
+ "host": "127.0.0.1",
213
+ "port": 55553,
214
+ "username": "msf",
215
+ "password": "",
216
+ "ssl": False,
212
217
  }
213
218
 
214
219
  def import_msf_data(
@@ -217,7 +222,7 @@ class MSFSyncManager:
217
222
  import_services: bool = True,
218
223
  import_vulns: bool = True,
219
224
  import_creds: bool = True,
220
- import_sessions: bool = True
225
+ import_sessions: bool = True,
221
226
  ) -> Dict[str, int]:
222
227
  """
223
228
  Import data from MSF database into SoulEyez
@@ -233,12 +238,12 @@ class MSFSyncManager:
233
238
  Dictionary with counts of imported items
234
239
  """
235
240
  stats = {
236
- 'hosts': 0,
237
- 'services': 0,
238
- 'vulns': 0,
239
- 'creds': 0,
240
- 'sessions': 0,
241
- 'errors': 0
241
+ "hosts": 0,
242
+ "services": 0,
243
+ "vulns": 0,
244
+ "creds": 0,
245
+ "sessions": 0,
246
+ "errors": 0,
242
247
  }
243
248
 
244
249
  try:
@@ -247,58 +252,62 @@ class MSFSyncManager:
247
252
  if import_hosts:
248
253
  try:
249
254
  hosts_imported = self._import_hosts(msf_db)
250
- stats['hosts'] = hosts_imported
255
+ stats["hosts"] = hosts_imported
251
256
  except (KeyError, AttributeError) as e:
252
257
  logger.error(f"Schema error importing hosts: {e}")
253
- logger.error("MSF database schema may have changed. Consider using XML export.")
254
- stats['errors'] += 1
258
+ logger.error(
259
+ "MSF database schema may have changed. Consider using XML export."
260
+ )
261
+ stats["errors"] += 1
255
262
 
256
263
  # Import services
257
264
  if import_services:
258
265
  try:
259
266
  services_imported = self._import_services(msf_db)
260
- stats['services'] = services_imported
267
+ stats["services"] = services_imported
261
268
  except (KeyError, AttributeError) as e:
262
269
  logger.error(f"Schema error importing services: {e}")
263
- stats['errors'] += 1
270
+ stats["errors"] += 1
264
271
 
265
272
  # Import vulnerabilities
266
273
  if import_vulns:
267
274
  try:
268
275
  vulns_imported = self._import_vulns(msf_db)
269
- stats['vulns'] = vulns_imported
276
+ stats["vulns"] = vulns_imported
270
277
  except (KeyError, AttributeError) as e:
271
278
  logger.error(f"Schema error importing vulnerabilities: {e}")
272
- stats['errors'] += 1
279
+ stats["errors"] += 1
273
280
 
274
281
  # Import credentials
275
282
  if import_creds:
276
283
  try:
277
284
  creds_imported = self._import_creds(msf_db)
278
- stats['creds'] = creds_imported
285
+ stats["creds"] = creds_imported
279
286
  except (KeyError, AttributeError) as e:
280
287
  logger.error(f"Schema error importing credentials: {e}")
281
- stats['errors'] += 1
288
+ stats["errors"] += 1
282
289
 
283
290
  # Import sessions
284
291
  if import_sessions:
285
292
  try:
286
293
  sessions_imported = self._import_sessions(msf_db)
287
- stats['sessions'] = sessions_imported
294
+ stats["sessions"] = sessions_imported
288
295
  except (KeyError, AttributeError) as e:
289
296
  logger.error(f"Schema error importing sessions: {e}")
290
- stats['errors'] += 1
297
+ stats["errors"] += 1
291
298
 
292
299
  except MSFDatabaseSchemaError as e:
293
300
  logger.error(f"MSF database schema is incompatible: {e}")
294
301
  logger.error("RECOMMENDATION: Use MSF XML export instead:")
295
302
  logger.error(" In msfconsole: db_export -f xml /tmp/msf_export.xml")
296
303
  logger.error(" Then import the XML file into SoulEyez")
297
- stats['errors'] += 1
304
+ stats["errors"] += 1
298
305
  except Exception as e:
299
306
  logger.error(f"Failed to import MSF data: {e}")
300
- logger.error("If this error persists, try using MSF XML export as an alternative")
301
- stats['errors'] += 1
307
+ logger.error(
308
+ "If this error persists, try using MSF XML export as an alternative"
309
+ )
310
+ stats["errors"] += 1
302
311
 
303
312
  return stats
304
313
 
@@ -312,24 +321,28 @@ class MSFSyncManager:
312
321
  for msf_host in msf_hosts:
313
322
  try:
314
323
  # Check if host already exists
315
- existing = host_mgr.get_host_by_ip(self.engagement_id, msf_host['address'])
324
+ existing = host_mgr.get_host_by_ip(
325
+ self.engagement_id, msf_host["address"]
326
+ )
316
327
 
317
328
  if not existing:
318
329
  # Add new host
319
330
  # Map MSF state ('alive'/'dead') to SoulEyez status ('up'/'down')
320
- msf_state = msf_host.get('state')
331
+ msf_state = msf_host.get("state")
321
332
  souleyez_status = _map_msf_state_to_status(msf_state)
322
333
 
323
334
  host_data = {
324
- 'ip': msf_host['address'],
325
- 'hostname': msf_host.get('name') or None,
326
- 'mac_address': msf_host.get('mac') or None,
327
- 'os': msf_host.get('os_name') or None,
328
- 'status': souleyez_status
335
+ "ip": msf_host["address"],
336
+ "hostname": msf_host.get("name") or None,
337
+ "mac_address": msf_host.get("mac") or None,
338
+ "os": msf_host.get("os_name") or None,
339
+ "status": souleyez_status,
329
340
  }
330
341
  host_mgr.add_or_update_host(self.engagement_id, host_data)
331
342
  count += 1
332
- logger.debug(f"Imported host {msf_host['address']} with status '{souleyez_status}' (MSF state: '{msf_state}')")
343
+ logger.debug(
344
+ f"Imported host {msf_host['address']} with status '{souleyez_status}' (MSF state: '{msf_state}')"
345
+ )
333
346
  except Exception as e:
334
347
  logger.error(f"Failed to import host {msf_host['address']}: {e}")
335
348
 
@@ -346,27 +359,33 @@ class MSFSyncManager:
346
359
  for msf_svc in msf_services:
347
360
  try:
348
361
  # Get or create host
349
- host = host_mgr.get_host_by_ip(self.engagement_id, msf_svc['host_address'])
362
+ host = host_mgr.get_host_by_ip(
363
+ self.engagement_id, msf_svc["host_address"]
364
+ )
350
365
  if not host:
351
366
  # Create host if it doesn't exist
352
- logger.debug(f"Creating host for service: {msf_svc['host_address']}")
353
- host_data = {'ip': msf_svc['host_address']}
367
+ logger.debug(
368
+ f"Creating host for service: {msf_svc['host_address']}"
369
+ )
370
+ host_data = {"ip": msf_svc["host_address"]}
354
371
  host_id = host_mgr.add_or_update_host(self.engagement_id, host_data)
355
372
  else:
356
- host_id = host['id']
373
+ host_id = host["id"]
357
374
 
358
375
  # Add or update service (add_service handles duplicates)
359
376
  service_data = {
360
- 'port': msf_svc['port'],
361
- 'protocol': msf_svc.get('proto', 'tcp'),
362
- 'state': msf_svc.get('state', 'open'),
363
- 'service': msf_svc.get('name') or 'unknown',
364
- 'product': msf_svc.get('info') or None
377
+ "port": msf_svc["port"],
378
+ "protocol": msf_svc.get("proto", "tcp"),
379
+ "state": msf_svc.get("state", "open"),
380
+ "service": msf_svc.get("name") or "unknown",
381
+ "product": msf_svc.get("info") or None,
365
382
  }
366
383
  host_mgr.add_service(host_id, service_data)
367
384
  count += 1
368
385
  except Exception as e:
369
- logger.error(f"Failed to import service on {msf_svc.get('host_address')}:{msf_svc.get('port')}: {e}")
386
+ logger.error(
387
+ f"Failed to import service on {msf_svc.get('host_address')}:{msf_svc.get('port')}: {e}"
388
+ )
370
389
  skipped += 1
371
390
 
372
391
  if skipped > 0:
@@ -386,46 +405,56 @@ class MSFSyncManager:
386
405
  for msf_vuln in msf_vulns:
387
406
  try:
388
407
  # Get host
389
- host = host_mgr.get_host_by_ip(self.engagement_id, msf_vuln['host_address'])
408
+ host = host_mgr.get_host_by_ip(
409
+ self.engagement_id, msf_vuln["host_address"]
410
+ )
390
411
  if not host:
391
- logger.debug(f"Skipping vuln - host not found: {msf_vuln['host_address']}")
412
+ logger.debug(
413
+ f"Skipping vuln - host not found: {msf_vuln['host_address']}"
414
+ )
392
415
  skipped += 1
393
416
  continue
394
417
 
395
418
  # Get service if available
396
- port = msf_vuln.get('service_port')
419
+ port = msf_vuln.get("service_port")
397
420
  if port:
398
- services = host_mgr.get_host_services(host['id'])
421
+ services = host_mgr.get_host_services(host["id"])
399
422
  matching_services = [
400
- s for s in services
401
- if s['port'] == port and s['protocol'] == msf_vuln.get('service_proto', 'tcp')
423
+ s
424
+ for s in services
425
+ if s["port"] == port
426
+ and s["protocol"] == msf_vuln.get("service_proto", "tcp")
402
427
  ]
403
428
  else:
404
429
  matching_services = []
405
430
 
406
431
  # Determine severity from vuln name/info
407
- severity = self._determine_severity(msf_vuln.get('name', ''))
432
+ severity = self._determine_severity(msf_vuln.get("name", ""))
408
433
 
409
434
  # Add finding
410
435
  findings_mgr.add_finding(
411
436
  engagement_id=self.engagement_id,
412
- title=msf_vuln.get('name', 'Unknown Vulnerability'),
413
- finding_type='vulnerability',
437
+ title=msf_vuln.get("name", "Unknown Vulnerability"),
438
+ finding_type="vulnerability",
414
439
  severity=severity,
415
- description=msf_vuln.get('info') or '',
416
- host_id=host['id'],
417
- tool='metasploit',
418
- refs=msf_vuln.get('refs') or '',
440
+ description=msf_vuln.get("info") or "",
441
+ host_id=host["id"],
442
+ tool="metasploit",
443
+ refs=msf_vuln.get("refs") or "",
419
444
  port=port,
420
- evidence=f"MSF Vuln ID: {msf_vuln['id']}"
445
+ evidence=f"MSF Vuln ID: {msf_vuln['id']}",
421
446
  )
422
447
  count += 1
423
448
  except Exception as e:
424
- logger.error(f"Failed to import vulnerability '{msf_vuln.get('name')}': {e}")
449
+ logger.error(
450
+ f"Failed to import vulnerability '{msf_vuln.get('name')}': {e}"
451
+ )
425
452
  skipped += 1
426
453
 
427
454
  if skipped > 0:
428
- logger.warning(f"Skipped {skipped} vulnerabilities (host not found or errors)")
455
+ logger.warning(
456
+ f"Skipped {skipped} vulnerabilities (host not found or errors)"
457
+ )
429
458
 
430
459
  return count
431
460
 
@@ -441,41 +470,55 @@ class MSFSyncManager:
441
470
  for msf_cred in msf_creds:
442
471
  try:
443
472
  # Skip if no host address
444
- if not msf_cred.get('host_address'):
445
- logger.debug(f"Skipping credential - no host address (service-less credential)")
473
+ if not msf_cred.get("host_address"):
474
+ logger.debug(
475
+ f"Skipping credential - no host address (service-less credential)"
476
+ )
446
477
  skipped += 1
447
478
  continue
448
479
 
449
480
  # Get host
450
- host = host_mgr.get_host_by_ip(self.engagement_id, msf_cred['host_address'])
481
+ host = host_mgr.get_host_by_ip(
482
+ self.engagement_id, msf_cred["host_address"]
483
+ )
451
484
  if not host:
452
- logger.debug(f"Skipping credential - host not found: {msf_cred['host_address']}")
485
+ logger.debug(
486
+ f"Skipping credential - host not found: {msf_cred['host_address']}"
487
+ )
453
488
  skipped += 1
454
489
  continue
455
490
 
456
491
  # Create credential finding
457
- username = msf_cred.get('username', 'N/A')
458
- private_data = msf_cred.get('private_data', 'N/A')
459
- private_type = msf_cred.get('private_type', 'password')
492
+ username = msf_cred.get("username", "N/A")
493
+ private_data = msf_cred.get("private_data", "N/A")
494
+ private_type = msf_cred.get("private_type", "password")
460
495
 
461
496
  findings_mgr.add_finding(
462
497
  engagement_id=self.engagement_id,
463
498
  title=f"Credential Found: {username}",
464
- finding_type='credential',
465
- severity='high',
499
+ finding_type="credential",
500
+ severity="high",
466
501
  description=f"Username: {username}\nType: {private_type}\nStatus: {msf_cred.get('status', 'unknown')}",
467
- host_id=host['id'],
468
- tool='metasploit',
469
- port=msf_cred.get('service_port'),
470
- evidence=f"Private: {private_data[:20]}..." if len(private_data) > 20 else private_data
502
+ host_id=host["id"],
503
+ tool="metasploit",
504
+ port=msf_cred.get("service_port"),
505
+ evidence=(
506
+ f"Private: {private_data[:20]}..."
507
+ if len(private_data) > 20
508
+ else private_data
509
+ ),
471
510
  )
472
511
  count += 1
473
512
  except Exception as e:
474
- logger.error(f"Failed to import credential for {msf_cred.get('username')}: {e}")
513
+ logger.error(
514
+ f"Failed to import credential for {msf_cred.get('username')}: {e}"
515
+ )
475
516
  skipped += 1
476
517
 
477
518
  if skipped > 0:
478
- logger.warning(f"Skipped {skipped} credentials (no host address or host not found)")
519
+ logger.warning(
520
+ f"Skipped {skipped} credentials (no host address or host not found)"
521
+ )
479
522
 
480
523
  return count
481
524
 
@@ -489,37 +532,42 @@ class MSFSyncManager:
489
532
 
490
533
  # Get database connection for msf_sessions functions
491
534
  from souleyez.storage.database import get_db
535
+
492
536
  db = get_db()
493
537
  conn = db.get_connection()
494
538
 
495
539
  for msf_session in msf_sessions:
496
540
  try:
497
541
  # Get host
498
- host = host_mgr.get_host_by_ip(self.engagement_id, msf_session['host_address'])
542
+ host = host_mgr.get_host_by_ip(
543
+ self.engagement_id, msf_session["host_address"]
544
+ )
499
545
  if not host:
500
- session_num = msf_session.get('local_id') or msf_session['id']
501
- logger.debug(f"Skipping session {session_num} - host not found: {msf_session['host_address']}")
546
+ session_num = msf_session.get("local_id") or msf_session["id"]
547
+ logger.debug(
548
+ f"Skipping session {session_num} - host not found: {msf_session['host_address']}"
549
+ )
502
550
  skipped += 1
503
551
  continue
504
552
 
505
553
  # Add session to msf_sessions table
506
- is_active = msf_session.get('closed_at') is None
554
+ is_active = msf_session.get("closed_at") is None
507
555
 
508
556
  session_id = add_msf_session(
509
557
  conn,
510
558
  engagement_id=self.engagement_id,
511
- host_id=host['id'],
512
- msf_session_id=msf_session.get('local_id') or msf_session['id'],
513
- session_type=msf_session.get('stype'),
514
- via_exploit=msf_session.get('via_exploit'),
515
- via_payload=msf_session.get('via_payload'),
516
- platform=msf_session.get('platform'),
559
+ host_id=host["id"],
560
+ msf_session_id=msf_session.get("local_id") or msf_session["id"],
561
+ session_type=msf_session.get("stype"),
562
+ via_exploit=msf_session.get("via_exploit"),
563
+ via_payload=msf_session.get("via_payload"),
564
+ platform=msf_session.get("platform"),
517
565
  arch=None, # Not available in DB schema
518
566
  username=None, # Not available in DB schema
519
- port=msf_session.get('port'),
567
+ port=msf_session.get("port"),
520
568
  tunnel_peer=None, # Not available in DB schema
521
- opened_at=msf_session.get('opened_at'),
522
- notes=msf_session.get('desc')
569
+ opened_at=msf_session.get("opened_at"),
570
+ notes=msf_session.get("desc"),
523
571
  )
524
572
 
525
573
  # Mark as closed if applicable
@@ -527,24 +575,24 @@ class MSFSyncManager:
527
575
  close_msf_session(
528
576
  conn,
529
577
  self.engagement_id,
530
- msf_session.get('local_id') or msf_session['id'],
531
- msf_session.get('close_reason')
578
+ msf_session.get("local_id") or msf_session["id"],
579
+ msf_session.get("close_reason"),
532
580
  )
533
581
 
534
582
  count += 1
535
583
 
536
584
  # If session was created via exploit, mark that exploit as successful
537
- via_exploit = msf_session.get('via_exploit')
538
- if via_exploit and via_exploit not in ['unknown', '']:
539
- session_num = msf_session.get('local_id') or msf_session['id']
585
+ via_exploit = msf_session.get("via_exploit")
586
+ if via_exploit and via_exploit not in ["unknown", ""]:
587
+ session_num = msf_session.get("local_id") or msf_session["id"]
540
588
  self._mark_exploit_success(
541
- host['id'],
542
- via_exploit,
543
- f"Session {session_num} created"
589
+ host["id"], via_exploit, f"Session {session_num} created"
544
590
  )
545
591
 
546
592
  except Exception as e:
547
- session_num = msf_session.get('local_id') or msf_session.get('id', 'unknown')
593
+ session_num = msf_session.get("local_id") or msf_session.get(
594
+ "id", "unknown"
595
+ )
548
596
  logger.error(f"Failed to import session {session_num}: {e}")
549
597
  skipped += 1
550
598
 
@@ -568,11 +616,7 @@ class MSFSyncManager:
568
616
  Returns:
569
617
  Dictionary with counts of updated exploits
570
618
  """
571
- stats = {
572
- 'success': 0,
573
- 'failed': 0,
574
- 'errors': 0
575
- }
619
+ stats = {"success": 0, "failed": 0, "errors": 0}
576
620
 
577
621
  try:
578
622
  with MSFDatabase(**self.msf_db_config) as msf_db:
@@ -584,42 +628,45 @@ class MSFSyncManager:
584
628
  for attempt in exploit_attempts:
585
629
  try:
586
630
  # Get host
587
- host = host_mgr.get_host_by_ip(self.engagement_id, attempt['host_address'])
631
+ host = host_mgr.get_host_by_ip(
632
+ self.engagement_id, attempt["host_address"]
633
+ )
588
634
  if not host:
589
635
  continue
590
636
 
591
637
  # Get service if available
592
638
  service_id = None
593
- if attempt.get('service_port'):
594
- services = host_mgr.get_host_services(host['id'])
639
+ if attempt.get("service_port"):
640
+ services = host_mgr.get_host_services(host["id"])
595
641
  matching_services = [
596
- s for s in services
597
- if s['port'] == attempt['service_port'] and
598
- s['protocol'] == attempt.get('service_proto', 'tcp')
642
+ s
643
+ for s in services
644
+ if s["port"] == attempt["service_port"]
645
+ and s["protocol"] == attempt.get("service_proto", "tcp")
599
646
  ]
600
647
  if matching_services:
601
- service_id = matching_services[0]['id']
648
+ service_id = matching_services[0]["id"]
602
649
 
603
650
  # Record exploit attempt with success status
604
- exploit_name = attempt.get('vuln_name', 'unknown')
651
+ exploit_name = attempt.get("vuln_name", "unknown")
605
652
  record_attempt(
606
653
  engagement_id=self.engagement_id,
607
- host_id=host['id'],
654
+ host_id=host["id"],
608
655
  exploit_identifier=f"msf:{exploit_name}",
609
656
  exploit_title=exploit_name,
610
- status='success',
657
+ status="success",
611
658
  service_id=service_id,
612
- notes=f"Exploited at: {attempt['exploited_at']}"
659
+ notes=f"Exploited at: {attempt['exploited_at']}",
613
660
  )
614
- stats['success'] += 1
661
+ stats["success"] += 1
615
662
 
616
663
  except Exception as e:
617
664
  logger.error(f"Failed to sync exploit result: {e}")
618
- stats['errors'] += 1
665
+ stats["errors"] += 1
619
666
 
620
667
  except Exception as e:
621
668
  logger.error(f"Failed to sync exploit results: {e}")
622
- stats['errors'] += 1
669
+ stats["errors"] += 1
623
670
 
624
671
  return stats
625
672
 
@@ -638,50 +685,55 @@ class MSFSyncManager:
638
685
  with MSFRPCClient(**self.msf_rpc_config) as rpc:
639
686
  rpc_sessions = rpc.list_sessions()
640
687
  for session_id, session_info in rpc_sessions.items():
641
- sessions.append({
642
- 'id': session_id,
643
- 'type': session_info.get('type', 'unknown'),
644
- 'tunnel': session_info.get('tunnel_peer', 'unknown'),
645
- 'via_exploit': session_info.get('via_exploit', 'unknown'),
646
- 'via_payload': session_info.get('via_payload', 'unknown'),
647
- 'info': session_info.get('info', ''),
648
- 'username': session_info.get('username', ''),
649
- 'platform': session_info.get('platform', ''),
650
- 'arch': session_info.get('arch', ''),
651
- 'source': 'rpc'
652
- })
688
+ sessions.append(
689
+ {
690
+ "id": session_id,
691
+ "type": session_info.get("type", "unknown"),
692
+ "tunnel": session_info.get("tunnel_peer", "unknown"),
693
+ "via_exploit": session_info.get(
694
+ "via_exploit", "unknown"
695
+ ),
696
+ "via_payload": session_info.get(
697
+ "via_payload", "unknown"
698
+ ),
699
+ "info": session_info.get("info", ""),
700
+ "username": session_info.get("username", ""),
701
+ "platform": session_info.get("platform", ""),
702
+ "arch": session_info.get("arch", ""),
703
+ "source": "rpc",
704
+ }
705
+ )
653
706
  return sessions
654
707
  except Exception as rpc_error:
655
- logger.debug(f"RPC not available, falling back to database: {rpc_error}")
708
+ logger.debug(
709
+ f"RPC not available, falling back to database: {rpc_error}"
710
+ )
656
711
 
657
712
  # Fallback to database
658
713
  with MSFDatabase(**self.msf_db_config) as msf_db:
659
714
  msf_sessions = msf_db.get_sessions(active_only=True)
660
715
  for session in msf_sessions:
661
- sessions.append({
662
- 'id': session['id'],
663
- 'type': session.get('stype', 'unknown'),
664
- 'host': session.get('host_address', 'unknown'),
665
- 'port': session.get('port', 0),
666
- 'via_exploit': session.get('via_exploit', 'unknown'),
667
- 'via_payload': session.get('via_payload', 'unknown'),
668
- 'platform': session.get('platform', ''),
669
- 'opened_at': session.get('opened_at'),
670
- 'last_seen': session.get('last_seen'),
671
- 'source': 'database'
672
- })
716
+ sessions.append(
717
+ {
718
+ "id": session["id"],
719
+ "type": session.get("stype", "unknown"),
720
+ "host": session.get("host_address", "unknown"),
721
+ "port": session.get("port", 0),
722
+ "via_exploit": session.get("via_exploit", "unknown"),
723
+ "via_payload": session.get("via_payload", "unknown"),
724
+ "platform": session.get("platform", ""),
725
+ "opened_at": session.get("opened_at"),
726
+ "last_seen": session.get("last_seen"),
727
+ "source": "database",
728
+ }
729
+ )
673
730
 
674
731
  except Exception as e:
675
732
  logger.error(f"Failed to get active sessions: {e}")
676
733
 
677
734
  return sessions
678
735
 
679
- def _mark_exploit_success(
680
- self,
681
- host_id: int,
682
- exploit_name: str,
683
- notes: str = ""
684
- ):
736
+ def _mark_exploit_success(self, host_id: int, exploit_name: str, notes: str = ""):
685
737
  """Mark an exploit as successful"""
686
738
  try:
687
739
  # Record successful exploit attempt
@@ -690,9 +742,9 @@ class MSFSyncManager:
690
742
  host_id=host_id,
691
743
  exploit_identifier=f"msf:{exploit_name}",
692
744
  exploit_title=exploit_name,
693
- status='success',
745
+ status="success",
694
746
  service_id=None,
695
- notes=notes
747
+ notes=notes,
696
748
  )
697
749
  except Exception as e:
698
750
  logger.debug(f"Failed to mark exploit success: {e}")
@@ -701,14 +753,22 @@ class MSFSyncManager:
701
753
  """Determine severity from vulnerability name"""
702
754
  vuln_lower = vuln_name.lower()
703
755
 
704
- if any(x in vuln_lower for x in ['rce', 'remote code', 'command execution', 'backdoor']):
705
- return 'critical'
706
- elif any(x in vuln_lower for x in ['exploit', 'overflow', 'injection', 'authentication bypass']):
707
- return 'high'
708
- elif any(x in vuln_lower for x in ['disclosure', 'exposure', 'misconfiguration']):
709
- return 'medium'
756
+ if any(
757
+ x in vuln_lower
758
+ for x in ["rce", "remote code", "command execution", "backdoor"]
759
+ ):
760
+ return "critical"
761
+ elif any(
762
+ x in vuln_lower
763
+ for x in ["exploit", "overflow", "injection", "authentication bypass"]
764
+ ):
765
+ return "high"
766
+ elif any(
767
+ x in vuln_lower for x in ["disclosure", "exposure", "misconfiguration"]
768
+ ):
769
+ return "medium"
710
770
  else:
711
- return 'low'
771
+ return "low"
712
772
 
713
773
  def get_msf_stats(self) -> Dict[str, Any]:
714
774
  """