souleyez 2.43.29__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 +22783 -10678
  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.29.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.29.dist-info → souleyez-2.43.32.dist-info}/WHEEL +1 -1
  353. souleyez-2.43.29.dist-info/RECORD +0 -379
  354. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/entry_points.txt +0 -0
  355. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/licenses/LICENSE +0 -0
  356. {souleyez-2.43.29.dist-info → souleyez-2.43.32.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,7 @@ logger = get_logger(__name__)
18
18
  @dataclass
19
19
  class VulnGap:
20
20
  """Represents a vulnerability gap between detection sources."""
21
+
21
22
  cve_id: str
22
23
  severity: str
23
24
  host_ip: str
@@ -31,6 +32,7 @@ class VulnGap:
31
32
  @dataclass
32
33
  class GapAnalysisResult:
33
34
  """Result of gap analysis."""
35
+
34
36
  wazuh_total: int = 0
35
37
  scan_total: int = 0
36
38
  wazuh_only: List[VulnGap] = field(default_factory=list)
@@ -71,24 +73,30 @@ class GapAnalyzer:
71
73
  result.scan_total = len(scan_findings)
72
74
 
73
75
  # Build lookup sets
74
- wazuh_cve_hosts = {(v['cve_id'], v['host_ip']): v for v in wazuh_vulns if v.get('cve_id')}
75
- scan_cve_hosts = {(f['cve_id'], f['host_ip']): f for f in scan_findings if f.get('cve_id')}
76
+ wazuh_cve_hosts = {
77
+ (v["cve_id"], v["host_ip"]): v for v in wazuh_vulns if v.get("cve_id")
78
+ }
79
+ scan_cve_hosts = {
80
+ (f["cve_id"], f["host_ip"]): f for f in scan_findings if f.get("cve_id")
81
+ }
76
82
 
77
83
  # Find confirmed (both sources)
78
84
  for key, wazuh_v in wazuh_cve_hosts.items():
79
85
  cve_id, host_ip = key
80
86
  if key in scan_cve_hosts:
81
87
  scan_f = scan_cve_hosts[key]
82
- result.confirmed.append(VulnGap(
83
- cve_id=cve_id,
84
- severity=wazuh_v.get('severity', 'Medium'),
85
- host_ip=host_ip,
86
- source='both',
87
- wazuh_details=wazuh_v,
88
- scan_details=scan_f,
89
- recommendation="High confidence - confirmed by both sources",
90
- confidence='high'
91
- ))
88
+ result.confirmed.append(
89
+ VulnGap(
90
+ cve_id=cve_id,
91
+ severity=wazuh_v.get("severity", "Medium"),
92
+ host_ip=host_ip,
93
+ source="both",
94
+ wazuh_details=wazuh_v,
95
+ scan_details=scan_f,
96
+ recommendation="High confidence - confirmed by both sources",
97
+ confidence="high",
98
+ )
99
+ )
92
100
 
93
101
  # Find Wazuh-only (scan missed)
94
102
  for key, wazuh_v in wazuh_cve_hosts.items():
@@ -97,29 +105,33 @@ class GapAnalyzer:
97
105
  # Check if any scan found this CVE on any host
98
106
  cve_found_elsewhere = any(c == cve_id for c, _ in scan_cve_hosts.keys())
99
107
 
100
- result.wazuh_only.append(VulnGap(
101
- cve_id=cve_id,
102
- severity=wazuh_v.get('severity', 'Medium'),
103
- host_ip=host_ip,
104
- source='wazuh',
105
- wazuh_details=wazuh_v,
106
- recommendation=self._get_scan_recommendation(cve_id, wazuh_v),
107
- confidence='medium' if cve_found_elsewhere else 'high'
108
- ))
108
+ result.wazuh_only.append(
109
+ VulnGap(
110
+ cve_id=cve_id,
111
+ severity=wazuh_v.get("severity", "Medium"),
112
+ host_ip=host_ip,
113
+ source="wazuh",
114
+ wazuh_details=wazuh_v,
115
+ recommendation=self._get_scan_recommendation(cve_id, wazuh_v),
116
+ confidence="medium" if cve_found_elsewhere else "high",
117
+ )
118
+ )
109
119
 
110
120
  # Find scan-only (Wazuh missed)
111
121
  for key, scan_f in scan_cve_hosts.items():
112
122
  cve_id, host_ip = key
113
123
  if key not in wazuh_cve_hosts:
114
- result.scan_only.append(VulnGap(
115
- cve_id=cve_id,
116
- severity=scan_f.get('severity', 'medium'),
117
- host_ip=host_ip,
118
- source='scan',
119
- scan_details=scan_f,
120
- recommendation="Wazuh agent may not have detection rule for this CVE",
121
- confidence='medium'
122
- ))
124
+ result.scan_only.append(
125
+ VulnGap(
126
+ cve_id=cve_id,
127
+ severity=scan_f.get("severity", "medium"),
128
+ host_ip=host_ip,
129
+ source="scan",
130
+ scan_details=scan_f,
131
+ recommendation="Wazuh agent may not have detection rule for this CVE",
132
+ confidence="medium",
133
+ )
134
+ )
123
135
 
124
136
  # Calculate coverage percentage
125
137
  if result.wazuh_total > 0:
@@ -136,12 +148,18 @@ class GapAnalyzer:
136
148
  result = self.analyze()
137
149
  return [
138
150
  {
139
- 'cve_id': gap.cve_id,
140
- 'severity': gap.severity,
141
- 'host_ip': gap.host_ip,
142
- 'package_name': gap.wazuh_details.get('package_name') if gap.wazuh_details else None,
143
- 'package_version': gap.wazuh_details.get('package_version') if gap.wazuh_details else None,
144
- 'recommendation': gap.recommendation
151
+ "cve_id": gap.cve_id,
152
+ "severity": gap.severity,
153
+ "host_ip": gap.host_ip,
154
+ "package_name": (
155
+ gap.wazuh_details.get("package_name") if gap.wazuh_details else None
156
+ ),
157
+ "package_version": (
158
+ gap.wazuh_details.get("package_version")
159
+ if gap.wazuh_details
160
+ else None
161
+ ),
162
+ "recommendation": gap.recommendation,
145
163
  }
146
164
  for gap in result.wazuh_only
147
165
  ]
@@ -158,11 +176,11 @@ class GapAnalyzer:
158
176
  result = self.analyze()
159
177
  return [
160
178
  {
161
- 'cve_id': gap.cve_id,
162
- 'severity': gap.severity,
163
- 'host_ip': gap.host_ip,
164
- 'tool': gap.scan_details.get('tool') if gap.scan_details else None,
165
- 'recommendation': gap.recommendation
179
+ "cve_id": gap.cve_id,
180
+ "severity": gap.severity,
181
+ "host_ip": gap.host_ip,
182
+ "tool": gap.scan_details.get("tool") if gap.scan_details else None,
183
+ "recommendation": gap.recommendation,
166
184
  }
167
185
  for gap in result.scan_only
168
186
  ]
@@ -174,12 +192,14 @@ class GapAnalyzer:
174
192
  result = self.analyze()
175
193
  return [
176
194
  {
177
- 'cve_id': gap.cve_id,
178
- 'severity': gap.severity,
179
- 'host_ip': gap.host_ip,
180
- 'package_name': gap.wazuh_details.get('package_name') if gap.wazuh_details else None,
181
- 'scan_tool': gap.scan_details.get('tool') if gap.scan_details else None,
182
- 'confidence': 'high'
195
+ "cve_id": gap.cve_id,
196
+ "severity": gap.severity,
197
+ "host_ip": gap.host_ip,
198
+ "package_name": (
199
+ gap.wazuh_details.get("package_name") if gap.wazuh_details else None
200
+ ),
201
+ "scan_tool": gap.scan_details.get("tool") if gap.scan_details else None,
202
+ "confidence": "high",
183
203
  }
184
204
  for gap in result.confirmed
185
205
  ]
@@ -194,13 +214,13 @@ class GapAnalyzer:
194
214
  result = self.analyze()
195
215
 
196
216
  return {
197
- 'wazuh_total': result.wazuh_total,
198
- 'scan_total': result.scan_total,
199
- 'wazuh_only_count': len(result.wazuh_only),
200
- 'scan_only_count': len(result.scan_only),
201
- 'confirmed_count': len(result.confirmed),
202
- 'coverage_pct': round(result.coverage_pct, 1),
203
- 'by_severity': self._get_severity_breakdown(result)
217
+ "wazuh_total": result.wazuh_total,
218
+ "scan_total": result.scan_total,
219
+ "wazuh_only_count": len(result.wazuh_only),
220
+ "scan_only_count": len(result.scan_only),
221
+ "confirmed_count": len(result.confirmed),
222
+ "coverage_pct": round(result.coverage_pct, 1),
223
+ "by_severity": self._get_severity_breakdown(result),
204
224
  }
205
225
 
206
226
  def get_actionable_gaps(self, limit: int = 20) -> List[Dict[str, Any]]:
@@ -212,22 +232,29 @@ class GapAnalyzer:
212
232
  result = self.analyze()
213
233
 
214
234
  # Sort by severity
215
- severity_order = {'Critical': 0, 'High': 1, 'Medium': 2, 'Low': 3}
235
+ severity_order = {"Critical": 0, "High": 1, "Medium": 2, "Low": 3}
216
236
 
217
237
  sorted_gaps = sorted(
218
- result.wazuh_only,
219
- key=lambda g: severity_order.get(g.severity, 4)
238
+ result.wazuh_only, key=lambda g: severity_order.get(g.severity, 4)
220
239
  )
221
240
 
222
241
  return [
223
242
  {
224
- 'cve_id': gap.cve_id,
225
- 'severity': gap.severity,
226
- 'host_ip': gap.host_ip,
227
- 'package': gap.wazuh_details.get('package_name') if gap.wazuh_details else None,
228
- 'version': gap.wazuh_details.get('package_version') if gap.wazuh_details else None,
229
- 'recommendation': gap.recommendation,
230
- 'priority': 'high' if gap.severity in ['Critical', 'High'] else 'medium'
243
+ "cve_id": gap.cve_id,
244
+ "severity": gap.severity,
245
+ "host_ip": gap.host_ip,
246
+ "package": (
247
+ gap.wazuh_details.get("package_name") if gap.wazuh_details else None
248
+ ),
249
+ "version": (
250
+ gap.wazuh_details.get("package_version")
251
+ if gap.wazuh_details
252
+ else None
253
+ ),
254
+ "recommendation": gap.recommendation,
255
+ "priority": (
256
+ "high" if gap.severity in ["Critical", "High"] else "medium"
257
+ ),
231
258
  }
232
259
  for gap in sorted_gaps[:limit]
233
260
  ]
@@ -254,7 +281,7 @@ class GapAnalyzer:
254
281
  vulns = []
255
282
  for r in results:
256
283
  vuln = dict(r)
257
- vuln['host_ip'] = r.get('mapped_host_ip') or r.get('host_ip')
284
+ vuln["host_ip"] = r.get("mapped_host_ip") or r.get("host_ip")
258
285
  vulns.append(vuln)
259
286
 
260
287
  return vulns
@@ -278,14 +305,16 @@ class GapAnalyzer:
278
305
 
279
306
  for r in nuclei_results:
280
307
  # Extract IP from matched_at URL
281
- host_ip = self._extract_ip(r.get('matched_at', ''))
308
+ host_ip = self._extract_ip(r.get("matched_at", ""))
282
309
  if host_ip:
283
- findings.append({
284
- 'cve_id': r['cve_id'],
285
- 'severity': r.get('severity', 'medium'),
286
- 'host_ip': host_ip,
287
- 'tool': 'nuclei'
288
- })
310
+ findings.append(
311
+ {
312
+ "cve_id": r["cve_id"],
313
+ "severity": r.get("severity", "medium"),
314
+ "host_ip": host_ip,
315
+ "tool": "nuclei",
316
+ }
317
+ )
289
318
 
290
319
  # Get from findings table (check refs/title for CVE)
291
320
  findings_query = """
@@ -303,14 +332,18 @@ class GapAnalyzer:
303
332
 
304
333
  for r in findings_results:
305
334
  # Extract CVE from title or refs
306
- cve_id = self._extract_cve(r.get('title', '')) or self._extract_cve(r.get('refs', ''))
307
- if cve_id and r.get('host_ip'):
308
- findings.append({
309
- 'cve_id': cve_id,
310
- 'severity': r.get('severity', 'medium'),
311
- 'host_ip': r['host_ip'],
312
- 'tool': r.get('tool', 'unknown')
313
- })
335
+ cve_id = self._extract_cve(r.get("title", "")) or self._extract_cve(
336
+ r.get("refs", "")
337
+ )
338
+ if cve_id and r.get("host_ip"):
339
+ findings.append(
340
+ {
341
+ "cve_id": cve_id,
342
+ "severity": r.get("severity", "medium"),
343
+ "host_ip": r["host_ip"],
344
+ "tool": r.get("tool", "unknown"),
345
+ }
346
+ )
314
347
 
315
348
  return findings
316
349
 
@@ -319,7 +352,7 @@ class GapAnalyzer:
319
352
  if not text:
320
353
  return None
321
354
 
322
- match = re.search(r'CVE-\d{4}-\d{4,}', text, re.IGNORECASE)
355
+ match = re.search(r"CVE-\d{4}-\d{4,}", text, re.IGNORECASE)
323
356
  if match:
324
357
  return match.group(0).upper()
325
358
 
@@ -331,7 +364,7 @@ class GapAnalyzer:
331
364
  return None
332
365
 
333
366
  # Try to extract IP from URL like https://10.0.0.1:443/path
334
- ip_pattern = r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
367
+ ip_pattern = r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"
335
368
  match = re.search(ip_pattern, url)
336
369
  if match:
337
370
  return match.group(1)
@@ -340,35 +373,37 @@ class GapAnalyzer:
340
373
 
341
374
  def _get_scan_recommendation(self, cve_id: str, wazuh_details: Dict) -> str:
342
375
  """Generate scan recommendation for a Wazuh-only CVE."""
343
- package = wazuh_details.get('package_name', 'unknown')
344
- severity = wazuh_details.get('severity', 'Medium')
376
+ package = wazuh_details.get("package_name", "unknown")
377
+ severity = wazuh_details.get("severity", "Medium")
345
378
 
346
- if severity in ['Critical', 'High']:
379
+ if severity in ["Critical", "High"]:
347
380
  return f"Run targeted nuclei scan: nuclei -t cves/{cve_id.lower()}.yaml"
348
381
 
349
382
  return f"Verify {cve_id} in {package} - may not be network exploitable"
350
383
 
351
- def _get_severity_breakdown(self, result: GapAnalysisResult) -> Dict[str, Dict[str, int]]:
384
+ def _get_severity_breakdown(
385
+ self, result: GapAnalysisResult
386
+ ) -> Dict[str, Dict[str, int]]:
352
387
  """Get counts by severity for each category."""
353
388
  breakdown = {
354
- 'wazuh_only': {'Critical': 0, 'High': 0, 'Medium': 0, 'Low': 0},
355
- 'scan_only': {'Critical': 0, 'High': 0, 'Medium': 0, 'Low': 0},
356
- 'confirmed': {'Critical': 0, 'High': 0, 'Medium': 0, 'Low': 0}
389
+ "wazuh_only": {"Critical": 0, "High": 0, "Medium": 0, "Low": 0},
390
+ "scan_only": {"Critical": 0, "High": 0, "Medium": 0, "Low": 0},
391
+ "confirmed": {"Critical": 0, "High": 0, "Medium": 0, "Low": 0},
357
392
  }
358
393
 
359
394
  for gap in result.wazuh_only:
360
- sev = gap.severity if gap.severity in breakdown['wazuh_only'] else 'Medium'
361
- breakdown['wazuh_only'][sev] += 1
395
+ sev = gap.severity if gap.severity in breakdown["wazuh_only"] else "Medium"
396
+ breakdown["wazuh_only"][sev] += 1
362
397
 
363
398
  for gap in result.scan_only:
364
399
  # Normalize severity (scan findings use lowercase)
365
- sev = gap.severity.capitalize() if gap.severity else 'Medium'
366
- if sev not in breakdown['scan_only']:
367
- sev = 'Medium'
368
- breakdown['scan_only'][sev] += 1
400
+ sev = gap.severity.capitalize() if gap.severity else "Medium"
401
+ if sev not in breakdown["scan_only"]:
402
+ sev = "Medium"
403
+ breakdown["scan_only"][sev] += 1
369
404
 
370
405
  for gap in result.confirmed:
371
- sev = gap.severity if gap.severity in breakdown['confirmed'] else 'Medium'
372
- breakdown['confirmed'][sev] += 1
406
+ sev = gap.severity if gap.severity in breakdown["confirmed"] else "Medium"
407
+ breakdown["confirmed"][sev] += 1
373
408
 
374
409
  return breakdown