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
souleyez/ai/ollama_service.py
CHANGED
|
@@ -5,6 +5,7 @@ This module provides the connection and management layer for Ollama,
|
|
|
5
5
|
enabling SoulEyez to generate AI-powered attack path recommendations
|
|
6
6
|
without sending sensitive engagement data to the cloud.
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import logging
|
|
9
10
|
from typing import Optional, Dict, Any
|
|
10
11
|
|
|
@@ -12,6 +13,7 @@ from typing import Optional, Dict, Any
|
|
|
12
13
|
try:
|
|
13
14
|
import ollama
|
|
14
15
|
from ollama import Client
|
|
16
|
+
|
|
15
17
|
OLLAMA_AVAILABLE = True
|
|
16
18
|
except ImportError:
|
|
17
19
|
ollama = None
|
|
@@ -35,8 +37,12 @@ class OllamaService:
|
|
|
35
37
|
DEFAULT_MODEL = "llama3.1:8b" # 8B parameter model for good quality reasoning
|
|
36
38
|
CONNECTION_TIMEOUT = 30 # seconds
|
|
37
39
|
|
|
38
|
-
def __init__(
|
|
39
|
-
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
endpoint: Optional[str] = None,
|
|
43
|
+
model: Optional[str] = None,
|
|
44
|
+
allow_failback: bool = True,
|
|
45
|
+
):
|
|
40
46
|
"""
|
|
41
47
|
Initialize Ollama service.
|
|
42
48
|
|
|
@@ -52,15 +58,16 @@ class OllamaService:
|
|
|
52
58
|
|
|
53
59
|
# Read endpoint from config if not provided
|
|
54
60
|
if endpoint is None:
|
|
55
|
-
endpoint = get(
|
|
61
|
+
endpoint = get("ai.ollama_url", self.DEFAULT_ENDPOINT)
|
|
56
62
|
|
|
57
63
|
self._original_endpoint = endpoint
|
|
58
64
|
self._failback_used = False
|
|
59
65
|
self._allow_failback = allow_failback
|
|
60
|
-
self._ollama_mode = get(
|
|
66
|
+
self._ollama_mode = get("ai.ollama_mode", "local")
|
|
61
67
|
|
|
62
68
|
# Security: Validate endpoint is localhost or VM host gateway
|
|
63
69
|
from souleyez.core.network_utils import is_valid_ollama_host
|
|
70
|
+
|
|
64
71
|
is_valid, reason = is_valid_ollama_host(endpoint)
|
|
65
72
|
if not is_valid:
|
|
66
73
|
logger.error(f"Ollama endpoint blocked: {reason}")
|
|
@@ -80,7 +87,7 @@ class OllamaService:
|
|
|
80
87
|
|
|
81
88
|
# Read model from config if not provided
|
|
82
89
|
if model is None:
|
|
83
|
-
model = get(
|
|
90
|
+
model = get("ai.ollama_model", self.DEFAULT_MODEL)
|
|
84
91
|
|
|
85
92
|
self.model = model
|
|
86
93
|
|
|
@@ -100,14 +107,16 @@ class OllamaService:
|
|
|
100
107
|
return False
|
|
101
108
|
|
|
102
109
|
# Only failback if we're in remote mode and not already on localhost
|
|
103
|
-
if self._ollama_mode !=
|
|
110
|
+
if self._ollama_mode != "remote":
|
|
104
111
|
return False
|
|
105
112
|
|
|
106
113
|
if self.endpoint == self.DEFAULT_ENDPOINT:
|
|
107
114
|
return False
|
|
108
115
|
|
|
109
|
-
logger.warning(
|
|
110
|
-
|
|
116
|
+
logger.warning(
|
|
117
|
+
f"Remote Ollama at {self._original_endpoint} unreachable, "
|
|
118
|
+
f"falling back to localhost"
|
|
119
|
+
)
|
|
111
120
|
|
|
112
121
|
# Try localhost
|
|
113
122
|
try:
|
|
@@ -183,7 +192,9 @@ class OllamaService:
|
|
|
183
192
|
if self._model_available:
|
|
184
193
|
logger.info(f"Model '{model_name}' is available")
|
|
185
194
|
else:
|
|
186
|
-
logger.warning(
|
|
195
|
+
logger.warning(
|
|
196
|
+
f"Model '{model_name}' not found. Available: {available_models}"
|
|
197
|
+
)
|
|
187
198
|
|
|
188
199
|
return self._model_available
|
|
189
200
|
except Exception as e:
|
|
@@ -216,7 +227,9 @@ class OllamaService:
|
|
|
216
227
|
logger.error(f"Failed to pull model: {e}")
|
|
217
228
|
return False
|
|
218
229
|
|
|
219
|
-
def generate(
|
|
230
|
+
def generate(
|
|
231
|
+
self, prompt: str, model_name: Optional[str] = None, timeout: int = 120
|
|
232
|
+
) -> Optional[str]:
|
|
220
233
|
"""
|
|
221
234
|
Generate a response from the LLM.
|
|
222
235
|
|
|
@@ -255,13 +268,10 @@ class OllamaService:
|
|
|
255
268
|
signal.alarm(timeout)
|
|
256
269
|
|
|
257
270
|
try:
|
|
258
|
-
response = self.client.generate(
|
|
259
|
-
model=model_name,
|
|
260
|
-
prompt=prompt
|
|
261
|
-
)
|
|
271
|
+
response = self.client.generate(model=model_name, prompt=prompt)
|
|
262
272
|
signal.alarm(0)
|
|
263
273
|
signal.signal(signal.SIGALRM, old_handler)
|
|
264
|
-
return response.get(
|
|
274
|
+
return response.get("response", "")
|
|
265
275
|
except TimeoutError:
|
|
266
276
|
signal.alarm(0)
|
|
267
277
|
signal.signal(signal.SIGALRM, old_handler)
|
|
@@ -274,11 +284,8 @@ class OllamaService:
|
|
|
274
284
|
return None
|
|
275
285
|
else:
|
|
276
286
|
# In a thread - can't use signals, just call directly
|
|
277
|
-
response = self.client.generate(
|
|
278
|
-
|
|
279
|
-
prompt=prompt
|
|
280
|
-
)
|
|
281
|
-
return response.get('response', '')
|
|
287
|
+
response = self.client.generate(model=model_name, prompt=prompt)
|
|
288
|
+
return response.get("response", "")
|
|
282
289
|
except Exception as e:
|
|
283
290
|
logger.error(f"Generation failed: {e}")
|
|
284
291
|
return None
|
|
@@ -291,36 +298,36 @@ class OllamaService:
|
|
|
291
298
|
dict: Status information including connection, models, etc.
|
|
292
299
|
"""
|
|
293
300
|
status = {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
301
|
+
"endpoint": self.endpoint,
|
|
302
|
+
"original_endpoint": self._original_endpoint,
|
|
303
|
+
"mode": self._ollama_mode,
|
|
304
|
+
"failback_used": self._failback_used,
|
|
305
|
+
"connected": False,
|
|
306
|
+
"models": [],
|
|
307
|
+
"default_model": self.DEFAULT_MODEL,
|
|
308
|
+
"configured_model": self.model,
|
|
309
|
+
"model_available": False,
|
|
310
|
+
"error": None,
|
|
304
311
|
}
|
|
305
312
|
|
|
306
313
|
# Check connection
|
|
307
314
|
if not self.check_connection():
|
|
308
|
-
status[
|
|
315
|
+
status["error"] = "Cannot connect to Ollama. Is it running?"
|
|
309
316
|
return status
|
|
310
317
|
|
|
311
|
-
status[
|
|
312
|
-
status[
|
|
313
|
-
status[
|
|
318
|
+
status["connected"] = True
|
|
319
|
+
status["endpoint"] = self.endpoint # Update in case failback changed it
|
|
320
|
+
status["failback_used"] = self._failback_used
|
|
314
321
|
|
|
315
322
|
# Get available models
|
|
316
323
|
try:
|
|
317
324
|
models_response = self.client.list()
|
|
318
|
-
status[
|
|
325
|
+
status["models"] = [m.model for m in models_response.models]
|
|
319
326
|
except Exception as e:
|
|
320
|
-
status[
|
|
327
|
+
status["error"] = f"Error listing models: {e}"
|
|
321
328
|
return status
|
|
322
329
|
|
|
323
330
|
# Check if default model is available
|
|
324
|
-
status[
|
|
331
|
+
status["model_available"] = self.check_model()
|
|
325
332
|
|
|
326
333
|
return status
|
souleyez/ai/path_scorer.py
CHANGED
|
@@ -18,52 +18,47 @@ class PathScorer:
|
|
|
18
18
|
|
|
19
19
|
# Scoring weights (configurable)
|
|
20
20
|
WEIGHTS = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
"success": 0.4,
|
|
22
|
+
"impact": 0.3,
|
|
23
|
+
"stealth": 0.2,
|
|
24
|
+
"complexity": 0.1, # Negative weight
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
# Success probability scoring
|
|
28
28
|
SUCCESS_SCORES = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
"has_valid_creds": 30,
|
|
30
|
+
"known_vulnerability": 25,
|
|
31
|
+
"common_service": 15, # SSH, RDP, HTTP
|
|
32
|
+
"uncommon_service": 5,
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
# Impact scoring
|
|
36
36
|
IMPACT_SCORES = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
"domain_controller": 40,
|
|
38
|
+
"database": 30,
|
|
39
|
+
"file_server": 20,
|
|
40
|
+
"workstation": 10,
|
|
41
|
+
"unknown": 5,
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
# Stealth scoring
|
|
45
45
|
STEALTH_SCORES = {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
"credential_reuse": 30, # No exploits
|
|
47
|
+
"quiet_exploit": 20,
|
|
48
|
+
"noisy_exploit": 10,
|
|
49
|
+
"unknown": 15, # Default to middle ground
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
# Complexity penalty
|
|
53
|
-
COMPLEXITY_PENALTIES = {
|
|
54
|
-
1: 0,
|
|
55
|
-
2: 5,
|
|
56
|
-
3: 5,
|
|
57
|
-
4: 10,
|
|
58
|
-
5: 10,
|
|
59
|
-
6: 20
|
|
60
|
-
}
|
|
53
|
+
COMPLEXITY_PENALTIES = {1: 0, 2: 5, 3: 5, 4: 10, 5: 10, 6: 20}
|
|
61
54
|
|
|
62
55
|
def __init__(self):
|
|
63
56
|
"""Initialize path scorer."""
|
|
64
57
|
pass
|
|
65
58
|
|
|
66
|
-
def score_path(
|
|
59
|
+
def score_path(
|
|
60
|
+
self, path: Dict[str, Any], engagement_data: Dict[str, Any]
|
|
61
|
+
) -> Dict[str, Any]:
|
|
67
62
|
"""
|
|
68
63
|
Score a single attack path.
|
|
69
64
|
|
|
@@ -85,93 +80,111 @@ class PathScorer:
|
|
|
85
80
|
|
|
86
81
|
# Calculate weighted total
|
|
87
82
|
total_score = (
|
|
88
|
-
(success_score * self.WEIGHTS[
|
|
89
|
-
(impact_score * self.WEIGHTS[
|
|
90
|
-
(stealth_score * self.WEIGHTS[
|
|
91
|
-
(complexity_penalty * self.WEIGHTS[
|
|
83
|
+
(success_score * self.WEIGHTS["success"])
|
|
84
|
+
+ (impact_score * self.WEIGHTS["impact"])
|
|
85
|
+
+ (stealth_score * self.WEIGHTS["stealth"])
|
|
86
|
+
- (complexity_penalty * self.WEIGHTS["complexity"])
|
|
92
87
|
)
|
|
93
88
|
|
|
94
89
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
"path": path,
|
|
91
|
+
"scores": {
|
|
92
|
+
"success": success_score,
|
|
93
|
+
"impact": impact_score,
|
|
94
|
+
"stealth": stealth_score,
|
|
95
|
+
"complexity": complexity_penalty,
|
|
101
96
|
},
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
"total_score": round(total_score, 2),
|
|
98
|
+
"rank": None, # Set by rank_paths()
|
|
104
99
|
}
|
|
105
100
|
|
|
106
|
-
def _score_success(
|
|
101
|
+
def _score_success(
|
|
102
|
+
self, path: Dict[str, Any], engagement_data: Dict[str, Any]
|
|
103
|
+
) -> float:
|
|
107
104
|
"""Score success probability based on available resources."""
|
|
108
105
|
score = 0
|
|
109
|
-
action = path.get(
|
|
110
|
-
target = path.get(
|
|
106
|
+
action = path.get("action", "").lower()
|
|
107
|
+
target = path.get("target", "").lower()
|
|
111
108
|
|
|
112
109
|
# Check for valid credentials
|
|
113
|
-
creds = engagement_data.get(
|
|
114
|
-
valid_creds = [c for c in creds if c.get(
|
|
115
|
-
if valid_creds and (
|
|
116
|
-
|
|
110
|
+
creds = engagement_data.get("credentials", [])
|
|
111
|
+
valid_creds = [c for c in creds if c.get("status") == "valid"]
|
|
112
|
+
if valid_creds and (
|
|
113
|
+
"credential" in action or "login" in action or "auth" in action
|
|
114
|
+
):
|
|
115
|
+
score += self.SUCCESS_SCORES["has_valid_creds"]
|
|
117
116
|
|
|
118
117
|
# Check for known vulnerabilities
|
|
119
|
-
findings = engagement_data.get(
|
|
120
|
-
critical_vulns = [
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
findings = engagement_data.get("findings", [])
|
|
119
|
+
critical_vulns = [
|
|
120
|
+
f for f in findings if f.get("severity") in ["critical", "high"]
|
|
121
|
+
]
|
|
122
|
+
if critical_vulns and ("exploit" in action or "vulnerability" in action):
|
|
123
|
+
score += self.SUCCESS_SCORES["known_vulnerability"]
|
|
123
124
|
|
|
124
125
|
# Check service type
|
|
125
|
-
common_services = [
|
|
126
|
+
common_services = ["ssh", "rdp", "http", "https", "mysql", "smb"]
|
|
126
127
|
if any(svc in target or svc in action for svc in common_services):
|
|
127
|
-
score += self.SUCCESS_SCORES[
|
|
128
|
+
score += self.SUCCESS_SCORES["common_service"]
|
|
128
129
|
else:
|
|
129
|
-
score += self.SUCCESS_SCORES[
|
|
130
|
+
score += self.SUCCESS_SCORES["uncommon_service"]
|
|
130
131
|
|
|
131
132
|
return min(score, 100) # Cap at 100
|
|
132
133
|
|
|
133
|
-
def _score_impact(
|
|
134
|
+
def _score_impact(
|
|
135
|
+
self, path: Dict[str, Any], engagement_data: Dict[str, Any]
|
|
136
|
+
) -> float:
|
|
134
137
|
"""Score potential impact of successful path."""
|
|
135
|
-
target = path.get(
|
|
136
|
-
action = path.get(
|
|
138
|
+
target = path.get("target", "").lower()
|
|
139
|
+
action = path.get("action", "").lower()
|
|
137
140
|
|
|
138
141
|
# Check target type
|
|
139
|
-
if
|
|
140
|
-
return self.IMPACT_SCORES[
|
|
142
|
+
if "domain controller" in target or "dc" in target or "ad" in target:
|
|
143
|
+
return self.IMPACT_SCORES["domain_controller"]
|
|
141
144
|
|
|
142
|
-
if
|
|
143
|
-
|
|
145
|
+
if (
|
|
146
|
+
"database" in target
|
|
147
|
+
or "mysql" in target
|
|
148
|
+
or "postgres" in target
|
|
149
|
+
or "sql" in target
|
|
150
|
+
):
|
|
151
|
+
return self.IMPACT_SCORES["database"]
|
|
144
152
|
|
|
145
|
-
if
|
|
146
|
-
return self.IMPACT_SCORES[
|
|
153
|
+
if "file server" in target or "smb" in target or "share" in target:
|
|
154
|
+
return self.IMPACT_SCORES["file_server"]
|
|
147
155
|
|
|
148
156
|
# Check for privilege escalation (high impact)
|
|
149
|
-
if
|
|
150
|
-
|
|
157
|
+
if (
|
|
158
|
+
"privilege" in action
|
|
159
|
+
or "escalat" in action
|
|
160
|
+
or "root" in action
|
|
161
|
+
or "admin" in action
|
|
162
|
+
):
|
|
163
|
+
return self.IMPACT_SCORES["database"] # High impact
|
|
151
164
|
|
|
152
165
|
# Default to workstation
|
|
153
|
-
return self.IMPACT_SCORES[
|
|
166
|
+
return self.IMPACT_SCORES["workstation"]
|
|
154
167
|
|
|
155
168
|
def _score_stealth(self, path: Dict[str, Any]) -> float:
|
|
156
169
|
"""Score stealth/detection likelihood."""
|
|
157
|
-
action = path.get(
|
|
170
|
+
action = path.get("action", "").lower()
|
|
158
171
|
|
|
159
172
|
# Credential reuse is stealthiest
|
|
160
|
-
if
|
|
161
|
-
return self.STEALTH_SCORES[
|
|
173
|
+
if "credential" in action and "exploit" not in action and "brute" not in action:
|
|
174
|
+
return self.STEALTH_SCORES["credential_reuse"]
|
|
162
175
|
|
|
163
176
|
# Known quiet exploits
|
|
164
|
-
quiet_keywords = [
|
|
177
|
+
quiet_keywords = ["pass-the-hash", "token", "kerberos", "golden ticket"]
|
|
165
178
|
if any(keyword in action for keyword in quiet_keywords):
|
|
166
|
-
return self.STEALTH_SCORES[
|
|
179
|
+
return self.STEALTH_SCORES["quiet_exploit"]
|
|
167
180
|
|
|
168
181
|
# Noisy exploits
|
|
169
|
-
noisy_keywords = [
|
|
182
|
+
noisy_keywords = ["brute", "scan", "spray", "exploit", "buffer overflow"]
|
|
170
183
|
if any(keyword in action for keyword in noisy_keywords):
|
|
171
|
-
return self.STEALTH_SCORES[
|
|
184
|
+
return self.STEALTH_SCORES["noisy_exploit"]
|
|
172
185
|
|
|
173
186
|
# Default
|
|
174
|
-
return self.STEALTH_SCORES[
|
|
187
|
+
return self.STEALTH_SCORES["unknown"]
|
|
175
188
|
|
|
176
189
|
def _score_complexity(self, path: Dict[str, Any]) -> float:
|
|
177
190
|
"""Score complexity (simpler = better, so this is a penalty)."""
|
|
@@ -179,7 +192,7 @@ class PathScorer:
|
|
|
179
192
|
# Multi-step paths get penalties based on length
|
|
180
193
|
|
|
181
194
|
# Check if this is a multi-step path
|
|
182
|
-
steps = path.get(
|
|
195
|
+
steps = path.get("steps", [])
|
|
183
196
|
if steps:
|
|
184
197
|
num_steps = len(steps)
|
|
185
198
|
else:
|
|
@@ -191,7 +204,9 @@ class PathScorer:
|
|
|
191
204
|
else:
|
|
192
205
|
return self.COMPLEXITY_PENALTIES[6]
|
|
193
206
|
|
|
194
|
-
def rank_paths(
|
|
207
|
+
def rank_paths(
|
|
208
|
+
self, paths: List[Dict[str, Any]], engagement_data: Dict[str, Any]
|
|
209
|
+
) -> List[Dict[str, Any]]:
|
|
195
210
|
"""
|
|
196
211
|
Score and rank multiple paths.
|
|
197
212
|
|
|
@@ -209,10 +224,10 @@ class PathScorer:
|
|
|
209
224
|
scored_paths.append(scored_path)
|
|
210
225
|
|
|
211
226
|
# Sort by total score (descending)
|
|
212
|
-
scored_paths.sort(key=lambda x: x[
|
|
227
|
+
scored_paths.sort(key=lambda x: x["total_score"], reverse=True)
|
|
213
228
|
|
|
214
229
|
# Assign ranks
|
|
215
230
|
for i, scored_path in enumerate(scored_paths, 1):
|
|
216
|
-
scored_path[
|
|
231
|
+
scored_path["rank"] = i
|
|
217
232
|
|
|
218
233
|
return scored_paths
|