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
@@ -12,7 +12,7 @@ logger = logging.getLogger(__name__)
12
12
  class ResultParser:
13
13
  """
14
14
  Parse command execution results and extract meaningful data.
15
-
15
+
16
16
  Handles different command types (SSH, MySQL, nmap, etc.)
17
17
  and extracts success status, access levels, and other metadata.
18
18
  """
@@ -22,61 +22,54 @@ class ResultParser:
22
22
  pass
23
23
 
24
24
  def parse_result(
25
- self,
26
- command: str,
27
- stdout: str,
28
- stderr: str,
29
- exit_code: int
25
+ self, command: str, stdout: str, stderr: str, exit_code: int
30
26
  ) -> Dict[str, Any]:
31
27
  """
32
28
  Parse command result based on command type.
33
-
29
+
34
30
  Args:
35
31
  command: Command that was executed
36
32
  stdout: Standard output
37
33
  stderr: Standard error
38
34
  exit_code: Exit code
39
-
35
+
40
36
  Returns:
41
37
  Dict with parsed results
42
38
  """
43
39
  cmd_lower = command.lower()
44
-
40
+
45
41
  # Detect command type and parse accordingly
46
- if 'sshpass' in cmd_lower or ('ssh' in cmd_lower and 'SSH_SUCCESS' in command):
42
+ if "sshpass" in cmd_lower or ("ssh" in cmd_lower and "SSH_SUCCESS" in command):
47
43
  return self.parse_ssh_result(stdout, stderr, exit_code)
48
- elif 'smbclient' in cmd_lower:
44
+ elif "smbclient" in cmd_lower:
49
45
  return self.parse_smb_result(stdout, stderr, exit_code)
50
- elif 'xfreerdp' in cmd_lower:
46
+ elif "xfreerdp" in cmd_lower:
51
47
  return self.parse_rdp_result(stdout, stderr, exit_code)
52
- elif 'lftp' in cmd_lower:
48
+ elif "lftp" in cmd_lower:
53
49
  return self.parse_ftp_result(stdout, stderr, exit_code)
54
- elif 'psql' in cmd_lower or 'PGPASSWORD' in command:
50
+ elif "psql" in cmd_lower or "PGPASSWORD" in command:
55
51
  return self.parse_postgresql_result(stdout, stderr, exit_code)
56
- elif 'mysql' in cmd_lower:
52
+ elif "mysql" in cmd_lower:
57
53
  return self.parse_mysql_result(stdout, stderr, exit_code, command)
58
- elif 'nmap' in cmd_lower:
54
+ elif "nmap" in cmd_lower:
59
55
  return self.parse_nmap_result(stdout, stderr, exit_code)
60
- elif 'curl' in cmd_lower:
56
+ elif "curl" in cmd_lower:
61
57
  return self.parse_http_result(stdout, stderr, exit_code)
62
58
  else:
63
59
  # Generic parsing
64
60
  return self.parse_generic_result(stdout, stderr, exit_code)
65
61
 
66
62
  def parse_ssh_result(
67
- self,
68
- stdout: str,
69
- stderr: str,
70
- exit_code: int
63
+ self, stdout: str, stderr: str, exit_code: int
71
64
  ) -> Dict[str, Any]:
72
65
  """
73
66
  Parse SSH credential test result.
74
-
67
+
75
68
  Args:
76
69
  stdout: Command output
77
70
  stderr: Error output
78
71
  exit_code: Exit code
79
-
72
+
80
73
  Returns:
81
74
  Dict with:
82
75
  - success: bool
@@ -86,59 +79,56 @@ class ResultParser:
86
79
  - details: str
87
80
  """
88
81
  result = {
89
- 'success': False,
90
- 'credential_valid': False,
91
- 'access_level': 'none',
92
- 'username': None,
93
- 'details': ''
82
+ "success": False,
83
+ "credential_valid": False,
84
+ "access_level": "none",
85
+ "username": None,
86
+ "details": "",
94
87
  }
95
-
88
+
96
89
  # Check for success marker
97
- if 'SSH_SUCCESS' in stdout:
98
- result['success'] = True
99
- result['credential_valid'] = True
100
-
90
+ if "SSH_SUCCESS" in stdout:
91
+ result["success"] = True
92
+ result["credential_valid"] = True
93
+
101
94
  # Extract username from whoami output
102
- whoami_match = re.search(r'SSH_SUCCESS\s+(\w+)', stdout)
95
+ whoami_match = re.search(r"SSH_SUCCESS\s+(\w+)", stdout)
103
96
  if whoami_match:
104
- result['username'] = whoami_match.group(1)
105
-
97
+ result["username"] = whoami_match.group(1)
98
+
106
99
  # Check if root
107
- if 'uid=0(root)' in stdout or result['username'] == 'root':
108
- result['access_level'] = 'root'
100
+ if "uid=0(root)" in stdout or result["username"] == "root":
101
+ result["access_level"] = "root"
109
102
  else:
110
- result['access_level'] = 'user'
111
-
112
- result['details'] = f"SSH login successful as {result['username']}"
113
-
103
+ result["access_level"] = "user"
104
+
105
+ result["details"] = f"SSH login successful as {result['username']}"
106
+
114
107
  elif exit_code == 0:
115
108
  # Connection succeeded but no marker (shouldn't happen with our command)
116
- result['success'] = True
117
- result['credential_valid'] = True
118
- result['access_level'] = 'user'
119
- result['details'] = 'SSH connection successful'
120
-
109
+ result["success"] = True
110
+ result["credential_valid"] = True
111
+ result["access_level"] = "user"
112
+ result["details"] = "SSH connection successful"
113
+
121
114
  else:
122
115
  # Authentication failed or connection error
123
- result['success'] = False
124
- result['credential_valid'] = False
125
-
126
- if 'Permission denied' in stderr or 'Authentication failed' in stderr:
127
- result['details'] = 'Invalid credentials'
128
- elif 'Connection refused' in stderr:
129
- result['details'] = 'SSH service not available'
130
- elif 'Connection timed out' in stderr:
131
- result['details'] = 'Connection timeout'
116
+ result["success"] = False
117
+ result["credential_valid"] = False
118
+
119
+ if "Permission denied" in stderr or "Authentication failed" in stderr:
120
+ result["details"] = "Invalid credentials"
121
+ elif "Connection refused" in stderr:
122
+ result["details"] = "SSH service not available"
123
+ elif "Connection timed out" in stderr:
124
+ result["details"] = "Connection timeout"
132
125
  else:
133
- result['details'] = f'SSH connection failed (exit code: {exit_code})'
134
-
126
+ result["details"] = f"SSH connection failed (exit code: {exit_code})"
127
+
135
128
  return result
136
129
 
137
130
  def parse_ftp_result(
138
- self,
139
- stdout: str,
140
- stderr: str,
141
- exit_code: int
131
+ self, stdout: str, stderr: str, exit_code: int
142
132
  ) -> Dict[str, Any]:
143
133
  """
144
134
  Parse FTP credential test result.
@@ -151,40 +141,41 @@ class ResultParser:
151
141
  - details: str
152
142
  """
153
143
  result = {
154
- 'success': False,
155
- 'credential_valid': False,
156
- 'access_level': 'user', # FTP doesn't have root concept
157
- 'details': ''
144
+ "success": False,
145
+ "credential_valid": False,
146
+ "access_level": "user", # FTP doesn't have root concept
147
+ "details": "",
158
148
  }
159
149
 
160
150
  # Check for failure marker
161
- if 'FTP_FAILED' in stdout or 'FTP_FAILED' in stderr:
162
- result['details'] = 'FTP connection failed'
151
+ if "FTP_FAILED" in stdout or "FTP_FAILED" in stderr:
152
+ result["details"] = "FTP connection failed"
163
153
  return result
164
154
 
165
155
  # Check for authentication errors
166
- if 'Login incorrect' in stderr or 'Login failed' in stderr or 'Authentication failed' in stderr:
167
- result['details'] = 'Invalid credentials'
156
+ if (
157
+ "Login incorrect" in stderr
158
+ or "Login failed" in stderr
159
+ or "Authentication failed" in stderr
160
+ ):
161
+ result["details"] = "Invalid credentials"
168
162
  return result
169
163
 
170
164
  # Check for connection errors
171
- if 'Connection refused' in stderr or 'Connection timed out' in stderr:
172
- result['details'] = 'Connection failed'
165
+ if "Connection refused" in stderr or "Connection timed out" in stderr:
166
+ result["details"] = "Connection failed"
173
167
  return result
174
168
 
175
169
  # If we got directory listing, credentials worked
176
- if exit_code == 0 or 'drw' in stdout or '-rw' in stdout:
177
- result['success'] = True
178
- result['credential_valid'] = True
179
- result['details'] = 'FTP login successful'
170
+ if exit_code == 0 or "drw" in stdout or "-rw" in stdout:
171
+ result["success"] = True
172
+ result["credential_valid"] = True
173
+ result["details"] = "FTP login successful"
180
174
 
181
175
  return result
182
176
 
183
177
  def parse_smb_result(
184
- self,
185
- stdout: str,
186
- stderr: str,
187
- exit_code: int
178
+ self, stdout: str, stderr: str, exit_code: int
188
179
  ) -> Dict[str, Any]:
189
180
  """
190
181
  Parse SMB credential test result.
@@ -198,52 +189,52 @@ class ResultParser:
198
189
  - details: str
199
190
  """
200
191
  result = {
201
- 'success': False,
202
- 'credential_valid': False,
203
- 'access_level': 'user',
204
- 'shares': [],
205
- 'details': ''
192
+ "success": False,
193
+ "credential_valid": False,
194
+ "access_level": "user",
195
+ "shares": [],
196
+ "details": "",
206
197
  }
207
198
 
208
199
  # Check for failure marker
209
- if 'SMB_FAILED' in stdout or 'SMB_FAILED' in stderr:
210
- result['details'] = 'SMB connection failed'
200
+ if "SMB_FAILED" in stdout or "SMB_FAILED" in stderr:
201
+ result["details"] = "SMB connection failed"
211
202
  return result
212
203
 
213
204
  # Check for authentication errors
214
- if 'NT_STATUS_LOGON_FAILURE' in stderr or 'NT_STATUS_ACCESS_DENIED' in stderr:
215
- result['details'] = 'Invalid credentials'
205
+ if "NT_STATUS_LOGON_FAILURE" in stderr or "NT_STATUS_ACCESS_DENIED" in stderr:
206
+ result["details"] = "Invalid credentials"
216
207
  return result
217
208
 
218
209
  # Check for connection errors
219
- if 'Connection refused' in stderr or 'Connection timed out' in stderr:
220
- result['details'] = 'Connection failed'
210
+ if "Connection refused" in stderr or "Connection timed out" in stderr:
211
+ result["details"] = "Connection failed"
221
212
  return result
222
213
 
223
- if 'NT_STATUS_IO_TIMEOUT' in stderr or 'Unable to connect' in stderr:
224
- result['details'] = 'Connection timeout'
214
+ if "NT_STATUS_IO_TIMEOUT" in stderr or "Unable to connect" in stderr:
215
+ result["details"] = "Connection timeout"
225
216
  return result
226
217
 
227
218
  # Check for successful share listing
228
- if 'Disk|' in stdout or 'IPC$' in stdout or 'Sharename' in stdout:
229
- result['success'] = True
230
- result['credential_valid'] = True
231
- result['details'] = 'SMB login successful'
219
+ if "Disk|" in stdout or "IPC$" in stdout or "Sharename" in stdout:
220
+ result["success"] = True
221
+ result["credential_valid"] = True
222
+ result["details"] = "SMB login successful"
232
223
 
233
224
  # Extract share names (optional enhancement)
234
225
  import re
235
- share_matches = re.findall(r'^\s+(\S+)\s+Disk', stdout, re.MULTILINE)
226
+
227
+ share_matches = re.findall(r"^\s+(\S+)\s+Disk", stdout, re.MULTILINE)
236
228
  if share_matches:
237
- result['shares'] = share_matches
238
- result['details'] = f"SMB login successful, found {len(share_matches)} shares"
229
+ result["shares"] = share_matches
230
+ result["details"] = (
231
+ f"SMB login successful, found {len(share_matches)} shares"
232
+ )
239
233
 
240
234
  return result
241
235
 
242
236
  def parse_rdp_result(
243
- self,
244
- stdout: str,
245
- stderr: str,
246
- exit_code: int
237
+ self, stdout: str, stderr: str, exit_code: int
247
238
  ) -> Dict[str, Any]:
248
239
  """
249
240
  Parse RDP credential test result.
@@ -256,53 +247,60 @@ class ResultParser:
256
247
  - details: str
257
248
  """
258
249
  result = {
259
- 'success': False,
260
- 'credential_valid': False,
261
- 'access_level': 'user',
262
- 'details': ''
250
+ "success": False,
251
+ "credential_valid": False,
252
+ "access_level": "user",
253
+ "details": "",
263
254
  }
264
255
 
265
256
  # Check for failure marker
266
- if 'RDP_FAILED' in stdout or 'RDP_FAILED' in stderr:
267
- result['details'] = 'RDP connection failed'
257
+ if "RDP_FAILED" in stdout or "RDP_FAILED" in stderr:
258
+ result["details"] = "RDP connection failed"
268
259
  return result
269
260
 
270
261
  # Check for authentication errors
271
- if 'Authentication failure' in stderr or 'ERRCONNECT_AUTHENTICATION_FAILED' in stderr:
272
- result['details'] = 'Invalid credentials'
262
+ if (
263
+ "Authentication failure" in stderr
264
+ or "ERRCONNECT_AUTHENTICATION_FAILED" in stderr
265
+ ):
266
+ result["details"] = "Invalid credentials"
273
267
  return result
274
268
 
275
- if 'Account restriction' in stderr or 'ERRCONNECT_ACCOUNT_DISABLED' in stderr:
276
- result['details'] = 'Account disabled or restricted'
269
+ if "Account restriction" in stderr or "ERRCONNECT_ACCOUNT_DISABLED" in stderr:
270
+ result["details"] = "Account disabled or restricted"
277
271
  return result
278
272
 
279
273
  # Check for connection errors
280
- if 'unable to connect' in stderr.lower() or 'connection failed' in stderr.lower():
281
- result['details'] = 'Connection failed'
274
+ if (
275
+ "unable to connect" in stderr.lower()
276
+ or "connection failed" in stderr.lower()
277
+ ):
278
+ result["details"] = "Connection failed"
282
279
  return result
283
280
 
284
- if 'Connection timeout' in stderr or 'connection timed out' in stderr.lower():
285
- result['details'] = 'Connection timeout'
281
+ if "Connection timeout" in stderr or "connection timed out" in stderr.lower():
282
+ result["details"] = "Connection timeout"
286
283
  return result
287
284
 
288
285
  # Check for successful authentication
289
286
  # xfreerdp with +auth-only returns 0 on successful auth
290
- if exit_code == 0 or 'Authentication only' in stdout or 'connected' in stdout.lower():
291
- result['success'] = True
292
- result['credential_valid'] = True
293
- result['details'] = 'RDP authentication successful'
287
+ if (
288
+ exit_code == 0
289
+ or "Authentication only" in stdout
290
+ or "connected" in stdout.lower()
291
+ ):
292
+ result["success"] = True
293
+ result["credential_valid"] = True
294
+ result["details"] = "RDP authentication successful"
294
295
 
295
296
  # Check if admin (optional - may not be detectable in auth-only mode)
296
- if 'administrator' in stdout.lower():
297
- result['access_level'] = 'admin'
297
+ if "administrator" in stdout.lower():
298
+ result["access_level"] = "admin"
298
299
 
299
300
  return result
300
301
 
301
302
  def parse_postgresql_result(
302
- self,
303
- stdout: str,
304
- stderr: str,
305
- exit_code: int
303
+ self, stdout: str, stderr: str, exit_code: int
306
304
  ) -> Dict[str, Any]:
307
305
  """
308
306
  Parse PostgreSQL credential test result.
@@ -315,171 +313,166 @@ class ResultParser:
315
313
  - details: str
316
314
  """
317
315
  result = {
318
- 'success': False,
319
- 'credential_valid': False,
320
- 'access_level': 'user',
321
- 'details': ''
316
+ "success": False,
317
+ "credential_valid": False,
318
+ "access_level": "user",
319
+ "details": "",
322
320
  }
323
321
 
324
322
  # Check for failure marker
325
- if 'PSQL_FAILED' in stdout:
326
- result['details'] = 'PostgreSQL connection failed'
323
+ if "PSQL_FAILED" in stdout:
324
+ result["details"] = "PostgreSQL connection failed"
327
325
  return result
328
326
 
329
327
  # Check for authentication errors
330
- if 'authentication failed' in stderr.lower() or 'password authentication failed' in stderr.lower():
331
- result['details'] = 'Invalid credentials'
328
+ if (
329
+ "authentication failed" in stderr.lower()
330
+ or "password authentication failed" in stderr.lower()
331
+ ):
332
+ result["details"] = "Invalid credentials"
332
333
  return result
333
334
 
334
335
  # Check for connection errors
335
- if 'could not connect' in stderr.lower() or 'Connection refused' in stderr:
336
- result['details'] = 'Connection failed'
336
+ if "could not connect" in stderr.lower() or "Connection refused" in stderr:
337
+ result["details"] = "Connection failed"
337
338
  return result
338
339
 
339
340
  # Check for version output (means connection worked)
340
- if 'PostgreSQL' in stdout or 'version' in stdout.lower():
341
- result['success'] = True
342
- result['credential_valid'] = True
341
+ if "PostgreSQL" in stdout or "version" in stdout.lower():
342
+ result["success"] = True
343
+ result["credential_valid"] = True
343
344
 
344
345
  # Check if superuser
345
- if 'superuser' in stdout.lower():
346
- result['access_level'] = 'admin'
346
+ if "superuser" in stdout.lower():
347
+ result["access_level"] = "admin"
347
348
 
348
- result['details'] = 'PostgreSQL login successful'
349
+ result["details"] = "PostgreSQL login successful"
349
350
 
350
351
  return result
351
352
 
352
353
  def parse_mysql_result(
353
- self,
354
- stdout: str,
355
- stderr: str,
356
- exit_code: int,
357
- command: str
354
+ self, stdout: str, stderr: str, exit_code: int, command: str
358
355
  ) -> Dict[str, Any]:
359
356
  """
360
357
  Parse MySQL command result.
361
-
358
+
362
359
  Args:
363
360
  stdout: Command output
364
361
  stderr: Error output
365
362
  exit_code: Exit code
366
363
  command: Original command
367
-
364
+
368
365
  Returns:
369
366
  Dict with success status and details
370
367
  """
371
368
  result = {
372
- 'success': False,
373
- 'credential_valid': False,
374
- 'details': '',
375
- 'databases': [],
376
- 'users': []
369
+ "success": False,
370
+ "credential_valid": False,
371
+ "details": "",
372
+ "databases": [],
373
+ "users": [],
377
374
  }
378
-
375
+
379
376
  # Check for version output (indicates successful connection)
380
- if 'version()' in stdout or re.search(r'\d+\.\d+\.\d+', stdout):
381
- result['success'] = True
382
- result['credential_valid'] = True
383
-
384
- version_match = re.search(r'(\d+\.\d+\.\d+)', stdout)
377
+ if "version()" in stdout or re.search(r"\d+\.\d+\.\d+", stdout):
378
+ result["success"] = True
379
+ result["credential_valid"] = True
380
+
381
+ version_match = re.search(r"(\d+\.\d+\.\d+)", stdout)
385
382
  if version_match:
386
- result['details'] = f"MySQL connection successful (version: {version_match.group(1)})"
383
+ result["details"] = (
384
+ f"MySQL connection successful (version: {version_match.group(1)})"
385
+ )
387
386
  else:
388
- result['details'] = "MySQL connection successful"
389
-
387
+ result["details"] = "MySQL connection successful"
388
+
390
389
  # Check for database enumeration
391
- if 'SHOW DATABASES' in command.upper():
392
- db_matches = re.findall(r'^([a-zA-Z_]\w*)$', stdout, re.MULTILINE)
393
- result['databases'] = [db for db in db_matches if db not in ['Database', 'version()']]
394
-
390
+ if "SHOW DATABASES" in command.upper():
391
+ db_matches = re.findall(r"^([a-zA-Z_]\w*)$", stdout, re.MULTILINE)
392
+ result["databases"] = [
393
+ db for db in db_matches if db not in ["Database", "version()"]
394
+ ]
395
+
395
396
  # Check for user enumeration
396
- if 'SELECT user' in command:
397
- user_matches = re.findall(r'(\w+)\s+(%|[\w.]+)', stdout)
398
- result['users'] = user_matches
399
-
397
+ if "SELECT user" in command:
398
+ user_matches = re.findall(r"(\w+)\s+(%|[\w.]+)", stdout)
399
+ result["users"] = user_matches
400
+
400
401
  # Check for errors
401
- if exit_code != 0 or 'ERROR' in stderr:
402
- result['success'] = False
403
- result['credential_valid'] = False
404
-
405
- if 'Access denied' in stderr:
406
- result['details'] = 'Invalid MySQL credentials'
407
- elif "Can't connect" in stderr or 'Connection refused' in stderr:
408
- result['details'] = 'MySQL service not available'
402
+ if exit_code != 0 or "ERROR" in stderr:
403
+ result["success"] = False
404
+ result["credential_valid"] = False
405
+
406
+ if "Access denied" in stderr:
407
+ result["details"] = "Invalid MySQL credentials"
408
+ elif "Can't connect" in stderr or "Connection refused" in stderr:
409
+ result["details"] = "MySQL service not available"
409
410
  else:
410
- result['details'] = f'MySQL command failed: {stderr[:100]}'
411
-
411
+ result["details"] = f"MySQL command failed: {stderr[:100]}"
412
+
412
413
  return result
413
414
 
414
415
  def parse_nmap_result(
415
- self,
416
- stdout: str,
417
- stderr: str,
418
- exit_code: int
416
+ self, stdout: str, stderr: str, exit_code: int
419
417
  ) -> Dict[str, Any]:
420
418
  """Parse nmap scan result."""
421
419
  result = {
422
- 'success': exit_code == 0,
423
- 'open_ports': [],
424
- 'services': {},
425
- 'details': ''
420
+ "success": exit_code == 0,
421
+ "open_ports": [],
422
+ "services": {},
423
+ "details": "",
426
424
  }
427
-
425
+
428
426
  if exit_code == 0:
429
427
  # Extract open ports
430
- port_lines = re.findall(r'^(\d+)/tcp\s+open\s+(.+)$', stdout, re.MULTILINE)
428
+ port_lines = re.findall(r"^(\d+)/tcp\s+open\s+(.+)$", stdout, re.MULTILINE)
431
429
  for port, service_info in port_lines:
432
- result['open_ports'].append(int(port))
433
- result['services'][port] = service_info.strip()
434
-
435
- result['details'] = f"Scan complete: {len(result['open_ports'])} open ports found"
430
+ result["open_ports"].append(int(port))
431
+ result["services"][port] = service_info.strip()
432
+
433
+ result["details"] = (
434
+ f"Scan complete: {len(result['open_ports'])} open ports found"
435
+ )
436
436
  else:
437
- result['details'] = 'Nmap scan failed'
438
-
437
+ result["details"] = "Nmap scan failed"
438
+
439
439
  return result
440
440
 
441
441
  def parse_http_result(
442
- self,
443
- stdout: str,
444
- stderr: str,
445
- exit_code: int
442
+ self, stdout: str, stderr: str, exit_code: int
446
443
  ) -> Dict[str, Any]:
447
444
  """Parse HTTP request result."""
448
- result = {
449
- 'success': False,
450
- 'status_code': None,
451
- 'server': None,
452
- 'details': ''
453
- }
454
-
445
+ result = {"success": False, "status_code": None, "server": None, "details": ""}
446
+
455
447
  # Extract HTTP status code
456
- status_match = re.search(r'HTTP/[\d.]+\s+(\d+)', stdout)
448
+ status_match = re.search(r"HTTP/[\d.]+\s+(\d+)", stdout)
457
449
  if status_match:
458
- result['success'] = True
459
- result['status_code'] = int(status_match.group(1))
460
-
450
+ result["success"] = True
451
+ result["status_code"] = int(status_match.group(1))
452
+
461
453
  # Extract server header
462
- server_match = re.search(r'Server:\s*(.+)', stdout, re.IGNORECASE)
454
+ server_match = re.search(r"Server:\s*(.+)", stdout, re.IGNORECASE)
463
455
  if server_match:
464
- result['server'] = server_match.group(1).strip()
465
-
466
- if result['success']:
467
- result['details'] = f"HTTP {result['status_code']}"
468
- if result['server']:
469
- result['details'] += f" - {result['server']}"
456
+ result["server"] = server_match.group(1).strip()
457
+
458
+ if result["success"]:
459
+ result["details"] = f"HTTP {result['status_code']}"
460
+ if result["server"]:
461
+ result["details"] += f" - {result['server']}"
470
462
  else:
471
- result['details'] = 'HTTP request failed'
472
-
463
+ result["details"] = "HTTP request failed"
464
+
473
465
  return result
474
466
 
475
467
  def parse_generic_result(
476
- self,
477
- stdout: str,
478
- stderr: str,
479
- exit_code: int
468
+ self, stdout: str, stderr: str, exit_code: int
480
469
  ) -> Dict[str, Any]:
481
470
  """Generic result parsing for unknown command types."""
482
471
  return {
483
- 'success': exit_code == 0,
484
- 'details': 'Command executed' if exit_code == 0 else f'Command failed (exit {exit_code})'
472
+ "success": exit_code == 0,
473
+ "details": (
474
+ "Command executed"
475
+ if exit_code == 0
476
+ else f"Command failed (exit {exit_code})"
477
+ ),
485
478
  }