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
@@ -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