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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9526 -2879
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1239 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2200 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +563 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +408 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +371 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +292 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +854 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +173 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +223 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +22827 -10678
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
  353. souleyez-2.43.34.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ SIEM Configuration Management.
4
4
  Stores SIEM connection settings with encrypted credentials.
5
5
  Supports multiple SIEM platforms: Wazuh, Splunk, Elastic, Sentinel.
6
6
  """
7
+
7
8
  import json
8
9
  from typing import Optional, Dict, Any, List
9
10
  from pathlib import Path
@@ -11,7 +12,7 @@ from souleyez.storage.database import get_db
11
12
  from souleyez.storage.crypto import get_crypto_manager
12
13
 
13
14
  # Supported SIEM types (Open Source first, then Commercial)
14
- SIEM_TYPES = ['wazuh', 'elastic', 'splunk', 'sentinel', 'google_secops']
15
+ SIEM_TYPES = ["wazuh", "elastic", "splunk", "sentinel", "google_secops"]
15
16
 
16
17
 
17
18
  class WazuhConfig:
@@ -22,7 +23,9 @@ class WazuhConfig:
22
23
  """
23
24
 
24
25
  @staticmethod
25
- def get_config(engagement_id: int, siem_type: str = None) -> Optional[Dict[str, Any]]:
26
+ def get_config(
27
+ engagement_id: int, siem_type: str = None
28
+ ) -> Optional[Dict[str, Any]]:
26
29
  """
27
30
  Get SIEM config for an engagement.
28
31
 
@@ -40,35 +43,44 @@ class WazuhConfig:
40
43
  # Check if new columns exist (migration 025+)
41
44
  cursor.execute("PRAGMA table_info(wazuh_config)")
42
45
  columns = [col[1] for col in cursor.fetchall()]
43
- has_new_columns = 'siem_type' in columns
46
+ has_new_columns = "siem_type" in columns
44
47
 
45
48
  # Query with or without new columns
46
49
  if has_new_columns:
47
50
  if siem_type:
48
- cursor.execute("""
51
+ cursor.execute(
52
+ """
49
53
  SELECT api_url, api_user, api_password, indexer_url, indexer_user,
50
54
  indexer_password, verify_ssl, enabled, siem_type, config_json
51
55
  FROM wazuh_config
52
56
  WHERE engagement_id = ? AND siem_type = ?
53
- """, (engagement_id, siem_type))
57
+ """,
58
+ (engagement_id, siem_type),
59
+ )
54
60
  else:
55
61
  # Get most recently updated config (the "current" selected SIEM)
56
62
  # Not filtering by enabled - user may have selected but not configured yet
57
- cursor.execute("""
63
+ cursor.execute(
64
+ """
58
65
  SELECT api_url, api_user, api_password, indexer_url, indexer_user,
59
66
  indexer_password, verify_ssl, enabled, siem_type, config_json
60
67
  FROM wazuh_config
61
68
  WHERE engagement_id = ?
62
69
  ORDER BY updated_at DESC
63
70
  LIMIT 1
64
- """, (engagement_id,))
71
+ """,
72
+ (engagement_id,),
73
+ )
65
74
  else:
66
- cursor.execute("""
75
+ cursor.execute(
76
+ """
67
77
  SELECT api_url, api_user, api_password, indexer_url, indexer_user,
68
78
  indexer_password, verify_ssl, enabled
69
79
  FROM wazuh_config
70
80
  WHERE engagement_id = ?
71
- """, (engagement_id,))
81
+ """,
82
+ (engagement_id,),
83
+ )
72
84
 
73
85
  row = cursor.fetchone()
74
86
  if not row:
@@ -107,7 +119,7 @@ class WazuhConfig:
107
119
  # Decrypt any encrypted fields in extra config
108
120
  crypto = get_crypto_manager()
109
121
  if crypto and extra_config:
110
- for key in ['password', 'client_secret', 'api_key', 'token']:
122
+ for key in ["password", "client_secret", "api_key", "token"]:
111
123
  if key in extra_config and extra_config[key]:
112
124
  try:
113
125
  extra_config[key] = crypto.decrypt(extra_config[key])
@@ -118,9 +130,9 @@ class WazuhConfig:
118
130
  pass
119
131
 
120
132
  # Map Wazuh fields to generic names for SIEMFactory compatibility
121
- if config['siem_type'] == 'wazuh':
122
- config['username'] = config.get('api_user')
123
- config['password'] = config.get('api_password')
133
+ if config["siem_type"] == "wazuh":
134
+ config["username"] = config.get("api_user")
135
+ config["password"] = config.get("api_password")
124
136
 
125
137
  return config
126
138
 
@@ -130,14 +142,12 @@ class WazuhConfig:
130
142
  cursor.execute("PRAGMA table_info(wazuh_config)")
131
143
  columns = [col[1] for col in cursor.fetchall()]
132
144
 
133
- if 'siem_type' not in columns:
145
+ if "siem_type" not in columns:
134
146
  cursor.execute(
135
147
  "ALTER TABLE wazuh_config ADD COLUMN siem_type TEXT DEFAULT 'wazuh'"
136
148
  )
137
- if 'config_json' not in columns:
138
- cursor.execute(
139
- "ALTER TABLE wazuh_config ADD COLUMN config_json TEXT"
140
- )
149
+ if "config_json" not in columns:
150
+ cursor.execute("ALTER TABLE wazuh_config ADD COLUMN config_json TEXT")
141
151
  conn.commit()
142
152
 
143
153
  @staticmethod
@@ -152,7 +162,7 @@ class WazuhConfig:
152
162
  verify_ssl: bool = False,
153
163
  enabled: bool = True,
154
164
  siem_type: str = "wazuh",
155
- config_json: Optional[Dict[str, Any]] = None
165
+ config_json: Optional[Dict[str, Any]] = None,
156
166
  ) -> bool:
157
167
  """
158
168
  Save SIEM config for an engagement.
@@ -199,15 +209,18 @@ class WazuhConfig:
199
209
  try:
200
210
  crypto = get_crypto_manager()
201
211
  if crypto:
202
- for key in ['password', 'client_secret', 'api_key', 'token']:
212
+ for key in ["password", "client_secret", "api_key", "token"]:
203
213
  if key in encrypted_config and encrypted_config[key]:
204
- encrypted_config[key] = crypto.encrypt(encrypted_config[key])
214
+ encrypted_config[key] = crypto.encrypt(
215
+ encrypted_config[key]
216
+ )
205
217
  except Exception:
206
218
  pass
207
219
  config_json_str = json.dumps(encrypted_config)
208
220
 
209
221
  # Upsert config - keyed by (engagement_id, siem_type) for multi-SIEM support
210
- cursor.execute("""
222
+ cursor.execute(
223
+ """
211
224
  INSERT INTO wazuh_config (
212
225
  engagement_id, api_url, api_user, api_password, indexer_url,
213
226
  indexer_user, indexer_password, verify_ssl, enabled,
@@ -225,21 +238,28 @@ class WazuhConfig:
225
238
  enabled = excluded.enabled,
226
239
  config_json = excluded.config_json,
227
240
  updated_at = CURRENT_TIMESTAMP
228
- """, (
229
- engagement_id, api_url, api_user, encrypted_api_password,
230
- indexer_url, indexer_user or 'admin', encrypted_indexer_password,
231
- verify_ssl, enabled, siem_type, config_json_str
232
- ))
241
+ """,
242
+ (
243
+ engagement_id,
244
+ api_url,
245
+ api_user,
246
+ encrypted_api_password,
247
+ indexer_url,
248
+ indexer_user or "admin",
249
+ encrypted_indexer_password,
250
+ verify_ssl,
251
+ enabled,
252
+ siem_type,
253
+ config_json_str,
254
+ ),
255
+ )
233
256
 
234
257
  conn.commit()
235
258
  return True
236
259
 
237
260
  @staticmethod
238
261
  def save_siem_config(
239
- engagement_id: int,
240
- siem_type: str,
241
- config: Dict[str, Any],
242
- enabled: bool = True
262
+ engagement_id: int, siem_type: str, config: Dict[str, Any], enabled: bool = True
243
263
  ) -> bool:
244
264
  """
245
265
  Save configuration for any SIEM type.
@@ -252,29 +272,29 @@ class WazuhConfig:
252
272
  config: SIEM-specific configuration dict
253
273
  enabled: Whether integration is enabled
254
274
  """
255
- if siem_type == 'wazuh':
275
+ if siem_type == "wazuh":
256
276
  # Use existing Wazuh fields for backwards compatibility
257
277
  return WazuhConfig.save_config(
258
278
  engagement_id=engagement_id,
259
- api_url=config.get('api_url', ''),
260
- api_user=config.get('username', config.get('api_user', '')),
261
- api_password=config.get('password', config.get('api_password', '')),
262
- indexer_url=config.get('indexer_url'),
263
- indexer_user=config.get('indexer_user'),
264
- indexer_password=config.get('indexer_password'),
265
- verify_ssl=config.get('verify_ssl', False),
279
+ api_url=config.get("api_url", ""),
280
+ api_user=config.get("username", config.get("api_user", "")),
281
+ api_password=config.get("password", config.get("api_password", "")),
282
+ indexer_url=config.get("indexer_url"),
283
+ indexer_user=config.get("indexer_user"),
284
+ indexer_password=config.get("indexer_password"),
285
+ verify_ssl=config.get("verify_ssl", False),
266
286
  enabled=enabled,
267
- siem_type='wazuh'
287
+ siem_type="wazuh",
268
288
  )
269
289
  else:
270
290
  # For other SIEMs, store config in config_json
271
291
  return WazuhConfig.save_config(
272
292
  engagement_id=engagement_id,
273
- api_url=config.get('api_url', config.get('elasticsearch_url', '')),
274
- verify_ssl=config.get('verify_ssl', False),
293
+ api_url=config.get("api_url", config.get("elasticsearch_url", "")),
294
+ verify_ssl=config.get("verify_ssl", False),
275
295
  enabled=enabled,
276
296
  siem_type=siem_type,
277
- config_json=config
297
+ config_json=config,
278
298
  )
279
299
 
280
300
  @staticmethod
@@ -292,10 +312,12 @@ class WazuhConfig:
292
312
  if siem_type:
293
313
  cursor.execute(
294
314
  "DELETE FROM wazuh_config WHERE engagement_id = ? AND siem_type = ?",
295
- (engagement_id, siem_type)
315
+ (engagement_id, siem_type),
296
316
  )
297
317
  else:
298
- cursor.execute("DELETE FROM wazuh_config WHERE engagement_id = ?", (engagement_id,))
318
+ cursor.execute(
319
+ "DELETE FROM wazuh_config WHERE engagement_id = ?", (engagement_id,)
320
+ )
299
321
  conn.commit()
300
322
  return cursor.rowcount > 0
301
323
 
@@ -320,28 +342,34 @@ class WazuhConfig:
320
342
  cursor.execute("PRAGMA table_info(wazuh_config)")
321
343
  columns = [col[1] for col in cursor.fetchall()]
322
344
 
323
- if 'siem_type' not in columns:
345
+ if "siem_type" not in columns:
324
346
  # Old schema - only one config possible
325
- cursor.execute("""
347
+ cursor.execute(
348
+ """
326
349
  SELECT 'wazuh' as siem_type, enabled, api_url
327
350
  FROM wazuh_config
328
351
  WHERE engagement_id = ?
329
- """, (engagement_id,))
352
+ """,
353
+ (engagement_id,),
354
+ )
330
355
  else:
331
- cursor.execute("""
356
+ cursor.execute(
357
+ """
332
358
  SELECT siem_type, enabled, api_url, updated_at
333
359
  FROM wazuh_config
334
360
  WHERE engagement_id = ?
335
361
  ORDER BY siem_type
336
- """, (engagement_id,))
362
+ """,
363
+ (engagement_id,),
364
+ )
337
365
 
338
366
  rows = cursor.fetchall()
339
367
  return [
340
368
  {
341
- 'siem_type': row[0],
342
- 'enabled': bool(row[1]),
343
- 'api_url': row[2],
344
- 'updated_at': row[3] if len(row) > 3 else None
369
+ "siem_type": row[0],
370
+ "enabled": bool(row[1]),
371
+ "api_url": row[2],
372
+ "updated_at": row[3] if len(row) > 3 else None,
345
373
  }
346
374
  for row in rows
347
375
  ]
@@ -373,8 +401,8 @@ class WazuhConfig:
373
401
  """
374
402
  config = WazuhConfig.get_config(engagement_id)
375
403
  if config:
376
- return config.get('siem_type', 'wazuh')
377
- return 'wazuh'
404
+ return config.get("siem_type", "wazuh")
405
+ return "wazuh"
378
406
 
379
407
  @staticmethod
380
408
  def set_current_siem(engagement_id: int, siem_type: str) -> bool:
@@ -395,7 +423,7 @@ class WazuhConfig:
395
423
  cursor = conn.cursor()
396
424
  cursor.execute(
397
425
  "UPDATE wazuh_config SET updated_at = CURRENT_TIMESTAMP WHERE engagement_id = ? AND siem_type = ?",
398
- (engagement_id, siem_type)
426
+ (engagement_id, siem_type),
399
427
  )
400
428
  conn.commit()
401
429
  return cursor.rowcount > 0
@@ -17,11 +17,7 @@ class WazuhHostMapper:
17
17
  def __init__(self):
18
18
  self.db = get_db()
19
19
 
20
- def map_agent_to_host(
21
- self,
22
- engagement_id: int,
23
- agent_ip: str
24
- ) -> Optional[int]:
20
+ def map_agent_to_host(self, engagement_id: int, agent_ip: str) -> Optional[int]:
25
21
  """
26
22
  Find SoulEyez host_id matching an agent IP.
27
23
 
@@ -42,7 +38,7 @@ class WazuhHostMapper:
42
38
  result = self.db.execute_one(query, (engagement_id, agent_ip))
43
39
 
44
40
  if result:
45
- return result['id']
41
+ return result["id"]
46
42
 
47
43
  return None
48
44
 
@@ -66,7 +62,7 @@ class WazuhHostMapper:
66
62
 
67
63
  mapping = {}
68
64
  for agent in agents:
69
- agent_ip = agent.get('agent_ip')
65
+ agent_ip = agent.get("agent_ip")
70
66
  if agent_ip:
71
67
  host_id = self.map_agent_to_host(engagement_id, agent_ip)
72
68
  mapping[agent_ip] = host_id
@@ -78,10 +74,7 @@ class WazuhHostMapper:
78
74
  return mapping
79
75
 
80
76
  def _update_vuln_host_mapping(
81
- self,
82
- engagement_id: int,
83
- agent_ip: str,
84
- host_id: int
77
+ self, engagement_id: int, agent_ip: str, host_id: int
85
78
  ) -> int:
86
79
  """
87
80
  Update all vulnerabilities from an agent IP with the host_id.
@@ -103,7 +96,7 @@ class WazuhHostMapper:
103
96
  WHERE engagement_id = ? AND agent_ip = ? AND host_id = ?
104
97
  """
105
98
  result = self.db.execute_one(count_query, (engagement_id, agent_ip, host_id))
106
- return result.get('count', 0) if result else 0
99
+ return result.get("count", 0) if result else 0
107
100
 
108
101
  def get_unmapped_agents(self, engagement_id: int) -> List[Dict[str, any]]:
109
102
  """
@@ -167,18 +160,15 @@ class WazuhHostMapper:
167
160
 
168
161
  if result:
169
162
  return {
170
- 'mapped': result.get('mapped', 0) or 0,
171
- 'unmapped': result.get('unmapped', 0) or 0,
172
- 'total': result.get('total', 0) or 0
163
+ "mapped": result.get("mapped", 0) or 0,
164
+ "unmapped": result.get("unmapped", 0) or 0,
165
+ "total": result.get("total", 0) or 0,
173
166
  }
174
167
 
175
- return {'mapped': 0, 'unmapped': 0, 'total': 0}
168
+ return {"mapped": 0, "unmapped": 0, "total": 0}
176
169
 
177
170
  def manual_map(
178
- self,
179
- engagement_id: int,
180
- agent_ip: str,
181
- host_id: int
171
+ self, engagement_id: int, agent_ip: str, host_id: int
182
172
  ) -> Tuple[bool, int]:
183
173
  """
184
174
  Manually map an agent IP to a host.
@@ -225,7 +215,7 @@ class WazuhHostMapper:
225
215
  WHERE engagement_id = ? AND agent_ip = ? AND host_id IS NULL
226
216
  """
227
217
  result = self.db.execute_one(count_query, (engagement_id, agent_ip))
228
- return result.get('count', 0) if result else 0
218
+ return result.get("count", 0) if result else 0
229
219
 
230
220
  def suggest_mappings(self, engagement_id: int) -> List[Dict[str, any]]:
231
221
  """
@@ -250,38 +240,40 @@ class WazuhHostMapper:
250
240
  suggestions = []
251
241
 
252
242
  for agent in unmapped:
253
- agent_ip = agent.get('agent_ip')
243
+ agent_ip = agent.get("agent_ip")
254
244
  if not agent_ip:
255
245
  continue
256
246
 
257
247
  # Try to find hosts in same subnet
258
- agent_parts = agent_ip.split('.')
248
+ agent_parts = agent_ip.split(".")
259
249
  if len(agent_parts) != 4:
260
250
  continue
261
251
 
262
- agent_subnet = '.'.join(agent_parts[:3])
252
+ agent_subnet = ".".join(agent_parts[:3])
263
253
 
264
254
  for host in hosts:
265
- host_ip = host.get('ip_address')
255
+ host_ip = host.get("ip_address")
266
256
  if not host_ip:
267
257
  continue
268
258
 
269
- host_parts = host_ip.split('.')
259
+ host_parts = host_ip.split(".")
270
260
  if len(host_parts) != 4:
271
261
  continue
272
262
 
273
- host_subnet = '.'.join(host_parts[:3])
263
+ host_subnet = ".".join(host_parts[:3])
274
264
 
275
265
  # Same subnet = possible match
276
266
  if agent_subnet == host_subnet:
277
- suggestions.append({
278
- 'agent_ip': agent_ip,
279
- 'agent_name': agent.get('agent_name'),
280
- 'suggested_host_id': host['id'],
281
- 'suggested_host_ip': host_ip,
282
- 'suggested_host_name': host.get('hostname'),
283
- 'confidence': 'medium',
284
- 'reason': 'Same subnet'
285
- })
267
+ suggestions.append(
268
+ {
269
+ "agent_ip": agent_ip,
270
+ "agent_name": agent.get("agent_name"),
271
+ "suggested_host_id": host["id"],
272
+ "suggested_host_ip": host_ip,
273
+ "suggested_host_name": host.get("hostname"),
274
+ "confidence": "medium",
275
+ "reason": "Same subnet",
276
+ }
277
+ )
286
278
 
287
279
  return suggestions