souleyez 2.43.29__py3-none-any.whl → 2.43.34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +22827 -10678
- 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.29.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
- souleyez-2.43.34.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/top_level.txt +0 -0
souleyez/detection/validator.py
CHANGED
|
@@ -37,16 +37,16 @@ def _get_job_by_id(job_id: int) -> Optional[Dict[str, Any]]:
|
|
|
37
37
|
"""Get a single job by ID from the job queue."""
|
|
38
38
|
jobs = _read_jobs_file()
|
|
39
39
|
for job in jobs:
|
|
40
|
-
if job.get(
|
|
40
|
+
if job.get("id") == job_id:
|
|
41
41
|
return job
|
|
42
42
|
return None
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def _reconstruct_command(job: Dict[str, Any]) -> str:
|
|
46
46
|
"""Reconstruct the command string from job data."""
|
|
47
|
-
tool = job.get(
|
|
48
|
-
target = job.get(
|
|
49
|
-
args = job.get(
|
|
47
|
+
tool = job.get("tool", "")
|
|
48
|
+
target = job.get("target", "")
|
|
49
|
+
args = job.get("args", [])
|
|
50
50
|
|
|
51
51
|
# Build command string
|
|
52
52
|
parts = [tool]
|
|
@@ -55,12 +55,13 @@ def _reconstruct_command(job: Dict[str, Any]) -> str:
|
|
|
55
55
|
if target and target not in args:
|
|
56
56
|
parts.append(target)
|
|
57
57
|
|
|
58
|
-
return
|
|
58
|
+
return " ".join(parts)
|
|
59
59
|
|
|
60
60
|
|
|
61
61
|
@dataclass
|
|
62
62
|
class DetectionResult:
|
|
63
63
|
"""Result of validating detection for a single job."""
|
|
64
|
+
|
|
64
65
|
job_id: int
|
|
65
66
|
status: str # 'detected', 'not_detected', 'partial', 'offline', 'unknown'
|
|
66
67
|
attack_type: str = "" # Tool name (e.g., 'nmap', 'hydra')
|
|
@@ -75,6 +76,7 @@ class DetectionResult:
|
|
|
75
76
|
@dataclass
|
|
76
77
|
class EngagementDetectionSummary:
|
|
77
78
|
"""Summary of detection coverage for an engagement."""
|
|
79
|
+
|
|
78
80
|
engagement_id: int
|
|
79
81
|
total_attacks: int
|
|
80
82
|
detected: int
|
|
@@ -139,36 +141,36 @@ class DetectionValidator:
|
|
|
139
141
|
job = _get_job_by_id(job_id)
|
|
140
142
|
if not job:
|
|
141
143
|
return DetectionResult(
|
|
142
|
-
job_id=job_id,
|
|
143
|
-
status='unknown',
|
|
144
|
-
reason="Job not found in queue"
|
|
144
|
+
job_id=job_id, status="unknown", reason="Job not found in queue"
|
|
145
145
|
)
|
|
146
146
|
|
|
147
147
|
# Verify engagement matches
|
|
148
|
-
if job.get(
|
|
148
|
+
if job.get("engagement_id") != self.engagement_id:
|
|
149
149
|
return DetectionResult(
|
|
150
150
|
job_id=job_id,
|
|
151
|
-
status=
|
|
152
|
-
reason="Job belongs to different engagement"
|
|
151
|
+
status="unknown",
|
|
152
|
+
reason="Job belongs to different engagement",
|
|
153
153
|
)
|
|
154
154
|
|
|
155
|
-
job_tool = job.get(
|
|
155
|
+
job_tool = job.get("tool") or "unknown"
|
|
156
156
|
job_command = _reconstruct_command(job)
|
|
157
157
|
# Use started_at or finished_at for execution time
|
|
158
|
-
executed_at =
|
|
158
|
+
executed_at = (
|
|
159
|
+
job.get("started_at") or job.get("finished_at") or job.get("created_at")
|
|
160
|
+
)
|
|
159
161
|
# Job ran successfully if status is done, no_results, or warning
|
|
160
162
|
# (all of these sent network traffic that should be detectable by SIEM)
|
|
161
|
-
job_status = job.get(
|
|
162
|
-
success = job_status in (
|
|
163
|
+
job_status = job.get("status", "")
|
|
164
|
+
success = job_status in ("done", "no_results", "warning")
|
|
163
165
|
|
|
164
166
|
# Extract target IP from command (common patterns)
|
|
165
167
|
target_ip = None
|
|
166
|
-
ip_pattern = r
|
|
168
|
+
ip_pattern = r"\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b"
|
|
167
169
|
ip_matches = re.findall(ip_pattern, job_command)
|
|
168
170
|
if ip_matches:
|
|
169
171
|
# Filter out common non-target IPs
|
|
170
172
|
for ip in ip_matches:
|
|
171
|
-
if not ip.startswith(
|
|
173
|
+
if not ip.startswith("127.") and not ip.startswith("0."):
|
|
172
174
|
target_ip = ip
|
|
173
175
|
break
|
|
174
176
|
|
|
@@ -176,23 +178,23 @@ class DetectionValidator:
|
|
|
176
178
|
if not success:
|
|
177
179
|
return DetectionResult(
|
|
178
180
|
job_id=job_id,
|
|
179
|
-
status=
|
|
181
|
+
status="unknown",
|
|
180
182
|
attack_type=job_tool,
|
|
181
|
-
target_ip=target_ip or
|
|
182
|
-
reason=f"Job did not complete successfully"
|
|
183
|
+
target_ip=target_ip or "",
|
|
184
|
+
reason=f"Job did not complete successfully",
|
|
183
185
|
)
|
|
184
186
|
|
|
185
187
|
# Get attack signature
|
|
186
188
|
signature = get_signature(job_tool)
|
|
187
189
|
|
|
188
190
|
# Check if offline tool (no network detection expected)
|
|
189
|
-
if signature.get(
|
|
191
|
+
if signature.get("offline"):
|
|
190
192
|
result = DetectionResult(
|
|
191
193
|
job_id=job_id,
|
|
192
|
-
status=
|
|
194
|
+
status="offline",
|
|
193
195
|
attack_type=job_tool,
|
|
194
|
-
target_ip=target_ip or
|
|
195
|
-
reason=f"{job_tool} is an offline tool, no network detection expected"
|
|
196
|
+
target_ip=target_ip or "",
|
|
197
|
+
reason=f"{job_tool} is an offline tool, no network detection expected",
|
|
196
198
|
)
|
|
197
199
|
self._save_result(result, job_id)
|
|
198
200
|
return result
|
|
@@ -202,28 +204,32 @@ class DetectionValidator:
|
|
|
202
204
|
if not client:
|
|
203
205
|
return DetectionResult(
|
|
204
206
|
job_id=job_id,
|
|
205
|
-
status=
|
|
207
|
+
status="unknown",
|
|
206
208
|
attack_type=job_tool,
|
|
207
|
-
target_ip=target_ip or
|
|
208
|
-
reason="SIEM not configured for this engagement"
|
|
209
|
+
target_ip=target_ip or "",
|
|
210
|
+
reason="SIEM not configured for this engagement",
|
|
209
211
|
)
|
|
210
212
|
|
|
211
213
|
# Parse timestamp
|
|
212
214
|
try:
|
|
213
215
|
if isinstance(executed_at, str):
|
|
214
|
-
exec_time = datetime.fromisoformat(
|
|
216
|
+
exec_time = datetime.fromisoformat(
|
|
217
|
+
executed_at.replace("Z", "+00:00").replace(" ", "T")
|
|
218
|
+
)
|
|
215
219
|
else:
|
|
216
220
|
exec_time = executed_at or datetime.now()
|
|
217
221
|
except Exception:
|
|
218
222
|
exec_time = datetime.now() - timedelta(hours=1)
|
|
219
223
|
|
|
220
224
|
# Query window: 5 minutes before execution to detection_window after
|
|
221
|
-
detection_window = signature.get(
|
|
225
|
+
detection_window = signature.get(
|
|
226
|
+
"detection_window_seconds", DEFAULT_DETECTION_WINDOW
|
|
227
|
+
)
|
|
222
228
|
query_start = exec_time - timedelta(minutes=5)
|
|
223
229
|
query_end = exec_time + timedelta(seconds=detection_window)
|
|
224
230
|
|
|
225
231
|
# Get expected rule IDs (convert to strings for compatibility)
|
|
226
|
-
expected_rules = signature.get(
|
|
232
|
+
expected_rules = signature.get("wazuh_rules", [])
|
|
227
233
|
rule_id_strings = [str(r) for r in expected_rules] if expected_rules else None
|
|
228
234
|
|
|
229
235
|
# Query SIEM for alerts
|
|
@@ -233,25 +239,25 @@ class DetectionValidator:
|
|
|
233
239
|
end_time=query_end,
|
|
234
240
|
dest_ip=target_ip, # Target IP is the destination
|
|
235
241
|
rule_ids=rule_id_strings,
|
|
236
|
-
limit=100
|
|
242
|
+
limit=100,
|
|
237
243
|
)
|
|
238
244
|
except Exception as e:
|
|
239
|
-
siem_type = client.siem_type if client else
|
|
245
|
+
siem_type = client.siem_type if client else "SIEM"
|
|
240
246
|
return DetectionResult(
|
|
241
247
|
job_id=job_id,
|
|
242
|
-
status=
|
|
243
|
-
reason=f"Error querying {siem_type}: {str(e)}"
|
|
248
|
+
status="unknown",
|
|
249
|
+
reason=f"Error querying {siem_type}: {str(e)}",
|
|
244
250
|
)
|
|
245
251
|
|
|
246
252
|
# Also search by patterns if no rule matches
|
|
247
|
-
if not alerts and signature.get(
|
|
248
|
-
for pattern in signature[
|
|
253
|
+
if not alerts and signature.get("search_patterns"):
|
|
254
|
+
for pattern in signature["search_patterns"][:3]: # Limit searches
|
|
249
255
|
try:
|
|
250
256
|
pattern_alerts = client.get_alerts(
|
|
251
257
|
start_time=query_start,
|
|
252
258
|
end_time=query_end,
|
|
253
259
|
search_text=pattern,
|
|
254
|
-
limit=50
|
|
260
|
+
limit=50,
|
|
255
261
|
)
|
|
256
262
|
alerts.extend(pattern_alerts)
|
|
257
263
|
except Exception:
|
|
@@ -261,51 +267,52 @@ class DetectionValidator:
|
|
|
261
267
|
seen_ids = set()
|
|
262
268
|
unique_alerts = []
|
|
263
269
|
for alert in alerts:
|
|
264
|
-
alert_id = getattr(alert,
|
|
270
|
+
alert_id = getattr(alert, "id", None) or str(
|
|
271
|
+
getattr(alert, "timestamp", "")
|
|
272
|
+
)
|
|
265
273
|
if alert_id not in seen_ids:
|
|
266
274
|
seen_ids.add(alert_id)
|
|
267
275
|
unique_alerts.append(alert)
|
|
268
276
|
|
|
269
277
|
# Determine detection status
|
|
270
278
|
if unique_alerts:
|
|
271
|
-
rule_ids = list(
|
|
272
|
-
getattr(a,
|
|
273
|
-
|
|
274
|
-
))
|
|
279
|
+
rule_ids = list(
|
|
280
|
+
set(getattr(a, "rule_id", "unknown") for a in unique_alerts)
|
|
281
|
+
)
|
|
275
282
|
# Convert SIEMAlert dataclass objects to dicts for storage
|
|
276
283
|
# Include all normalized fields (rule_id, rule_name, severity, etc.)
|
|
277
284
|
alert_dicts = []
|
|
278
285
|
for a in unique_alerts[:20]:
|
|
279
|
-
if hasattr(a,
|
|
286
|
+
if hasattr(a, "__dataclass_fields__"):
|
|
280
287
|
# Convert dataclass to dict
|
|
281
288
|
alert_dict = asdict(a)
|
|
282
289
|
# Convert datetime to ISO string for JSON serialization
|
|
283
|
-
if isinstance(alert_dict.get(
|
|
284
|
-
alert_dict[
|
|
290
|
+
if isinstance(alert_dict.get("timestamp"), datetime):
|
|
291
|
+
alert_dict["timestamp"] = alert_dict["timestamp"].isoformat()
|
|
285
292
|
alert_dicts.append(alert_dict)
|
|
286
293
|
elif isinstance(a, dict):
|
|
287
294
|
alert_dicts.append(a)
|
|
288
295
|
else:
|
|
289
296
|
# Fallback: try to extract raw_data or convert to dict
|
|
290
|
-
alert_dicts.append(getattr(a,
|
|
297
|
+
alert_dicts.append(getattr(a, "raw_data", {}) or {})
|
|
291
298
|
result = DetectionResult(
|
|
292
299
|
job_id=job_id,
|
|
293
|
-
status=
|
|
300
|
+
status="detected",
|
|
294
301
|
attack_type=job_tool,
|
|
295
|
-
target_ip=target_ip or
|
|
302
|
+
target_ip=target_ip or "",
|
|
296
303
|
alerts_count=len(unique_alerts),
|
|
297
304
|
alerts=alert_dicts,
|
|
298
305
|
rule_ids=rule_ids,
|
|
299
|
-
reason=f"Found {len(unique_alerts)} related alert(s)"
|
|
306
|
+
reason=f"Found {len(unique_alerts)} related alert(s)",
|
|
300
307
|
)
|
|
301
308
|
else:
|
|
302
309
|
result = DetectionResult(
|
|
303
310
|
job_id=job_id,
|
|
304
|
-
status=
|
|
311
|
+
status="not_detected",
|
|
305
312
|
attack_type=job_tool,
|
|
306
|
-
target_ip=target_ip or
|
|
313
|
+
target_ip=target_ip or "",
|
|
307
314
|
alerts_count=0,
|
|
308
|
-
reason=f"No alerts found for {job_tool} attack on {target_ip}"
|
|
315
|
+
reason=f"No alerts found for {job_tool} attack on {target_ip}",
|
|
309
316
|
)
|
|
310
317
|
|
|
311
318
|
# Save result to database
|
|
@@ -322,21 +329,21 @@ class DetectionValidator:
|
|
|
322
329
|
return
|
|
323
330
|
|
|
324
331
|
# Extract target IP from command or use job target
|
|
325
|
-
target_ip = job.get(
|
|
332
|
+
target_ip = job.get("target")
|
|
326
333
|
command = _reconstruct_command(job)
|
|
327
334
|
|
|
328
335
|
# Try to extract more specific IP from command
|
|
329
|
-
ip_pattern = r
|
|
336
|
+
ip_pattern = r"\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b"
|
|
330
337
|
ip_matches = re.findall(ip_pattern, command)
|
|
331
338
|
if ip_matches:
|
|
332
339
|
for ip in ip_matches:
|
|
333
|
-
if not ip.startswith(
|
|
340
|
+
if not ip.startswith("127.") and not ip.startswith("0."):
|
|
334
341
|
target_ip = ip
|
|
335
342
|
break
|
|
336
343
|
|
|
337
344
|
# Get timestamps
|
|
338
|
-
attack_start = job.get(
|
|
339
|
-
attack_end = job.get(
|
|
345
|
+
attack_start = job.get("started_at") or job.get("created_at")
|
|
346
|
+
attack_end = job.get("finished_at") or attack_start
|
|
340
347
|
|
|
341
348
|
db = get_db()
|
|
342
349
|
conn = db.get_connection()
|
|
@@ -346,24 +353,27 @@ class DetectionValidator:
|
|
|
346
353
|
cursor.execute("DELETE FROM detection_results WHERE job_id = ?", (job_id,))
|
|
347
354
|
|
|
348
355
|
# Insert new result
|
|
349
|
-
cursor.execute(
|
|
356
|
+
cursor.execute(
|
|
357
|
+
"""
|
|
350
358
|
INSERT INTO detection_results
|
|
351
359
|
(job_id, engagement_id, attack_type, target_ip, attack_start, attack_end,
|
|
352
360
|
detection_status, alerts_count, wazuh_alerts_json, rule_ids, checked_at)
|
|
353
361
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
354
|
-
""",
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
362
|
+
""",
|
|
363
|
+
(
|
|
364
|
+
job_id,
|
|
365
|
+
self.engagement_id,
|
|
366
|
+
job.get("tool"), # attack_type (tool)
|
|
367
|
+
target_ip,
|
|
368
|
+
attack_start,
|
|
369
|
+
attack_end,
|
|
370
|
+
result.status,
|
|
371
|
+
result.alerts_count,
|
|
372
|
+
json.dumps(result.alerts[:20]) if result.alerts else None,
|
|
373
|
+
",".join(result.rule_ids) if result.rule_ids else None,
|
|
374
|
+
datetime.now().isoformat(),
|
|
375
|
+
),
|
|
376
|
+
)
|
|
367
377
|
conn.commit()
|
|
368
378
|
|
|
369
379
|
def validate_engagement(self) -> EngagementDetectionSummary:
|
|
@@ -378,20 +388,21 @@ class DetectionValidator:
|
|
|
378
388
|
# Get all completed jobs for this engagement from job queue
|
|
379
389
|
all_jobs = _read_jobs_file()
|
|
380
390
|
completed_jobs = [
|
|
381
|
-
job
|
|
382
|
-
|
|
383
|
-
|
|
391
|
+
job
|
|
392
|
+
for job in all_jobs
|
|
393
|
+
if job.get("engagement_id") == self.engagement_id
|
|
394
|
+
and job.get("status") == "done"
|
|
384
395
|
# Only include known attack tools (filter out echo, cat, etc.)
|
|
385
|
-
and job.get(
|
|
396
|
+
and job.get("tool", "").lower() in ATTACK_SIGNATURES
|
|
386
397
|
]
|
|
387
398
|
|
|
388
399
|
# Sort by finished_at descending
|
|
389
400
|
completed_jobs.sort(
|
|
390
|
-
key=lambda j: j.get(
|
|
391
|
-
reverse=True
|
|
401
|
+
key=lambda j: j.get("finished_at") or j.get("started_at") or "",
|
|
402
|
+
reverse=True,
|
|
392
403
|
)
|
|
393
404
|
|
|
394
|
-
job_ids = [job.get(
|
|
405
|
+
job_ids = [job.get("id") for job in completed_jobs if job.get("id") is not None]
|
|
395
406
|
|
|
396
407
|
if not job_ids:
|
|
397
408
|
return EngagementDetectionSummary(
|
|
@@ -402,7 +413,7 @@ class DetectionValidator:
|
|
|
402
413
|
partial=0,
|
|
403
414
|
offline=0,
|
|
404
415
|
unknown=0,
|
|
405
|
-
coverage_percent=0.0
|
|
416
|
+
coverage_percent=0.0,
|
|
406
417
|
)
|
|
407
418
|
|
|
408
419
|
# Validate each job
|
|
@@ -412,11 +423,11 @@ class DetectionValidator:
|
|
|
412
423
|
results.append(result)
|
|
413
424
|
|
|
414
425
|
# Calculate summary
|
|
415
|
-
detected = sum(1 for r in results if r.status ==
|
|
416
|
-
not_detected = sum(1 for r in results if r.status ==
|
|
417
|
-
partial = sum(1 for r in results if r.status ==
|
|
418
|
-
offline = sum(1 for r in results if r.status ==
|
|
419
|
-
unknown = sum(1 for r in results if r.status ==
|
|
426
|
+
detected = sum(1 for r in results if r.status == "detected")
|
|
427
|
+
not_detected = sum(1 for r in results if r.status == "not_detected")
|
|
428
|
+
partial = sum(1 for r in results if r.status == "partial")
|
|
429
|
+
offline = sum(1 for r in results if r.status == "offline")
|
|
430
|
+
unknown = sum(1 for r in results if r.status == "unknown")
|
|
420
431
|
|
|
421
432
|
# Calculate coverage (excluding offline and unknown)
|
|
422
433
|
countable = detected + not_detected + partial
|
|
@@ -431,10 +442,10 @@ class DetectionValidator:
|
|
|
431
442
|
offline=offline,
|
|
432
443
|
unknown=unknown,
|
|
433
444
|
coverage_percent=round(coverage, 1),
|
|
434
|
-
results=results
|
|
445
|
+
results=results,
|
|
435
446
|
)
|
|
436
447
|
|
|
437
448
|
def get_detection_gaps(self) -> List[DetectionResult]:
|
|
438
449
|
"""Get list of attacks that were NOT detected."""
|
|
439
450
|
summary = self.validate_engagement()
|
|
440
|
-
return [r for r in summary.results if r.status ==
|
|
451
|
+
return [r for r in summary.results if r.status == "not_detected"]
|
souleyez/devtools.py
CHANGED
|
@@ -16,12 +16,12 @@ import subprocess
|
|
|
16
16
|
import shutil
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
|
-
CSI =
|
|
20
|
-
RESET = CSI +
|
|
21
|
-
BOLD = CSI +
|
|
22
|
-
GREEN = CSI +
|
|
23
|
-
RED = CSI +
|
|
24
|
-
CYAN = CSI +
|
|
19
|
+
CSI = "\033["
|
|
20
|
+
RESET = CSI + "0m"
|
|
21
|
+
BOLD = CSI + "1m"
|
|
22
|
+
GREEN = CSI + "32m"
|
|
23
|
+
RED = CSI + "31m"
|
|
24
|
+
CYAN = CSI + "36m"
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def _ok(msg):
|
|
@@ -37,7 +37,9 @@ def _warn(msg):
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def _run(cmd):
|
|
40
|
-
return subprocess.run(
|
|
40
|
+
return subprocess.run(
|
|
41
|
+
cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
|
42
|
+
)
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
def dev_repair():
|
|
@@ -53,13 +55,16 @@ def dev_repair():
|
|
|
53
55
|
# 1) Sanity: are we in the repo root (setup.py present)?
|
|
54
56
|
setup_py = project / "setup.py"
|
|
55
57
|
if not setup_py.exists():
|
|
56
|
-
_warn(
|
|
58
|
+
_warn(
|
|
59
|
+
"setup.py not found in current directory. Run from your project root (e.g., ~/apps/souleyez_app)."
|
|
60
|
+
)
|
|
57
61
|
return 2
|
|
58
62
|
_ok("Project root looks good (setup.py found).")
|
|
59
63
|
|
|
60
64
|
# 2) Check import path for souleyez
|
|
61
65
|
try:
|
|
62
66
|
import importlib.util
|
|
67
|
+
|
|
63
68
|
spec = importlib.util.find_spec("souleyez")
|
|
64
69
|
_info(f"import find_spec: {spec}")
|
|
65
70
|
except Exception as e:
|
|
@@ -76,7 +81,12 @@ def dev_repair():
|
|
|
76
81
|
|
|
77
82
|
# 5) Clean site-packages leftovers (dist-info + old copies)
|
|
78
83
|
_info("Cleaning old site-packages metadata...")
|
|
79
|
-
site =
|
|
84
|
+
site = (
|
|
85
|
+
Path(sys.prefix)
|
|
86
|
+
/ "lib"
|
|
87
|
+
/ f"python{sys.version_info.major}.{sys.version_info.minor}"
|
|
88
|
+
/ "site-packages"
|
|
89
|
+
)
|
|
80
90
|
for pat in ("souleyez-*.dist-info", "souleyez"):
|
|
81
91
|
for p in site.glob(pat):
|
|
82
92
|
try:
|
|
@@ -107,10 +117,13 @@ def dev_repair():
|
|
|
107
117
|
# 9) Verify import location
|
|
108
118
|
try:
|
|
109
119
|
import souleyez
|
|
120
|
+
|
|
110
121
|
_ok(f"'souleyez' imports from: {souleyez.__file__}")
|
|
111
122
|
except Exception as e:
|
|
112
123
|
_warn(f"Import failed after reinstall: {e}")
|
|
113
124
|
return 1
|
|
114
125
|
|
|
115
|
-
_ok(
|
|
126
|
+
_ok(
|
|
127
|
+
"Dev repair complete. If version still mismatches, ensure no stray pyproject.toml remains and venv is active."
|
|
128
|
+
)
|
|
116
129
|
return 0
|
souleyez/docs/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# SoulEyez Documentation
|
|
2
2
|
|
|
3
|
-
**Version:** 2.43.
|
|
4
|
-
**Last Updated:** January
|
|
3
|
+
**Version:** 2.43.34
|
|
4
|
+
**Last Updated:** January 26, 2026
|
|
5
5
|
**Organization:** CyberSoul Security
|
|
6
6
|
|
|
7
7
|
Welcome to the SoulEyez documentation! This documentation covers architecture, development, user guides, and operational information for the SoulEyez penetration testing platform.
|
|
@@ -238,8 +238,8 @@ Legal and compliance documentation.
|
|
|
238
238
|
|
|
239
239
|
## 📞 Need Help?
|
|
240
240
|
|
|
241
|
-
- **Issues**: https://github.com/
|
|
242
|
-
- **Discussions**: https://github.com/
|
|
241
|
+
- **Issues**: https://github.com/cyber-soul-security/SoulEyez/issues
|
|
242
|
+
- **Discussions**: https://github.com/cyber-soul-security/SoulEyez/discussions
|
|
243
243
|
- **Security**: cysoul.secit@gmail.com
|
|
244
244
|
- **General**: cysoul.secit@gmail.com
|
|
245
245
|
|
|
@@ -292,7 +292,7 @@ souleyez jobs enqueue nmap 192.168.1.100 -a "-sS" -l "SYN Scan DC01"
|
|
|
292
292
|
souleyez jobs enqueue nikto http://example.com -l "Web Vuln Scan"
|
|
293
293
|
|
|
294
294
|
# Directory enumeration
|
|
295
|
-
souleyez jobs enqueue gobuster http://example.com -a "dir -w /
|
|
295
|
+
souleyez jobs enqueue gobuster http://example.com -a "dir -w data/wordlists/web_dirs_common.txt"
|
|
296
296
|
```
|
|
297
297
|
|
|
298
298
|
---
|
|
@@ -801,7 +801,7 @@ souleyez dashboard
|
|
|
801
801
|
### Web Application Testing
|
|
802
802
|
```bash
|
|
803
803
|
souleyez jobs enqueue nikto http://example.com -l "Nikto Scan"
|
|
804
|
-
souleyez jobs enqueue gobuster http://example.com -a "dir -w /
|
|
804
|
+
souleyez jobs enqueue gobuster http://example.com -a "dir -w data/wordlists/web_dirs_common.txt" -l "Dir Enum"
|
|
805
805
|
souleyez findings list
|
|
806
806
|
```
|
|
807
807
|
|