souleyez 2.43.26__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.
- souleyez/__init__.py +1 -2
- souleyez/ai/__init__.py +21 -15
- souleyez/ai/action_mapper.py +249 -150
- souleyez/ai/chain_advisor.py +116 -100
- souleyez/ai/claude_provider.py +29 -28
- souleyez/ai/context_builder.py +80 -62
- souleyez/ai/executor.py +158 -117
- souleyez/ai/feedback_handler.py +136 -121
- souleyez/ai/llm_factory.py +27 -20
- souleyez/ai/llm_provider.py +4 -2
- souleyez/ai/ollama_provider.py +6 -9
- souleyez/ai/ollama_service.py +44 -37
- souleyez/ai/path_scorer.py +91 -76
- souleyez/ai/recommender.py +176 -144
- souleyez/ai/report_context.py +74 -73
- souleyez/ai/report_service.py +84 -66
- souleyez/ai/result_parser.py +222 -229
- souleyez/ai/safety.py +67 -44
- souleyez/auth/__init__.py +23 -22
- souleyez/auth/audit.py +36 -26
- souleyez/auth/engagement_access.py +65 -48
- souleyez/auth/permissions.py +14 -3
- souleyez/auth/session_manager.py +54 -37
- souleyez/auth/user_manager.py +109 -64
- souleyez/commands/audit.py +40 -43
- souleyez/commands/auth.py +35 -15
- souleyez/commands/deliverables.py +55 -50
- souleyez/commands/engagement.py +47 -28
- souleyez/commands/license.py +32 -23
- souleyez/commands/screenshots.py +36 -32
- souleyez/commands/user.py +82 -36
- souleyez/config.py +52 -44
- souleyez/core/credential_tester.py +87 -81
- souleyez/core/cve_mappings.py +179 -192
- souleyez/core/cve_matcher.py +162 -148
- souleyez/core/msf_auto_mapper.py +100 -83
- souleyez/core/msf_chain_engine.py +294 -256
- souleyez/core/msf_database.py +153 -70
- souleyez/core/msf_integration.py +679 -673
- souleyez/core/msf_rpc_client.py +40 -42
- souleyez/core/msf_rpc_manager.py +77 -79
- souleyez/core/msf_sync_manager.py +241 -181
- souleyez/core/network_utils.py +22 -15
- souleyez/core/parser_handler.py +34 -25
- souleyez/core/pending_chains.py +114 -63
- souleyez/core/templates.py +158 -107
- souleyez/core/tool_chaining.py +9526 -2879
- souleyez/core/version_utils.py +79 -94
- souleyez/core/vuln_correlation.py +136 -89
- souleyez/core/web_utils.py +33 -32
- souleyez/data/wordlists/ad_users.txt +378 -0
- souleyez/data/wordlists/api_endpoints_large.txt +769 -0
- souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
- souleyez/data/wordlists/lfi_payloads.txt +82 -0
- souleyez/data/wordlists/passwords_brute.txt +1548 -0
- souleyez/data/wordlists/passwords_crack.txt +2479 -0
- souleyez/data/wordlists/passwords_spray.txt +386 -0
- souleyez/data/wordlists/subdomains_large.txt +5057 -0
- souleyez/data/wordlists/usernames_common.txt +694 -0
- souleyez/data/wordlists/web_dirs_large.txt +4769 -0
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +12 -17
- souleyez/detection/mitre_mappings.py +61 -55
- souleyez/detection/validator.py +97 -86
- souleyez/devtools.py +23 -10
- souleyez/docs/README.md +4 -4
- souleyez/docs/api-reference/cli-commands.md +2 -2
- souleyez/docs/developer-guide/adding-new-tools.md +562 -0
- souleyez/docs/user-guide/auto-chaining.md +30 -8
- souleyez/docs/user-guide/getting-started.md +1 -1
- souleyez/docs/user-guide/installation.md +26 -3
- souleyez/docs/user-guide/metasploit-integration.md +2 -2
- souleyez/docs/user-guide/rbac.md +1 -1
- souleyez/docs/user-guide/scope-management.md +1 -1
- souleyez/docs/user-guide/siem-integration.md +1 -1
- souleyez/docs/user-guide/tools-reference.md +1 -8
- souleyez/docs/user-guide/worker-management.md +1 -1
- souleyez/engine/background.py +1239 -535
- souleyez/engine/base.py +4 -1
- souleyez/engine/job_status.py +17 -49
- souleyez/engine/log_sanitizer.py +103 -77
- souleyez/engine/manager.py +38 -7
- souleyez/engine/result_handler.py +2200 -1550
- souleyez/engine/worker_manager.py +50 -41
- souleyez/export/evidence_bundle.py +72 -62
- souleyez/feature_flags/features.py +16 -20
- souleyez/feature_flags.py +5 -9
- souleyez/handlers/__init__.py +11 -0
- souleyez/handlers/base.py +188 -0
- souleyez/handlers/bash_handler.py +277 -0
- souleyez/handlers/bloodhound_handler.py +243 -0
- souleyez/handlers/certipy_handler.py +311 -0
- souleyez/handlers/crackmapexec_handler.py +486 -0
- souleyez/handlers/dnsrecon_handler.py +344 -0
- souleyez/handlers/enum4linux_handler.py +400 -0
- souleyez/handlers/evil_winrm_handler.py +493 -0
- souleyez/handlers/ffuf_handler.py +815 -0
- souleyez/handlers/gobuster_handler.py +1114 -0
- souleyez/handlers/gpp_extract_handler.py +334 -0
- souleyez/handlers/hashcat_handler.py +444 -0
- souleyez/handlers/hydra_handler.py +563 -0
- souleyez/handlers/impacket_getuserspns_handler.py +343 -0
- souleyez/handlers/impacket_psexec_handler.py +222 -0
- souleyez/handlers/impacket_secretsdump_handler.py +426 -0
- souleyez/handlers/john_handler.py +286 -0
- souleyez/handlers/katana_handler.py +425 -0
- souleyez/handlers/kerbrute_handler.py +298 -0
- souleyez/handlers/ldapsearch_handler.py +636 -0
- souleyez/handlers/lfi_extract_handler.py +464 -0
- souleyez/handlers/msf_auxiliary_handler.py +408 -0
- souleyez/handlers/msf_exploit_handler.py +380 -0
- souleyez/handlers/nikto_handler.py +413 -0
- souleyez/handlers/nmap_handler.py +821 -0
- souleyez/handlers/nuclei_handler.py +359 -0
- souleyez/handlers/nxc_handler.py +371 -0
- souleyez/handlers/rdp_sec_check_handler.py +353 -0
- souleyez/handlers/registry.py +292 -0
- souleyez/handlers/responder_handler.py +232 -0
- souleyez/handlers/service_explorer_handler.py +434 -0
- souleyez/handlers/smbclient_handler.py +344 -0
- souleyez/handlers/smbmap_handler.py +510 -0
- souleyez/handlers/smbpasswd_handler.py +296 -0
- souleyez/handlers/sqlmap_handler.py +1116 -0
- souleyez/handlers/theharvester_handler.py +601 -0
- souleyez/handlers/web_login_test_handler.py +327 -0
- souleyez/handlers/whois_handler.py +277 -0
- souleyez/handlers/wpscan_handler.py +554 -0
- souleyez/history.py +32 -16
- souleyez/importers/msf_importer.py +106 -75
- souleyez/importers/smart_importer.py +208 -147
- souleyez/integrations/siem/__init__.py +10 -10
- souleyez/integrations/siem/base.py +17 -18
- souleyez/integrations/siem/elastic.py +108 -122
- souleyez/integrations/siem/factory.py +207 -80
- souleyez/integrations/siem/googlesecops.py +146 -154
- souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
- souleyez/integrations/siem/sentinel.py +107 -109
- souleyez/integrations/siem/splunk.py +246 -212
- souleyez/integrations/siem/wazuh.py +65 -71
- souleyez/integrations/wazuh/__init__.py +5 -5
- souleyez/integrations/wazuh/client.py +70 -93
- souleyez/integrations/wazuh/config.py +85 -57
- souleyez/integrations/wazuh/host_mapper.py +28 -36
- souleyez/integrations/wazuh/sync.py +78 -68
- souleyez/intelligence/__init__.py +4 -5
- souleyez/intelligence/correlation_analyzer.py +309 -295
- souleyez/intelligence/exploit_knowledge.py +661 -623
- souleyez/intelligence/exploit_suggestions.py +159 -139
- souleyez/intelligence/gap_analyzer.py +132 -97
- souleyez/intelligence/gap_detector.py +251 -214
- souleyez/intelligence/sensitive_tables.py +266 -129
- souleyez/intelligence/service_parser.py +137 -123
- souleyez/intelligence/surface_analyzer.py +407 -268
- souleyez/intelligence/target_parser.py +159 -162
- souleyez/licensing/__init__.py +6 -6
- souleyez/licensing/validator.py +17 -19
- souleyez/log_config.py +79 -54
- souleyez/main.py +1505 -687
- souleyez/migrations/fix_job_counter.py +16 -14
- souleyez/parsers/bloodhound_parser.py +41 -39
- souleyez/parsers/crackmapexec_parser.py +178 -111
- souleyez/parsers/dalfox_parser.py +72 -77
- souleyez/parsers/dnsrecon_parser.py +103 -91
- souleyez/parsers/enum4linux_parser.py +183 -153
- souleyez/parsers/ffuf_parser.py +29 -25
- souleyez/parsers/gobuster_parser.py +301 -41
- souleyez/parsers/hashcat_parser.py +324 -79
- souleyez/parsers/http_fingerprint_parser.py +350 -103
- souleyez/parsers/hydra_parser.py +131 -111
- souleyez/parsers/impacket_parser.py +231 -178
- souleyez/parsers/john_parser.py +98 -86
- souleyez/parsers/katana_parser.py +316 -0
- souleyez/parsers/msf_parser.py +943 -498
- souleyez/parsers/nikto_parser.py +346 -65
- souleyez/parsers/nmap_parser.py +262 -174
- souleyez/parsers/nuclei_parser.py +40 -44
- souleyez/parsers/responder_parser.py +26 -26
- souleyez/parsers/searchsploit_parser.py +74 -74
- souleyez/parsers/service_explorer_parser.py +279 -0
- souleyez/parsers/smbmap_parser.py +180 -124
- souleyez/parsers/sqlmap_parser.py +434 -308
- souleyez/parsers/theharvester_parser.py +75 -57
- souleyez/parsers/whois_parser.py +135 -94
- souleyez/parsers/wpscan_parser.py +278 -190
- souleyez/plugins/afp.py +44 -36
- souleyez/plugins/afp_brute.py +114 -46
- souleyez/plugins/ard.py +48 -37
- souleyez/plugins/bloodhound.py +95 -61
- souleyez/plugins/certipy.py +303 -0
- souleyez/plugins/crackmapexec.py +186 -85
- souleyez/plugins/dalfox.py +120 -59
- souleyez/plugins/dns_hijack.py +146 -41
- souleyez/plugins/dnsrecon.py +97 -61
- souleyez/plugins/enum4linux.py +91 -66
- souleyez/plugins/evil_winrm.py +291 -0
- souleyez/plugins/ffuf.py +166 -90
- souleyez/plugins/firmware_extract.py +133 -29
- souleyez/plugins/gobuster.py +387 -190
- souleyez/plugins/gpp_extract.py +393 -0
- souleyez/plugins/hashcat.py +100 -73
- souleyez/plugins/http_fingerprint.py +854 -267
- souleyez/plugins/hydra.py +566 -200
- souleyez/plugins/impacket_getnpusers.py +117 -69
- souleyez/plugins/impacket_psexec.py +84 -64
- souleyez/plugins/impacket_secretsdump.py +103 -69
- souleyez/plugins/impacket_smbclient.py +89 -75
- souleyez/plugins/john.py +86 -69
- souleyez/plugins/katana.py +313 -0
- souleyez/plugins/kerbrute.py +237 -0
- souleyez/plugins/lfi_extract.py +541 -0
- souleyez/plugins/macos_ssh.py +117 -48
- souleyez/plugins/mdns.py +35 -30
- souleyez/plugins/msf_auxiliary.py +253 -130
- souleyez/plugins/msf_exploit.py +239 -161
- souleyez/plugins/nikto.py +134 -78
- souleyez/plugins/nmap.py +275 -91
- souleyez/plugins/nuclei.py +180 -89
- souleyez/plugins/nxc.py +285 -0
- souleyez/plugins/plugin_base.py +35 -36
- souleyez/plugins/plugin_template.py +13 -5
- souleyez/plugins/rdp_sec_check.py +130 -0
- souleyez/plugins/responder.py +112 -71
- souleyez/plugins/router_http_brute.py +76 -65
- souleyez/plugins/router_ssh_brute.py +118 -41
- souleyez/plugins/router_telnet_brute.py +124 -42
- souleyez/plugins/routersploit.py +91 -59
- souleyez/plugins/routersploit_exploit.py +77 -55
- souleyez/plugins/searchsploit.py +91 -77
- souleyez/plugins/service_explorer.py +1160 -0
- souleyez/plugins/smbmap.py +122 -72
- souleyez/plugins/smbpasswd.py +215 -0
- souleyez/plugins/sqlmap.py +301 -113
- souleyez/plugins/theharvester.py +127 -75
- souleyez/plugins/tr069.py +79 -57
- souleyez/plugins/upnp.py +65 -47
- souleyez/plugins/upnp_abuse.py +73 -55
- souleyez/plugins/vnc_access.py +129 -42
- souleyez/plugins/vnc_brute.py +109 -38
- souleyez/plugins/web_login_test.py +417 -0
- souleyez/plugins/whois.py +77 -58
- souleyez/plugins/wpscan.py +173 -69
- souleyez/reporting/__init__.py +2 -1
- souleyez/reporting/attack_chain.py +411 -346
- souleyez/reporting/charts.py +436 -501
- souleyez/reporting/compliance_mappings.py +334 -201
- souleyez/reporting/detection_report.py +126 -125
- souleyez/reporting/formatters.py +828 -591
- souleyez/reporting/generator.py +386 -302
- souleyez/reporting/metrics.py +72 -75
- souleyez/scanner.py +35 -29
- souleyez/security/__init__.py +37 -11
- souleyez/security/scope_validator.py +175 -106
- souleyez/security/validation.py +223 -149
- souleyez/security.py +22 -6
- souleyez/storage/credentials.py +247 -186
- souleyez/storage/crypto.py +296 -129
- souleyez/storage/database.py +73 -50
- souleyez/storage/db.py +58 -36
- souleyez/storage/deliverable_evidence.py +177 -128
- souleyez/storage/deliverable_exporter.py +282 -246
- souleyez/storage/deliverable_templates.py +134 -116
- souleyez/storage/deliverables.py +135 -130
- souleyez/storage/engagements.py +109 -56
- souleyez/storage/evidence.py +181 -152
- souleyez/storage/execution_log.py +31 -17
- souleyez/storage/exploit_attempts.py +93 -57
- souleyez/storage/exploits.py +67 -36
- souleyez/storage/findings.py +48 -61
- souleyez/storage/hosts.py +176 -144
- souleyez/storage/migrate_to_engagements.py +43 -19
- souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
- souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
- souleyez/storage/migrations/_003_add_execution_log.py +14 -8
- souleyez/storage/migrations/_005_screenshots.py +13 -5
- souleyez/storage/migrations/_006_deliverables.py +13 -5
- souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
- souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
- souleyez/storage/migrations/_010_evidence_linking.py +17 -10
- souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
- souleyez/storage/migrations/_012_team_collaboration.py +34 -21
- souleyez/storage/migrations/_013_add_host_tags.py +12 -6
- souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
- souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
- souleyez/storage/migrations/_016_add_domain_field.py +10 -4
- souleyez/storage/migrations/_017_msf_sessions.py +16 -8
- souleyez/storage/migrations/_018_add_osint_target.py +10 -6
- souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
- souleyez/storage/migrations/_020_add_rbac.py +36 -15
- souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
- souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
- souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
- souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
- souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
- souleyez/storage/migrations/__init__.py +26 -26
- souleyez/storage/migrations/migration_manager.py +19 -19
- souleyez/storage/msf_sessions.py +100 -65
- souleyez/storage/osint.py +17 -24
- souleyez/storage/recommendation_engine.py +269 -235
- souleyez/storage/screenshots.py +33 -32
- souleyez/storage/smb_shares.py +136 -92
- souleyez/storage/sqlmap_data.py +183 -128
- souleyez/storage/team_collaboration.py +135 -141
- souleyez/storage/timeline_tracker.py +122 -94
- souleyez/storage/wazuh_vulns.py +64 -66
- souleyez/storage/web_paths.py +33 -37
- souleyez/testing/credential_tester.py +221 -205
- souleyez/ui/__init__.py +1 -1
- souleyez/ui/ai_quotes.py +12 -12
- souleyez/ui/attack_surface.py +2439 -1516
- souleyez/ui/chain_rules_view.py +914 -382
- souleyez/ui/correlation_view.py +312 -230
- souleyez/ui/dashboard.py +2382 -1130
- souleyez/ui/deliverables_view.py +148 -62
- souleyez/ui/design_system.py +13 -13
- souleyez/ui/errors.py +49 -49
- souleyez/ui/evidence_linking_view.py +284 -179
- souleyez/ui/evidence_vault.py +393 -285
- souleyez/ui/exploit_suggestions_view.py +555 -349
- souleyez/ui/export_view.py +100 -66
- souleyez/ui/gap_analysis_view.py +315 -171
- souleyez/ui/help_system.py +105 -97
- souleyez/ui/intelligence_view.py +436 -293
- souleyez/ui/interactive.py +23434 -10286
- souleyez/ui/interactive_selector.py +75 -68
- souleyez/ui/log_formatter.py +47 -39
- souleyez/ui/menu_components.py +22 -13
- souleyez/ui/msf_auxiliary_menu.py +184 -133
- souleyez/ui/pending_chains_view.py +336 -172
- souleyez/ui/progress_indicators.py +5 -3
- souleyez/ui/recommendations_view.py +195 -137
- souleyez/ui/rule_builder.py +343 -225
- souleyez/ui/setup_wizard.py +678 -284
- souleyez/ui/shortcuts.py +217 -165
- souleyez/ui/splunk_gap_analysis_view.py +452 -270
- souleyez/ui/splunk_vulns_view.py +139 -86
- souleyez/ui/team_dashboard.py +498 -335
- souleyez/ui/template_selector.py +196 -105
- souleyez/ui/terminal.py +6 -6
- souleyez/ui/timeline_view.py +198 -127
- souleyez/ui/tool_setup.py +264 -164
- souleyez/ui/tutorial.py +202 -72
- souleyez/ui/tutorial_state.py +40 -40
- souleyez/ui/wazuh_vulns_view.py +235 -141
- souleyez/ui/wordlist_browser.py +260 -107
- souleyez/ui.py +464 -312
- souleyez/utils/tool_checker.py +427 -367
- souleyez/utils.py +33 -29
- souleyez/wordlists.py +134 -167
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.26.dist-info/RECORD +0 -379
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.26.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
|
@@ -4,6 +4,7 @@ Wazuh API Client for SoulEyez Detection Validation.
|
|
|
4
4
|
Connects to Wazuh Manager API (port 55000) for management operations
|
|
5
5
|
and Wazuh Indexer API (port 9200) for querying alerts.
|
|
6
6
|
"""
|
|
7
|
+
|
|
7
8
|
import requests
|
|
8
9
|
from datetime import datetime, timedelta
|
|
9
10
|
from typing import List, Dict, Optional, Any
|
|
@@ -24,7 +25,7 @@ class WazuhClient:
|
|
|
24
25
|
verify_ssl: bool = False,
|
|
25
26
|
indexer_url: Optional[str] = None,
|
|
26
27
|
indexer_user: Optional[str] = None,
|
|
27
|
-
indexer_password: Optional[str] = None
|
|
28
|
+
indexer_password: Optional[str] = None,
|
|
28
29
|
):
|
|
29
30
|
"""
|
|
30
31
|
Initialize Wazuh API client.
|
|
@@ -38,7 +39,7 @@ class WazuhClient:
|
|
|
38
39
|
indexer_user: Indexer username (defaults to 'admin')
|
|
39
40
|
indexer_password: Indexer password (defaults to manager password)
|
|
40
41
|
"""
|
|
41
|
-
self.api_url = api_url.rstrip(
|
|
42
|
+
self.api_url = api_url.rstrip("/")
|
|
42
43
|
self.username = username
|
|
43
44
|
self.password = password
|
|
44
45
|
self.verify_ssl = verify_ssl
|
|
@@ -48,13 +49,13 @@ class WazuhClient:
|
|
|
48
49
|
# Derive indexer URL from manager URL if not provided
|
|
49
50
|
# Manager is on :55000, Indexer is on :9200
|
|
50
51
|
if indexer_url:
|
|
51
|
-
self.indexer_url = indexer_url.rstrip(
|
|
52
|
+
self.indexer_url = indexer_url.rstrip("/")
|
|
52
53
|
else:
|
|
53
54
|
# Replace port 55000 with 9200
|
|
54
|
-
self.indexer_url = self.api_url.replace(
|
|
55
|
+
self.indexer_url = self.api_url.replace(":55000", ":9200")
|
|
55
56
|
|
|
56
57
|
# Indexer credentials (often different from Manager)
|
|
57
|
-
self.indexer_user = indexer_user or
|
|
58
|
+
self.indexer_user = indexer_user or "admin"
|
|
58
59
|
self.indexer_password = indexer_password or password
|
|
59
60
|
|
|
60
61
|
def _get_token(self) -> str:
|
|
@@ -66,15 +67,12 @@ class WazuhClient:
|
|
|
66
67
|
# Authenticate
|
|
67
68
|
url = f"{self.api_url}/security/user/authenticate"
|
|
68
69
|
response = requests.post(
|
|
69
|
-
url,
|
|
70
|
-
auth=(self.username, self.password),
|
|
71
|
-
verify=self.verify_ssl,
|
|
72
|
-
timeout=30
|
|
70
|
+
url, auth=(self.username, self.password), verify=self.verify_ssl, timeout=30
|
|
73
71
|
)
|
|
74
72
|
response.raise_for_status()
|
|
75
73
|
|
|
76
74
|
data = response.json()
|
|
77
|
-
self._token = data.get(
|
|
75
|
+
self._token = data.get("data", {}).get("token")
|
|
78
76
|
# Tokens typically expire in 15 minutes, refresh at 10
|
|
79
77
|
self._token_expiry = datetime.now() + timedelta(minutes=10)
|
|
80
78
|
|
|
@@ -88,7 +86,7 @@ class WazuhClient:
|
|
|
88
86
|
method: str,
|
|
89
87
|
endpoint: str,
|
|
90
88
|
params: Optional[Dict] = None,
|
|
91
|
-
json_data: Optional[Dict] = None
|
|
89
|
+
json_data: Optional[Dict] = None,
|
|
92
90
|
) -> Dict[str, Any]:
|
|
93
91
|
"""Make authenticated API request."""
|
|
94
92
|
token = self._get_token()
|
|
@@ -96,7 +94,7 @@ class WazuhClient:
|
|
|
96
94
|
|
|
97
95
|
headers = {
|
|
98
96
|
"Authorization": f"Bearer {token}",
|
|
99
|
-
"Content-Type": "application/json"
|
|
97
|
+
"Content-Type": "application/json",
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
response = requests.request(
|
|
@@ -106,7 +104,7 @@ class WazuhClient:
|
|
|
106
104
|
params=params,
|
|
107
105
|
json=json_data,
|
|
108
106
|
verify=self.verify_ssl,
|
|
109
|
-
timeout=60
|
|
107
|
+
timeout=60,
|
|
110
108
|
)
|
|
111
109
|
response.raise_for_status()
|
|
112
110
|
return response.json()
|
|
@@ -128,7 +126,9 @@ class WazuhClient:
|
|
|
128
126
|
# Get hostname from agent 000 (manager)
|
|
129
127
|
hostname = "unknown"
|
|
130
128
|
try:
|
|
131
|
-
agent_result = self._request(
|
|
129
|
+
agent_result = self._request(
|
|
130
|
+
"GET", "/agents", params={"agents_list": "000"}
|
|
131
|
+
)
|
|
132
132
|
agent_items = agent_result.get("data", {}).get("affected_items", [])
|
|
133
133
|
if agent_items:
|
|
134
134
|
hostname = agent_items[0].get("name", "unknown")
|
|
@@ -139,7 +139,7 @@ class WazuhClient:
|
|
|
139
139
|
"connected": True,
|
|
140
140
|
"version": info.get("version", "unknown"),
|
|
141
141
|
"cluster": data.get("cluster", {}).get("enabled", False),
|
|
142
|
-
"hostname": hostname
|
|
142
|
+
"hostname": hostname,
|
|
143
143
|
}
|
|
144
144
|
except requests.exceptions.ConnectionError as e:
|
|
145
145
|
return {"connected": False, "error": f"Connection failed: {str(e)}"}
|
|
@@ -155,7 +155,7 @@ class WazuhClient:
|
|
|
155
155
|
agent_ip: Optional[str] = None,
|
|
156
156
|
rule_ids: Optional[List[int]] = None,
|
|
157
157
|
limit: int = 100,
|
|
158
|
-
search_text: Optional[str] = None
|
|
158
|
+
search_text: Optional[str] = None,
|
|
159
159
|
) -> List[Dict[str, Any]]:
|
|
160
160
|
"""
|
|
161
161
|
Query alerts from Wazuh Indexer (Elasticsearch-based).
|
|
@@ -178,9 +178,9 @@ class WazuhClient:
|
|
|
178
178
|
if start_time or end_time:
|
|
179
179
|
time_range = {}
|
|
180
180
|
if start_time:
|
|
181
|
-
time_range["gte"] = start_time.strftime(
|
|
181
|
+
time_range["gte"] = start_time.strftime("%Y-%m-%dT%H:%M:%S")
|
|
182
182
|
if end_time:
|
|
183
|
-
time_range["lte"] = end_time.strftime(
|
|
183
|
+
time_range["lte"] = end_time.strftime("%Y-%m-%dT%H:%M:%S")
|
|
184
184
|
must_clauses.append({"range": {"timestamp": time_range}})
|
|
185
185
|
|
|
186
186
|
# Rule ID filter
|
|
@@ -189,28 +189,26 @@ class WazuhClient:
|
|
|
189
189
|
|
|
190
190
|
# Agent IP filter (check various IP fields)
|
|
191
191
|
if agent_ip:
|
|
192
|
-
must_clauses.append(
|
|
193
|
-
|
|
194
|
-
"
|
|
195
|
-
|
|
192
|
+
must_clauses.append(
|
|
193
|
+
{
|
|
194
|
+
"multi_match": {
|
|
195
|
+
"query": agent_ip,
|
|
196
|
+
"fields": ["agent.ip", "data.srcip", "data.src_ip", "src_ip"],
|
|
197
|
+
}
|
|
196
198
|
}
|
|
197
|
-
|
|
199
|
+
)
|
|
198
200
|
|
|
199
201
|
# Free text search
|
|
200
202
|
if search_text:
|
|
201
|
-
must_clauses.append({
|
|
202
|
-
"query_string": {"query": f"*{search_text}*"}
|
|
203
|
-
})
|
|
203
|
+
must_clauses.append({"query_string": {"query": f"*{search_text}*"}})
|
|
204
204
|
|
|
205
205
|
# Build the query
|
|
206
206
|
query = {
|
|
207
207
|
"size": limit,
|
|
208
208
|
"sort": [{"timestamp": {"order": "desc"}}],
|
|
209
209
|
"query": {
|
|
210
|
-
"bool": {
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
}
|
|
210
|
+
"bool": {"must": must_clauses if must_clauses else [{"match_all": {}}]}
|
|
211
|
+
},
|
|
214
212
|
}
|
|
215
213
|
|
|
216
214
|
try:
|
|
@@ -220,7 +218,7 @@ class WazuhClient:
|
|
|
220
218
|
auth=(self.indexer_user, self.indexer_password),
|
|
221
219
|
json=query,
|
|
222
220
|
verify=self.verify_ssl,
|
|
223
|
-
timeout=30
|
|
221
|
+
timeout=30,
|
|
224
222
|
)
|
|
225
223
|
response.raise_for_status()
|
|
226
224
|
data = response.json()
|
|
@@ -239,11 +237,7 @@ class WazuhClient:
|
|
|
239
237
|
raise
|
|
240
238
|
|
|
241
239
|
def get_alerts_by_src_ip(
|
|
242
|
-
self,
|
|
243
|
-
src_ip: str,
|
|
244
|
-
start_time: datetime,
|
|
245
|
-
end_time: datetime,
|
|
246
|
-
limit: int = 100
|
|
240
|
+
self, src_ip: str, start_time: datetime, end_time: datetime, limit: int = 100
|
|
247
241
|
) -> List[Dict[str, Any]]:
|
|
248
242
|
"""
|
|
249
243
|
Get alerts where source IP matches (attacker IP).
|
|
@@ -252,10 +246,7 @@ class WazuhClient:
|
|
|
252
246
|
we look for alerts triggered by our attack source IP.
|
|
253
247
|
"""
|
|
254
248
|
return self.get_alerts(
|
|
255
|
-
start_time=start_time,
|
|
256
|
-
end_time=end_time,
|
|
257
|
-
agent_ip=src_ip,
|
|
258
|
-
limit=limit
|
|
249
|
+
start_time=start_time, end_time=end_time, agent_ip=src_ip, limit=limit
|
|
259
250
|
)
|
|
260
251
|
|
|
261
252
|
def get_rules(self, rule_ids: Optional[List[int]] = None) -> List[Dict[str, Any]]:
|
|
@@ -267,7 +258,9 @@ class WazuhClient:
|
|
|
267
258
|
result = self._request("GET", "/rules", params=params)
|
|
268
259
|
return result.get("data", {}).get("affected_items", [])
|
|
269
260
|
|
|
270
|
-
def get_rule_file_content(
|
|
261
|
+
def get_rule_file_content(
|
|
262
|
+
self, filename: str, relative_dirname: str = None
|
|
263
|
+
) -> Optional[str]:
|
|
271
264
|
"""
|
|
272
265
|
Get the content of a rule file.
|
|
273
266
|
|
|
@@ -297,13 +290,13 @@ class WazuhClient:
|
|
|
297
290
|
headers=headers,
|
|
298
291
|
params=params,
|
|
299
292
|
verify=self.verify_ssl,
|
|
300
|
-
timeout=30
|
|
293
|
+
timeout=30,
|
|
301
294
|
)
|
|
302
295
|
|
|
303
296
|
if response.status_code == 200:
|
|
304
297
|
# Check if response is JSON or raw text
|
|
305
|
-
content_type = response.headers.get(
|
|
306
|
-
if
|
|
298
|
+
content_type = response.headers.get("Content-Type", "")
|
|
299
|
+
if "application/json" in content_type:
|
|
307
300
|
# JSON response - extract content from data
|
|
308
301
|
data = response.json()
|
|
309
302
|
affected_items = data.get("data", {}).get("affected_items", [])
|
|
@@ -311,7 +304,7 @@ class WazuhClient:
|
|
|
311
304
|
item = affected_items[0]
|
|
312
305
|
# The content might be in a 'content' field
|
|
313
306
|
if isinstance(item, dict):
|
|
314
|
-
return item.get(
|
|
307
|
+
return item.get("content") or item.get("data")
|
|
315
308
|
return item if isinstance(item, str) else None
|
|
316
309
|
else:
|
|
317
310
|
# Raw text response
|
|
@@ -320,7 +313,9 @@ class WazuhClient:
|
|
|
320
313
|
except Exception:
|
|
321
314
|
return None
|
|
322
315
|
|
|
323
|
-
def get_rule_xml(
|
|
316
|
+
def get_rule_xml(
|
|
317
|
+
self, rule_id: int, filename: str, relative_dirname: str = None
|
|
318
|
+
) -> Optional[str]:
|
|
324
319
|
"""
|
|
325
320
|
Extract a specific rule's XML from a rule file.
|
|
326
321
|
|
|
@@ -357,10 +352,7 @@ class WazuhClient:
|
|
|
357
352
|
# =========================================================================
|
|
358
353
|
|
|
359
354
|
def get_agent_vulnerabilities(
|
|
360
|
-
self,
|
|
361
|
-
agent_id: str,
|
|
362
|
-
severity: Optional[str] = None,
|
|
363
|
-
limit: int = 1000
|
|
355
|
+
self, agent_id: str, severity: Optional[str] = None, limit: int = 1000
|
|
364
356
|
) -> List[Dict[str, Any]]:
|
|
365
357
|
"""
|
|
366
358
|
Fetch vulnerabilities for a specific agent from Wazuh Indexer.
|
|
@@ -375,9 +367,7 @@ class WazuhClient:
|
|
|
375
367
|
Returns:
|
|
376
368
|
List of vulnerability dictionaries
|
|
377
369
|
"""
|
|
378
|
-
must_clauses = [
|
|
379
|
-
{"term": {"agent.id": agent_id}}
|
|
380
|
-
]
|
|
370
|
+
must_clauses = [{"term": {"agent.id": agent_id}}]
|
|
381
371
|
|
|
382
372
|
if severity:
|
|
383
373
|
must_clauses.append({"term": {"vulnerability.severity": severity}})
|
|
@@ -385,11 +375,7 @@ class WazuhClient:
|
|
|
385
375
|
query = {
|
|
386
376
|
"size": limit,
|
|
387
377
|
"sort": [{"vulnerability.severity": {"order": "desc"}}],
|
|
388
|
-
"query": {
|
|
389
|
-
"bool": {
|
|
390
|
-
"must": must_clauses
|
|
391
|
-
}
|
|
392
|
-
}
|
|
378
|
+
"query": {"bool": {"must": must_clauses}},
|
|
393
379
|
}
|
|
394
380
|
|
|
395
381
|
try:
|
|
@@ -398,13 +384,15 @@ class WazuhClient:
|
|
|
398
384
|
auth=(self.indexer_user, self.indexer_password),
|
|
399
385
|
json=query,
|
|
400
386
|
verify=self.verify_ssl,
|
|
401
|
-
timeout=60
|
|
387
|
+
timeout=60,
|
|
402
388
|
)
|
|
403
389
|
response.raise_for_status()
|
|
404
390
|
data = response.json()
|
|
405
391
|
|
|
406
392
|
hits = data.get("hits", {}).get("hits", [])
|
|
407
|
-
return [
|
|
393
|
+
return [
|
|
394
|
+
self._normalize_vulnerability(hit.get("_source", {})) for hit in hits
|
|
395
|
+
]
|
|
408
396
|
|
|
409
397
|
except requests.exceptions.ConnectionError:
|
|
410
398
|
return []
|
|
@@ -417,7 +405,7 @@ class WazuhClient:
|
|
|
417
405
|
self,
|
|
418
406
|
severity: Optional[str] = None,
|
|
419
407
|
agent_ids: Optional[List[str]] = None,
|
|
420
|
-
limit: int = 5000
|
|
408
|
+
limit: int = 5000,
|
|
421
409
|
) -> List[Dict[str, Any]]:
|
|
422
410
|
"""
|
|
423
411
|
Fetch all vulnerabilities from Wazuh Indexer.
|
|
@@ -442,13 +430,11 @@ class WazuhClient:
|
|
|
442
430
|
"size": limit,
|
|
443
431
|
"sort": [
|
|
444
432
|
{"vulnerability.severity": {"order": "desc"}},
|
|
445
|
-
{"agent.id": {"order": "asc"}}
|
|
433
|
+
{"agent.id": {"order": "asc"}},
|
|
446
434
|
],
|
|
447
435
|
"query": {
|
|
448
|
-
"bool": {
|
|
449
|
-
|
|
450
|
-
}
|
|
451
|
-
}
|
|
436
|
+
"bool": {"must": must_clauses if must_clauses else [{"match_all": {}}]}
|
|
437
|
+
},
|
|
452
438
|
}
|
|
453
439
|
|
|
454
440
|
try:
|
|
@@ -457,13 +443,15 @@ class WazuhClient:
|
|
|
457
443
|
auth=(self.indexer_user, self.indexer_password),
|
|
458
444
|
json=query,
|
|
459
445
|
verify=self.verify_ssl,
|
|
460
|
-
timeout=120
|
|
446
|
+
timeout=120,
|
|
461
447
|
)
|
|
462
448
|
response.raise_for_status()
|
|
463
449
|
data = response.json()
|
|
464
450
|
|
|
465
451
|
hits = data.get("hits", {}).get("hits", [])
|
|
466
|
-
return [
|
|
452
|
+
return [
|
|
453
|
+
self._normalize_vulnerability(hit.get("_source", {})) for hit in hits
|
|
454
|
+
]
|
|
467
455
|
|
|
468
456
|
except requests.exceptions.ConnectionError:
|
|
469
457
|
return []
|
|
@@ -491,12 +479,10 @@ class WazuhClient:
|
|
|
491
479
|
"by_severity": {
|
|
492
480
|
"terms": {"field": "vulnerability.severity", "size": 10}
|
|
493
481
|
}
|
|
494
|
-
}
|
|
482
|
+
},
|
|
495
483
|
},
|
|
496
|
-
"total": {
|
|
497
|
-
|
|
498
|
-
}
|
|
499
|
-
}
|
|
484
|
+
"total": {"value_count": {"field": "vulnerability.id"}},
|
|
485
|
+
},
|
|
500
486
|
}
|
|
501
487
|
|
|
502
488
|
try:
|
|
@@ -505,7 +491,7 @@ class WazuhClient:
|
|
|
505
491
|
auth=(self.indexer_user, self.indexer_password),
|
|
506
492
|
json=query,
|
|
507
493
|
verify=self.verify_ssl,
|
|
508
|
-
timeout=30
|
|
494
|
+
timeout=30,
|
|
509
495
|
)
|
|
510
496
|
response.raise_for_status()
|
|
511
497
|
data = response.json()
|
|
@@ -523,15 +509,17 @@ class WazuhClient:
|
|
|
523
509
|
agent_id = bucket["key"]
|
|
524
510
|
agent_breakdown[agent_id] = {
|
|
525
511
|
"total": bucket["doc_count"],
|
|
526
|
-
"by_severity": {}
|
|
512
|
+
"by_severity": {},
|
|
527
513
|
}
|
|
528
514
|
for sev_bucket in bucket.get("by_severity", {}).get("buckets", []):
|
|
529
|
-
agent_breakdown[agent_id]["by_severity"][sev_bucket["key"]] =
|
|
515
|
+
agent_breakdown[agent_id]["by_severity"][sev_bucket["key"]] = (
|
|
516
|
+
sev_bucket["doc_count"]
|
|
517
|
+
)
|
|
530
518
|
|
|
531
519
|
return {
|
|
532
520
|
"total": aggs.get("total", {}).get("value", 0),
|
|
533
521
|
"by_severity": severity_counts,
|
|
534
|
-
"by_agent": agent_breakdown
|
|
522
|
+
"by_agent": agent_breakdown,
|
|
535
523
|
}
|
|
536
524
|
|
|
537
525
|
except requests.exceptions.ConnectionError:
|
|
@@ -551,25 +539,19 @@ class WazuhClient:
|
|
|
551
539
|
f"{self.indexer_url}/wazuh-states-vulnerabilities-*/_count",
|
|
552
540
|
auth=(self.indexer_user, self.indexer_password),
|
|
553
541
|
verify=self.verify_ssl,
|
|
554
|
-
timeout=10
|
|
542
|
+
timeout=10,
|
|
555
543
|
)
|
|
556
544
|
|
|
557
545
|
if response.status_code == 200:
|
|
558
546
|
data = response.json()
|
|
559
|
-
return {
|
|
560
|
-
"accessible": True,
|
|
561
|
-
"count": data.get("count", 0)
|
|
562
|
-
}
|
|
547
|
+
return {"accessible": True, "count": data.get("count", 0)}
|
|
563
548
|
elif response.status_code == 404:
|
|
564
549
|
return {
|
|
565
550
|
"accessible": False,
|
|
566
|
-
"error": "Vulnerability index not found. Ensure vulnerability detection is enabled in Wazuh."
|
|
551
|
+
"error": "Vulnerability index not found. Ensure vulnerability detection is enabled in Wazuh.",
|
|
567
552
|
}
|
|
568
553
|
else:
|
|
569
|
-
return {
|
|
570
|
-
"accessible": False,
|
|
571
|
-
"error": f"HTTP {response.status_code}"
|
|
572
|
-
}
|
|
554
|
+
return {"accessible": False, "error": f"HTTP {response.status_code}"}
|
|
573
555
|
|
|
574
556
|
except requests.exceptions.ConnectionError as e:
|
|
575
557
|
return {"accessible": False, "error": f"Connection failed: {str(e)}"}
|
|
@@ -609,7 +591,6 @@ class WazuhClient:
|
|
|
609
591
|
"agent_id": agent.get("id"),
|
|
610
592
|
"agent_name": agent.get("name"),
|
|
611
593
|
"agent_ip": agent.get("ip"),
|
|
612
|
-
|
|
613
594
|
# Vulnerability info
|
|
614
595
|
"cve_id": vuln.get("id"),
|
|
615
596
|
"name": vuln.get("title") or vuln.get("description", "")[:100],
|
|
@@ -617,18 +598,14 @@ class WazuhClient:
|
|
|
617
598
|
"cvss_score": cvss_score,
|
|
618
599
|
"cvss_version": cvss_version,
|
|
619
600
|
"published_date": vuln.get("published"),
|
|
620
|
-
|
|
621
601
|
# Package info
|
|
622
602
|
"package_name": package.get("name"),
|
|
623
603
|
"package_version": package.get("version"),
|
|
624
604
|
"package_architecture": package.get("architecture"),
|
|
625
|
-
|
|
626
605
|
# References
|
|
627
606
|
"reference_urls": vuln.get("reference", []),
|
|
628
|
-
|
|
629
607
|
# Detection time
|
|
630
608
|
"detection_time": vuln.get("detected_at"),
|
|
631
|
-
|
|
632
609
|
# Raw data for debugging
|
|
633
|
-
"raw_data": doc
|
|
610
|
+
"raw_data": doc,
|
|
634
611
|
}
|