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
@@ -14,10 +14,12 @@ import time
14
14
 
15
15
  # Suppress SSL warnings for self-signed certs (msfrpcd uses self-signed)
16
16
  from urllib3.exceptions import InsecureRequestWarning
17
- warnings.filterwarnings('ignore', category=InsecureRequestWarning)
17
+
18
+ warnings.filterwarnings("ignore", category=InsecureRequestWarning)
18
19
 
19
20
  try:
20
21
  import msgpack
22
+
21
23
  MSGPACK_AVAILABLE = True
22
24
  except ImportError:
23
25
  MSGPACK_AVAILABLE = False
@@ -34,7 +36,7 @@ class MSFRPCClient:
34
36
  port: int = 55553,
35
37
  username: str = "msf",
36
38
  password: str = "",
37
- ssl: bool = False
39
+ ssl: bool = False,
38
40
  ):
39
41
  """
40
42
  Initialize MSF RPC client
@@ -48,8 +50,7 @@ class MSFRPCClient:
48
50
  """
49
51
  if not MSGPACK_AVAILABLE:
50
52
  raise ImportError(
51
- "msgpack is required for MSF RPC. "
52
- "Install with: pip install msgpack"
53
+ "msgpack is required for MSF RPC. " "Install with: pip install msgpack"
53
54
  )
54
55
 
55
56
  self.host = host
@@ -58,9 +59,7 @@ class MSFRPCClient:
58
59
  self.password = password
59
60
  self.ssl = ssl
60
61
  self.token = None
61
- self.headers = {
62
- 'Content-Type': 'binary/message-pack'
63
- }
62
+ self.headers = {"Content-Type": "binary/message-pack"}
64
63
 
65
64
  @property
66
65
  def url(self) -> str:
@@ -75,8 +74,7 @@ class MSFRPCClient:
75
74
  """
76
75
  if isinstance(obj, dict):
77
76
  return {
78
- (k.decode() if isinstance(k, bytes) else k):
79
- self._normalize_keys(v)
77
+ (k.decode() if isinstance(k, bytes) else k): self._normalize_keys(v)
80
78
  for k, v in obj.items()
81
79
  }
82
80
  elif isinstance(obj, list):
@@ -112,7 +110,10 @@ class MSFRPCClient:
112
110
  data=request_data,
113
111
  headers=self.headers,
114
112
  verify=False, # nosec B501 - Metasploit RPC uses self-signed certs
115
- timeout=(5, 30) # (connect_timeout, read_timeout) - fast fail on SSL mismatch
113
+ timeout=(
114
+ 5,
115
+ 30,
116
+ ), # (connect_timeout, read_timeout) - fast fail on SSL mismatch
116
117
  )
117
118
  response.raise_for_status()
118
119
 
@@ -124,9 +125,11 @@ class MSFRPCClient:
124
125
 
125
126
  # Check for errors
126
127
  if isinstance(result, dict):
127
- if 'error' in result:
128
- raise Exception(f"RPC error: {result.get('error_message', 'Unknown error')}")
129
- if 'error_message' in result:
128
+ if "error" in result:
129
+ raise Exception(
130
+ f"RPC error: {result.get('error_message', 'Unknown error')}"
131
+ )
132
+ if "error_message" in result:
130
133
  raise Exception(f"RPC error: {result['error_message']}")
131
134
 
132
135
  return result
@@ -143,8 +146,8 @@ class MSFRPCClient:
143
146
  """
144
147
  try:
145
148
  result = self._call("auth.login", self.username, self.password)
146
- if isinstance(result, dict) and 'token' in result:
147
- self.token = result['token']
149
+ if isinstance(result, dict) and "token" in result:
150
+ self.token = result["token"]
148
151
  logger.info(f"Authenticated with MSF RPC at {self.host}:{self.port}")
149
152
  return True
150
153
  logger.error("Authentication failed: No token received")
@@ -194,9 +197,13 @@ class MSFRPCClient:
194
197
  List of module names
195
198
  """
196
199
  try:
197
- result = self._call("module.exploits" if module_type == "exploit" else f"module.{module_type}s")
198
- if isinstance(result, dict) and 'modules' in result:
199
- return result['modules']
200
+ result = self._call(
201
+ "module.exploits"
202
+ if module_type == "exploit"
203
+ else f"module.{module_type}s"
204
+ )
205
+ if isinstance(result, dict) and "modules" in result:
206
+ return result["modules"]
200
207
  return []
201
208
  except Exception as e:
202
209
  logger.error(f"Failed to list modules: {e}")
@@ -239,10 +246,7 @@ class MSFRPCClient:
239
246
  return {}
240
247
 
241
248
  def execute_module(
242
- self,
243
- module_type: str,
244
- module_name: str,
245
- options: Dict[str, Any]
249
+ self, module_type: str, module_name: str, options: Dict[str, Any]
246
250
  ) -> Dict[str, Any]:
247
251
  """
248
252
  Execute a module
@@ -260,7 +264,7 @@ class MSFRPCClient:
260
264
  return result
261
265
  except Exception as e:
262
266
  logger.error(f"Failed to execute module: {e}")
263
- return {'error': str(e)}
267
+ return {"error": str(e)}
264
268
 
265
269
  def list_sessions(self) -> Dict[str, Dict[str, Any]]:
266
270
  """
@@ -309,10 +313,7 @@ class MSFRPCClient:
309
313
  return False
310
314
 
311
315
  def run_session_command(
312
- self,
313
- session_id: int,
314
- command: str,
315
- timeout: int = 30
316
+ self, session_id: int, command: str, timeout: int = 30
316
317
  ) -> str:
317
318
  """
318
319
  Run a command in a session
@@ -334,8 +335,8 @@ class MSFRPCClient:
334
335
 
335
336
  # Read output
336
337
  result = self._call("session.shell_read", str(session_id))
337
- if isinstance(result, dict) and 'data' in result:
338
- return result['data']
338
+ if isinstance(result, dict) and "data" in result:
339
+ return result["data"]
339
340
  return ""
340
341
  except Exception as e:
341
342
  logger.error(f"Failed to run session command: {e}")
@@ -400,8 +401,8 @@ class MSFRPCClient:
400
401
  """
401
402
  try:
402
403
  result = self._call("console.list")
403
- if isinstance(result, dict) and 'consoles' in result:
404
- return result['consoles']
404
+ if isinstance(result, dict) and "consoles" in result:
405
+ return result["consoles"]
405
406
  return []
406
407
  except Exception as e:
407
408
  logger.error(f"Failed to get console list: {e}")
@@ -416,8 +417,8 @@ class MSFRPCClient:
416
417
  """
417
418
  try:
418
419
  result = self._call("console.create")
419
- if isinstance(result, dict) and 'id' in result:
420
- return result['id']
420
+ if isinstance(result, dict) and "id" in result:
421
+ return result["id"]
421
422
  return None
422
423
  except Exception as e:
423
424
  logger.error(f"Failed to create console: {e}")
@@ -453,8 +454,8 @@ class MSFRPCClient:
453
454
  """
454
455
  try:
455
456
  result = self._call("console.write", console_id, command + "\n")
456
- if isinstance(result, dict) and 'wrote' in result:
457
- return result['wrote']
457
+ if isinstance(result, dict) and "wrote" in result:
458
+ return result["wrote"]
458
459
  return 0
459
460
  except Exception as e:
460
461
  logger.error(f"Failed to write to console: {e}")
@@ -473,8 +474,8 @@ class MSFRPCClient:
473
474
  try:
474
475
  result = self._call("console.read", console_id)
475
476
  if isinstance(result, dict):
476
- output = result.get('data', '')
477
- busy = result.get('busy', False)
477
+ output = result.get("data", "")
478
+ busy = result.get("busy", False)
478
479
  return (output, busy)
479
480
  return ("", False)
480
481
  except Exception as e:
@@ -482,10 +483,7 @@ class MSFRPCClient:
482
483
  return ("", False)
483
484
 
484
485
  def run_console_command(
485
- self,
486
- console_id: str,
487
- command: str,
488
- timeout: int = 30
486
+ self, console_id: str, command: str, timeout: int = 30
489
487
  ) -> str:
490
488
  """
491
489
  Run a command in console and wait for output
@@ -532,7 +530,7 @@ def test_msf_rpc_connection(
532
530
  host: str = "127.0.0.1",
533
531
  port: int = 55553,
534
532
  username: str = "msf",
535
- password: str = ""
533
+ password: str = "",
536
534
  ) -> bool:
537
535
  """
538
536
  Test MSF RPC connection
@@ -27,6 +27,7 @@ def is_pro_enabled() -> bool:
27
27
  """Check if user has an active Pro license."""
28
28
  try:
29
29
  from souleyez.licensing.validator import get_active_license
30
+
30
31
  license_info = get_active_license()
31
32
  if license_info and license_info.is_valid:
32
33
  return license_info.tier.upper() == "PRO"
@@ -37,7 +38,7 @@ def is_pro_enabled() -> bool:
37
38
 
38
39
  # Session file for sharing decrypted msfrpc password with background worker
39
40
  # This file is written when vault is unlocked and cleared on logout
40
- _SESSION_FILE = os.path.join(os.path.expanduser('~'), '.souleyez', '.msfrpc_session')
41
+ _SESSION_FILE = os.path.join(os.path.expanduser("~"), ".souleyez", ".msfrpc_session")
41
42
 
42
43
 
43
44
  def write_msfrpc_session(password: str) -> bool:
@@ -51,11 +52,12 @@ def write_msfrpc_session(password: str) -> bool:
51
52
  """
52
53
  try:
53
54
  import stat
55
+
54
56
  session_dir = os.path.dirname(_SESSION_FILE)
55
57
  os.makedirs(session_dir, exist_ok=True)
56
58
 
57
59
  # Write with secure permissions
58
- with open(_SESSION_FILE, 'w') as f:
60
+ with open(_SESSION_FILE, "w") as f:
59
61
  f.write(password)
60
62
  os.chmod(_SESSION_FILE, stat.S_IRUSR | stat.S_IWUSR) # 600
61
63
 
@@ -86,7 +88,7 @@ def _read_msfrpc_session() -> Optional[str]:
86
88
  """Read password from session file if it exists."""
87
89
  try:
88
90
  if os.path.exists(_SESSION_FILE):
89
- with open(_SESSION_FILE, 'r') as f:
91
+ with open(_SESSION_FILE, "r") as f:
90
92
  password = f.read().strip()
91
93
  if password:
92
94
  return password
@@ -112,7 +114,8 @@ def get_msfrpc_password() -> Optional[str]:
112
114
  # Fall back to decryption (works if vault is unlocked in this process)
113
115
  try:
114
116
  from souleyez.storage.crypto import get_crypto_manager
115
- encrypted = config.get('msfrpc.password')
117
+
118
+ encrypted = config.get("msfrpc.password")
116
119
  if not encrypted:
117
120
  return None
118
121
  crypto = get_crypto_manager()
@@ -131,6 +134,7 @@ def set_msfrpc_password(password: str) -> bool:
131
134
  """Encrypt and store msfrpc password in config."""
132
135
  try:
133
136
  from souleyez.storage.crypto import get_crypto_manager
137
+
134
138
  crypto = get_crypto_manager()
135
139
  if crypto:
136
140
  encrypted = crypto.encrypt(password)
@@ -138,9 +142,9 @@ def set_msfrpc_password(password: str) -> bool:
138
142
  encrypted = password # No crypto manager, store plain
139
143
 
140
144
  cfg = config.read_config()
141
- if 'msfrpc' not in cfg:
142
- cfg['msfrpc'] = {}
143
- cfg['msfrpc']['password'] = encrypted
145
+ if "msfrpc" not in cfg:
146
+ cfg["msfrpc"] = {}
147
+ cfg["msfrpc"]["password"] = encrypted
144
148
  config.write_config(cfg)
145
149
  return True
146
150
  except Exception as e:
@@ -169,7 +173,7 @@ class MSFRPCManager:
169
173
  return cls._instance
170
174
 
171
175
  @classmethod
172
- def get_instance(cls) -> 'MSFRPCManager':
176
+ def get_instance(cls) -> "MSFRPCManager":
173
177
  """Get singleton instance."""
174
178
  if cls._instance is None:
175
179
  cls._instance = cls()
@@ -202,7 +206,7 @@ class MSFRPCManager:
202
206
  return False
203
207
 
204
208
  # Check if enabled in config
205
- if not config.get('msfrpc.enabled', False):
209
+ if not config.get("msfrpc.enabled", False):
206
210
  logger.debug("MSF RPC unavailable: Not enabled in config")
207
211
  return False
208
212
 
@@ -239,11 +243,11 @@ class MSFRPCManager:
239
243
  # Create new client
240
244
  try:
241
245
  client = MSFRPCClient(
242
- host=config.get('msfrpc.host', '127.0.0.1'),
243
- port=config.get('msfrpc.port', 55553),
244
- username=config.get('msfrpc.username', 'msf'),
245
- password=get_msfrpc_password() or '',
246
- ssl=config.get('msfrpc.ssl', False)
246
+ host=config.get("msfrpc.host", "127.0.0.1"),
247
+ port=config.get("msfrpc.port", 55553),
248
+ username=config.get("msfrpc.username", "msf"),
249
+ password=get_msfrpc_password() or "",
250
+ ssl=config.get("msfrpc.ssl", False),
247
251
  )
248
252
 
249
253
  if client.login():
@@ -263,7 +267,7 @@ class MSFRPCManager:
263
267
  """Public method to get RPC client."""
264
268
  if not is_pro_enabled():
265
269
  return None
266
- if not config.get('msfrpc.enabled', False):
270
+ if not config.get("msfrpc.enabled", False):
267
271
  return None
268
272
  return self._get_client()
269
273
 
@@ -279,28 +283,25 @@ class MSFRPCManager:
279
283
  # Check Pro license first
280
284
  if not is_pro_enabled():
281
285
  return {
282
- 'status': 'no_license',
283
- 'reason': 'Pro license required for msfrpcd integration'
286
+ "status": "no_license",
287
+ "reason": "Pro license required for msfrpcd integration",
284
288
  }
285
289
 
286
290
  if not MSGPACK_AVAILABLE:
287
291
  return {
288
- 'status': 'unavailable',
289
- 'reason': 'msgpack not installed (pip install msgpack)'
292
+ "status": "unavailable",
293
+ "reason": "msgpack not installed (pip install msgpack)",
290
294
  }
291
295
 
292
- if not config.get('msfrpc.enabled', False):
293
- return {
294
- 'status': 'disabled',
295
- 'reason': 'msfrpc.enabled is False in config'
296
- }
296
+ if not config.get("msfrpc.enabled", False):
297
+ return {"status": "disabled", "reason": "msfrpc.enabled is False in config"}
297
298
 
298
299
  # Check if password can be retrieved (vault must be unlocked)
299
300
  password = get_msfrpc_password()
300
- if not password and config.get('msfrpc.password'):
301
+ if not password and config.get("msfrpc.password"):
301
302
  return {
302
- 'status': 'vault_locked',
303
- 'reason': 'Credential vault is locked - unlock to connect'
303
+ "status": "vault_locked",
304
+ "reason": "Credential vault is locked - unlock to connect",
304
305
  }
305
306
 
306
307
  try:
@@ -309,26 +310,23 @@ class MSFRPCManager:
309
310
  version = client.get_version()
310
311
  sessions = client.list_sessions()
311
312
  return {
312
- 'status': 'connected',
313
- 'version': version.get('version', 'unknown'),
314
- 'ruby': version.get('ruby', 'unknown'),
315
- 'active_sessions': len(sessions)
313
+ "status": "connected",
314
+ "version": version.get("version", "unknown"),
315
+ "ruby": version.get("ruby", "unknown"),
316
+ "active_sessions": len(sessions),
316
317
  }
317
318
  except Exception as e:
318
- return {'status': 'error', 'reason': str(e)}
319
+ return {"status": "error", "reason": str(e)}
319
320
 
320
- return {
321
- 'status': 'unavailable',
322
- 'reason': 'Could not connect to msfrpcd'
323
- }
321
+ return {"status": "unavailable", "reason": "Could not connect to msfrpcd"}
324
322
 
325
323
  def get_start_command(self) -> str:
326
324
  """Get the command to start msfrpcd with current config."""
327
- host = config.get('msfrpc.host', '127.0.0.1')
328
- port = config.get('msfrpc.port', 55553)
329
- username = config.get('msfrpc.username', 'msf')
330
- password = get_msfrpc_password() or 'YOUR_PASSWORD'
331
- ssl = config.get('msfrpc.ssl', False)
325
+ host = config.get("msfrpc.host", "127.0.0.1")
326
+ port = config.get("msfrpc.port", 55553)
327
+ username = config.get("msfrpc.username", "msf")
328
+ password = get_msfrpc_password() or "YOUR_PASSWORD"
329
+ ssl = config.get("msfrpc.ssl", False)
332
330
 
333
331
  cmd = f"msfrpcd -U {username} -P {password} -a {host} -p {port}"
334
332
  if not ssl:
@@ -345,19 +343,16 @@ class MSFRPCManager:
345
343
  """
346
344
  # Check if msfrpcd is already running
347
345
  if self.is_available():
348
- return {
349
- 'success': True,
350
- 'message': 'msfrpcd is already running'
351
- }
346
+ return {"success": True, "message": "msfrpcd is already running"}
352
347
 
353
348
  # Check if msfrpcd binary exists
354
- msfrpcd_path = shutil.which('msfrpcd')
349
+ msfrpcd_path = shutil.which("msfrpcd")
355
350
  if not msfrpcd_path:
356
351
  # Check common MSF install locations
357
352
  common_paths = [
358
- '/opt/metasploit-framework/bin/msfrpcd',
359
- '/usr/share/metasploit-framework/msfrpcd',
360
- '/usr/bin/msfrpcd'
353
+ "/opt/metasploit-framework/bin/msfrpcd",
354
+ "/usr/share/metasploit-framework/msfrpcd",
355
+ "/usr/bin/msfrpcd",
361
356
  ]
362
357
  for path in common_paths:
363
358
  if shutil.which(path):
@@ -366,43 +361,47 @@ class MSFRPCManager:
366
361
 
367
362
  if not msfrpcd_path:
368
363
  return {
369
- 'success': False,
370
- 'message': 'msfrpcd not found. Is Metasploit Framework installed?',
371
- 'command': self.get_start_command()
364
+ "success": False,
365
+ "message": "msfrpcd not found. Is Metasploit Framework installed?",
366
+ "command": self.get_start_command(),
372
367
  }
373
368
 
374
369
  # Check if password is configured
375
370
  password = get_msfrpc_password()
376
371
  if not password:
377
372
  return {
378
- 'success': False,
379
- 'message': 'msfrpc password not configured. Set it in Settings first.',
380
- 'command': self.get_start_command()
373
+ "success": False,
374
+ "message": "msfrpc password not configured. Set it in Settings first.",
375
+ "command": self.get_start_command(),
381
376
  }
382
377
 
383
378
  # Try to start msfrpcd
384
379
  try:
385
- host = config.get('msfrpc.host', '127.0.0.1')
386
- port = config.get('msfrpc.port', 55553)
387
- username = config.get('msfrpc.username', 'msf')
388
- ssl = config.get('msfrpc.ssl', False)
380
+ host = config.get("msfrpc.host", "127.0.0.1")
381
+ port = config.get("msfrpc.port", 55553)
382
+ username = config.get("msfrpc.username", "msf")
383
+ ssl = config.get("msfrpc.ssl", False)
389
384
 
390
385
  cmd = [
391
386
  msfrpcd_path,
392
- '-U', username,
393
- '-P', password,
394
- '-a', host,
395
- '-p', str(port)
387
+ "-U",
388
+ username,
389
+ "-P",
390
+ password,
391
+ "-a",
392
+ host,
393
+ "-p",
394
+ str(port),
396
395
  ]
397
396
  if not ssl:
398
- cmd.append('-S') # -S flag DISABLES SSL
397
+ cmd.append("-S") # -S flag DISABLES SSL
399
398
 
400
399
  # Start in background
401
400
  proc = subprocess.Popen(
402
401
  cmd,
403
402
  stdout=subprocess.DEVNULL,
404
403
  stderr=subprocess.DEVNULL,
405
- start_new_session=True
404
+ start_new_session=True,
406
405
  )
407
406
 
408
407
  # Wait a moment for it to start
@@ -411,30 +410,27 @@ class MSFRPCManager:
411
410
  # Check if it's running
412
411
  if proc.poll() is not None:
413
412
  return {
414
- 'success': False,
415
- 'message': 'msfrpcd failed to start. Check if port is in use.',
416
- 'command': self.get_start_command()
413
+ "success": False,
414
+ "message": "msfrpcd failed to start. Check if port is in use.",
415
+ "command": self.get_start_command(),
417
416
  }
418
417
 
419
418
  # Try to connect
420
419
  time.sleep(1)
421
420
  if self.is_available():
422
- return {
423
- 'success': True,
424
- 'message': f'msfrpcd started on {host}:{port}'
425
- }
421
+ return {"success": True, "message": f"msfrpcd started on {host}:{port}"}
426
422
  else:
427
423
  return {
428
- 'success': False,
429
- 'message': 'msfrpcd started but connection failed. Check credentials.',
430
- 'command': self.get_start_command()
424
+ "success": False,
425
+ "message": "msfrpcd started but connection failed. Check credentials.",
426
+ "command": self.get_start_command(),
431
427
  }
432
428
 
433
429
  except Exception as e:
434
430
  return {
435
- 'success': False,
436
- 'message': f'Failed to start msfrpcd: {e}',
437
- 'command': self.get_start_command()
431
+ "success": False,
432
+ "message": f"Failed to start msfrpcd: {e}",
433
+ "command": self.get_start_command(),
438
434
  }
439
435
 
440
436
  def list_sessions(self) -> Dict[str, Dict[str, Any]]:
@@ -470,7 +466,9 @@ class MSFRPCManager:
470
466
  logger.error(f"Failed to kill session: {e}")
471
467
  return False
472
468
 
473
- def run_session_command(self, session_id: int, command: str, timeout: int = 30) -> str:
469
+ def run_session_command(
470
+ self, session_id: int, command: str, timeout: int = 30
471
+ ) -> str:
474
472
  """Run a command in a session."""
475
473
  client = self.get_client()
476
474
  if not client: