souleyez 2.43.28__py3-none-any.whl → 2.43.32__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 (356) 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 +9592 -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 +1238 -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 +2198 -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 +288 -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/whois_handler.py +277 -0
  126. souleyez/handlers/wpscan_handler.py +554 -0
  127. souleyez/history.py +32 -16
  128. souleyez/importers/msf_importer.py +106 -75
  129. souleyez/importers/smart_importer.py +208 -147
  130. souleyez/integrations/siem/__init__.py +10 -10
  131. souleyez/integrations/siem/base.py +17 -18
  132. souleyez/integrations/siem/elastic.py +108 -122
  133. souleyez/integrations/siem/factory.py +207 -80
  134. souleyez/integrations/siem/googlesecops.py +146 -154
  135. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  136. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  137. souleyez/integrations/siem/sentinel.py +107 -109
  138. souleyez/integrations/siem/splunk.py +246 -212
  139. souleyez/integrations/siem/wazuh.py +65 -71
  140. souleyez/integrations/wazuh/__init__.py +5 -5
  141. souleyez/integrations/wazuh/client.py +70 -93
  142. souleyez/integrations/wazuh/config.py +85 -57
  143. souleyez/integrations/wazuh/host_mapper.py +28 -36
  144. souleyez/integrations/wazuh/sync.py +78 -68
  145. souleyez/intelligence/__init__.py +4 -5
  146. souleyez/intelligence/correlation_analyzer.py +309 -295
  147. souleyez/intelligence/exploit_knowledge.py +661 -623
  148. souleyez/intelligence/exploit_suggestions.py +159 -139
  149. souleyez/intelligence/gap_analyzer.py +132 -97
  150. souleyez/intelligence/gap_detector.py +251 -214
  151. souleyez/intelligence/sensitive_tables.py +266 -129
  152. souleyez/intelligence/service_parser.py +137 -123
  153. souleyez/intelligence/surface_analyzer.py +407 -268
  154. souleyez/intelligence/target_parser.py +159 -162
  155. souleyez/licensing/__init__.py +6 -6
  156. souleyez/licensing/validator.py +17 -19
  157. souleyez/log_config.py +79 -54
  158. souleyez/main.py +1505 -687
  159. souleyez/migrations/fix_job_counter.py +16 -14
  160. souleyez/parsers/bloodhound_parser.py +41 -39
  161. souleyez/parsers/crackmapexec_parser.py +178 -111
  162. souleyez/parsers/dalfox_parser.py +72 -77
  163. souleyez/parsers/dnsrecon_parser.py +103 -91
  164. souleyez/parsers/enum4linux_parser.py +183 -153
  165. souleyez/parsers/ffuf_parser.py +29 -25
  166. souleyez/parsers/gobuster_parser.py +301 -41
  167. souleyez/parsers/hashcat_parser.py +324 -79
  168. souleyez/parsers/http_fingerprint_parser.py +350 -103
  169. souleyez/parsers/hydra_parser.py +131 -111
  170. souleyez/parsers/impacket_parser.py +231 -178
  171. souleyez/parsers/john_parser.py +98 -86
  172. souleyez/parsers/katana_parser.py +316 -0
  173. souleyez/parsers/msf_parser.py +943 -498
  174. souleyez/parsers/nikto_parser.py +346 -65
  175. souleyez/parsers/nmap_parser.py +262 -174
  176. souleyez/parsers/nuclei_parser.py +40 -44
  177. souleyez/parsers/responder_parser.py +26 -26
  178. souleyez/parsers/searchsploit_parser.py +74 -74
  179. souleyez/parsers/service_explorer_parser.py +279 -0
  180. souleyez/parsers/smbmap_parser.py +180 -124
  181. souleyez/parsers/sqlmap_parser.py +434 -308
  182. souleyez/parsers/theharvester_parser.py +75 -57
  183. souleyez/parsers/whois_parser.py +135 -94
  184. souleyez/parsers/wpscan_parser.py +278 -190
  185. souleyez/plugins/afp.py +44 -36
  186. souleyez/plugins/afp_brute.py +114 -46
  187. souleyez/plugins/ard.py +48 -37
  188. souleyez/plugins/bloodhound.py +95 -61
  189. souleyez/plugins/certipy.py +303 -0
  190. souleyez/plugins/crackmapexec.py +186 -85
  191. souleyez/plugins/dalfox.py +120 -59
  192. souleyez/plugins/dns_hijack.py +146 -41
  193. souleyez/plugins/dnsrecon.py +97 -61
  194. souleyez/plugins/enum4linux.py +91 -66
  195. souleyez/plugins/evil_winrm.py +291 -0
  196. souleyez/plugins/ffuf.py +166 -90
  197. souleyez/plugins/firmware_extract.py +133 -29
  198. souleyez/plugins/gobuster.py +387 -190
  199. souleyez/plugins/gpp_extract.py +393 -0
  200. souleyez/plugins/hashcat.py +100 -73
  201. souleyez/plugins/http_fingerprint.py +854 -267
  202. souleyez/plugins/hydra.py +566 -200
  203. souleyez/plugins/impacket_getnpusers.py +117 -69
  204. souleyez/plugins/impacket_psexec.py +84 -64
  205. souleyez/plugins/impacket_secretsdump.py +103 -69
  206. souleyez/plugins/impacket_smbclient.py +89 -75
  207. souleyez/plugins/john.py +86 -69
  208. souleyez/plugins/katana.py +313 -0
  209. souleyez/plugins/kerbrute.py +237 -0
  210. souleyez/plugins/lfi_extract.py +541 -0
  211. souleyez/plugins/macos_ssh.py +117 -48
  212. souleyez/plugins/mdns.py +35 -30
  213. souleyez/plugins/msf_auxiliary.py +253 -130
  214. souleyez/plugins/msf_exploit.py +239 -161
  215. souleyez/plugins/nikto.py +134 -78
  216. souleyez/plugins/nmap.py +275 -91
  217. souleyez/plugins/nuclei.py +180 -89
  218. souleyez/plugins/nxc.py +285 -0
  219. souleyez/plugins/plugin_base.py +35 -36
  220. souleyez/plugins/plugin_template.py +13 -5
  221. souleyez/plugins/rdp_sec_check.py +130 -0
  222. souleyez/plugins/responder.py +112 -71
  223. souleyez/plugins/router_http_brute.py +76 -65
  224. souleyez/plugins/router_ssh_brute.py +118 -41
  225. souleyez/plugins/router_telnet_brute.py +124 -42
  226. souleyez/plugins/routersploit.py +91 -59
  227. souleyez/plugins/routersploit_exploit.py +77 -55
  228. souleyez/plugins/searchsploit.py +91 -77
  229. souleyez/plugins/service_explorer.py +1160 -0
  230. souleyez/plugins/smbmap.py +122 -72
  231. souleyez/plugins/smbpasswd.py +215 -0
  232. souleyez/plugins/sqlmap.py +301 -113
  233. souleyez/plugins/theharvester.py +127 -75
  234. souleyez/plugins/tr069.py +79 -57
  235. souleyez/plugins/upnp.py +65 -47
  236. souleyez/plugins/upnp_abuse.py +73 -55
  237. souleyez/plugins/vnc_access.py +129 -42
  238. souleyez/plugins/vnc_brute.py +109 -38
  239. souleyez/plugins/whois.py +77 -58
  240. souleyez/plugins/wpscan.py +173 -69
  241. souleyez/reporting/__init__.py +2 -1
  242. souleyez/reporting/attack_chain.py +411 -346
  243. souleyez/reporting/charts.py +436 -501
  244. souleyez/reporting/compliance_mappings.py +334 -201
  245. souleyez/reporting/detection_report.py +126 -125
  246. souleyez/reporting/formatters.py +828 -591
  247. souleyez/reporting/generator.py +386 -302
  248. souleyez/reporting/metrics.py +72 -75
  249. souleyez/scanner.py +35 -29
  250. souleyez/security/__init__.py +37 -11
  251. souleyez/security/scope_validator.py +175 -106
  252. souleyez/security/validation.py +223 -149
  253. souleyez/security.py +22 -6
  254. souleyez/storage/credentials.py +247 -186
  255. souleyez/storage/crypto.py +296 -129
  256. souleyez/storage/database.py +73 -50
  257. souleyez/storage/db.py +58 -36
  258. souleyez/storage/deliverable_evidence.py +177 -128
  259. souleyez/storage/deliverable_exporter.py +282 -246
  260. souleyez/storage/deliverable_templates.py +134 -116
  261. souleyez/storage/deliverables.py +135 -130
  262. souleyez/storage/engagements.py +109 -56
  263. souleyez/storage/evidence.py +181 -152
  264. souleyez/storage/execution_log.py +31 -17
  265. souleyez/storage/exploit_attempts.py +93 -57
  266. souleyez/storage/exploits.py +67 -36
  267. souleyez/storage/findings.py +48 -61
  268. souleyez/storage/hosts.py +176 -144
  269. souleyez/storage/migrate_to_engagements.py +43 -19
  270. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  271. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  272. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  273. souleyez/storage/migrations/_005_screenshots.py +13 -5
  274. souleyez/storage/migrations/_006_deliverables.py +13 -5
  275. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  276. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  277. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  278. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  279. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  280. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  281. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  282. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  283. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  284. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  285. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  286. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  287. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  288. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  289. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  290. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  291. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  292. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  293. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  294. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  295. souleyez/storage/migrations/__init__.py +26 -26
  296. souleyez/storage/migrations/migration_manager.py +19 -19
  297. souleyez/storage/msf_sessions.py +100 -65
  298. souleyez/storage/osint.py +17 -24
  299. souleyez/storage/recommendation_engine.py +269 -235
  300. souleyez/storage/screenshots.py +33 -32
  301. souleyez/storage/smb_shares.py +136 -92
  302. souleyez/storage/sqlmap_data.py +183 -128
  303. souleyez/storage/team_collaboration.py +135 -141
  304. souleyez/storage/timeline_tracker.py +122 -94
  305. souleyez/storage/wazuh_vulns.py +64 -66
  306. souleyez/storage/web_paths.py +33 -37
  307. souleyez/testing/credential_tester.py +221 -205
  308. souleyez/ui/__init__.py +1 -1
  309. souleyez/ui/ai_quotes.py +12 -12
  310. souleyez/ui/attack_surface.py +2439 -1516
  311. souleyez/ui/chain_rules_view.py +914 -382
  312. souleyez/ui/correlation_view.py +312 -230
  313. souleyez/ui/dashboard.py +2382 -1130
  314. souleyez/ui/deliverables_view.py +148 -62
  315. souleyez/ui/design_system.py +13 -13
  316. souleyez/ui/errors.py +49 -49
  317. souleyez/ui/evidence_linking_view.py +284 -179
  318. souleyez/ui/evidence_vault.py +393 -285
  319. souleyez/ui/exploit_suggestions_view.py +555 -349
  320. souleyez/ui/export_view.py +100 -66
  321. souleyez/ui/gap_analysis_view.py +315 -171
  322. souleyez/ui/help_system.py +105 -97
  323. souleyez/ui/intelligence_view.py +436 -293
  324. souleyez/ui/interactive.py +23142 -10430
  325. souleyez/ui/interactive_selector.py +75 -68
  326. souleyez/ui/log_formatter.py +47 -39
  327. souleyez/ui/menu_components.py +22 -13
  328. souleyez/ui/msf_auxiliary_menu.py +184 -133
  329. souleyez/ui/pending_chains_view.py +336 -172
  330. souleyez/ui/progress_indicators.py +5 -3
  331. souleyez/ui/recommendations_view.py +195 -137
  332. souleyez/ui/rule_builder.py +343 -225
  333. souleyez/ui/setup_wizard.py +678 -284
  334. souleyez/ui/shortcuts.py +217 -165
  335. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  336. souleyez/ui/splunk_vulns_view.py +139 -86
  337. souleyez/ui/team_dashboard.py +498 -335
  338. souleyez/ui/template_selector.py +196 -105
  339. souleyez/ui/terminal.py +6 -6
  340. souleyez/ui/timeline_view.py +198 -127
  341. souleyez/ui/tool_setup.py +264 -164
  342. souleyez/ui/tutorial.py +202 -72
  343. souleyez/ui/tutorial_state.py +40 -40
  344. souleyez/ui/wazuh_vulns_view.py +235 -141
  345. souleyez/ui/wordlist_browser.py +260 -107
  346. souleyez/ui.py +464 -312
  347. souleyez/utils/tool_checker.py +427 -367
  348. souleyez/utils.py +33 -29
  349. souleyez/wordlists.py +134 -167
  350. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/METADATA +1 -1
  351. souleyez-2.43.32.dist-info/RECORD +441 -0
  352. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/WHEEL +1 -1
  353. souleyez-2.43.28.dist-info/RECORD +0 -379
  354. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/entry_points.txt +0 -0
  355. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/licenses/LICENSE +0 -0
  356. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,554 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ WPScan handler.
4
+
5
+ Consolidates parsing and display logic for WPScan WordPress security scanner jobs.
6
+ """
7
+ import logging
8
+ import os
9
+ import re
10
+ from typing import Any, Dict, Optional
11
+ from urllib.parse import urlparse
12
+
13
+ import click
14
+
15
+ from souleyez.engine.job_status import STATUS_DONE, STATUS_ERROR, STATUS_NO_RESULTS
16
+ from souleyez.handlers.base import BaseToolHandler
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ class WPScanHandler(BaseToolHandler):
22
+ """Handler for WPScan WordPress security scanner jobs."""
23
+
24
+ tool_name = "wpscan"
25
+ display_name = "WPScan"
26
+
27
+ # All handlers enabled
28
+ has_error_handler = True
29
+ has_warning_handler = True
30
+ has_no_results_handler = True
31
+ has_done_handler = True
32
+
33
+ def parse_job(
34
+ self,
35
+ engagement_id: int,
36
+ log_path: str,
37
+ job: Dict[str, Any],
38
+ host_manager: Optional[Any] = None,
39
+ findings_manager: Optional[Any] = None,
40
+ credentials_manager: Optional[Any] = None,
41
+ ) -> Dict[str, Any]:
42
+ """
43
+ Parse WPScan job results.
44
+
45
+ Extracts WordPress vulnerabilities for plugins, themes, and core.
46
+ """
47
+ try:
48
+ from souleyez.parsers.wpscan_parser import parse_wpscan_output
49
+ from souleyez.engine.result_handler import detect_tool_error
50
+
51
+ # Import managers if not provided
52
+ if host_manager is None:
53
+ from souleyez.storage.hosts import HostManager
54
+
55
+ host_manager = HostManager()
56
+ if findings_manager is None:
57
+ from souleyez.storage.findings import FindingsManager
58
+
59
+ findings_manager = FindingsManager()
60
+ if credentials_manager is None:
61
+ from souleyez.storage.credentials import CredentialsManager
62
+
63
+ credentials_manager = CredentialsManager()
64
+
65
+ target = job.get("target", "")
66
+
67
+ # Read log file
68
+ with open(log_path, "r", encoding="utf-8", errors="replace") as f:
69
+ output = f.read()
70
+
71
+ parsed = parse_wpscan_output(output, target)
72
+
73
+ # Get or create host
74
+ host_id = None
75
+ target_ip = target
76
+ if "://" in str(target):
77
+ parsed_url = urlparse(target)
78
+ target_ip = parsed_url.hostname or target
79
+
80
+ if target_ip:
81
+ host = host_manager.get_host_by_ip(engagement_id, target_ip)
82
+ if not host:
83
+ host_id = host_manager.add_or_update_host(
84
+ engagement_id, {"ip": target_ip, "status": "up"}
85
+ )
86
+ else:
87
+ host_id = host["id"]
88
+
89
+ # Store findings
90
+ findings_added = 0
91
+
92
+ # WordPress version vulnerabilities
93
+ for vuln in parsed.get("findings", []):
94
+ findings_manager.add_finding(
95
+ engagement_id=engagement_id,
96
+ host_id=host_id,
97
+ title=vuln["title"],
98
+ finding_type="vulnerability",
99
+ severity=vuln.get("severity", "medium"),
100
+ description=vuln.get("description", ""),
101
+ tool="wpscan",
102
+ refs=", ".join(vuln.get("references", [])),
103
+ )
104
+ findings_added += 1
105
+
106
+ # Plugin vulnerabilities (parser returns list of dicts)
107
+ plugins = parsed.get("plugins", [])
108
+ if isinstance(plugins, list):
109
+ for plugin_data in plugins:
110
+ if isinstance(plugin_data, dict):
111
+ plugin_name = plugin_data.get("name", "Unknown")
112
+ for vuln in plugin_data.get("vulnerabilities", []):
113
+ findings_manager.add_finding(
114
+ engagement_id=engagement_id,
115
+ host_id=host_id,
116
+ title=f"WordPress Plugin: {plugin_name} - {vuln['title']}",
117
+ finding_type="vulnerability",
118
+ severity=vuln.get("severity", "medium"),
119
+ description=vuln.get("description", ""),
120
+ tool="wpscan",
121
+ refs=", ".join(vuln.get("references", [])),
122
+ )
123
+ findings_added += 1
124
+
125
+ # Theme vulnerabilities (parser returns list of dicts)
126
+ themes = parsed.get("themes", [])
127
+ if isinstance(themes, list):
128
+ for theme_data in themes:
129
+ if isinstance(theme_data, dict):
130
+ theme_name = theme_data.get("name", "Unknown")
131
+ for vuln in theme_data.get("vulnerabilities", []):
132
+ findings_manager.add_finding(
133
+ engagement_id=engagement_id,
134
+ host_id=host_id,
135
+ title=f"WordPress Theme: {theme_name} - {vuln['title']}",
136
+ finding_type="vulnerability",
137
+ severity=vuln.get("severity", "medium"),
138
+ description=vuln.get("description", ""),
139
+ tool="wpscan",
140
+ refs=", ".join(vuln.get("references", [])),
141
+ )
142
+ findings_added += 1
143
+
144
+ # Check for wpscan errors
145
+ wpscan_error = detect_tool_error(output, "wpscan")
146
+
147
+ # Count WordPress info as results (version, users, plugins, themes)
148
+ wp_version = parsed.get("version") or parsed.get("wordpress_version")
149
+ users_list = parsed.get("users", [])
150
+ users_found = len(users_list)
151
+ plugins_found = len(parsed.get("plugins", []))
152
+ themes_found = len(parsed.get("themes", []))
153
+ has_wp_info = (
154
+ wp_version or users_found > 0 or plugins_found > 0 or themes_found > 0
155
+ )
156
+
157
+ # Add enumerated users to credentials (for brute force chaining)
158
+ credentials_added = 0
159
+ if users_list and credentials_manager:
160
+ for username in users_list:
161
+ try:
162
+ credentials_manager.add_credential(
163
+ engagement_id=engagement_id,
164
+ host_id=host_id,
165
+ service="wordpress",
166
+ username=username,
167
+ password=None,
168
+ status="untested",
169
+ tool="wpscan",
170
+ )
171
+ credentials_added += 1
172
+ except Exception as cred_err:
173
+ logger.debug(
174
+ f"Could not add credential for {username}: {cred_err}"
175
+ )
176
+
177
+ # Determine status based on results
178
+ if wpscan_error:
179
+ status = STATUS_ERROR
180
+ elif findings_added > 0 or has_wp_info:
181
+ # CVE findings OR WordPress info = successful scan
182
+ status = STATUS_DONE
183
+ else:
184
+ status = STATUS_NO_RESULTS
185
+
186
+ return {
187
+ "tool": "wpscan",
188
+ "status": status,
189
+ "target": target,
190
+ "wp_version": parsed.get("version") or parsed.get("wordpress_version"),
191
+ "plugins_found": len(parsed.get("plugins", {})),
192
+ "themes_found": len(parsed.get("themes", {})),
193
+ "users_found": len(parsed.get("users", [])),
194
+ "findings_added": findings_added,
195
+ "users": parsed.get("users", []), # For hydra chaining
196
+ }
197
+
198
+ except Exception as e:
199
+ logger.error(f"Error parsing wpscan job: {e}")
200
+ return {"error": str(e)}
201
+
202
+ def display_done(
203
+ self,
204
+ job: Dict[str, Any],
205
+ log_path: str,
206
+ show_all: bool = False,
207
+ show_passwords: bool = False,
208
+ ) -> None:
209
+ """Display successful WPScan results."""
210
+ try:
211
+ from souleyez.parsers.wpscan_parser import parse_wpscan_output
212
+
213
+ if not log_path or not os.path.exists(log_path):
214
+ return
215
+
216
+ with open(log_path, "r", encoding="utf-8", errors="replace") as f:
217
+ log_content = f.read()
218
+ parsed = parse_wpscan_output(log_content, job.get("target", ""))
219
+
220
+ # Check if we have any results
221
+ has_results = (
222
+ parsed.get("wordpress_version")
223
+ or parsed.get("findings")
224
+ or parsed.get("plugins")
225
+ or parsed.get("themes")
226
+ or parsed.get("users")
227
+ or parsed.get("info")
228
+ )
229
+
230
+ if not has_results:
231
+ self.display_no_results(job, log_path)
232
+ return
233
+
234
+ # Header
235
+ click.echo(click.style("=" * 70, fg="cyan"))
236
+ click.echo(
237
+ click.style("WPSCAN WORDPRESS SECURITY SCAN", bold=True, fg="cyan")
238
+ )
239
+ click.echo(click.style("=" * 70, fg="cyan"))
240
+ click.echo()
241
+
242
+ # WordPress version with status
243
+ if parsed.get("wordpress_version"):
244
+ version_str = parsed["wordpress_version"]
245
+ version_status = parsed.get("version_status")
246
+ version_date = parsed.get("version_release_date")
247
+
248
+ click.echo(click.style("WordPress Version: ", bold=True), nl=False)
249
+ if version_status == "Insecure":
250
+ click.echo(
251
+ click.style(f"{version_str} (Insecure)", fg="red", bold=True),
252
+ nl=False,
253
+ )
254
+ elif version_status == "Outdated":
255
+ click.echo(
256
+ click.style(
257
+ f"{version_str} (Outdated)", fg="yellow", bold=True
258
+ ),
259
+ nl=False,
260
+ )
261
+ else:
262
+ click.echo(version_str, nl=False)
263
+
264
+ if version_date:
265
+ click.echo(f" - Released {version_date}")
266
+ else:
267
+ click.echo()
268
+ click.echo()
269
+
270
+ # Additional information/findings
271
+ info_items = parsed.get("info", [])
272
+ if info_items:
273
+ click.echo(click.style("Scan Findings:", bold=True))
274
+ for item in info_items:
275
+ title = item.get("title", "Unknown")
276
+ severity = item.get("severity", "info").lower()
277
+ item_type = item.get("type", "info")
278
+
279
+ # Icon based on severity/type
280
+ if severity == "low" or item_type == "warning":
281
+ icon = "!"
282
+ color = "yellow"
283
+ elif item_type == "config":
284
+ icon = "*"
285
+ color = "cyan"
286
+ elif item_type == "disclosure":
287
+ icon = "#"
288
+ color = "yellow"
289
+ elif item_type == "header":
290
+ icon = "?"
291
+ color = "cyan"
292
+ else:
293
+ icon = "i"
294
+ color = "white"
295
+
296
+ click.echo(click.style(f" [{icon}] {title}", fg=color))
297
+
298
+ click.echo()
299
+
300
+ # Vulnerabilities/Findings
301
+ findings = parsed.get("findings", [])
302
+ if findings:
303
+ # Group by severity
304
+ severity_order = ["critical", "high", "medium", "low", "info"]
305
+ severity_groups = {sev: [] for sev in severity_order}
306
+ for finding in findings:
307
+ severity = finding.get("severity", "medium").lower()
308
+ if severity in severity_groups:
309
+ severity_groups[severity].append(finding)
310
+
311
+ click.echo(
312
+ click.style(
313
+ f"Vulnerabilities Found: {len(findings)}", bold=True, fg="red"
314
+ )
315
+ )
316
+ click.echo()
317
+
318
+ max_per_severity = None if show_all else 3
319
+ for severity in severity_order:
320
+ if severity_groups[severity]:
321
+ # Color code by severity
322
+ if severity in ("critical", "high"):
323
+ color = "red"
324
+ elif severity == "medium":
325
+ color = "yellow"
326
+ elif severity == "low":
327
+ color = "green"
328
+ else:
329
+ color = "cyan"
330
+
331
+ click.echo(
332
+ click.style(
333
+ f"[{severity.upper()}] ({len(severity_groups[severity])})",
334
+ bold=True,
335
+ fg=color,
336
+ )
337
+ )
338
+
339
+ display_findings = (
340
+ severity_groups[severity]
341
+ if max_per_severity is None
342
+ else severity_groups[severity][:max_per_severity]
343
+ )
344
+ for finding in display_findings:
345
+ click.echo(
346
+ f" - {finding.get('title', 'Unknown vulnerability')}"
347
+ )
348
+ if finding.get("type") and finding.get("name"):
349
+ click.echo(
350
+ f" Type: {finding['type']} - {finding['name']}"
351
+ )
352
+ if finding.get("fixed_in"):
353
+ click.echo(f" Fixed in: {finding['fixed_in']}")
354
+ if finding.get("references"):
355
+ refs = finding["references"][
356
+ :2
357
+ ] # Show first 2 references
358
+ for ref in refs:
359
+ click.echo(f" Ref: {ref}")
360
+ click.echo()
361
+
362
+ if (
363
+ max_per_severity
364
+ and len(severity_groups[severity]) > max_per_severity
365
+ ):
366
+ click.echo(
367
+ f" ... and {len(severity_groups[severity]) - max_per_severity} more {severity} severity issues"
368
+ )
369
+ click.echo()
370
+
371
+ # Plugins
372
+ plugins = parsed.get("plugins", [])
373
+ if plugins:
374
+ vulnerable_plugins = [p for p in plugins if p.get("vulnerable")]
375
+ click.echo(click.style(f"Plugins Detected: {len(plugins)}", bold=True))
376
+ if vulnerable_plugins:
377
+ click.echo(
378
+ click.style(
379
+ f" ! {len(vulnerable_plugins)} vulnerable", fg="red"
380
+ )
381
+ )
382
+ click.echo()
383
+
384
+ max_plugins = None if show_all else 5
385
+ display_plugins = (
386
+ plugins if max_plugins is None else plugins[:max_plugins]
387
+ )
388
+ for plugin in display_plugins:
389
+ vuln_marker = (
390
+ click.style("!", fg="red") if plugin.get("vulnerable") else " "
391
+ )
392
+ plugin_name = plugin.get("name", "Unknown")
393
+ plugin_version = plugin.get("version", "Unknown version")
394
+ click.echo(f" {vuln_marker} {plugin_name} - {plugin_version}")
395
+
396
+ if max_plugins and len(plugins) > max_plugins:
397
+ click.echo(f" ... and {len(plugins) - max_plugins} more plugins")
398
+ click.echo()
399
+
400
+ # Themes
401
+ themes = parsed.get("themes", [])
402
+ if themes:
403
+ vulnerable_themes = [t for t in themes if t.get("vulnerable")]
404
+ click.echo(click.style(f"Themes Detected: {len(themes)}", bold=True))
405
+ if vulnerable_themes:
406
+ click.echo(
407
+ click.style(
408
+ f" ! {len(vulnerable_themes)} vulnerable", fg="red"
409
+ )
410
+ )
411
+ click.echo()
412
+
413
+ max_themes = None if show_all else 3
414
+ display_themes = themes if max_themes is None else themes[:max_themes]
415
+ for theme in display_themes:
416
+ vuln_marker = (
417
+ click.style("!", fg="red") if theme.get("vulnerable") else " "
418
+ )
419
+ theme_name = theme.get("name", "Unknown")
420
+ theme_version = theme.get("version", "Unknown version")
421
+ click.echo(f" {vuln_marker} {theme_name} - {theme_version}")
422
+
423
+ if max_themes and len(themes) > max_themes:
424
+ click.echo(f" ... and {len(themes) - max_themes} more themes")
425
+ click.echo()
426
+
427
+ # Enumerated users
428
+ users = parsed.get("users", [])
429
+ if users:
430
+ click.echo(
431
+ click.style(
432
+ f"Users Enumerated: {len(users)}", bold=True, fg="yellow"
433
+ )
434
+ )
435
+ max_users = None if show_all else 10
436
+ display_users = users if max_users is None else users[:max_users]
437
+ for user in display_users:
438
+ click.echo(f" - {user}")
439
+ if max_users and len(users) > max_users:
440
+ click.echo(f" ... and {len(users) - max_users} more users")
441
+ click.echo()
442
+
443
+ click.echo(click.style("=" * 70, fg="cyan"))
444
+ click.echo()
445
+
446
+ except Exception as e:
447
+ logger.debug(f"Error in display_done: {e}")
448
+
449
+ def display_warning(
450
+ self,
451
+ job: Dict[str, Any],
452
+ log_path: str,
453
+ log_content: Optional[str] = None,
454
+ ) -> None:
455
+ """Display warning status for WPScan."""
456
+ click.echo(click.style("=" * 70, fg="yellow"))
457
+ click.echo(click.style("[WARNING] WPSCAN", bold=True, fg="yellow"))
458
+ click.echo(click.style("=" * 70, fg="yellow"))
459
+ click.echo()
460
+ click.echo(" Scan completed with warnings. Check raw logs for details.")
461
+ click.echo(" Press [r] to view raw logs.")
462
+ click.echo()
463
+ click.echo(click.style("=" * 70, fg="yellow"))
464
+ click.echo()
465
+
466
+ def display_error(
467
+ self,
468
+ job: Dict[str, Any],
469
+ log_path: str,
470
+ log_content: Optional[str] = None,
471
+ ) -> None:
472
+ """Display error status for WPScan."""
473
+ # Read log if not provided
474
+ if log_content is None and log_path and os.path.exists(log_path):
475
+ try:
476
+ with open(log_path, "r", encoding="utf-8", errors="replace") as f:
477
+ log_content = f.read()
478
+ except Exception:
479
+ log_content = ""
480
+
481
+ click.echo(click.style("=" * 70, fg="red"))
482
+ click.echo(click.style("[ERROR] WPSCAN FAILED", bold=True, fg="red"))
483
+ click.echo(click.style("=" * 70, fg="red"))
484
+ click.echo()
485
+
486
+ # Check for common wpscan errors
487
+ error_msg = None
488
+ if log_content:
489
+ if "The target is NOT running WordPress" in log_content:
490
+ error_msg = "Target is not running WordPress"
491
+ elif "could not resolve" in log_content.lower():
492
+ error_msg = "Could not resolve target hostname"
493
+ elif (
494
+ "Connection refused" in log_content
495
+ or "Unable to connect" in log_content
496
+ ):
497
+ error_msg = "Connection refused - web server may be down"
498
+ elif "timed out" in log_content.lower() or "timeout" in log_content.lower():
499
+ error_msg = "Connection timed out - target may be slow or filtering"
500
+ elif "SSL" in log_content and (
501
+ "error" in log_content.lower() or "fail" in log_content.lower()
502
+ ):
503
+ error_msg = "SSL error - try with --disable-tls-checks"
504
+ elif (
505
+ "api limit" in log_content.lower()
506
+ or "rate limit" in log_content.lower()
507
+ ):
508
+ error_msg = "WPScan API rate limit reached - try again later"
509
+ elif "[!]" in log_content:
510
+ match = re.search(r"\[!\]\s*(.+?)(?:\n|$)", log_content)
511
+ if match:
512
+ error_msg = match.group(1).strip()[:100]
513
+
514
+ if error_msg:
515
+ click.echo(f" {error_msg}")
516
+ else:
517
+ click.echo(" Scan failed - see raw logs for details (press 'r')")
518
+
519
+ click.echo()
520
+ click.echo(click.style("=" * 70, fg="red"))
521
+ click.echo()
522
+
523
+ def display_no_results(
524
+ self,
525
+ job: Dict[str, Any],
526
+ log_path: str,
527
+ ) -> None:
528
+ """Display no_results status for WPScan."""
529
+ click.echo(click.style("=" * 70, fg="cyan"))
530
+ click.echo(click.style("WPSCAN WORDPRESS SECURITY SCAN", bold=True, fg="cyan"))
531
+ click.echo(click.style("=" * 70, fg="cyan"))
532
+ click.echo()
533
+
534
+ if job.get("target"):
535
+ click.echo(click.style(f"Target: {job.get('target')}", bold=True))
536
+ click.echo()
537
+
538
+ click.echo(
539
+ click.style(
540
+ "Result: No WordPress detected or no issues found",
541
+ fg="green",
542
+ bold=True,
543
+ )
544
+ )
545
+ click.echo()
546
+ click.echo(" The scan did not find WordPress or any security issues.")
547
+ click.echo()
548
+ click.echo(click.style("Tips:", dim=True))
549
+ click.echo(" - Verify the target is a WordPress site")
550
+ click.echo(" - Try enumeration: --enumerate ap,at,u")
551
+ click.echo(" - Check API token for vuln database access")
552
+ click.echo()
553
+ click.echo(click.style("=" * 70, fg="cyan"))
554
+ click.echo()
souleyez/history.py CHANGED
@@ -14,7 +14,9 @@ def load_history():
14
14
  return read_json(HISTORY_FILE)
15
15
 
16
16
 
17
- def add_history_entry(target, args, label, logpath, xmlpath=None, tool="nmap", summary=None):
17
+ def add_history_entry(
18
+ target, args, label, logpath, xmlpath=None, tool="nmap", summary=None
19
+ ):
18
20
  """
19
21
  Add an entry to the history.
20
22
  """
@@ -28,7 +30,7 @@ def add_history_entry(target, args, label, logpath, xmlpath=None, tool="nmap", s
28
30
  "label": label,
29
31
  "log": str(logpath),
30
32
  "xml": str(xmlpath) if xmlpath else None,
31
- "summary": summary if isinstance(summary, dict) else None
33
+ "summary": summary if isinstance(summary, dict) else None,
32
34
  }
33
35
  h.insert(0, entry)
34
36
  write_json(HISTORY_FILE, h[:200])
@@ -46,7 +48,7 @@ def get_tools():
46
48
 
47
49
  def _safe_filename_component(s):
48
50
  s = str(s or "")
49
- return "".join(c if (c.isalnum() or c in ('.', '-', '_')) else '_' for c in s)
51
+ return "".join(c if (c.isalnum() or c in (".", "-", "_")) else "_" for c in s)
50
52
 
51
53
 
52
54
  def _make_export_name(entry, ext):
@@ -76,19 +78,33 @@ def export_entry_csv(entry):
76
78
  if not per_host:
77
79
  per_host = [{"addr": "", "up": None, "open": summary.get("open_ports", 0)}]
78
80
 
79
- with open(path, "w", newline='', encoding="utf-8") as csvfile:
81
+ with open(path, "w", newline="", encoding="utf-8") as csvfile:
80
82
  writer = csv.writer(csvfile)
81
- writer.writerow(["timestamp", "tool", "target", "label", "host", "up", "open_ports", "log", "xml"])
83
+ writer.writerow(
84
+ [
85
+ "timestamp",
86
+ "tool",
87
+ "target",
88
+ "label",
89
+ "host",
90
+ "up",
91
+ "open_ports",
92
+ "log",
93
+ "xml",
94
+ ]
95
+ )
82
96
  for h in per_host:
83
- writer.writerow([
84
- entry.get("ts", ""),
85
- entry.get("tool", ""),
86
- entry.get("target", ""),
87
- entry.get("label", ""),
88
- h.get("addr", ""),
89
- bool(h.get("up")) if h.get("up") is not None else "",
90
- h.get("open", 0),
91
- entry.get("log", ""),
92
- entry.get("xml", "")
93
- ])
97
+ writer.writerow(
98
+ [
99
+ entry.get("ts", ""),
100
+ entry.get("tool", ""),
101
+ entry.get("target", ""),
102
+ entry.get("label", ""),
103
+ h.get("addr", ""),
104
+ bool(h.get("up")) if h.get("up") is not None else "",
105
+ h.get("open", 0),
106
+ entry.get("log", ""),
107
+ entry.get("xml", ""),
108
+ ]
109
+ )
94
110
  return path