souleyez 3.0.0__py3-none-any.whl → 3.0.9__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 -1
- souleyez/ai/__init__.py +7 -7
- souleyez/ai/action_mapper.py +3 -2
- souleyez/ai/chain_advisor.py +2 -1
- souleyez/ai/claude_provider.py +2 -2
- souleyez/ai/context_builder.py +4 -2
- souleyez/ai/executor.py +9 -6
- souleyez/ai/feedback_handler.py +4 -2
- souleyez/ai/llm_provider.py +2 -2
- souleyez/ai/ollama_provider.py +2 -2
- souleyez/ai/ollama_service.py +10 -26
- souleyez/ai/path_scorer.py +2 -1
- souleyez/ai/recommender.py +6 -4
- souleyez/ai/report_context.py +2 -2
- souleyez/ai/report_service.py +5 -5
- souleyez/ai/result_parser.py +3 -2
- souleyez/ai/safety.py +5 -2
- souleyez/auth/__init__.py +6 -6
- souleyez/auth/audit.py +2 -2
- souleyez/auth/engagement_access.py +5 -7
- souleyez/auth/permissions.py +1 -1
- souleyez/auth/session_manager.py +5 -5
- souleyez/auth/user_manager.py +4 -5
- souleyez/commands/audit.py +6 -5
- souleyez/commands/auth.py +6 -5
- souleyez/commands/deliverables.py +2 -3
- souleyez/commands/engagement.py +3 -3
- souleyez/commands/license.py +3 -2
- souleyez/commands/screenshots.py +5 -4
- souleyez/commands/user.py +10 -8
- souleyez/config.py +4 -2
- souleyez/core/credential_tester.py +4 -2
- souleyez/core/cve_mappings.py +2 -1
- souleyez/core/cve_matcher.py +2 -1
- souleyez/core/msf_auto_mapper.py +2 -0
- souleyez/core/msf_chain_engine.py +3 -1
- souleyez/core/msf_database.py +7 -13
- souleyez/core/msf_integration.py +2 -2
- souleyez/core/msf_rpc_client.py +3 -2
- souleyez/core/msf_rpc_manager.py +4 -4
- souleyez/core/msf_sync_manager.py +7 -7
- souleyez/core/network_utils.py +1 -1
- souleyez/core/parser_handler.py +2 -1
- souleyez/core/pending_chains.py +4 -3
- souleyez/core/templates.py +5 -2
- souleyez/core/tool_chaining.py +101 -70
- souleyez/core/version_utils.py +1 -0
- souleyez/core/vuln_correlation.py +3 -2
- souleyez/core/web_utils.py +2 -1
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +1 -1
- souleyez/detection/mitre_mappings.py +1 -2
- souleyez/detection/validator.py +5 -4
- souleyez/devtools.py +4 -2
- souleyez/docs/README.md +2 -2
- souleyez/engine/background.py +168 -7
- souleyez/engine/base.py +2 -1
- souleyez/engine/loader.py +4 -2
- souleyez/engine/log_sanitizer.py +1 -0
- souleyez/engine/manager.py +3 -1
- souleyez/engine/result_handler.py +50 -67
- souleyez/engine/worker_manager.py +6 -4
- souleyez/export/evidence_bundle.py +1 -0
- souleyez/handlers/base.py +1 -0
- souleyez/handlers/bash_handler.py +1 -0
- souleyez/handlers/bloodhound_handler.py +1 -0
- souleyez/handlers/certipy_handler.py +1 -0
- souleyez/handlers/crackmapexec_handler.py +2 -20
- souleyez/handlers/dnsrecon_handler.py +2 -1
- souleyez/handlers/enum4linux_handler.py +65 -37
- souleyez/handlers/evil_winrm_handler.py +1 -0
- souleyez/handlers/ffuf_handler.py +3 -1
- souleyez/handlers/gobuster_handler.py +7 -6
- souleyez/handlers/gpp_extract_handler.py +1 -0
- souleyez/handlers/hashcat_handler.py +1 -0
- souleyez/handlers/hydra_handler.py +5 -2
- souleyez/handlers/impacket_getuserspns_handler.py +1 -0
- souleyez/handlers/impacket_psexec_handler.py +1 -0
- souleyez/handlers/impacket_secretsdump_handler.py +1 -0
- souleyez/handlers/john_handler.py +1 -0
- souleyez/handlers/katana_handler.py +39 -2
- souleyez/handlers/kerbrute_handler.py +1 -0
- souleyez/handlers/ldapsearch_handler.py +90 -17
- souleyez/handlers/lfi_extract_handler.py +1 -0
- souleyez/handlers/msf_auxiliary_handler.py +1 -0
- souleyez/handlers/msf_exploit_handler.py +1 -0
- souleyez/handlers/nikto_handler.py +2 -1
- souleyez/handlers/nmap_handler.py +2 -1
- souleyez/handlers/nuclei_handler.py +2 -1
- souleyez/handlers/nxc_handler.py +3 -18
- souleyez/handlers/rdp_sec_check_handler.py +1 -0
- souleyez/handlers/registry.py +1 -0
- souleyez/handlers/responder_handler.py +1 -0
- souleyez/handlers/service_explorer_handler.py +2 -1
- souleyez/handlers/smbclient_handler.py +1 -0
- souleyez/handlers/smbmap_handler.py +3 -2
- souleyez/handlers/sqlmap_handler.py +6 -4
- souleyez/handlers/theharvester_handler.py +2 -1
- souleyez/handlers/web_login_test_handler.py +1 -0
- souleyez/handlers/whois_handler.py +3 -2
- souleyez/handlers/wpscan_handler.py +2 -1
- souleyez/history.py +4 -3
- souleyez/importers/msf_importer.py +5 -3
- souleyez/importers/smart_importer.py +6 -4
- souleyez/integrations/siem/__init__.py +6 -6
- souleyez/integrations/siem/base.py +1 -1
- souleyez/integrations/siem/elastic.py +3 -3
- souleyez/integrations/siem/factory.py +1 -2
- souleyez/integrations/siem/googlesecops.py +4 -4
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +1 -1
- souleyez/integrations/siem/sentinel.py +3 -3
- souleyez/integrations/siem/splunk.py +3 -3
- souleyez/integrations/siem/wazuh.py +4 -4
- souleyez/integrations/wazuh/__init__.py +1 -1
- souleyez/integrations/wazuh/client.py +3 -2
- souleyez/integrations/wazuh/config.py +3 -2
- souleyez/integrations/wazuh/host_mapper.py +3 -1
- souleyez/integrations/wazuh/sync.py +4 -1
- souleyez/intelligence/__init__.py +1 -1
- souleyez/intelligence/correlation_analyzer.py +6 -5
- souleyez/intelligence/exploit_knowledge.py +4 -4
- souleyez/intelligence/exploit_suggestions.py +4 -3
- souleyez/intelligence/gap_analyzer.py +5 -3
- souleyez/intelligence/gap_detector.py +2 -0
- souleyez/intelligence/sensitive_tables.py +1 -1
- souleyez/intelligence/service_parser.py +1 -0
- souleyez/intelligence/surface_analyzer.py +9 -9
- souleyez/intelligence/target_parser.py +1 -0
- souleyez/licensing/__init__.py +3 -3
- souleyez/main.py +25 -18
- souleyez/migrations/fix_job_counter.py +2 -1
- souleyez/parsers/bloodhound_parser.py +1 -0
- souleyez/parsers/crackmapexec_parser.py +2 -1
- souleyez/parsers/dalfox_parser.py +3 -2
- souleyez/parsers/dnsrecon_parser.py +2 -1
- souleyez/parsers/enum4linux_parser.py +2 -1
- souleyez/parsers/ffuf_parser.py +2 -1
- souleyez/parsers/gobuster_parser.py +2 -1
- souleyez/parsers/hashcat_parser.py +3 -2
- souleyez/parsers/http_fingerprint_parser.py +2 -1
- souleyez/parsers/hydra_parser.py +2 -1
- souleyez/parsers/impacket_parser.py +2 -1
- souleyez/parsers/john_parser.py +4 -3
- souleyez/parsers/katana_parser.py +134 -2
- souleyez/parsers/msf_parser.py +2 -1
- souleyez/parsers/nikto_parser.py +2 -1
- souleyez/parsers/nmap_parser.py +14 -3
- souleyez/parsers/nuclei_parser.py +3 -2
- souleyez/parsers/responder_parser.py +1 -0
- souleyez/parsers/searchsploit_parser.py +3 -2
- souleyez/parsers/service_explorer_parser.py +1 -0
- souleyez/parsers/smbmap_parser.py +2 -1
- souleyez/parsers/sqlmap_parser.py +36 -2
- souleyez/parsers/theharvester_parser.py +2 -1
- souleyez/parsers/whois_parser.py +2 -1
- souleyez/parsers/wpscan_parser.py +3 -2
- souleyez/plugins/afp.py +3 -1
- souleyez/plugins/afp_brute.py +3 -1
- souleyez/plugins/ard.py +3 -1
- souleyez/plugins/bloodhound.py +3 -2
- souleyez/plugins/certipy.py +1 -0
- souleyez/plugins/crackmapexec.py +11 -7
- souleyez/plugins/dalfox.py +5 -2
- souleyez/plugins/dns_hijack.py +3 -1
- souleyez/plugins/dnsrecon.py +3 -1
- souleyez/plugins/enum4linux.py +3 -1
- souleyez/plugins/evil_winrm.py +1 -0
- souleyez/plugins/ffuf.py +3 -1
- souleyez/plugins/firmware_extract.py +3 -2
- souleyez/plugins/gobuster.py +6 -3
- souleyez/plugins/gpp_extract.py +1 -0
- souleyez/plugins/hashcat.py +2 -1
- souleyez/plugins/http_fingerprint.py +57 -7
- souleyez/plugins/hydra.py +5 -3
- souleyez/plugins/impacket_common.py +40 -0
- souleyez/plugins/impacket_getnpusers.py +19 -2
- souleyez/plugins/impacket_getuserspns.py +158 -0
- souleyez/plugins/impacket_psexec.py +19 -2
- souleyez/plugins/impacket_secretsdump.py +19 -2
- souleyez/plugins/impacket_smbclient.py +19 -2
- souleyez/plugins/john.py +2 -1
- souleyez/plugins/katana.py +48 -6
- souleyez/plugins/kerbrute.py +1 -0
- souleyez/plugins/lfi_extract.py +1 -0
- souleyez/plugins/macos_ssh.py +3 -1
- souleyez/plugins/mdns.py +3 -1
- souleyez/plugins/msf_auxiliary.py +3 -2
- souleyez/plugins/msf_exploit.py +6 -5
- souleyez/plugins/nikto.py +5 -2
- souleyez/plugins/nmap.py +6 -4
- souleyez/plugins/nuclei.py +3 -1
- souleyez/plugins/nxc.py +1 -0
- souleyez/plugins/plugin_base.py +3 -2
- souleyez/plugins/plugin_template.py +3 -2
- souleyez/plugins/rdp_sec_check.py +1 -0
- souleyez/plugins/responder.py +2 -1
- souleyez/plugins/router_http_brute.py +3 -1
- souleyez/plugins/router_ssh_brute.py +3 -1
- souleyez/plugins/router_telnet_brute.py +3 -1
- souleyez/plugins/routersploit.py +5 -3
- souleyez/plugins/routersploit_exploit.py +5 -3
- souleyez/plugins/searchsploit.py +1 -0
- souleyez/plugins/service_explorer.py +2 -1
- souleyez/plugins/smbmap.py +3 -1
- souleyez/plugins/smbpasswd.py +1 -0
- souleyez/plugins/sqlmap.py +3 -1
- souleyez/plugins/theharvester.py +3 -1
- souleyez/plugins/tr069.py +3 -1
- souleyez/plugins/upnp.py +3 -1
- souleyez/plugins/upnp_abuse.py +4 -2
- souleyez/plugins/vnc_access.py +4 -2
- souleyez/plugins/vnc_brute.py +3 -1
- souleyez/plugins/web_login_test.py +1 -0
- souleyez/plugins/whois.py +3 -1
- souleyez/plugins/wpscan.py +3 -1
- souleyez/reporting/attack_chain.py +2 -1
- souleyez/reporting/charts.py +1 -0
- souleyez/reporting/compliance_mappings.py +1 -0
- souleyez/reporting/detection_report.py +10 -10
- souleyez/reporting/formatters.py +7 -12
- souleyez/reporting/generator.py +34 -46
- souleyez/reporting/metrics.py +2 -1
- souleyez/scanner.py +6 -3
- souleyez/security/__init__.py +7 -5
- souleyez/security/scope_validator.py +5 -4
- souleyez/security.py +5 -2
- souleyez/storage/credentials.py +14 -19
- souleyez/storage/crypto.py +7 -4
- souleyez/storage/database.py +6 -6
- souleyez/storage/db.py +8 -8
- souleyez/storage/deliverable_evidence.py +2 -1
- souleyez/storage/deliverable_exporter.py +3 -2
- souleyez/storage/deliverable_templates.py +2 -1
- souleyez/storage/deliverables.py +2 -1
- souleyez/storage/engagements.py +6 -4
- souleyez/storage/evidence.py +5 -4
- souleyez/storage/execution_log.py +4 -2
- souleyez/storage/exploit_attempts.py +3 -2
- souleyez/storage/exploits.py +3 -1
- souleyez/storage/findings.py +3 -1
- souleyez/storage/hosts.py +5 -2
- souleyez/storage/migrate_to_engagements.py +14 -24
- souleyez/storage/migrations/_001_add_credential_enhancements.py +12 -21
- souleyez/storage/migrations/_003_add_execution_log.py +8 -13
- souleyez/storage/migrations/_005_screenshots.py +2 -4
- souleyez/storage/migrations/_006_deliverables.py +2 -4
- souleyez/storage/migrations/_007_deliverable_templates.py +4 -8
- souleyez/storage/migrations/_008_add_nuclei_table.py +2 -4
- souleyez/storage/migrations/_010_evidence_linking.py +6 -12
- souleyez/storage/migrations/_012_team_collaboration.py +12 -24
- souleyez/storage/migrations/_013_add_host_tags.py +2 -4
- souleyez/storage/migrations/_014_exploit_attempts.py +10 -20
- souleyez/storage/migrations/_015_add_mac_os_fields.py +4 -8
- souleyez/storage/migrations/_016_add_domain_field.py +2 -4
- souleyez/storage/migrations/_017_msf_sessions.py +8 -16
- souleyez/storage/migrations/_018_add_osint_target.py +4 -8
- souleyez/storage/migrations/_019_add_engagement_type.py +4 -8
- souleyez/storage/migrations/_020_add_rbac.py +9 -17
- souleyez/storage/migrations/_021_wazuh_integration.py +4 -8
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +2 -4
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +4 -8
- souleyez/storage/migrations/_026_add_engagement_scope.py +4 -8
- souleyez/storage/migrations/_027_multi_siem_persistence.py +8 -16
- souleyez/storage/migrations/__init__.py +1 -4
- souleyez/storage/migrations/migration_manager.py +6 -9
- souleyez/storage/msf_sessions.py +1 -1
- souleyez/storage/osint.py +3 -1
- souleyez/storage/recommendation_engine.py +3 -2
- souleyez/storage/screenshots.py +2 -1
- souleyez/storage/smb_shares.py +3 -1
- souleyez/storage/sqlmap_data.py +6 -4
- souleyez/storage/team_collaboration.py +3 -2
- souleyez/storage/timeline_tracker.py +2 -1
- souleyez/storage/wazuh_vulns.py +3 -1
- souleyez/storage/web_paths.py +3 -1
- souleyez/testing/credential_tester.py +2 -0
- souleyez/ui/__init__.py +2 -1
- souleyez/ui/ai_quotes.py +1 -1
- souleyez/ui/attack_surface.py +50 -28
- souleyez/ui/chain_rules_view.py +6 -3
- souleyez/ui/correlation_view.py +3 -2
- souleyez/ui/dashboard.py +85 -139
- souleyez/ui/deliverables_view.py +1 -1
- souleyez/ui/design_system.py +5 -3
- souleyez/ui/errors.py +3 -1
- souleyez/ui/evidence_linking_view.py +2 -1
- souleyez/ui/evidence_vault.py +11 -6
- souleyez/ui/exploit_suggestions_view.py +11 -7
- souleyez/ui/export_view.py +3 -1
- souleyez/ui/gap_analysis_view.py +6 -3
- souleyez/ui/help_system.py +4 -1
- souleyez/ui/intelligence_view.py +7 -3
- souleyez/ui/interactive.py +1280 -558
- souleyez/ui/interactive_selector.py +3 -2
- souleyez/ui/log_formatter.py +1 -0
- souleyez/ui/menu_components.py +3 -1
- souleyez/ui/msf_auxiliary_menu.py +4 -1
- souleyez/ui/pending_chains_view.py +15 -12
- souleyez/ui/progress_indicators.py +5 -2
- souleyez/ui/recommendations_view.py +4 -2
- souleyez/ui/rule_builder.py +4 -1
- souleyez/ui/setup_wizard.py +10 -8
- souleyez/ui/shortcuts.py +1 -1
- souleyez/ui/splunk_gap_analysis_view.py +7 -4
- souleyez/ui/splunk_vulns_view.py +4 -1
- souleyez/ui/team_dashboard.py +7 -5
- souleyez/ui/template_selector.py +2 -1
- souleyez/ui/terminal.py +3 -2
- souleyez/ui/timeline_view.py +2 -1
- souleyez/ui/tool_setup.py +92 -31
- souleyez/ui/tutorial.py +7 -4
- souleyez/ui/tutorial_state.py +3 -2
- souleyez/ui/wazuh_vulns_view.py +5 -2
- souleyez/ui/wordlist_browser.py +4 -3
- souleyez/ui.py +13 -7
- souleyez/utils/tool_checker.py +95 -17
- souleyez/utils.py +4 -4
- souleyez/wordlists.py +1 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/METADATA +1 -1
- souleyez-3.0.9.dist-info/RECORD +445 -0
- souleyez-3.0.0.dist-info/RECORD +0 -443
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/WHEEL +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/entry_points.txt +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/licenses/LICENSE +0 -0
- {souleyez-3.0.0.dist-info → souleyez-3.0.9.dist-info}/top_level.txt +0 -0
souleyez/engine/background.py
CHANGED
|
@@ -73,6 +73,10 @@ TRANSIENT_ERROR_PATTERNS = [
|
|
|
73
73
|
"Resource temporarily unavailable",
|
|
74
74
|
"SMBTimeout",
|
|
75
75
|
"timed out while waiting",
|
|
76
|
+
# Impacket-specific timeout patterns
|
|
77
|
+
"] timed out", # Matches: [Errno Connection error (IP:port)] timed out
|
|
78
|
+
"RemoteOperations failed", # Impacket remote operation failures
|
|
79
|
+
"Errno Connection error", # Generic Impacket connection errors
|
|
76
80
|
]
|
|
77
81
|
|
|
78
82
|
_lock = threading.RLock() # Reentrant lock allows nested acquisition by same thread
|
|
@@ -88,6 +92,40 @@ def _is_transient_error(log_content: str) -> bool:
|
|
|
88
92
|
return False
|
|
89
93
|
|
|
90
94
|
|
|
95
|
+
def _is_netexec_flaky_empty(log_content: str, job: dict) -> bool:
|
|
96
|
+
"""
|
|
97
|
+
Check if netexec/crackmapexec produced no output (flaky ARM behavior).
|
|
98
|
+
|
|
99
|
+
On ARM, netexec is ~20% flaky - it exits 0 but sometimes produces
|
|
100
|
+
zero output. This detects ONLY that case.
|
|
101
|
+
|
|
102
|
+
DOES NOT retry when:
|
|
103
|
+
- Access denied (has "[-]" error output)
|
|
104
|
+
- Connected but no shares (legitimate result)
|
|
105
|
+
- Any netexec output exists
|
|
106
|
+
"""
|
|
107
|
+
tool = job.get("tool", "").lower()
|
|
108
|
+
args = job.get("args", [])
|
|
109
|
+
|
|
110
|
+
if tool not in ("crackmapexec", "nxc", "netexec"):
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
args_str = " ".join(args).lower() if args else ""
|
|
114
|
+
if "--shares" not in args_str and "smb" not in args_str:
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
lower = log_content.lower()
|
|
118
|
+
|
|
119
|
+
# If we got ANY netexec output, it's a real response - don't retry
|
|
120
|
+
# This includes error output like "[-]" which indicates access denied
|
|
121
|
+
has_any_output = any(
|
|
122
|
+
x in lower for x in ["smb", "[*]", "[+]", "[-]", "445", "signing"]
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Only retry if truly zero output (ARM flakiness)
|
|
126
|
+
return not has_any_output
|
|
127
|
+
|
|
128
|
+
|
|
91
129
|
class _CrossProcessLock:
|
|
92
130
|
"""
|
|
93
131
|
Cross-process file lock using fcntl.flock().
|
|
@@ -1516,6 +1554,78 @@ def _is_stdbuf_available() -> bool:
|
|
|
1516
1554
|
return _stdbuf_available
|
|
1517
1555
|
|
|
1518
1556
|
|
|
1557
|
+
def _is_python_tool(cmd: List[str]) -> bool:
|
|
1558
|
+
"""
|
|
1559
|
+
Check if a command is a Python-based tool.
|
|
1560
|
+
|
|
1561
|
+
stdbuf doesn't work well with Python scripts because Python manages its own
|
|
1562
|
+
buffering internally. On some systems (especially ARM), stdbuf can prevent
|
|
1563
|
+
Python tools from producing any output at all.
|
|
1564
|
+
|
|
1565
|
+
Args:
|
|
1566
|
+
cmd: Command array to check
|
|
1567
|
+
|
|
1568
|
+
Returns:
|
|
1569
|
+
True if the command is a Python tool that should skip stdbuf
|
|
1570
|
+
"""
|
|
1571
|
+
if not cmd:
|
|
1572
|
+
return False
|
|
1573
|
+
|
|
1574
|
+
executable = cmd[0]
|
|
1575
|
+
|
|
1576
|
+
# Direct Python invocation
|
|
1577
|
+
if executable in ("python", "python3") or executable.startswith("python"):
|
|
1578
|
+
return True
|
|
1579
|
+
|
|
1580
|
+
# .py extension
|
|
1581
|
+
if executable.endswith(".py"):
|
|
1582
|
+
return True
|
|
1583
|
+
|
|
1584
|
+
# Known Python tools that don't work with stdbuf
|
|
1585
|
+
# These are tools installed via pip/pipx that are Python scripts
|
|
1586
|
+
python_tools = {
|
|
1587
|
+
"netexec",
|
|
1588
|
+
"nxc",
|
|
1589
|
+
"crackmapexec",
|
|
1590
|
+
"cme",
|
|
1591
|
+
"smbmap",
|
|
1592
|
+
"impacket-GetNPUsers",
|
|
1593
|
+
"impacket-GetUserSPNs",
|
|
1594
|
+
"impacket-secretsdump",
|
|
1595
|
+
"impacket-psexec",
|
|
1596
|
+
"impacket-smbclient",
|
|
1597
|
+
"impacket-wmiexec",
|
|
1598
|
+
"impacket-dcomexec",
|
|
1599
|
+
"impacket-atexec",
|
|
1600
|
+
"GetNPUsers.py",
|
|
1601
|
+
"GetUserSPNs.py",
|
|
1602
|
+
"secretsdump.py",
|
|
1603
|
+
"psexec.py",
|
|
1604
|
+
"smbclient.py",
|
|
1605
|
+
"certipy",
|
|
1606
|
+
"bloodhound-python",
|
|
1607
|
+
"ldapdomaindump",
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
# Check base name (handle full paths)
|
|
1611
|
+
base_name = os.path.basename(executable)
|
|
1612
|
+
if base_name in python_tools:
|
|
1613
|
+
return True
|
|
1614
|
+
|
|
1615
|
+
# Check shebang for Python interpreter
|
|
1616
|
+
exe_path = shutil.which(executable)
|
|
1617
|
+
if exe_path:
|
|
1618
|
+
try:
|
|
1619
|
+
with open(exe_path, "rb") as f:
|
|
1620
|
+
first_bytes = f.read(100)
|
|
1621
|
+
if first_bytes.startswith(b"#!") and b"python" in first_bytes:
|
|
1622
|
+
return True
|
|
1623
|
+
except (IOError, OSError):
|
|
1624
|
+
pass
|
|
1625
|
+
|
|
1626
|
+
return False
|
|
1627
|
+
|
|
1628
|
+
|
|
1519
1629
|
def _wrap_cmd_for_line_buffering(cmd: List[str]) -> List[str]:
|
|
1520
1630
|
"""
|
|
1521
1631
|
Wrap a command with stdbuf for line-buffered output when available.
|
|
@@ -1524,6 +1634,9 @@ def _wrap_cmd_for_line_buffering(cmd: List[str]) -> List[str]:
|
|
|
1524
1634
|
improving real-time log monitoring and ensuring output is captured
|
|
1525
1635
|
before process termination.
|
|
1526
1636
|
|
|
1637
|
+
Note: Python tools are excluded because stdbuf interferes with Python's
|
|
1638
|
+
internal buffering and can cause zero output on some systems (Ubuntu ARM).
|
|
1639
|
+
|
|
1527
1640
|
Args:
|
|
1528
1641
|
cmd: Command to wrap
|
|
1529
1642
|
|
|
@@ -1533,6 +1646,10 @@ def _wrap_cmd_for_line_buffering(cmd: List[str]) -> List[str]:
|
|
|
1533
1646
|
if not cmd:
|
|
1534
1647
|
return cmd
|
|
1535
1648
|
|
|
1649
|
+
# Skip stdbuf for Python tools - causes output capture failures on ARM
|
|
1650
|
+
if _is_python_tool(cmd):
|
|
1651
|
+
return cmd
|
|
1652
|
+
|
|
1536
1653
|
if _is_stdbuf_available():
|
|
1537
1654
|
# stdbuf -oL = line-buffered stdout, -eL = line-buffered stderr
|
|
1538
1655
|
return ["stdbuf", "-oL", "-eL"] + cmd
|
|
@@ -2064,21 +2181,30 @@ def run_job(jid: int) -> None:
|
|
|
2064
2181
|
job = get_job(jid)
|
|
2065
2182
|
retry_count = job.get("metadata", {}).get("retry_count", 0)
|
|
2066
2183
|
if retry_count < MAX_RETRIES:
|
|
2067
|
-
# Read log to check for transient errors
|
|
2068
|
-
# Note: Check even when rc==0 because tools like nxc may exit 0 but log errors
|
|
2184
|
+
# Read log to check for transient errors or flaky netexec
|
|
2069
2185
|
log_path = job.get("log", "")
|
|
2070
2186
|
if log_path and os.path.exists(log_path):
|
|
2071
2187
|
try:
|
|
2072
2188
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
2073
2189
|
log_content = f.read()
|
|
2074
|
-
|
|
2075
|
-
|
|
2190
|
+
|
|
2191
|
+
# Check for transient errors OR flaky netexec (connected but no shares)
|
|
2192
|
+
is_transient = _is_transient_error(log_content)
|
|
2193
|
+
is_netexec_flaky = _is_netexec_flaky_empty(log_content, job)
|
|
2194
|
+
retry_reason = None
|
|
2195
|
+
|
|
2196
|
+
if is_transient:
|
|
2197
|
+
retry_reason = "transient error"
|
|
2198
|
+
elif is_netexec_flaky:
|
|
2199
|
+
retry_reason = "netexec connected but no shares"
|
|
2200
|
+
|
|
2201
|
+
if retry_reason:
|
|
2076
2202
|
logger.info(
|
|
2077
|
-
"
|
|
2203
|
+
f"Auto-retrying job: {retry_reason}",
|
|
2078
2204
|
extra={"job_id": jid, "retry_count": retry_count + 1},
|
|
2079
2205
|
)
|
|
2080
2206
|
_append_worker_log(
|
|
2081
|
-
f"job {jid}:
|
|
2207
|
+
f"job {jid}: {retry_reason}, auto-retry {retry_count + 1}/{MAX_RETRIES}"
|
|
2082
2208
|
)
|
|
2083
2209
|
|
|
2084
2210
|
# Build new job metadata with incremented retry count
|
|
@@ -2095,7 +2221,7 @@ def run_job(jid: int) -> None:
|
|
|
2095
2221
|
engagement_id=job.get("engagement_id"),
|
|
2096
2222
|
metadata=new_metadata,
|
|
2097
2223
|
parent_id=job.get("metadata", {}).get("parent_id"),
|
|
2098
|
-
reason=f"Auto-retry {retry_count + 1}/{MAX_RETRIES} (
|
|
2224
|
+
reason=f"Auto-retry {retry_count + 1}/{MAX_RETRIES} ({retry_reason})",
|
|
2099
2225
|
rule_id=job.get("metadata", {}).get("rule_id"),
|
|
2100
2226
|
skip_scope_check=True, # Already validated on first run
|
|
2101
2227
|
)
|
|
@@ -2120,6 +2246,41 @@ def run_job(jid: int) -> None:
|
|
|
2120
2246
|
|
|
2121
2247
|
# Re-fetch job to get updated data
|
|
2122
2248
|
job = get_job(jid)
|
|
2249
|
+
|
|
2250
|
+
# Ensure log file is fully flushed to disk before parsing
|
|
2251
|
+
# Some tools (especially Python-based like impacket) may have buffered output
|
|
2252
|
+
log_path = job.get("log", "")
|
|
2253
|
+
if log_path and os.path.exists(log_path):
|
|
2254
|
+
# Wait for completion marker with retries
|
|
2255
|
+
# Log files end with "Exit Code:" or "=== Completed"
|
|
2256
|
+
max_retries = 5
|
|
2257
|
+
retry_delay = 0.2 # 200ms between retries
|
|
2258
|
+
log_complete = False
|
|
2259
|
+
|
|
2260
|
+
for attempt in range(max_retries):
|
|
2261
|
+
try:
|
|
2262
|
+
with open(
|
|
2263
|
+
log_path, "r", encoding="utf-8", errors="replace"
|
|
2264
|
+
) as f:
|
|
2265
|
+
content = f.read()
|
|
2266
|
+
# Check for completion markers
|
|
2267
|
+
if "Exit Code:" in content or "=== Completed" in content:
|
|
2268
|
+
log_complete = True
|
|
2269
|
+
break
|
|
2270
|
+
except Exception:
|
|
2271
|
+
pass
|
|
2272
|
+
|
|
2273
|
+
if attempt < max_retries - 1:
|
|
2274
|
+
time.sleep(retry_delay)
|
|
2275
|
+
|
|
2276
|
+
if not log_complete:
|
|
2277
|
+
# Last resort: force filesystem sync and proceed anyway
|
|
2278
|
+
try:
|
|
2279
|
+
with open(log_path, "a") as f:
|
|
2280
|
+
os.fsync(f.fileno())
|
|
2281
|
+
except Exception:
|
|
2282
|
+
pass
|
|
2283
|
+
|
|
2123
2284
|
parse_result = handle_job_result(job)
|
|
2124
2285
|
|
|
2125
2286
|
# Handle parse failure cases
|
souleyez/engine/base.py
CHANGED
souleyez/engine/loader.py
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Simple plugin loader for souleyez (L1).
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
from __future__ import annotations
|
|
6
|
-
|
|
7
|
+
|
|
7
8
|
import importlib
|
|
8
|
-
|
|
9
|
+
import pkgutil
|
|
10
|
+
from typing import Any, Dict
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
def _safe_import_module(fullname: str):
|
souleyez/engine/log_sanitizer.py
CHANGED
souleyez/engine/manager.py
CHANGED
|
@@ -6,11 +6,13 @@ This manager contains a small built-in adapter for "nmap" that uses your existin
|
|
|
6
6
|
souleyez.scanner.run_nmap function. Later plugins can be added by implementing
|
|
7
7
|
ScannerPlugin in their own modules and registering them here.
|
|
8
8
|
"""
|
|
9
|
+
|
|
10
|
+
import importlib
|
|
9
11
|
import threading
|
|
10
12
|
from typing import Optional
|
|
13
|
+
|
|
11
14
|
from ..storage.db import init_db, insert_scan, update_scan
|
|
12
15
|
from ..utils import timestamp_str
|
|
13
|
-
import importlib
|
|
14
16
|
|
|
15
17
|
# lazy import existing run_nmap (your project has souleyez/scanner.py)
|
|
16
18
|
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
"""
|
|
3
3
|
souleyez.engine.result_handler - Auto-parse job results
|
|
4
4
|
"""
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import logging
|
|
7
|
-
|
|
8
|
-
from
|
|
7
|
+
import os
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from .job_status import STATUS_DONE, STATUS_ERROR, STATUS_NO_RESULTS, STATUS_WARNING
|
|
9
11
|
|
|
10
12
|
logger = logging.getLogger(__name__)
|
|
11
13
|
|
|
@@ -49,7 +51,8 @@ TOOL_ERROR_PATTERNS = {
|
|
|
49
51
|
"nuclei": [
|
|
50
52
|
"could not connect",
|
|
51
53
|
"context deadline exceeded",
|
|
52
|
-
"no address found"
|
|
54
|
+
# Note: "no address found" removed - nuclei reports this as [INF] when
|
|
55
|
+
# templates try alternate ports, not a real error. Exit code 0 = success.
|
|
53
56
|
],
|
|
54
57
|
"ffuf": [
|
|
55
58
|
"error making request",
|
|
@@ -129,36 +132,11 @@ def handle_job_result(job: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
129
132
|
"""
|
|
130
133
|
tool = job.get("tool", "").lower()
|
|
131
134
|
log_path = job.get("log")
|
|
132
|
-
status = job.get("status")
|
|
133
|
-
|
|
134
|
-
# Some tools return non-zero exit codes even on success
|
|
135
|
-
# Parse 'done' jobs and 'error' jobs for certain tools
|
|
136
|
-
# gobuster: exits 1 for wildcard detection (but we can still parse and retry)
|
|
137
|
-
# enum4linux: exits non-zero when some queries fail, even with partial results
|
|
138
|
-
# msf_exploit: exits 0 even on failure, but we want to parse either way
|
|
139
|
-
# hashcat: exits 1 when exhausted (no passwords cracked) - not an error
|
|
140
|
-
# web_login_test: exits 1 when credentials fail - not an error, just invalid creds
|
|
141
|
-
tools_with_nonzero_success = [
|
|
142
|
-
"gobuster",
|
|
143
|
-
"msf_auxiliary",
|
|
144
|
-
"msfconsole",
|
|
145
|
-
"enum4linux",
|
|
146
|
-
"msf_exploit",
|
|
147
|
-
"wpscan",
|
|
148
|
-
"hashcat",
|
|
149
|
-
"web_login_test",
|
|
150
|
-
]
|
|
151
|
-
|
|
152
|
-
if status == "done":
|
|
153
|
-
# Always parse successful jobs
|
|
154
|
-
pass
|
|
155
|
-
elif status == "error" and tool in tools_with_nonzero_success:
|
|
156
|
-
# Parse error jobs for tools that can succeed with non-zero exit codes
|
|
157
|
-
pass
|
|
158
|
-
else:
|
|
159
|
-
# Skip other error/failed jobs
|
|
160
|
-
return None
|
|
161
135
|
|
|
136
|
+
# Always try to parse if log file exists
|
|
137
|
+
# Status checks were causing race conditions where jobs showed "done"
|
|
138
|
+
# but get_job() returned stale status, causing parse to be skipped
|
|
139
|
+
# The caller (background.py) only calls us for completed jobs anyway
|
|
162
140
|
if not log_path or not os.path.exists(log_path):
|
|
163
141
|
logger.error(
|
|
164
142
|
f"Job {job.get('id')} parse failed: log file missing or does not exist (path={log_path})"
|
|
@@ -193,9 +171,9 @@ def handle_job_result(job: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
|
193
171
|
|
|
194
172
|
try:
|
|
195
173
|
from souleyez.handlers.registry import get_handler
|
|
196
|
-
from souleyez.storage.hosts import HostManager
|
|
197
|
-
from souleyez.storage.findings import FindingsManager
|
|
198
174
|
from souleyez.storage.credentials import CredentialsManager
|
|
175
|
+
from souleyez.storage.findings import FindingsManager
|
|
176
|
+
from souleyez.storage.hosts import HostManager
|
|
199
177
|
|
|
200
178
|
handler = get_handler(tool)
|
|
201
179
|
if handler:
|
|
@@ -306,7 +284,7 @@ def reparse_job(job_id: int) -> Dict[str, Any]:
|
|
|
306
284
|
Returns:
|
|
307
285
|
Dict with 'success', 'message', and optionally 'parse_result'
|
|
308
286
|
"""
|
|
309
|
-
from .background import
|
|
287
|
+
from .background import _read_jobs, _write_jobs, get_job
|
|
310
288
|
|
|
311
289
|
# Get the job
|
|
312
290
|
job = get_job(job_id)
|
|
@@ -345,9 +323,9 @@ def reparse_job(job_id: int) -> Dict[str, Any]:
|
|
|
345
323
|
|
|
346
324
|
try:
|
|
347
325
|
from souleyez.handlers.registry import get_handler
|
|
348
|
-
from souleyez.storage.hosts import HostManager
|
|
349
|
-
from souleyez.storage.findings import FindingsManager
|
|
350
326
|
from souleyez.storage.credentials import CredentialsManager
|
|
327
|
+
from souleyez.storage.findings import FindingsManager
|
|
328
|
+
from souleyez.storage.hosts import HostManager
|
|
351
329
|
|
|
352
330
|
handler = get_handler(tool)
|
|
353
331
|
if handler:
|
|
@@ -527,10 +505,10 @@ def parse_nmap_job(
|
|
|
527
505
|
) -> Dict[str, Any]:
|
|
528
506
|
"""Parse nmap job results."""
|
|
529
507
|
try:
|
|
508
|
+
from souleyez.core.cve_matcher import CVEMatcher
|
|
530
509
|
from souleyez.parsers.nmap_parser import parse_nmap_log
|
|
531
|
-
from souleyez.storage.hosts import HostManager
|
|
532
510
|
from souleyez.storage.findings import FindingsManager
|
|
533
|
-
from souleyez.
|
|
511
|
+
from souleyez.storage.hosts import HostManager
|
|
534
512
|
|
|
535
513
|
# Parse the log file
|
|
536
514
|
parsed = parse_nmap_log(log_path)
|
|
@@ -804,11 +782,11 @@ def parse_theharvester_job(
|
|
|
804
782
|
"""Parse theHarvester job results."""
|
|
805
783
|
try:
|
|
806
784
|
from souleyez.parsers.theharvester_parser import (
|
|
807
|
-
parse_theharvester_output,
|
|
808
785
|
get_osint_stats,
|
|
786
|
+
parse_theharvester_output,
|
|
809
787
|
)
|
|
810
|
-
from souleyez.storage.osint import OsintManager
|
|
811
788
|
from souleyez.storage.hosts import HostManager
|
|
789
|
+
from souleyez.storage.osint import OsintManager
|
|
812
790
|
|
|
813
791
|
# Read the log file
|
|
814
792
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -1301,14 +1279,15 @@ def parse_gobuster_job(
|
|
|
1301
1279
|
) -> Dict[str, Any]:
|
|
1302
1280
|
"""Parse gobuster job results."""
|
|
1303
1281
|
try:
|
|
1282
|
+
from urllib.parse import urlparse
|
|
1283
|
+
|
|
1304
1284
|
from souleyez.parsers.gobuster_parser import (
|
|
1305
|
-
parse_gobuster_output,
|
|
1306
|
-
get_paths_stats,
|
|
1307
1285
|
generate_next_steps,
|
|
1286
|
+
get_paths_stats,
|
|
1287
|
+
parse_gobuster_output,
|
|
1308
1288
|
)
|
|
1309
|
-
from souleyez.storage.web_paths import WebPathsManager
|
|
1310
1289
|
from souleyez.storage.hosts import HostManager
|
|
1311
|
-
from
|
|
1290
|
+
from souleyez.storage.web_paths import WebPathsManager
|
|
1312
1291
|
|
|
1313
1292
|
# Read the log file
|
|
1314
1293
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -1532,11 +1511,12 @@ def parse_sqlmap_job(
|
|
|
1532
1511
|
) -> Dict[str, Any]:
|
|
1533
1512
|
"""Parse sqlmap job results."""
|
|
1534
1513
|
try:
|
|
1535
|
-
from
|
|
1514
|
+
from urllib.parse import urlparse
|
|
1515
|
+
|
|
1516
|
+
from souleyez.parsers.sqlmap_parser import get_sqli_stats, parse_sqlmap_output
|
|
1536
1517
|
from souleyez.storage.findings import FindingsManager
|
|
1537
1518
|
from souleyez.storage.hosts import HostManager
|
|
1538
1519
|
from souleyez.storage.sqlmap_data import SQLMapDataManager
|
|
1539
|
-
from urllib.parse import urlparse
|
|
1540
1520
|
|
|
1541
1521
|
# Read the log file
|
|
1542
1522
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -1973,9 +1953,9 @@ def _extract_credentials_from_dump(
|
|
|
1973
1953
|
tuple: (count of credentials added, list of plaintext credential dicts for chaining)
|
|
1974
1954
|
"""
|
|
1975
1955
|
from souleyez.intelligence.sensitive_tables import is_sensitive_table
|
|
1956
|
+
from souleyez.log_config import get_logger
|
|
1976
1957
|
from souleyez.storage.credentials import CredentialsManager
|
|
1977
1958
|
from souleyez.storage.hosts import HostManager
|
|
1978
|
-
from souleyez.log_config import get_logger
|
|
1979
1959
|
|
|
1980
1960
|
logger = get_logger(__name__)
|
|
1981
1961
|
cred_manager = CredentialsManager()
|
|
@@ -2189,10 +2169,11 @@ def parse_msf_auxiliary_job(
|
|
|
2189
2169
|
"""Parse MSF auxiliary module job results."""
|
|
2190
2170
|
try:
|
|
2191
2171
|
import re
|
|
2172
|
+
|
|
2192
2173
|
from souleyez.parsers.msf_parser import parse_msf_log
|
|
2193
|
-
from souleyez.storage.hosts import HostManager
|
|
2194
|
-
from souleyez.storage.findings import FindingsManager
|
|
2195
2174
|
from souleyez.storage.credentials import CredentialsManager
|
|
2175
|
+
from souleyez.storage.findings import FindingsManager
|
|
2176
|
+
from souleyez.storage.hosts import HostManager
|
|
2196
2177
|
|
|
2197
2178
|
# Read raw log for connection failure detection
|
|
2198
2179
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -2348,8 +2329,9 @@ def parse_msf_exploit_job(
|
|
|
2348
2329
|
"""Parse MSF exploit module job results."""
|
|
2349
2330
|
try:
|
|
2350
2331
|
import re
|
|
2351
|
-
|
|
2332
|
+
|
|
2352
2333
|
from souleyez.storage.findings import FindingsManager
|
|
2334
|
+
from souleyez.storage.hosts import HostManager
|
|
2353
2335
|
|
|
2354
2336
|
# Read the log file
|
|
2355
2337
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -2501,10 +2483,10 @@ def parse_smbmap_job(
|
|
|
2501
2483
|
) -> Dict[str, Any]:
|
|
2502
2484
|
"""Parse smbmap job results."""
|
|
2503
2485
|
try:
|
|
2504
|
-
from souleyez.parsers.smbmap_parser import
|
|
2505
|
-
from souleyez.storage.smb_shares import SMBSharesManager
|
|
2486
|
+
from souleyez.parsers.smbmap_parser import extract_findings, parse_smbmap_output
|
|
2506
2487
|
from souleyez.storage.findings import FindingsManager
|
|
2507
2488
|
from souleyez.storage.hosts import HostManager
|
|
2489
|
+
from souleyez.storage.smb_shares import SMBSharesManager
|
|
2508
2490
|
|
|
2509
2491
|
# Read the log file
|
|
2510
2492
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -2604,9 +2586,9 @@ def parse_whois_job(
|
|
|
2604
2586
|
"""Parse WHOIS job results."""
|
|
2605
2587
|
try:
|
|
2606
2588
|
from souleyez.parsers.whois_parser import (
|
|
2607
|
-
parse_whois_output,
|
|
2608
|
-
map_to_osint_data,
|
|
2609
2589
|
extract_emails,
|
|
2590
|
+
map_to_osint_data,
|
|
2591
|
+
parse_whois_output,
|
|
2610
2592
|
)
|
|
2611
2593
|
from souleyez.storage.osint import OsintManager
|
|
2612
2594
|
|
|
@@ -2666,8 +2648,8 @@ def parse_dnsrecon_job(
|
|
|
2666
2648
|
"""Parse dnsrecon job results."""
|
|
2667
2649
|
try:
|
|
2668
2650
|
from souleyez.parsers.dnsrecon_parser import parse_dnsrecon_output
|
|
2669
|
-
from souleyez.storage.osint import OsintManager
|
|
2670
2651
|
from souleyez.storage.hosts import HostManager
|
|
2652
|
+
from souleyez.storage.osint import OsintManager
|
|
2671
2653
|
|
|
2672
2654
|
# Read the log file
|
|
2673
2655
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -2818,8 +2800,8 @@ def parse_hydra_job(
|
|
|
2818
2800
|
try:
|
|
2819
2801
|
from souleyez.parsers.hydra_parser import parse_hydra_output
|
|
2820
2802
|
from souleyez.storage.credentials import CredentialsManager
|
|
2821
|
-
from souleyez.storage.hosts import HostManager
|
|
2822
2803
|
from souleyez.storage.findings import FindingsManager
|
|
2804
|
+
from souleyez.storage.hosts import HostManager
|
|
2823
2805
|
|
|
2824
2806
|
# Read the log file
|
|
2825
2807
|
with open(log_path, "r", encoding="utf-8", errors="replace") as f:
|
|
@@ -3165,9 +3147,9 @@ def parse_enum4linux_job(
|
|
|
3165
3147
|
"""Parse enum4linux job results."""
|
|
3166
3148
|
try:
|
|
3167
3149
|
from souleyez.parsers.enum4linux_parser import (
|
|
3168
|
-
parse_enum4linux_output,
|
|
3169
|
-
get_smb_stats,
|
|
3170
3150
|
categorize_share,
|
|
3151
|
+
get_smb_stats,
|
|
3152
|
+
parse_enum4linux_output,
|
|
3171
3153
|
)
|
|
3172
3154
|
from souleyez.storage.findings import FindingsManager
|
|
3173
3155
|
from souleyez.storage.hosts import HostManager
|
|
@@ -3310,8 +3292,8 @@ def parse_crackmapexec_job(
|
|
|
3310
3292
|
"""Parse CrackMapExec job results."""
|
|
3311
3293
|
try:
|
|
3312
3294
|
from souleyez.parsers.crackmapexec_parser import parse_crackmapexec
|
|
3313
|
-
from souleyez.storage.hosts import HostManager
|
|
3314
3295
|
from souleyez.storage.credentials import CredentialsManager
|
|
3296
|
+
from souleyez.storage.hosts import HostManager
|
|
3315
3297
|
|
|
3316
3298
|
target = job.get("target", "")
|
|
3317
3299
|
parsed = parse_crackmapexec(log_path, target)
|
|
@@ -3379,8 +3361,8 @@ def parse_ffuf_job(
|
|
|
3379
3361
|
"""Parse ffuf job results."""
|
|
3380
3362
|
try:
|
|
3381
3363
|
from souleyez.parsers.ffuf_parser import parse_ffuf
|
|
3382
|
-
from souleyez.storage.hosts import HostManager
|
|
3383
3364
|
from souleyez.storage.findings import FindingsManager
|
|
3365
|
+
from souleyez.storage.hosts import HostManager
|
|
3384
3366
|
|
|
3385
3367
|
target = job.get("target", "")
|
|
3386
3368
|
parsed = parse_ffuf(log_path, target)
|
|
@@ -3709,8 +3691,8 @@ def parse_nikto_job(
|
|
|
3709
3691
|
"""Parse nikto web server scanner results."""
|
|
3710
3692
|
try:
|
|
3711
3693
|
from souleyez.parsers.nikto_parser import (
|
|
3712
|
-
parse_nikto_output,
|
|
3713
3694
|
generate_next_steps,
|
|
3695
|
+
parse_nikto_output,
|
|
3714
3696
|
)
|
|
3715
3697
|
from souleyez.storage.findings import FindingsManager
|
|
3716
3698
|
from souleyez.storage.hosts import HostManager
|
|
@@ -3834,14 +3816,15 @@ def parse_http_fingerprint_job(
|
|
|
3834
3816
|
based on detected WAF, CDN, or managed hosting platform.
|
|
3835
3817
|
"""
|
|
3836
3818
|
try:
|
|
3819
|
+
from urllib.parse import urlparse
|
|
3820
|
+
|
|
3837
3821
|
from souleyez.parsers.http_fingerprint_parser import (
|
|
3838
|
-
parse_http_fingerprint_output,
|
|
3839
3822
|
build_fingerprint_context,
|
|
3840
|
-
get_tool_recommendations,
|
|
3841
3823
|
generate_next_steps,
|
|
3824
|
+
get_tool_recommendations,
|
|
3825
|
+
parse_http_fingerprint_output,
|
|
3842
3826
|
)
|
|
3843
3827
|
from souleyez.storage.hosts import HostManager
|
|
3844
|
-
from urllib.parse import urlparse
|
|
3845
3828
|
|
|
3846
3829
|
target = job.get("target", "")
|
|
3847
3830
|
|
|
@@ -4084,8 +4067,8 @@ def parse_hashcat_job(
|
|
|
4084
4067
|
"""Parse hashcat job results and extract cracked passwords."""
|
|
4085
4068
|
try:
|
|
4086
4069
|
from souleyez.parsers.hashcat_parser import (
|
|
4087
|
-
parse_hashcat_output,
|
|
4088
4070
|
map_to_credentials,
|
|
4071
|
+
parse_hashcat_output,
|
|
4089
4072
|
)
|
|
4090
4073
|
from souleyez.storage.credentials import CredentialsManager
|
|
4091
4074
|
from souleyez.storage.findings import FindingsManager
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
"""
|
|
3
3
|
Worker health check and management utilities
|
|
4
4
|
"""
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import time
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import Any, Dict, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
import psutil
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def is_worker_running() -> Tuple[bool, Optional[int]]:
|
|
@@ -49,7 +51,7 @@ def is_worker_healthy() -> Tuple[bool, Optional[int], Optional[str]]:
|
|
|
49
51
|
- pid: Worker PID if found, None otherwise
|
|
50
52
|
- issue: Description of issue if not healthy, None otherwise
|
|
51
53
|
"""
|
|
52
|
-
from souleyez.engine.background import
|
|
54
|
+
from souleyez.engine.background import HEARTBEAT_STALE_THRESHOLD, get_heartbeat_age
|
|
53
55
|
|
|
54
56
|
is_running, pid = is_worker_running()
|
|
55
57
|
|
|
@@ -169,7 +171,7 @@ def get_worker_health() -> Dict[str, Any]:
|
|
|
169
171
|
- cpu_percent: CPU usage percentage
|
|
170
172
|
- memory_mb: Memory usage in MB
|
|
171
173
|
"""
|
|
172
|
-
from souleyez.engine.background import
|
|
174
|
+
from souleyez.engine.background import HEARTBEAT_STALE_THRESHOLD, get_heartbeat_age
|
|
173
175
|
|
|
174
176
|
is_running, pid = is_worker_running()
|
|
175
177
|
heartbeat_age = get_heartbeat_age()
|
souleyez/handlers/base.py
CHANGED
|
@@ -5,6 +5,7 @@ Base handler class for tool result parsing and display.
|
|
|
5
5
|
All tool handlers inherit from BaseToolHandler and implement
|
|
6
6
|
the required methods for their specific tool.
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import logging
|
|
9
10
|
from abc import ABC, abstractmethod
|
|
10
11
|
from typing import Any, Dict, Optional
|