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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9564 -2881
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1239 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2200 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +564 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +409 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +417 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +292 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +913 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +219 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +237 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +23034 -10679
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
  353. souleyez-3.0.0.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@ from datetime import datetime
13
13
  try:
14
14
  import psycopg2
15
15
  from psycopg2.extras import RealDictCursor
16
+
16
17
  PSYCOPG2_AVAILABLE = True
17
18
  except ImportError:
18
19
  PSYCOPG2_AVAILABLE = False
@@ -20,12 +21,13 @@ except ImportError:
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
23
  # Tested MSF versions (major.minor)
23
- TESTED_MSF_VERSIONS = ['6.2', '6.3', '6.4']
24
+ TESTED_MSF_VERSIONS = ["6.2", "6.3", "6.4"]
24
25
  KNOWN_SCHEMA_VERSION = 20230313 # MSF schema version we're compatible with
25
26
 
26
27
 
27
28
  class MSFDatabaseSchemaError(Exception):
28
29
  """Raised when MSF database schema is incompatible"""
30
+
29
31
  pass
30
32
 
31
33
 
@@ -39,7 +41,7 @@ class MSFDatabase:
39
41
  database: str = "msf",
40
42
  username: str = "msf",
41
43
  password: str = "",
42
- workspace: str = "default"
44
+ workspace: str = "default",
43
45
  ):
44
46
  """
45
47
  Initialize MSF database connection
@@ -81,7 +83,7 @@ class MSFDatabase:
81
83
  database=self.database,
82
84
  user=self.username,
83
85
  password=self.password,
84
- connect_timeout=10 # Prevent hanging on unreachable database
86
+ connect_timeout=10, # Prevent hanging on unreachable database
85
87
  )
86
88
 
87
89
  # Get workspace ID
@@ -109,8 +111,7 @@ class MSFDatabase:
109
111
  try:
110
112
  with self.conn.cursor() as cur:
111
113
  cur.execute(
112
- "SELECT id FROM workspaces WHERE name = %s",
113
- (workspace_name,)
114
+ "SELECT id FROM workspaces WHERE name = %s", (workspace_name,)
114
115
  )
115
116
  result = cur.fetchone()
116
117
  return result[0] if result else None
@@ -125,10 +126,14 @@ class MSFDatabase:
125
126
  version_info = self._get_msf_version_info()
126
127
 
127
128
  if version_info:
128
- version_str = version_info.get('version', 'unknown')
129
+ version_str = version_info.get("version", "unknown")
129
130
  # Check if version is tested
130
- major_minor = '.'.join(version_str.split('.')[:2]) if '.' in version_str else version_str
131
- if major_minor not in TESTED_MSF_VERSIONS and version_str != 'unknown':
131
+ major_minor = (
132
+ ".".join(version_str.split(".")[:2])
133
+ if "." in version_str
134
+ else version_str
135
+ )
136
+ if major_minor not in TESTED_MSF_VERSIONS and version_str != "unknown":
132
137
  logger.warning(
133
138
  f"MSF version {version_str} has not been tested with this integration. "
134
139
  f"Tested versions: {', '.join(TESTED_MSF_VERSIONS)}. "
@@ -152,18 +157,20 @@ class MSFDatabase:
152
157
  # or check for schema_migrations to infer version
153
158
  with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
154
159
  # Check latest schema migration
155
- cur.execute("""
160
+ cur.execute(
161
+ """
156
162
  SELECT version
157
163
  FROM schema_migrations
158
164
  ORDER BY version DESC
159
165
  LIMIT 1
160
- """)
166
+ """
167
+ )
161
168
  result = cur.fetchone()
162
169
 
163
170
  if result:
164
171
  return {
165
- 'schema_version': str(result['version']),
166
- 'version': 'unknown' # Can't reliably determine from DB
172
+ "schema_version": str(result["version"]),
173
+ "version": "unknown", # Can't reliably determine from DB
167
174
  }
168
175
  except Exception as e:
169
176
  logger.debug(f"Could not determine MSF version: {e}")
@@ -175,33 +182,48 @@ class MSFDatabase:
175
182
  try:
176
183
  with self.conn.cursor() as cur:
177
184
  # Check critical tables exist
178
- critical_tables = ['hosts', 'services', 'vulns', 'sessions', 'workspaces']
185
+ critical_tables = [
186
+ "hosts",
187
+ "services",
188
+ "vulns",
189
+ "sessions",
190
+ "workspaces",
191
+ ]
179
192
 
180
193
  for table in critical_tables:
181
- cur.execute("""
194
+ cur.execute(
195
+ """
182
196
  SELECT EXISTS (
183
197
  SELECT FROM information_schema.tables
184
198
  WHERE table_name = %s
185
199
  )
186
- """, (table,))
200
+ """,
201
+ (table,),
202
+ )
187
203
 
188
204
  exists = cur.fetchone()[0]
189
205
  if not exists:
190
- logger.error(f"Critical table '{table}' not found in MSF database")
206
+ logger.error(
207
+ f"Critical table '{table}' not found in MSF database"
208
+ )
191
209
  return False
192
210
 
193
211
  # Validate key columns exist in hosts table
194
- cur.execute("""
212
+ cur.execute(
213
+ """
195
214
  SELECT column_name
196
215
  FROM information_schema.columns
197
216
  WHERE table_name = 'hosts'
198
- """)
217
+ """
218
+ )
199
219
  columns = {row[0] for row in cur.fetchall()}
200
220
 
201
- required_columns = {'id', 'address', 'workspace_id'}
221
+ required_columns = {"id", "address", "workspace_id"}
202
222
  if not required_columns.issubset(columns):
203
223
  missing = required_columns - columns
204
- logger.error(f"Required columns missing from 'hosts' table: {missing}")
224
+ logger.error(
225
+ f"Required columns missing from 'hosts' table: {missing}"
226
+ )
205
227
  return False
206
228
 
207
229
  return True
@@ -219,7 +241,8 @@ class MSFDatabase:
219
241
  """
220
242
  try:
221
243
  with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
222
- cur.execute("""
244
+ cur.execute(
245
+ """
223
246
  SELECT id, name, created_at, updated_at,
224
247
  boundary,
225
248
  description,
@@ -227,7 +250,8 @@ class MSFDatabase:
227
250
  limit_to_network
228
251
  FROM workspaces
229
252
  ORDER BY name
230
- """)
253
+ """
254
+ )
231
255
  return [dict(row) for row in cur.fetchall()]
232
256
  except Exception as e:
233
257
  logger.error(f"Failed to list workspaces: {e}")
@@ -250,7 +274,8 @@ class MSFDatabase:
250
274
 
251
275
  try:
252
276
  with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
253
- cur.execute("""
277
+ cur.execute(
278
+ """
254
279
  SELECT
255
280
  id,
256
281
  address,
@@ -270,13 +295,17 @@ class MSFDatabase:
270
295
  FROM hosts
271
296
  WHERE workspace_id = %s
272
297
  ORDER BY address
273
- """, (ws_id,))
298
+ """,
299
+ (ws_id,),
300
+ )
274
301
  return [dict(row) for row in cur.fetchall()]
275
302
  except Exception as e:
276
303
  logger.error(f"Failed to get hosts: {e}")
277
304
  return []
278
305
 
279
- def get_services(self, workspace_id: Optional[int] = None, host_id: Optional[int] = None) -> List[Dict[str, Any]]:
306
+ def get_services(
307
+ self, workspace_id: Optional[int] = None, host_id: Optional[int] = None
308
+ ) -> List[Dict[str, Any]]:
280
309
  """
281
310
  Get all services from MSF database
282
311
 
@@ -343,7 +372,8 @@ class MSFDatabase:
343
372
  with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
344
373
  # Try full query first (without v.refs which doesn't exist in some MSF versions)
345
374
  try:
346
- cur.execute("""
375
+ cur.execute(
376
+ """
347
377
  SELECT
348
378
  v.id,
349
379
  v.host_id,
@@ -361,16 +391,21 @@ class MSFDatabase:
361
391
  LEFT JOIN services s ON v.service_id = s.id
362
392
  WHERE h.workspace_id = %s
363
393
  ORDER BY v.created_at DESC
364
- """, (ws_id,))
394
+ """,
395
+ (ws_id,),
396
+ )
365
397
  results = cur.fetchall()
366
398
  return [dict(row) for row in results]
367
399
  except Exception as schema_error:
368
400
  # Rollback the failed transaction
369
401
  self.conn.rollback()
370
- logger.warning(f"Full vulns query failed, trying minimal query: {schema_error}")
402
+ logger.warning(
403
+ f"Full vulns query failed, trying minimal query: {schema_error}"
404
+ )
371
405
 
372
406
  # Try minimal query with only essential columns
373
- cur.execute("""
407
+ cur.execute(
408
+ """
374
409
  SELECT
375
410
  v.id,
376
411
  v.host_id,
@@ -382,12 +417,15 @@ class MSFDatabase:
382
417
  JOIN hosts h ON v.host_id = h.id
383
418
  WHERE h.workspace_id = %s
384
419
  ORDER BY v.id DESC
385
- """, (ws_id,))
420
+ """,
421
+ (ws_id,),
422
+ )
386
423
  results = cur.fetchall()
387
424
  return [dict(row) for row in results]
388
425
  except Exception as e:
389
426
  logger.error(f"Failed to get vulnerabilities: {e}")
390
427
  import traceback
428
+
391
429
  logger.error(f"Traceback: {traceback.format_exc()}")
392
430
  # Rollback transaction on error
393
431
  try:
@@ -415,7 +453,8 @@ class MSFDatabase:
415
453
  with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
416
454
  # MSF uses a complex credential schema with cores, logins, and privates
417
455
  # We'll join them to get usable credential data
418
- cur.execute("""
456
+ cur.execute(
457
+ """
419
458
  SELECT
420
459
  l.id,
421
460
  l.core_id,
@@ -439,7 +478,9 @@ class MSFDatabase:
439
478
  LEFT JOIN hosts h ON s.host_id = h.id
440
479
  WHERE c.workspace_id = %s
441
480
  ORDER BY l.created_at DESC
442
- """, (ws_id,))
481
+ """,
482
+ (ws_id,),
483
+ )
443
484
  return [dict(row) for row in cur.fetchall()]
444
485
  except Exception as e:
445
486
  logger.debug(f"Failed to get credentials: {e}")
@@ -450,7 +491,9 @@ class MSFDatabase:
450
491
  pass
451
492
  return []
452
493
 
453
- def get_sessions(self, workspace_id: Optional[int] = None, active_only: bool = True) -> List[Dict[str, Any]]:
494
+ def get_sessions(
495
+ self, workspace_id: Optional[int] = None, active_only: bool = True
496
+ ) -> List[Dict[str, Any]]:
454
497
  """
455
498
  Get all sessions from MSF database
456
499
 
@@ -503,7 +546,9 @@ class MSFDatabase:
503
546
  except Exception as schema_error:
504
547
  # Rollback the failed transaction
505
548
  self.conn.rollback()
506
- logger.warning(f"Full sessions query failed, trying minimal query: {schema_error}")
549
+ logger.warning(
550
+ f"Full sessions query failed, trying minimal query: {schema_error}"
551
+ )
507
552
 
508
553
  # Try minimal query with only essential columns
509
554
  query = """
@@ -532,6 +577,7 @@ class MSFDatabase:
532
577
  except Exception as e:
533
578
  logger.error(f"Failed to get sessions: {e}")
534
579
  import traceback
580
+
535
581
  logger.error(f"Traceback: {traceback.format_exc()}")
536
582
  # Rollback transaction on error
537
583
  try:
@@ -552,7 +598,8 @@ class MSFDatabase:
552
598
  """
553
599
  try:
554
600
  with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
555
- cur.execute("""
601
+ cur.execute(
602
+ """
556
603
  SELECT
557
604
  id,
558
605
  session_id,
@@ -565,13 +612,17 @@ class MSFDatabase:
565
612
  FROM session_events
566
613
  WHERE session_id = %s
567
614
  ORDER BY created_at
568
- """, (session_id,))
615
+ """,
616
+ (session_id,),
617
+ )
569
618
  return [dict(row) for row in cur.fetchall()]
570
619
  except Exception as e:
571
620
  logger.error(f"Failed to get session events: {e}")
572
621
  return []
573
622
 
574
- def get_exploit_attempts(self, workspace_id: Optional[int] = None) -> List[Dict[str, Any]]:
623
+ def get_exploit_attempts(
624
+ self, workspace_id: Optional[int] = None
625
+ ) -> List[Dict[str, Any]]:
575
626
  """
576
627
  Get exploit attempt data from MSF database
577
628
 
@@ -591,7 +642,8 @@ class MSFDatabase:
591
642
 
592
643
  try:
593
644
  with self.conn.cursor(cursor_factory=RealDictCursor) as cur:
594
- cur.execute("""
645
+ cur.execute(
646
+ """
595
647
  SELECT
596
648
  v.id,
597
649
  v.host_id,
@@ -609,13 +661,17 @@ class MSFDatabase:
609
661
  WHERE h.workspace_id = %s
610
662
  AND v.exploited_at IS NOT NULL
611
663
  ORDER BY v.exploited_at DESC
612
- """, (ws_id,))
664
+ """,
665
+ (ws_id,),
666
+ )
613
667
  return [dict(row) for row in cur.fetchall()]
614
668
  except Exception as e:
615
669
  logger.error(f"Failed to get exploit attempts: {e}")
616
670
  return []
617
671
 
618
- def get_database_stats(self, workspace_id: Optional[int] = None, max_retries: int = 3) -> Dict[str, int]:
672
+ def get_database_stats(
673
+ self, workspace_id: Optional[int] = None, max_retries: int = 3
674
+ ) -> Dict[str, int]:
619
675
  """
620
676
  Get statistics about MSF database contents
621
677
 
@@ -632,16 +688,17 @@ class MSFDatabase:
632
688
  return {}
633
689
 
634
690
  stats = {
635
- 'hosts': 0,
636
- 'services': 0,
637
- 'vulns': 0,
638
- 'creds': 0,
639
- 'active_sessions': 0,
640
- 'total_sessions': 0
691
+ "hosts": 0,
692
+ "services": 0,
693
+ "vulns": 0,
694
+ "creds": 0,
695
+ "active_sessions": 0,
696
+ "total_sessions": 0,
641
697
  }
642
698
 
643
699
  # Retry logic for database locks
644
700
  import time
701
+
645
702
  for attempt in range(max_retries):
646
703
  try:
647
704
  with self.conn.cursor() as cur:
@@ -649,35 +706,41 @@ class MSFDatabase:
649
706
  try:
650
707
  cur.execute(
651
708
  "SELECT COUNT(*) FROM hosts WHERE workspace_id = %s",
652
- (ws_id,)
709
+ (ws_id,),
653
710
  )
654
- stats['hosts'] = cur.fetchone()[0]
711
+ stats["hosts"] = cur.fetchone()[0]
655
712
  except Exception as e:
656
713
  logger.warning(f"Failed to count hosts: {e}")
657
714
  self.conn.rollback() # Clear aborted transaction state
658
715
 
659
716
  # Count services
660
717
  try:
661
- cur.execute("""
718
+ cur.execute(
719
+ """
662
720
  SELECT COUNT(*)
663
721
  FROM services s
664
722
  JOIN hosts h ON s.host_id = h.id
665
723
  WHERE h.workspace_id = %s
666
- """, (ws_id,))
667
- stats['services'] = cur.fetchone()[0]
724
+ """,
725
+ (ws_id,),
726
+ )
727
+ stats["services"] = cur.fetchone()[0]
668
728
  except Exception as e:
669
729
  logger.warning(f"Failed to count services: {e}")
670
730
  self.conn.rollback() # Clear aborted transaction state
671
731
 
672
732
  # Count vulns
673
733
  try:
674
- cur.execute("""
734
+ cur.execute(
735
+ """
675
736
  SELECT COUNT(*)
676
737
  FROM vulns v
677
738
  JOIN hosts h ON v.host_id = h.id
678
739
  WHERE h.workspace_id = %s
679
- """, (ws_id,))
680
- stats['vulns'] = cur.fetchone()[0]
740
+ """,
741
+ (ws_id,),
742
+ )
743
+ stats["vulns"] = cur.fetchone()[0]
681
744
  except Exception as e:
682
745
  logger.warning(f"Failed to count vulns: {e}")
683
746
  self.conn.rollback() # Clear aborted transaction state
@@ -685,39 +748,50 @@ class MSFDatabase:
685
748
  # Count creds - credential schema varies by MSF version
686
749
  # Some versions don't have credential tables at all
687
750
  try:
688
- cur.execute("""
751
+ cur.execute(
752
+ """
689
753
  SELECT COUNT(*)
690
754
  FROM cores
691
755
  WHERE workspace_id = %s
692
- """, (ws_id,))
693
- stats['creds'] = cur.fetchone()[0]
756
+ """,
757
+ (ws_id,),
758
+ )
759
+ stats["creds"] = cur.fetchone()[0]
694
760
  except Exception as e:
695
- logger.debug(f"Failed to count credentials (table may not exist in this MSF version): {e}")
761
+ logger.debug(
762
+ f"Failed to count credentials (table may not exist in this MSF version): {e}"
763
+ )
696
764
  self.conn.rollback() # Clear aborted transaction state
697
765
 
698
766
  # Count active sessions
699
767
  try:
700
- cur.execute("""
768
+ cur.execute(
769
+ """
701
770
  SELECT COUNT(*)
702
771
  FROM sessions s
703
772
  JOIN hosts h ON s.host_id = h.id
704
773
  WHERE h.workspace_id = %s
705
774
  AND s.closed_at IS NULL
706
- """, (ws_id,))
707
- stats['active_sessions'] = cur.fetchone()[0]
775
+ """,
776
+ (ws_id,),
777
+ )
778
+ stats["active_sessions"] = cur.fetchone()[0]
708
779
  except Exception as e:
709
780
  logger.warning(f"Failed to count active sessions: {e}")
710
781
  self.conn.rollback() # Clear aborted transaction state
711
782
 
712
783
  # Count total sessions
713
784
  try:
714
- cur.execute("""
785
+ cur.execute(
786
+ """
715
787
  SELECT COUNT(*)
716
788
  FROM sessions s
717
789
  JOIN hosts h ON s.host_id = h.id
718
790
  WHERE h.workspace_id = %s
719
- """, (ws_id,))
720
- stats['total_sessions'] = cur.fetchone()[0]
791
+ """,
792
+ (ws_id,),
793
+ )
794
+ stats["total_sessions"] = cur.fetchone()[0]
721
795
  except Exception as e:
722
796
  logger.warning(f"Failed to count total sessions: {e}")
723
797
  self.conn.rollback() # Clear aborted transaction state
@@ -728,10 +802,14 @@ class MSFDatabase:
728
802
  except Exception as e:
729
803
  # Check if it's a database lock error
730
804
  error_msg = str(e).lower()
731
- if 'lock' in error_msg or 'locked' in error_msg:
805
+ if "lock" in error_msg or "locked" in error_msg:
732
806
  if attempt < max_retries - 1:
733
- wait_time = 0.5 * (attempt + 1) # Incremental backoff: 0.5s, 1s, 1.5s
734
- logger.warning(f"MSF database is locked (attempt {attempt + 1}/{max_retries}), retrying in {wait_time}s...")
807
+ wait_time = 0.5 * (
808
+ attempt + 1
809
+ ) # Incremental backoff: 0.5s, 1s, 1.5s
810
+ logger.warning(
811
+ f"MSF database is locked (attempt {attempt + 1}/{max_retries}), retrying in {wait_time}s..."
812
+ )
735
813
  time.sleep(wait_time)
736
814
  # Rollback the transaction before retrying
737
815
  try:
@@ -739,11 +817,16 @@ class MSFDatabase:
739
817
  except:
740
818
  pass
741
819
  else:
742
- logger.error(f"MSF database is locked after {max_retries} attempts: {e}")
743
- logger.error("Recommendation: Wait for MSF operations to complete, then try again")
820
+ logger.error(
821
+ f"MSF database is locked after {max_retries} attempts: {e}"
822
+ )
823
+ logger.error(
824
+ "Recommendation: Wait for MSF operations to complete, then try again"
825
+ )
744
826
  else:
745
827
  logger.error(f"Failed to get database stats: {e}")
746
828
  import traceback
829
+
747
830
  logger.debug(f"Traceback: {traceback.format_exc()}")
748
831
  break # Don't retry for non-lock errors
749
832
 
@@ -764,7 +847,7 @@ def test_msf_database_connection(
764
847
  port: int = 5432,
765
848
  database: str = "msf",
766
849
  username: str = "msf",
767
- password: str = ""
850
+ password: str = "",
768
851
  ) -> bool:
769
852
  """
770
853
  Test MSF database connection