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.
Potentially problematic release.
This version of souleyez might be problematic. Click here for more details.
- 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
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Nuclei handler.
|
|
4
|
+
|
|
5
|
+
Consolidates parsing and display logic for nuclei vulnerability scanning jobs.
|
|
6
|
+
"""
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
from urllib.parse import urlparse
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
from souleyez.engine.job_status import STATUS_DONE, STATUS_ERROR, STATUS_NO_RESULTS
|
|
16
|
+
from souleyez.handlers.base import BaseToolHandler
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class NucleiHandler(BaseToolHandler):
|
|
22
|
+
"""Handler for nuclei vulnerability scanning jobs."""
|
|
23
|
+
|
|
24
|
+
tool_name = "nuclei"
|
|
25
|
+
display_name = "Nuclei"
|
|
26
|
+
|
|
27
|
+
# All handlers enabled
|
|
28
|
+
has_error_handler = True
|
|
29
|
+
has_warning_handler = True
|
|
30
|
+
has_no_results_handler = True
|
|
31
|
+
has_done_handler = True
|
|
32
|
+
|
|
33
|
+
# Severity mapping for database storage
|
|
34
|
+
SEVERITY_MAP = {"critical": 5, "high": 4, "medium": 3, "low": 2, "info": 1}
|
|
35
|
+
|
|
36
|
+
def parse_job(
|
|
37
|
+
self,
|
|
38
|
+
engagement_id: int,
|
|
39
|
+
log_path: str,
|
|
40
|
+
job: Dict[str, Any],
|
|
41
|
+
host_manager: Optional[Any] = None,
|
|
42
|
+
findings_manager: Optional[Any] = None,
|
|
43
|
+
credentials_manager: Optional[Any] = None,
|
|
44
|
+
) -> Dict[str, Any]:
|
|
45
|
+
"""
|
|
46
|
+
Parse nuclei job results.
|
|
47
|
+
|
|
48
|
+
Extracts vulnerabilities and stores them as findings.
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
from souleyez.parsers.nuclei_parser import parse_nuclei
|
|
52
|
+
from souleyez.engine.result_handler import detect_tool_error
|
|
53
|
+
|
|
54
|
+
# Import managers if not provided
|
|
55
|
+
if host_manager is None:
|
|
56
|
+
from souleyez.storage.hosts import HostManager
|
|
57
|
+
|
|
58
|
+
host_manager = HostManager()
|
|
59
|
+
if findings_manager is None:
|
|
60
|
+
from souleyez.storage.findings import FindingsManager
|
|
61
|
+
|
|
62
|
+
findings_manager = FindingsManager()
|
|
63
|
+
|
|
64
|
+
target = job.get("target", "")
|
|
65
|
+
parsed = parse_nuclei(log_path, target)
|
|
66
|
+
|
|
67
|
+
if "error" in parsed:
|
|
68
|
+
return parsed
|
|
69
|
+
|
|
70
|
+
# Get or create host
|
|
71
|
+
host_id = None
|
|
72
|
+
parsed_url = urlparse(target)
|
|
73
|
+
target_host = parsed_url.hostname or target
|
|
74
|
+
|
|
75
|
+
if target_host:
|
|
76
|
+
host_id = host_manager.add_or_update_host(
|
|
77
|
+
engagement_id, {"ip": target_host, "status": "up"}
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Store findings
|
|
81
|
+
findings_added = 0
|
|
82
|
+
for finding in parsed.get("findings", []):
|
|
83
|
+
findings_added += self._store_finding(
|
|
84
|
+
finding, engagement_id, host_id, job, findings_manager
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Check for nuclei errors
|
|
88
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
89
|
+
log_content = f.read()
|
|
90
|
+
nuclei_error = detect_tool_error(log_content, "nuclei")
|
|
91
|
+
|
|
92
|
+
# Determine status based on results
|
|
93
|
+
if nuclei_error:
|
|
94
|
+
status = STATUS_ERROR
|
|
95
|
+
elif parsed.get("findings_count", 0) > 0:
|
|
96
|
+
status = STATUS_DONE
|
|
97
|
+
else:
|
|
98
|
+
status = STATUS_NO_RESULTS
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
"tool": "nuclei",
|
|
102
|
+
"status": status,
|
|
103
|
+
"target": target,
|
|
104
|
+
"findings_count": parsed.get("findings_count", 0),
|
|
105
|
+
"findings_added": findings_added,
|
|
106
|
+
"critical": parsed.get("critical", 0),
|
|
107
|
+
"high": parsed.get("high", 0),
|
|
108
|
+
"medium": parsed.get("medium", 0),
|
|
109
|
+
"low": parsed.get("low", 0),
|
|
110
|
+
"info": parsed.get("info", 0),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.error(f"Error parsing nuclei job: {e}")
|
|
115
|
+
return {"error": str(e)}
|
|
116
|
+
|
|
117
|
+
def _store_finding(
|
|
118
|
+
self,
|
|
119
|
+
finding: Dict,
|
|
120
|
+
engagement_id: int,
|
|
121
|
+
host_id: Optional[int],
|
|
122
|
+
job: Dict,
|
|
123
|
+
findings_manager: Any,
|
|
124
|
+
) -> int:
|
|
125
|
+
"""Store a single nuclei finding."""
|
|
126
|
+
severity = finding.get("severity", "info")
|
|
127
|
+
title = finding.get("name", "Unknown Vulnerability")
|
|
128
|
+
description = finding.get("description", "")
|
|
129
|
+
|
|
130
|
+
# Add CVE and reference info to description
|
|
131
|
+
if finding.get("cve_id"):
|
|
132
|
+
description += f"\n\nCVE: {finding['cve_id']}"
|
|
133
|
+
if finding.get("cvss_score"):
|
|
134
|
+
description += f"\nCVSS Score: {finding['cvss_score']}"
|
|
135
|
+
if finding.get("references"):
|
|
136
|
+
description += f"\n\nReferences:\n" + "\n".join(finding["references"])
|
|
137
|
+
|
|
138
|
+
# Add deduplication attribution if this scan covered multiple IPs
|
|
139
|
+
metadata = job.get("metadata", {})
|
|
140
|
+
associated_ips = metadata.get("associated_ips", [])
|
|
141
|
+
if associated_ips and len(associated_ips) > 1:
|
|
142
|
+
target_host = job.get("target", "")
|
|
143
|
+
representative_ip = metadata.get("representative_ip", target_host)
|
|
144
|
+
domain = metadata.get("domain_context", "")
|
|
145
|
+
description += f"\n\n[Web Target Deduplication] This finding was discovered on {representative_ip}"
|
|
146
|
+
description += f" (representative IP for {len(associated_ips)} IPs serving"
|
|
147
|
+
if domain:
|
|
148
|
+
description += f" {domain}"
|
|
149
|
+
description += f"). All affected IPs: {', '.join(associated_ips)}"
|
|
150
|
+
|
|
151
|
+
# Build evidence with matched_at, cve_id, and template_id
|
|
152
|
+
evidence = f"Template: {finding.get('template_id', 'unknown')}"
|
|
153
|
+
if finding.get("matched_at"):
|
|
154
|
+
evidence += f"\nMatched at: {finding.get('matched_at')}"
|
|
155
|
+
if finding.get("cve_id"):
|
|
156
|
+
evidence += f"\nCVE: {finding.get('cve_id')}"
|
|
157
|
+
|
|
158
|
+
findings_manager.add_finding(
|
|
159
|
+
engagement_id=engagement_id,
|
|
160
|
+
host_id=host_id,
|
|
161
|
+
title=title,
|
|
162
|
+
description=description,
|
|
163
|
+
severity=severity,
|
|
164
|
+
finding_type="vulnerability",
|
|
165
|
+
tool="nuclei",
|
|
166
|
+
evidence=evidence,
|
|
167
|
+
)
|
|
168
|
+
return 1
|
|
169
|
+
|
|
170
|
+
def display_done(
|
|
171
|
+
self,
|
|
172
|
+
job: Dict[str, Any],
|
|
173
|
+
log_path: str,
|
|
174
|
+
show_all: bool = False,
|
|
175
|
+
show_passwords: bool = False,
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Display successful nuclei scan results."""
|
|
178
|
+
try:
|
|
179
|
+
from souleyez.parsers.nuclei_parser import parse_nuclei_output
|
|
180
|
+
|
|
181
|
+
if not log_path or not os.path.exists(log_path):
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
185
|
+
log_content = f.read()
|
|
186
|
+
parsed = parse_nuclei_output(log_content, job.get("target", ""))
|
|
187
|
+
|
|
188
|
+
findings = parsed.get("findings", [])
|
|
189
|
+
|
|
190
|
+
# Always show summary header
|
|
191
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
192
|
+
click.echo(click.style("VULNERABILITY SCAN", bold=True, fg="cyan"))
|
|
193
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
194
|
+
click.echo()
|
|
195
|
+
|
|
196
|
+
click.echo(
|
|
197
|
+
click.style(f"Target: {job.get('target', 'unknown')}", bold=True)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if findings:
|
|
201
|
+
click.echo(
|
|
202
|
+
click.style(
|
|
203
|
+
f"Result: {len(findings)} vulnerability(ies) found",
|
|
204
|
+
fg="red",
|
|
205
|
+
bold=True,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
click.echo()
|
|
209
|
+
|
|
210
|
+
# Group by severity
|
|
211
|
+
by_severity = {}
|
|
212
|
+
for finding in findings:
|
|
213
|
+
severity = finding.get("severity", "info").lower()
|
|
214
|
+
if severity not in by_severity:
|
|
215
|
+
by_severity[severity] = []
|
|
216
|
+
by_severity[severity].append(finding)
|
|
217
|
+
|
|
218
|
+
# Display in order: critical, high, medium, low, info
|
|
219
|
+
max_per_severity = None if show_all else 5
|
|
220
|
+
for severity in ["critical", "high", "medium", "low", "info"]:
|
|
221
|
+
if severity in by_severity:
|
|
222
|
+
items = by_severity[severity]
|
|
223
|
+
self._display_severity_section(
|
|
224
|
+
severity, items, max_per_severity
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
228
|
+
click.echo()
|
|
229
|
+
else:
|
|
230
|
+
self.display_no_results(job, log_path)
|
|
231
|
+
|
|
232
|
+
except Exception as e:
|
|
233
|
+
logger.debug(f"Error in display_done: {e}")
|
|
234
|
+
|
|
235
|
+
def _display_severity_section(
|
|
236
|
+
self, severity: str, items: List[Dict], max_items: Optional[int]
|
|
237
|
+
) -> None:
|
|
238
|
+
"""Display a severity section with findings."""
|
|
239
|
+
# Color code severity
|
|
240
|
+
if severity == "critical":
|
|
241
|
+
sev_display = click.style(severity.upper(), fg="red", bold=True)
|
|
242
|
+
elif severity == "high":
|
|
243
|
+
sev_display = click.style(severity.upper(), fg="red")
|
|
244
|
+
elif severity == "medium":
|
|
245
|
+
sev_display = click.style(severity.upper(), fg="yellow")
|
|
246
|
+
elif severity == "low":
|
|
247
|
+
sev_display = click.style(severity.upper(), fg="blue")
|
|
248
|
+
else:
|
|
249
|
+
sev_display = click.style(severity.upper(), dim=True)
|
|
250
|
+
|
|
251
|
+
click.echo(f"{sev_display}: {len(items)} finding(s)")
|
|
252
|
+
|
|
253
|
+
display_items = items if max_items is None else items[:max_items]
|
|
254
|
+
for finding in display_items:
|
|
255
|
+
template = finding.get("template_id", "unknown")
|
|
256
|
+
url = finding.get("matched_at", "")
|
|
257
|
+
click.echo(f" - [{template}] {url}")
|
|
258
|
+
|
|
259
|
+
if max_items and len(items) > max_items:
|
|
260
|
+
click.echo(f" ... and {len(items) - max_items} more")
|
|
261
|
+
click.echo()
|
|
262
|
+
|
|
263
|
+
def display_warning(
|
|
264
|
+
self,
|
|
265
|
+
job: Dict[str, Any],
|
|
266
|
+
log_path: str,
|
|
267
|
+
log_content: Optional[str] = None,
|
|
268
|
+
) -> None:
|
|
269
|
+
"""Display warning status for nuclei scan."""
|
|
270
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
271
|
+
click.echo(click.style("[WARNING] NUCLEI SCAN", bold=True, fg="yellow"))
|
|
272
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
273
|
+
click.echo()
|
|
274
|
+
click.echo(" Scan completed with warnings. Check raw logs for details.")
|
|
275
|
+
click.echo(" Press [r] to view raw logs.")
|
|
276
|
+
click.echo()
|
|
277
|
+
click.echo(click.style("=" * 70, fg="yellow"))
|
|
278
|
+
click.echo()
|
|
279
|
+
|
|
280
|
+
def display_error(
|
|
281
|
+
self,
|
|
282
|
+
job: Dict[str, Any],
|
|
283
|
+
log_path: str,
|
|
284
|
+
log_content: Optional[str] = None,
|
|
285
|
+
) -> None:
|
|
286
|
+
"""Display error status for nuclei scan."""
|
|
287
|
+
# Read log if not provided
|
|
288
|
+
if log_content is None and log_path and os.path.exists(log_path):
|
|
289
|
+
try:
|
|
290
|
+
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
291
|
+
log_content = f.read()
|
|
292
|
+
except Exception:
|
|
293
|
+
log_content = ""
|
|
294
|
+
|
|
295
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
296
|
+
click.echo(click.style("[ERROR] NUCLEI SCAN FAILED", bold=True, fg="red"))
|
|
297
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
298
|
+
click.echo()
|
|
299
|
+
|
|
300
|
+
# Check for common nuclei errors
|
|
301
|
+
error_msg = None
|
|
302
|
+
if log_content:
|
|
303
|
+
if (
|
|
304
|
+
"Could not run nuclei" in log_content
|
|
305
|
+
or "not found" in log_content.lower()
|
|
306
|
+
):
|
|
307
|
+
error_msg = "Nuclei binary not found - check installation"
|
|
308
|
+
elif "no templates" in log_content.lower():
|
|
309
|
+
error_msg = "No templates found - update nuclei templates"
|
|
310
|
+
elif "rate limit" in log_content.lower():
|
|
311
|
+
error_msg = "Rate limited by target - try with -rl flag"
|
|
312
|
+
elif "timed out" in log_content.lower() or "timeout" in log_content.lower():
|
|
313
|
+
error_msg = "Scan timed out - target may be slow or filtering"
|
|
314
|
+
elif "Connection refused" in log_content:
|
|
315
|
+
error_msg = "Connection refused - target may be down"
|
|
316
|
+
elif "could not connect" in log_content.lower():
|
|
317
|
+
error_msg = "Could not connect to target"
|
|
318
|
+
elif "[ERR]" in log_content or "[FTL]" in log_content:
|
|
319
|
+
match = re.search(r"\[(ERR|FTL)\]\s*(.+?)(?:\n|$)", log_content)
|
|
320
|
+
if match:
|
|
321
|
+
error_msg = match.group(2).strip()[:100]
|
|
322
|
+
|
|
323
|
+
if error_msg:
|
|
324
|
+
click.echo(f" {error_msg}")
|
|
325
|
+
else:
|
|
326
|
+
click.echo(" Scan failed - see raw logs for details (press 'r')")
|
|
327
|
+
|
|
328
|
+
click.echo()
|
|
329
|
+
click.echo(click.style("=" * 70, fg="red"))
|
|
330
|
+
click.echo()
|
|
331
|
+
|
|
332
|
+
def display_no_results(
|
|
333
|
+
self,
|
|
334
|
+
job: Dict[str, Any],
|
|
335
|
+
log_path: str,
|
|
336
|
+
) -> None:
|
|
337
|
+
"""Display no_results status for nuclei scan."""
|
|
338
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
339
|
+
click.echo(click.style("VULNERABILITY SCAN", bold=True, fg="cyan"))
|
|
340
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
341
|
+
click.echo()
|
|
342
|
+
|
|
343
|
+
click.echo(click.style(f"Target: {job.get('target', 'unknown')}", bold=True))
|
|
344
|
+
click.echo(
|
|
345
|
+
click.style("Result: No vulnerabilities detected", fg="green", bold=True)
|
|
346
|
+
)
|
|
347
|
+
click.echo()
|
|
348
|
+
click.echo(" The scan completed without finding any vulnerabilities.")
|
|
349
|
+
click.echo(" This could mean the target is secure or templates didn't match.")
|
|
350
|
+
click.echo()
|
|
351
|
+
click.echo(click.style("Tips:", dim=True))
|
|
352
|
+
click.echo(
|
|
353
|
+
" - Try different severity levels: -severity critical,high,medium,low"
|
|
354
|
+
)
|
|
355
|
+
click.echo(" - Try specific tags: -tags cve,exposure,misconfiguration")
|
|
356
|
+
click.echo(" - Update templates: nuclei -update-templates")
|
|
357
|
+
click.echo()
|
|
358
|
+
click.echo(click.style("=" * 70, fg="cyan"))
|
|
359
|
+
click.echo()
|