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
@@ -4,6 +4,7 @@ Wazuh API Client for SoulEyez Detection Validation.
4
4
  Connects to Wazuh Manager API (port 55000) for management operations
5
5
  and Wazuh Indexer API (port 9200) for querying alerts.
6
6
  """
7
+
7
8
  import requests
8
9
  from datetime import datetime, timedelta
9
10
  from typing import List, Dict, Optional, Any
@@ -24,7 +25,7 @@ class WazuhClient:
24
25
  verify_ssl: bool = False,
25
26
  indexer_url: Optional[str] = None,
26
27
  indexer_user: Optional[str] = None,
27
- indexer_password: Optional[str] = None
28
+ indexer_password: Optional[str] = None,
28
29
  ):
29
30
  """
30
31
  Initialize Wazuh API client.
@@ -38,7 +39,7 @@ class WazuhClient:
38
39
  indexer_user: Indexer username (defaults to 'admin')
39
40
  indexer_password: Indexer password (defaults to manager password)
40
41
  """
41
- self.api_url = api_url.rstrip('/')
42
+ self.api_url = api_url.rstrip("/")
42
43
  self.username = username
43
44
  self.password = password
44
45
  self.verify_ssl = verify_ssl
@@ -48,13 +49,13 @@ class WazuhClient:
48
49
  # Derive indexer URL from manager URL if not provided
49
50
  # Manager is on :55000, Indexer is on :9200
50
51
  if indexer_url:
51
- self.indexer_url = indexer_url.rstrip('/')
52
+ self.indexer_url = indexer_url.rstrip("/")
52
53
  else:
53
54
  # Replace port 55000 with 9200
54
- self.indexer_url = self.api_url.replace(':55000', ':9200')
55
+ self.indexer_url = self.api_url.replace(":55000", ":9200")
55
56
 
56
57
  # Indexer credentials (often different from Manager)
57
- self.indexer_user = indexer_user or 'admin'
58
+ self.indexer_user = indexer_user or "admin"
58
59
  self.indexer_password = indexer_password or password
59
60
 
60
61
  def _get_token(self) -> str:
@@ -66,15 +67,12 @@ class WazuhClient:
66
67
  # Authenticate
67
68
  url = f"{self.api_url}/security/user/authenticate"
68
69
  response = requests.post(
69
- url,
70
- auth=(self.username, self.password),
71
- verify=self.verify_ssl,
72
- timeout=30
70
+ url, auth=(self.username, self.password), verify=self.verify_ssl, timeout=30
73
71
  )
74
72
  response.raise_for_status()
75
73
 
76
74
  data = response.json()
77
- self._token = data.get('data', {}).get('token')
75
+ self._token = data.get("data", {}).get("token")
78
76
  # Tokens typically expire in 15 minutes, refresh at 10
79
77
  self._token_expiry = datetime.now() + timedelta(minutes=10)
80
78
 
@@ -88,7 +86,7 @@ class WazuhClient:
88
86
  method: str,
89
87
  endpoint: str,
90
88
  params: Optional[Dict] = None,
91
- json_data: Optional[Dict] = None
89
+ json_data: Optional[Dict] = None,
92
90
  ) -> Dict[str, Any]:
93
91
  """Make authenticated API request."""
94
92
  token = self._get_token()
@@ -96,7 +94,7 @@ class WazuhClient:
96
94
 
97
95
  headers = {
98
96
  "Authorization": f"Bearer {token}",
99
- "Content-Type": "application/json"
97
+ "Content-Type": "application/json",
100
98
  }
101
99
 
102
100
  response = requests.request(
@@ -106,7 +104,7 @@ class WazuhClient:
106
104
  params=params,
107
105
  json=json_data,
108
106
  verify=self.verify_ssl,
109
- timeout=60
107
+ timeout=60,
110
108
  )
111
109
  response.raise_for_status()
112
110
  return response.json()
@@ -128,7 +126,9 @@ class WazuhClient:
128
126
  # Get hostname from agent 000 (manager)
129
127
  hostname = "unknown"
130
128
  try:
131
- agent_result = self._request("GET", "/agents", params={"agents_list": "000"})
129
+ agent_result = self._request(
130
+ "GET", "/agents", params={"agents_list": "000"}
131
+ )
132
132
  agent_items = agent_result.get("data", {}).get("affected_items", [])
133
133
  if agent_items:
134
134
  hostname = agent_items[0].get("name", "unknown")
@@ -139,7 +139,7 @@ class WazuhClient:
139
139
  "connected": True,
140
140
  "version": info.get("version", "unknown"),
141
141
  "cluster": data.get("cluster", {}).get("enabled", False),
142
- "hostname": hostname
142
+ "hostname": hostname,
143
143
  }
144
144
  except requests.exceptions.ConnectionError as e:
145
145
  return {"connected": False, "error": f"Connection failed: {str(e)}"}
@@ -155,7 +155,7 @@ class WazuhClient:
155
155
  agent_ip: Optional[str] = None,
156
156
  rule_ids: Optional[List[int]] = None,
157
157
  limit: int = 100,
158
- search_text: Optional[str] = None
158
+ search_text: Optional[str] = None,
159
159
  ) -> List[Dict[str, Any]]:
160
160
  """
161
161
  Query alerts from Wazuh Indexer (Elasticsearch-based).
@@ -178,9 +178,9 @@ class WazuhClient:
178
178
  if start_time or end_time:
179
179
  time_range = {}
180
180
  if start_time:
181
- time_range["gte"] = start_time.strftime('%Y-%m-%dT%H:%M:%S')
181
+ time_range["gte"] = start_time.strftime("%Y-%m-%dT%H:%M:%S")
182
182
  if end_time:
183
- time_range["lte"] = end_time.strftime('%Y-%m-%dT%H:%M:%S')
183
+ time_range["lte"] = end_time.strftime("%Y-%m-%dT%H:%M:%S")
184
184
  must_clauses.append({"range": {"timestamp": time_range}})
185
185
 
186
186
  # Rule ID filter
@@ -189,28 +189,26 @@ class WazuhClient:
189
189
 
190
190
  # Agent IP filter (check various IP fields)
191
191
  if agent_ip:
192
- must_clauses.append({
193
- "multi_match": {
194
- "query": agent_ip,
195
- "fields": ["agent.ip", "data.srcip", "data.src_ip", "src_ip"]
192
+ must_clauses.append(
193
+ {
194
+ "multi_match": {
195
+ "query": agent_ip,
196
+ "fields": ["agent.ip", "data.srcip", "data.src_ip", "src_ip"],
197
+ }
196
198
  }
197
- })
199
+ )
198
200
 
199
201
  # Free text search
200
202
  if search_text:
201
- must_clauses.append({
202
- "query_string": {"query": f"*{search_text}*"}
203
- })
203
+ must_clauses.append({"query_string": {"query": f"*{search_text}*"}})
204
204
 
205
205
  # Build the query
206
206
  query = {
207
207
  "size": limit,
208
208
  "sort": [{"timestamp": {"order": "desc"}}],
209
209
  "query": {
210
- "bool": {
211
- "must": must_clauses if must_clauses else [{"match_all": {}}]
212
- }
213
- }
210
+ "bool": {"must": must_clauses if must_clauses else [{"match_all": {}}]}
211
+ },
214
212
  }
215
213
 
216
214
  try:
@@ -220,7 +218,7 @@ class WazuhClient:
220
218
  auth=(self.indexer_user, self.indexer_password),
221
219
  json=query,
222
220
  verify=self.verify_ssl,
223
- timeout=30
221
+ timeout=30,
224
222
  )
225
223
  response.raise_for_status()
226
224
  data = response.json()
@@ -239,11 +237,7 @@ class WazuhClient:
239
237
  raise
240
238
 
241
239
  def get_alerts_by_src_ip(
242
- self,
243
- src_ip: str,
244
- start_time: datetime,
245
- end_time: datetime,
246
- limit: int = 100
240
+ self, src_ip: str, start_time: datetime, end_time: datetime, limit: int = 100
247
241
  ) -> List[Dict[str, Any]]:
248
242
  """
249
243
  Get alerts where source IP matches (attacker IP).
@@ -252,10 +246,7 @@ class WazuhClient:
252
246
  we look for alerts triggered by our attack source IP.
253
247
  """
254
248
  return self.get_alerts(
255
- start_time=start_time,
256
- end_time=end_time,
257
- agent_ip=src_ip,
258
- limit=limit
249
+ start_time=start_time, end_time=end_time, agent_ip=src_ip, limit=limit
259
250
  )
260
251
 
261
252
  def get_rules(self, rule_ids: Optional[List[int]] = None) -> List[Dict[str, Any]]:
@@ -267,7 +258,9 @@ class WazuhClient:
267
258
  result = self._request("GET", "/rules", params=params)
268
259
  return result.get("data", {}).get("affected_items", [])
269
260
 
270
- def get_rule_file_content(self, filename: str, relative_dirname: str = None) -> Optional[str]:
261
+ def get_rule_file_content(
262
+ self, filename: str, relative_dirname: str = None
263
+ ) -> Optional[str]:
271
264
  """
272
265
  Get the content of a rule file.
273
266
 
@@ -297,13 +290,13 @@ class WazuhClient:
297
290
  headers=headers,
298
291
  params=params,
299
292
  verify=self.verify_ssl,
300
- timeout=30
293
+ timeout=30,
301
294
  )
302
295
 
303
296
  if response.status_code == 200:
304
297
  # Check if response is JSON or raw text
305
- content_type = response.headers.get('Content-Type', '')
306
- if 'application/json' in content_type:
298
+ content_type = response.headers.get("Content-Type", "")
299
+ if "application/json" in content_type:
307
300
  # JSON response - extract content from data
308
301
  data = response.json()
309
302
  affected_items = data.get("data", {}).get("affected_items", [])
@@ -311,7 +304,7 @@ class WazuhClient:
311
304
  item = affected_items[0]
312
305
  # The content might be in a 'content' field
313
306
  if isinstance(item, dict):
314
- return item.get('content') or item.get('data')
307
+ return item.get("content") or item.get("data")
315
308
  return item if isinstance(item, str) else None
316
309
  else:
317
310
  # Raw text response
@@ -320,7 +313,9 @@ class WazuhClient:
320
313
  except Exception:
321
314
  return None
322
315
 
323
- def get_rule_xml(self, rule_id: int, filename: str, relative_dirname: str = None) -> Optional[str]:
316
+ def get_rule_xml(
317
+ self, rule_id: int, filename: str, relative_dirname: str = None
318
+ ) -> Optional[str]:
324
319
  """
325
320
  Extract a specific rule's XML from a rule file.
326
321
 
@@ -357,10 +352,7 @@ class WazuhClient:
357
352
  # =========================================================================
358
353
 
359
354
  def get_agent_vulnerabilities(
360
- self,
361
- agent_id: str,
362
- severity: Optional[str] = None,
363
- limit: int = 1000
355
+ self, agent_id: str, severity: Optional[str] = None, limit: int = 1000
364
356
  ) -> List[Dict[str, Any]]:
365
357
  """
366
358
  Fetch vulnerabilities for a specific agent from Wazuh Indexer.
@@ -375,9 +367,7 @@ class WazuhClient:
375
367
  Returns:
376
368
  List of vulnerability dictionaries
377
369
  """
378
- must_clauses = [
379
- {"term": {"agent.id": agent_id}}
380
- ]
370
+ must_clauses = [{"term": {"agent.id": agent_id}}]
381
371
 
382
372
  if severity:
383
373
  must_clauses.append({"term": {"vulnerability.severity": severity}})
@@ -385,11 +375,7 @@ class WazuhClient:
385
375
  query = {
386
376
  "size": limit,
387
377
  "sort": [{"vulnerability.severity": {"order": "desc"}}],
388
- "query": {
389
- "bool": {
390
- "must": must_clauses
391
- }
392
- }
378
+ "query": {"bool": {"must": must_clauses}},
393
379
  }
394
380
 
395
381
  try:
@@ -398,13 +384,15 @@ class WazuhClient:
398
384
  auth=(self.indexer_user, self.indexer_password),
399
385
  json=query,
400
386
  verify=self.verify_ssl,
401
- timeout=60
387
+ timeout=60,
402
388
  )
403
389
  response.raise_for_status()
404
390
  data = response.json()
405
391
 
406
392
  hits = data.get("hits", {}).get("hits", [])
407
- return [self._normalize_vulnerability(hit.get("_source", {})) for hit in hits]
393
+ return [
394
+ self._normalize_vulnerability(hit.get("_source", {})) for hit in hits
395
+ ]
408
396
 
409
397
  except requests.exceptions.ConnectionError:
410
398
  return []
@@ -417,7 +405,7 @@ class WazuhClient:
417
405
  self,
418
406
  severity: Optional[str] = None,
419
407
  agent_ids: Optional[List[str]] = None,
420
- limit: int = 5000
408
+ limit: int = 5000,
421
409
  ) -> List[Dict[str, Any]]:
422
410
  """
423
411
  Fetch all vulnerabilities from Wazuh Indexer.
@@ -442,13 +430,11 @@ class WazuhClient:
442
430
  "size": limit,
443
431
  "sort": [
444
432
  {"vulnerability.severity": {"order": "desc"}},
445
- {"agent.id": {"order": "asc"}}
433
+ {"agent.id": {"order": "asc"}},
446
434
  ],
447
435
  "query": {
448
- "bool": {
449
- "must": must_clauses if must_clauses else [{"match_all": {}}]
450
- }
451
- }
436
+ "bool": {"must": must_clauses if must_clauses else [{"match_all": {}}]}
437
+ },
452
438
  }
453
439
 
454
440
  try:
@@ -457,13 +443,15 @@ class WazuhClient:
457
443
  auth=(self.indexer_user, self.indexer_password),
458
444
  json=query,
459
445
  verify=self.verify_ssl,
460
- timeout=120
446
+ timeout=120,
461
447
  )
462
448
  response.raise_for_status()
463
449
  data = response.json()
464
450
 
465
451
  hits = data.get("hits", {}).get("hits", [])
466
- return [self._normalize_vulnerability(hit.get("_source", {})) for hit in hits]
452
+ return [
453
+ self._normalize_vulnerability(hit.get("_source", {})) for hit in hits
454
+ ]
467
455
 
468
456
  except requests.exceptions.ConnectionError:
469
457
  return []
@@ -491,12 +479,10 @@ class WazuhClient:
491
479
  "by_severity": {
492
480
  "terms": {"field": "vulnerability.severity", "size": 10}
493
481
  }
494
- }
482
+ },
495
483
  },
496
- "total": {
497
- "value_count": {"field": "vulnerability.id"}
498
- }
499
- }
484
+ "total": {"value_count": {"field": "vulnerability.id"}},
485
+ },
500
486
  }
501
487
 
502
488
  try:
@@ -505,7 +491,7 @@ class WazuhClient:
505
491
  auth=(self.indexer_user, self.indexer_password),
506
492
  json=query,
507
493
  verify=self.verify_ssl,
508
- timeout=30
494
+ timeout=30,
509
495
  )
510
496
  response.raise_for_status()
511
497
  data = response.json()
@@ -523,15 +509,17 @@ class WazuhClient:
523
509
  agent_id = bucket["key"]
524
510
  agent_breakdown[agent_id] = {
525
511
  "total": bucket["doc_count"],
526
- "by_severity": {}
512
+ "by_severity": {},
527
513
  }
528
514
  for sev_bucket in bucket.get("by_severity", {}).get("buckets", []):
529
- agent_breakdown[agent_id]["by_severity"][sev_bucket["key"]] = sev_bucket["doc_count"]
515
+ agent_breakdown[agent_id]["by_severity"][sev_bucket["key"]] = (
516
+ sev_bucket["doc_count"]
517
+ )
530
518
 
531
519
  return {
532
520
  "total": aggs.get("total", {}).get("value", 0),
533
521
  "by_severity": severity_counts,
534
- "by_agent": agent_breakdown
522
+ "by_agent": agent_breakdown,
535
523
  }
536
524
 
537
525
  except requests.exceptions.ConnectionError:
@@ -551,25 +539,19 @@ class WazuhClient:
551
539
  f"{self.indexer_url}/wazuh-states-vulnerabilities-*/_count",
552
540
  auth=(self.indexer_user, self.indexer_password),
553
541
  verify=self.verify_ssl,
554
- timeout=10
542
+ timeout=10,
555
543
  )
556
544
 
557
545
  if response.status_code == 200:
558
546
  data = response.json()
559
- return {
560
- "accessible": True,
561
- "count": data.get("count", 0)
562
- }
547
+ return {"accessible": True, "count": data.get("count", 0)}
563
548
  elif response.status_code == 404:
564
549
  return {
565
550
  "accessible": False,
566
- "error": "Vulnerability index not found. Ensure vulnerability detection is enabled in Wazuh."
551
+ "error": "Vulnerability index not found. Ensure vulnerability detection is enabled in Wazuh.",
567
552
  }
568
553
  else:
569
- return {
570
- "accessible": False,
571
- "error": f"HTTP {response.status_code}"
572
- }
554
+ return {"accessible": False, "error": f"HTTP {response.status_code}"}
573
555
 
574
556
  except requests.exceptions.ConnectionError as e:
575
557
  return {"accessible": False, "error": f"Connection failed: {str(e)}"}
@@ -609,7 +591,6 @@ class WazuhClient:
609
591
  "agent_id": agent.get("id"),
610
592
  "agent_name": agent.get("name"),
611
593
  "agent_ip": agent.get("ip"),
612
-
613
594
  # Vulnerability info
614
595
  "cve_id": vuln.get("id"),
615
596
  "name": vuln.get("title") or vuln.get("description", "")[:100],
@@ -617,18 +598,14 @@ class WazuhClient:
617
598
  "cvss_score": cvss_score,
618
599
  "cvss_version": cvss_version,
619
600
  "published_date": vuln.get("published"),
620
-
621
601
  # Package info
622
602
  "package_name": package.get("name"),
623
603
  "package_version": package.get("version"),
624
604
  "package_architecture": package.get("architecture"),
625
-
626
605
  # References
627
606
  "reference_urls": vuln.get("reference", []),
628
-
629
607
  # Detection time
630
608
  "detection_time": vuln.get("detected_at"),
631
-
632
609
  # Raw data for debugging
633
- "raw_data": doc
610
+ "raw_data": doc,
634
611
  }