souleyez 2.22.0__py3-none-any.whl → 2.26.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- souleyez/__init__.py +1 -1
- souleyez/assets/__init__.py +1 -0
- souleyez/assets/souleyez-icon.png +0 -0
- souleyez/core/msf_sync_manager.py +15 -5
- souleyez/core/tool_chaining.py +126 -26
- souleyez/detection/validator.py +4 -2
- souleyez/docs/README.md +2 -2
- souleyez/docs/user-guide/installation.md +14 -1
- souleyez/engine/background.py +17 -1
- souleyez/engine/result_handler.py +89 -0
- souleyez/main.py +103 -4
- souleyez/parsers/crackmapexec_parser.py +101 -43
- souleyez/parsers/dnsrecon_parser.py +50 -35
- souleyez/parsers/enum4linux_parser.py +101 -21
- souleyez/parsers/http_fingerprint_parser.py +319 -0
- souleyez/parsers/hydra_parser.py +56 -5
- souleyez/parsers/impacket_parser.py +123 -44
- souleyez/parsers/john_parser.py +47 -14
- souleyez/parsers/msf_parser.py +20 -5
- souleyez/parsers/nmap_parser.py +48 -27
- souleyez/parsers/smbmap_parser.py +39 -23
- souleyez/parsers/sqlmap_parser.py +18 -9
- souleyez/parsers/theharvester_parser.py +21 -13
- souleyez/plugins/http_fingerprint.py +592 -0
- souleyez/plugins/nuclei.py +41 -17
- souleyez/ui/interactive.py +99 -7
- souleyez/ui/setup_wizard.py +93 -5
- souleyez/ui/tool_setup.py +52 -52
- souleyez/utils/tool_checker.py +45 -5
- {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/METADATA +16 -3
- {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/RECORD +35 -31
- {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/WHEEL +0 -0
- {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/entry_points.txt +0 -0
- {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/top_level.txt +0 -0
souleyez/ui/interactive.py
CHANGED
|
@@ -130,6 +130,63 @@ def render_standard_header(title: str, width: int = None) -> None:
|
|
|
130
130
|
click.echo()
|
|
131
131
|
|
|
132
132
|
|
|
133
|
+
def parse_syslog_description(desc: str) -> str:
|
|
134
|
+
"""
|
|
135
|
+
Extract meaningful message from syslog-formatted descriptions.
|
|
136
|
+
|
|
137
|
+
Syslog format: <timestamp> <host> [timestamp] <program>[pid]: <message>
|
|
138
|
+
Example input: "Jan 8 07:00:05 192.168.1.111 Jan 8 07:00:05 eyez CRON[537281]: pam_unix(cron:session): session closed for user yoda"
|
|
139
|
+
Example output: "CRON: pam_unix(cron:session): session closed for user yoda"
|
|
140
|
+
"""
|
|
141
|
+
import re
|
|
142
|
+
|
|
143
|
+
if not desc:
|
|
144
|
+
return 'No description'
|
|
145
|
+
|
|
146
|
+
# Try to find the actual message after common syslog patterns
|
|
147
|
+
# Pattern 1: Look for process name with PID followed by colon (e.g., "CRON[537281]:")
|
|
148
|
+
pid_match = re.search(r'([A-Za-z_][A-Za-z0-9_-]*)\[(\d+)\]:\s*(.+)$', desc)
|
|
149
|
+
if pid_match:
|
|
150
|
+
process_name = pid_match.group(1)
|
|
151
|
+
message = pid_match.group(3)
|
|
152
|
+
return f"{process_name}: {message}"
|
|
153
|
+
|
|
154
|
+
# Pattern 2: Look for systemd-style messages (e.g., "systemd[1]: Started...")
|
|
155
|
+
systemd_match = re.search(r'(systemd(?:-[a-z]+)?)\[?\d*\]?:\s*(.+)$', desc, re.IGNORECASE)
|
|
156
|
+
if systemd_match:
|
|
157
|
+
return f"{systemd_match.group(1)}: {systemd_match.group(2)}"
|
|
158
|
+
|
|
159
|
+
# Pattern 3: Look for kernel messages
|
|
160
|
+
kernel_match = re.search(r'kernel:\s*(.+)$', desc)
|
|
161
|
+
if kernel_match:
|
|
162
|
+
return f"kernel: {kernel_match.group(1)}"
|
|
163
|
+
|
|
164
|
+
# Pattern 4: Generic - find content after last colon that has substance
|
|
165
|
+
colon_parts = desc.split(': ')
|
|
166
|
+
if len(colon_parts) > 1:
|
|
167
|
+
# Get the meaningful part (usually after the first "process:" pattern)
|
|
168
|
+
for i, part in enumerate(colon_parts):
|
|
169
|
+
# Skip parts that look like timestamps or IPs
|
|
170
|
+
if not re.match(r'^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|\d{1,3}\.\d{1,3}|\d{4}-\d{2})', part):
|
|
171
|
+
# Found something meaningful - join from here
|
|
172
|
+
meaningful = ': '.join(colon_parts[i:])
|
|
173
|
+
if len(meaningful) > 10: # Ensure it's substantial
|
|
174
|
+
return meaningful
|
|
175
|
+
|
|
176
|
+
# Pattern 5: Strip leading timestamp patterns
|
|
177
|
+
# Remove patterns like "Jan 8 07:00:05 192.168.1.111 Jan 8 07:00:05 hostname"
|
|
178
|
+
stripped = re.sub(
|
|
179
|
+
r'^(?:[A-Z][a-z]{2}\s+\d+\s+\d{2}:\d{2}:\d{2}\s+\S+\s*)+',
|
|
180
|
+
'', desc
|
|
181
|
+
).strip()
|
|
182
|
+
|
|
183
|
+
if stripped and len(stripped) > 5:
|
|
184
|
+
return stripped
|
|
185
|
+
|
|
186
|
+
# Fallback: return original if no patterns matched
|
|
187
|
+
return desc
|
|
188
|
+
|
|
189
|
+
|
|
133
190
|
def _show_upgrade_prompt(feature_name: str):
|
|
134
191
|
"""Show upgrade prompt when FREE user tries to access Pro feature."""
|
|
135
192
|
from rich.panel import Panel
|
|
@@ -5345,7 +5402,7 @@ def view_job_detail(job_id: int):
|
|
|
5345
5402
|
|
|
5346
5403
|
# Check if tool has a parser - if yes, hide raw logs by default
|
|
5347
5404
|
tool = job.get('tool', '')
|
|
5348
|
-
has_parser = tool in ['dnsrecon', 'nmap', 'nuclei', 'nikto', 'dalfox', 'theharvester', 'sqlmap', 'ffuf', 'gobuster', 'wpscan', 'crackmapexec', 'hydra', 'whois', 'smbmap', 'enum4linux', 'msf_auxiliary', 'searchsploit']
|
|
5405
|
+
has_parser = tool in ['dnsrecon', 'nmap', 'ard', 'nuclei', 'nikto', 'dalfox', 'theharvester', 'sqlmap', 'ffuf', 'gobuster', 'wpscan', 'crackmapexec', 'hydra', 'whois', 'smbmap', 'enum4linux', 'msf_auxiliary', 'searchsploit']
|
|
5349
5406
|
|
|
5350
5407
|
# Show log file if exists
|
|
5351
5408
|
log_path = job.get('log')
|
|
@@ -5941,7 +5998,9 @@ def view_job_detail(job_id: int):
|
|
|
5941
5998
|
pass
|
|
5942
5999
|
|
|
5943
6000
|
# Parse and display Nmap results if available (only when not showing raw logs)
|
|
5944
|
-
|
|
6001
|
+
# ARD plugin uses nmap under the hood, so include it here
|
|
6002
|
+
nmap_based_tools = ['nmap', 'ard']
|
|
6003
|
+
if not show_raw_logs and job.get('tool') in nmap_based_tools and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
|
|
5945
6004
|
try:
|
|
5946
6005
|
from souleyez.parsers.nmap_parser import parse_nmap_output
|
|
5947
6006
|
with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
|
|
@@ -8898,7 +8957,8 @@ def _view_wazuh_alerts(engagement_id: int):
|
|
|
8898
8957
|
icon = get_level_icon(level)
|
|
8899
8958
|
rule_id = str(alert.get('rule_id', 'N/A'))[:10]
|
|
8900
8959
|
agent_name = alert.get('agent_name', 'N/A')[:15]
|
|
8901
|
-
|
|
8960
|
+
raw_desc = alert.get('description') or 'No description'
|
|
8961
|
+
desc = parse_syslog_description(raw_desc)[:45]
|
|
8902
8962
|
ts = alert.get('timestamp', 'N/A')
|
|
8903
8963
|
if hasattr(ts, 'strftime'):
|
|
8904
8964
|
ts = ts.strftime('%Y-%m-%d %H:%M:%S')
|
|
@@ -9075,7 +9135,8 @@ def _view_alert_detail(alert: dict):
|
|
|
9075
9135
|
|
|
9076
9136
|
# Get values from normalized format first, then fall back to raw_data
|
|
9077
9137
|
rule_id = alert.get('rule_id') or rule.get('id', 'N/A')
|
|
9078
|
-
|
|
9138
|
+
raw_description = alert.get('description') or rule.get('description', 'N/A')
|
|
9139
|
+
description = parse_syslog_description(raw_description)
|
|
9079
9140
|
level = alert.get('level', 0) or rule.get('level', 0)
|
|
9080
9141
|
severity = alert.get('severity', 'info')
|
|
9081
9142
|
|
|
@@ -15790,6 +15851,37 @@ def view_findings(engagement_id: int):
|
|
|
15790
15851
|
summary_parts.append(f"Filters: {', '.join(active_filters)}")
|
|
15791
15852
|
|
|
15792
15853
|
click.echo(" " + " | ".join(summary_parts))
|
|
15854
|
+
|
|
15855
|
+
# Show tool distribution legend
|
|
15856
|
+
if findings:
|
|
15857
|
+
tool_counts = {}
|
|
15858
|
+
for f in findings:
|
|
15859
|
+
tool = f.get('tool') or 'unknown'
|
|
15860
|
+
tool_counts[tool] = tool_counts.get(tool, 0) + 1
|
|
15861
|
+
|
|
15862
|
+
# Sort by count (descending) and format
|
|
15863
|
+
sorted_tools = sorted(tool_counts.items(), key=lambda x: x[1], reverse=True)
|
|
15864
|
+
tool_parts = [f"{tool}({count})" for tool, count in sorted_tools]
|
|
15865
|
+
|
|
15866
|
+
# Display on one or more lines if needed
|
|
15867
|
+
tool_legend = " Tools: " + " | ".join(tool_parts)
|
|
15868
|
+
if len(tool_legend) > width - 4:
|
|
15869
|
+
# Wrap to multiple lines if too long
|
|
15870
|
+
lines = []
|
|
15871
|
+
current_line = " Tools: "
|
|
15872
|
+
for i, part in enumerate(tool_parts):
|
|
15873
|
+
test_line = current_line + part + (" | " if i < len(tool_parts) - 1 else "")
|
|
15874
|
+
if len(test_line) > width - 4 and current_line != " Tools: ":
|
|
15875
|
+
lines.append(current_line.rstrip(" | "))
|
|
15876
|
+
current_line = " " + part + (" | " if i < len(tool_parts) - 1 else "")
|
|
15877
|
+
else:
|
|
15878
|
+
current_line = test_line
|
|
15879
|
+
lines.append(current_line.rstrip(" | "))
|
|
15880
|
+
for line in lines:
|
|
15881
|
+
click.echo(click.style(line, fg='cyan'))
|
|
15882
|
+
else:
|
|
15883
|
+
click.echo(click.style(tool_legend, fg='cyan'))
|
|
15884
|
+
|
|
15793
15885
|
click.echo()
|
|
15794
15886
|
|
|
15795
15887
|
if not findings:
|
|
@@ -29526,11 +29618,11 @@ def _check_msfdb_ready() -> bool:
|
|
|
29526
29618
|
click.echo(" The Metasploit database needs to be initialized for full functionality.")
|
|
29527
29619
|
click.echo(" Without it, you won't be able to store hosts, credentials, or loot.")
|
|
29528
29620
|
click.echo()
|
|
29529
|
-
if click.confirm(" Initialize database now? (runs: msfdb init)", default=True):
|
|
29621
|
+
if click.confirm(" Initialize database now? (runs: sudo msfdb init)", default=True):
|
|
29530
29622
|
click.echo()
|
|
29531
|
-
click.echo(click.style(" Running msfdb init...", fg='cyan'))
|
|
29623
|
+
click.echo(click.style(" Running sudo msfdb init...", fg='cyan'))
|
|
29532
29624
|
try:
|
|
29533
|
-
result = subprocess.run(['msfdb', 'init'], capture_output=False, text=True)
|
|
29625
|
+
result = subprocess.run(['sudo', 'msfdb', 'init'], capture_output=False, text=True)
|
|
29534
29626
|
if result.returncode == 0:
|
|
29535
29627
|
click.echo(click.style(" Database initialized successfully!", fg='green'))
|
|
29536
29628
|
click.echo()
|
souleyez/ui/setup_wizard.py
CHANGED
|
@@ -95,6 +95,74 @@ def _configure_sudoers(tool_name: str, tool_path: str) -> bool:
|
|
|
95
95
|
return False
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
def _install_desktop_shortcut():
|
|
99
|
+
"""
|
|
100
|
+
Install desktop shortcut for SoulEyez in Applications menu.
|
|
101
|
+
|
|
102
|
+
This runs silently during setup - any errors are ignored to not
|
|
103
|
+
disrupt the setup flow.
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
applications_dir = Path.home() / '.local' / 'share' / 'applications'
|
|
107
|
+
icons_dir = Path.home() / '.local' / 'share' / 'icons'
|
|
108
|
+
desktop_file = applications_dir / 'souleyez.desktop'
|
|
109
|
+
icon_dest = icons_dir / 'souleyez.png'
|
|
110
|
+
|
|
111
|
+
# Skip if already installed
|
|
112
|
+
if desktop_file.exists():
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
# Create directories
|
|
116
|
+
applications_dir.mkdir(parents=True, exist_ok=True)
|
|
117
|
+
icons_dir.mkdir(parents=True, exist_ok=True)
|
|
118
|
+
|
|
119
|
+
# Find and copy icon
|
|
120
|
+
try:
|
|
121
|
+
# Try importlib.resources first (Python 3.9+)
|
|
122
|
+
try:
|
|
123
|
+
from importlib.resources import files
|
|
124
|
+
icon_source = files('souleyez.assets').joinpath('souleyez-icon.png')
|
|
125
|
+
with open(icon_source, 'rb') as src:
|
|
126
|
+
icon_data = src.read()
|
|
127
|
+
except (ImportError, TypeError, FileNotFoundError):
|
|
128
|
+
# Fallback: find icon relative to this file
|
|
129
|
+
icon_source = Path(__file__).parent.parent / 'assets' / 'souleyez-icon.png'
|
|
130
|
+
with open(icon_source, 'rb') as src:
|
|
131
|
+
icon_data = src.read()
|
|
132
|
+
|
|
133
|
+
with open(icon_dest, 'wb') as dst:
|
|
134
|
+
dst.write(icon_data)
|
|
135
|
+
except Exception:
|
|
136
|
+
icon_dest = "utilities-terminal" # Fallback to system icon
|
|
137
|
+
|
|
138
|
+
# Create .desktop file
|
|
139
|
+
desktop_content = f"""[Desktop Entry]
|
|
140
|
+
Name=SoulEyez
|
|
141
|
+
Comment=AI-Powered Penetration Testing Platform
|
|
142
|
+
Exec=souleyez interactive
|
|
143
|
+
Icon={icon_dest}
|
|
144
|
+
Terminal=true
|
|
145
|
+
Type=Application
|
|
146
|
+
Categories=Security;System;Network;
|
|
147
|
+
Keywords=pentest;security;hacking;nmap;metasploit;
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
desktop_file.write_text(desktop_content)
|
|
151
|
+
|
|
152
|
+
# Update desktop database (optional)
|
|
153
|
+
try:
|
|
154
|
+
subprocess.run(['update-desktop-database', str(applications_dir)],
|
|
155
|
+
capture_output=True, check=False, timeout=5)
|
|
156
|
+
except Exception:
|
|
157
|
+
pass
|
|
158
|
+
|
|
159
|
+
click.echo(f" {click.style('✓', fg='green')} Desktop shortcut: Added to Applications menu")
|
|
160
|
+
|
|
161
|
+
except Exception:
|
|
162
|
+
# Silently ignore errors - desktop shortcut is nice-to-have
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
|
|
98
166
|
def _run_tool_installs(
|
|
99
167
|
missing_tools: List[Dict],
|
|
100
168
|
wrong_version_tools: List[Dict],
|
|
@@ -834,14 +902,16 @@ def _install_ollama() -> bool:
|
|
|
834
902
|
|
|
835
903
|
click.echo()
|
|
836
904
|
click.echo(" Installing Ollama...")
|
|
837
|
-
click.echo(" " + click.style("(This may take a minute)", fg='bright_black'))
|
|
905
|
+
click.echo(" " + click.style("(This may take a minute - downloading ~100MB)", fg='bright_black'))
|
|
838
906
|
click.echo()
|
|
839
907
|
|
|
840
908
|
try:
|
|
841
|
-
# Run the official Ollama install script
|
|
909
|
+
# Run the official Ollama install script with timeout
|
|
910
|
+
# Add curl timeouts to fail faster on network issues
|
|
842
911
|
result = subprocess.run(
|
|
843
|
-
['bash', '-c', 'curl -fsSL https://ollama.ai/install.sh | sh'],
|
|
844
|
-
check=False
|
|
912
|
+
['bash', '-c', 'curl --connect-timeout 30 --max-time 300 -fsSL https://ollama.ai/install.sh | sh'],
|
|
913
|
+
check=False,
|
|
914
|
+
timeout=360 # 6 minute total timeout
|
|
845
915
|
)
|
|
846
916
|
|
|
847
917
|
if result.returncode == 0:
|
|
@@ -853,10 +923,25 @@ def _install_ollama() -> bool:
|
|
|
853
923
|
else:
|
|
854
924
|
click.echo()
|
|
855
925
|
click.echo(" " + click.style("✗ Installation failed", fg='red'))
|
|
856
|
-
click.echo("
|
|
926
|
+
click.echo(" " + click.style("This is usually a network issue (slow connection or timeout).", fg='yellow'))
|
|
927
|
+
click.echo()
|
|
928
|
+
click.echo(" To install manually:")
|
|
929
|
+
click.echo(" curl -fsSL https://ollama.ai/install.sh | sh")
|
|
930
|
+
click.echo()
|
|
931
|
+
click.echo(" Or visit: https://ollama.ai")
|
|
857
932
|
click.pause(" Press any key to continue...")
|
|
858
933
|
return False
|
|
859
934
|
|
|
935
|
+
except subprocess.TimeoutExpired:
|
|
936
|
+
click.echo()
|
|
937
|
+
click.echo(" " + click.style("✗ Installation timed out", fg='red'))
|
|
938
|
+
click.echo(" " + click.style("The download took too long. Check your internet connection.", fg='yellow'))
|
|
939
|
+
click.echo()
|
|
940
|
+
click.echo(" To install manually:")
|
|
941
|
+
click.echo(" curl -fsSL https://ollama.ai/install.sh | sh")
|
|
942
|
+
click.pause(" Press any key to continue...")
|
|
943
|
+
return False
|
|
944
|
+
|
|
860
945
|
except Exception as e:
|
|
861
946
|
click.echo()
|
|
862
947
|
click.echo(click.style(f" ✗ Error: {e}", fg='red'))
|
|
@@ -1089,6 +1174,9 @@ def _wizard_summary(encryption_enabled, engagement_info, tool_status, ai_enabled
|
|
|
1089
1174
|
click.echo(" " + click.style("You're ready to start!", bold=True, fg='cyan'))
|
|
1090
1175
|
click.echo()
|
|
1091
1176
|
|
|
1177
|
+
# Install desktop shortcut automatically
|
|
1178
|
+
_install_desktop_shortcut()
|
|
1179
|
+
|
|
1092
1180
|
# Prompt for interactive tutorial
|
|
1093
1181
|
click.echo(" ┌" + "─" * 56 + "┐")
|
|
1094
1182
|
click.echo(" │" + click.style(" Would you like to run the interactive tutorial?", fg='cyan').center(65) + "│")
|
souleyez/ui/tool_setup.py
CHANGED
|
@@ -82,32 +82,38 @@ def _ensure_path_configured():
|
|
|
82
82
|
os.environ["PATH"] = ":".join(new_paths) + ":" + current_path
|
|
83
83
|
|
|
84
84
|
|
|
85
|
-
def
|
|
86
|
-
"""Add tool paths to
|
|
87
|
-
bashrc = Path.home() / ".bashrc"
|
|
85
|
+
def _add_paths_to_shell_rc():
|
|
86
|
+
"""Add tool paths to shell rc files (bash and zsh) if not already present."""
|
|
88
87
|
paths_to_add = [
|
|
89
88
|
('export PATH="$HOME/go/bin:$PATH"', "go/bin"),
|
|
90
89
|
('export PATH="$HOME/.local/bin:$PATH"', ".local/bin"),
|
|
91
90
|
]
|
|
92
91
|
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
# Update both .bashrc and .zshrc (Kali Linux uses zsh by default)
|
|
93
|
+
rc_files = [
|
|
94
|
+
Path.home() / ".bashrc",
|
|
95
|
+
Path.home() / ".zshrc",
|
|
96
|
+
]
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
for rc_file in rc_files:
|
|
99
|
+
if not rc_file.exists():
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
content = rc_file.read_text()
|
|
104
|
+
additions = []
|
|
105
|
+
|
|
106
|
+
for line, marker in paths_to_add:
|
|
107
|
+
if marker not in content:
|
|
108
|
+
additions.append(line)
|
|
109
|
+
|
|
110
|
+
if additions:
|
|
111
|
+
with open(rc_file, "a") as f:
|
|
112
|
+
f.write("\n# Added by souleyez setup\n")
|
|
113
|
+
for line in additions:
|
|
114
|
+
f.write(line + "\n")
|
|
115
|
+
except Exception:
|
|
116
|
+
pass # Don't fail on PATH configuration issues
|
|
111
117
|
|
|
112
118
|
|
|
113
119
|
def _run_command(cmd: str, console, description: str = "", capture: bool = False) -> tuple:
|
|
@@ -319,15 +325,16 @@ def _ensure_msfdb_initialized(console):
|
|
|
319
325
|
|
|
320
326
|
if not click.confirm(" Initialize MSF database now?", default=True):
|
|
321
327
|
console.print()
|
|
322
|
-
console.print(" [yellow]Skipped.[/yellow] Run 'msfdb init' manually when needed.")
|
|
328
|
+
console.print(" [yellow]Skipped.[/yellow] Run 'sudo msfdb init' manually when needed.")
|
|
323
329
|
return
|
|
324
330
|
|
|
325
331
|
console.print()
|
|
326
332
|
console.print(" [dim]Initializing MSF database (this may take a minute)...[/dim]")
|
|
327
333
|
|
|
328
334
|
try:
|
|
335
|
+
# Use sudo for msfdb init (required on Kali and some other distros)
|
|
329
336
|
result = subprocess.run(
|
|
330
|
-
[msfdb_path, 'init'],
|
|
337
|
+
['sudo', msfdb_path, 'init'],
|
|
331
338
|
capture_output=True,
|
|
332
339
|
timeout=300 # 5 minute timeout
|
|
333
340
|
)
|
|
@@ -392,32 +399,12 @@ def run_tool_setup(check_only: bool = False, install_all: bool = False):
|
|
|
392
399
|
console.print(f" Detected OS: [bold]{distro_names.get(distro, distro)}[/bold]")
|
|
393
400
|
console.print()
|
|
394
401
|
|
|
402
|
+
# Show distro-specific messaging
|
|
395
403
|
if distro in ('kali', 'parrot'):
|
|
396
404
|
console.print(" [green]✓ You're on a pentesting distro![/green]")
|
|
397
|
-
console.print("
|
|
405
|
+
console.print(" Most tools are available via apt. Some may use pipx or direct download.")
|
|
398
406
|
console.print()
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
if check_only:
|
|
402
|
-
return
|
|
403
|
-
|
|
404
|
-
missing = get_missing_tools(distro)
|
|
405
|
-
if missing:
|
|
406
|
-
console.print()
|
|
407
|
-
if install_all or click.confirm(" Install missing tools via apt?", default=True):
|
|
408
|
-
_install_apt_tools(console, missing)
|
|
409
|
-
else:
|
|
410
|
-
console.print(" [green]✓ All tools are installed![/green]")
|
|
411
|
-
|
|
412
|
-
# Configure sudoers for privileged scans
|
|
413
|
-
_configure_sudoers(console)
|
|
414
|
-
|
|
415
|
-
# Initialize MSF database if needed
|
|
416
|
-
_ensure_msfdb_initialized(console)
|
|
417
|
-
return
|
|
418
|
-
|
|
419
|
-
# Ubuntu/Debian path
|
|
420
|
-
if distro in ('ubuntu', 'debian'):
|
|
407
|
+
elif distro in ('ubuntu', 'debian'):
|
|
421
408
|
console.print(" [yellow]Note:[/yellow] Some pentesting tools aren't in Ubuntu/Debian repos.")
|
|
422
409
|
console.print(" This wizard will install them using pipx, go, snap, or from source.")
|
|
423
410
|
console.print()
|
|
@@ -431,6 +418,8 @@ def run_tool_setup(check_only: bool = False, install_all: bool = False):
|
|
|
431
418
|
if not missing:
|
|
432
419
|
console.print()
|
|
433
420
|
console.print(" [green]✓ All tools are installed![/green]")
|
|
421
|
+
# Still need to run post-install tasks (sudoers, MSF db, etc.)
|
|
422
|
+
_run_post_install_tasks(console, distro)
|
|
434
423
|
return
|
|
435
424
|
|
|
436
425
|
console.print()
|
|
@@ -528,8 +517,8 @@ def run_tool_setup(check_only: bool = False, install_all: bool = False):
|
|
|
528
517
|
else:
|
|
529
518
|
failed_tools.append(tool['name'])
|
|
530
519
|
|
|
531
|
-
# Configure PATH in
|
|
532
|
-
|
|
520
|
+
# Configure PATH in shell rc files (bash and zsh)
|
|
521
|
+
_add_paths_to_shell_rc()
|
|
533
522
|
|
|
534
523
|
# Final status
|
|
535
524
|
console.print()
|
|
@@ -547,6 +536,15 @@ def run_tool_setup(check_only: bool = False, install_all: bool = False):
|
|
|
547
536
|
_ensure_path_configured()
|
|
548
537
|
_show_tool_status(console)
|
|
549
538
|
|
|
539
|
+
# Run post-install tasks
|
|
540
|
+
_run_post_install_tasks(console, distro)
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def _run_post_install_tasks(console, distro: str):
|
|
544
|
+
"""Run tasks that should happen after tool installation or when all tools are present."""
|
|
545
|
+
# Ensure PATH is configured in shell rc files
|
|
546
|
+
_add_paths_to_shell_rc()
|
|
547
|
+
|
|
550
548
|
# Configure passwordless sudo for privileged scans
|
|
551
549
|
_configure_sudoers(console)
|
|
552
550
|
|
|
@@ -554,12 +552,14 @@ def run_tool_setup(check_only: bool = False, install_all: bool = False):
|
|
|
554
552
|
_ensure_msfdb_initialized(console)
|
|
555
553
|
|
|
556
554
|
# Remind about PATH for pipx/go tools
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
555
|
+
console.print()
|
|
556
|
+
console.print(" [yellow]Important:[/yellow] To use newly installed tools, either:")
|
|
557
|
+
console.print(" 1. Restart your terminal, OR")
|
|
558
|
+
if distro in ('kali', 'parrot'):
|
|
559
|
+
console.print(" 2. Run: [cyan]source ~/.zshrc[/cyan] (Kali uses zsh)")
|
|
560
|
+
else:
|
|
561
561
|
console.print(" 2. Run: [cyan]source ~/.bashrc[/cyan]")
|
|
562
|
-
|
|
562
|
+
console.print()
|
|
563
563
|
|
|
564
564
|
|
|
565
565
|
def _show_tool_status(console):
|
souleyez/utils/tool_checker.py
CHANGED
|
@@ -223,9 +223,9 @@ EXTERNAL_TOOLS = {
|
|
|
223
223
|
},
|
|
224
224
|
'dalfox': {
|
|
225
225
|
'command': 'dalfox',
|
|
226
|
-
'install_kali': '
|
|
227
|
-
'install_ubuntu': '
|
|
228
|
-
'install_method': '
|
|
226
|
+
'install_kali': 'go install github.com/hahwul/dalfox/v2@latest',
|
|
227
|
+
'install_ubuntu': 'go install github.com/hahwul/dalfox/v2@latest',
|
|
228
|
+
'install_method': 'go',
|
|
229
229
|
'description': 'XSS vulnerability scanner'
|
|
230
230
|
},
|
|
231
231
|
},
|
|
@@ -721,6 +721,7 @@ def check_msfdb_status() -> Dict[str, any]:
|
|
|
721
721
|
- message: str - Human-readable status message
|
|
722
722
|
"""
|
|
723
723
|
import subprocess
|
|
724
|
+
from pathlib import Path
|
|
724
725
|
|
|
725
726
|
result = {
|
|
726
727
|
'initialized': False,
|
|
@@ -734,6 +735,32 @@ def check_msfdb_status() -> Dict[str, any]:
|
|
|
734
735
|
result['message'] = 'msfdb command not found - Metasploit may not be installed'
|
|
735
736
|
return result
|
|
736
737
|
|
|
738
|
+
# Helper to check if PostgreSQL is running
|
|
739
|
+
def check_postgresql_running() -> bool:
|
|
740
|
+
try:
|
|
741
|
+
proc = subprocess.run(
|
|
742
|
+
['systemctl', 'is-active', 'postgresql'],
|
|
743
|
+
capture_output=True,
|
|
744
|
+
text=True,
|
|
745
|
+
timeout=5
|
|
746
|
+
)
|
|
747
|
+
return proc.returncode == 0 and 'active' in proc.stdout.lower()
|
|
748
|
+
except Exception:
|
|
749
|
+
return False
|
|
750
|
+
|
|
751
|
+
# Helper to check system-wide MSF database config (Kali fallback)
|
|
752
|
+
def check_system_config() -> bool:
|
|
753
|
+
"""Check if system-wide database.yml exists with valid PostgreSQL config."""
|
|
754
|
+
config_path = Path('/usr/share/metasploit-framework/config/database.yml')
|
|
755
|
+
if config_path.exists():
|
|
756
|
+
try:
|
|
757
|
+
content = config_path.read_text()
|
|
758
|
+
# Check for PostgreSQL adapter configuration
|
|
759
|
+
return 'adapter: postgresql' in content and 'database: msf' in content
|
|
760
|
+
except Exception:
|
|
761
|
+
return False
|
|
762
|
+
return False
|
|
763
|
+
|
|
737
764
|
try:
|
|
738
765
|
# Run msfdb status
|
|
739
766
|
proc = subprocess.run(
|
|
@@ -743,10 +770,23 @@ def check_msfdb_status() -> Dict[str, any]:
|
|
|
743
770
|
timeout=10
|
|
744
771
|
)
|
|
745
772
|
output = proc.stdout + proc.stderr
|
|
746
|
-
|
|
747
|
-
# Parse output for status indicators
|
|
748
773
|
output_lower = output.lower()
|
|
749
774
|
|
|
775
|
+
# Check if msfdb requires root (common on Kali)
|
|
776
|
+
if 'run as root' in output_lower or (proc.returncode != 0 and 'error' in output_lower):
|
|
777
|
+
# Fall back to checking system config file and PostgreSQL status
|
|
778
|
+
result['running'] = check_postgresql_running()
|
|
779
|
+
if check_system_config():
|
|
780
|
+
result['initialized'] = True
|
|
781
|
+
if result['running']:
|
|
782
|
+
result['connected'] = True
|
|
783
|
+
result['message'] = 'Database initialized and running'
|
|
784
|
+
else:
|
|
785
|
+
result['message'] = 'Database initialized but PostgreSQL not running - run: sudo systemctl start postgresql'
|
|
786
|
+
else:
|
|
787
|
+
result['message'] = 'Need sudo to verify - run: sudo msfdb status'
|
|
788
|
+
return result
|
|
789
|
+
|
|
750
790
|
# Check if database is initialized
|
|
751
791
|
if 'no database' in output_lower or 'not initialized' in output_lower:
|
|
752
792
|
result['message'] = 'Database not initialized - run: msfdb init'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: souleyez
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.26.0
|
|
4
4
|
Summary: AI-Powered Penetration Testing Platform with 40+ integrated tools
|
|
5
5
|
Author-email: CyberSoul Security <contact@cybersoulsecurity.com>
|
|
6
6
|
Maintainer-email: CyberSoul Security <contact@cybersoulsecurity.com>
|
|
@@ -72,7 +72,7 @@ Welcome to the SoulEyez beta! Thank you for helping us test and improve this pen
|
|
|
72
72
|
|
|
73
73
|
> ⚠️ **Important**: Only use SoulEyez on systems you have explicit authorization to test.
|
|
74
74
|
|
|
75
|
-
## Version: 2.
|
|
75
|
+
## Version: 2.26.0
|
|
76
76
|
|
|
77
77
|
### What's Included
|
|
78
78
|
|
|
@@ -110,6 +110,17 @@ Welcome to the SoulEyez beta! Thank you for helping us test and improve this pen
|
|
|
110
110
|
- **Python**: 3.8 or newer
|
|
111
111
|
- **Storage**: ~500MB for SoulEyez + tools
|
|
112
112
|
|
|
113
|
+
> **🐉 Kali Linux Recommended**
|
|
114
|
+
>
|
|
115
|
+
> SoulEyez performs significantly better on **Kali Linux** than other distributions:
|
|
116
|
+
> - All pentesting tools pre-installed and optimized
|
|
117
|
+
> - Metasploit database and RPC already configured
|
|
118
|
+
> - Security-focused kernel and networking stack
|
|
119
|
+
> - No dependency hunting or version conflicts
|
|
120
|
+
> - Wordlists, databases, and tool configs ready to go
|
|
121
|
+
>
|
|
122
|
+
> While Ubuntu and other Debian-based distros are supported, you may experience slower setup times and occasional tool compatibility issues.
|
|
123
|
+
|
|
113
124
|
### Known Issues
|
|
114
125
|
|
|
115
126
|
- Very large scan outputs (>10MB) may slow the interface
|
|
@@ -127,6 +138,8 @@ pipx ensurepath # Add pipx apps to your PATH
|
|
|
127
138
|
source ~/.bashrc # Reload your shell (or close and reopen terminal)
|
|
128
139
|
```
|
|
129
140
|
|
|
141
|
+
> **Kali Linux users:** Kali uses zsh by default. Use `source ~/.zshrc` instead of `source ~/.bashrc`
|
|
142
|
+
|
|
130
143
|
> 💡 **What's pipx?** It's like `apt` but for Python command-line tools. It keeps each tool isolated so they don't conflict with each other.
|
|
131
144
|
|
|
132
145
|
### Step 2: Install SoulEyez
|
|
@@ -297,4 +310,4 @@ Happy hacking! 🛡️
|
|
|
297
310
|
|
|
298
311
|
---
|
|
299
312
|
|
|
300
|
-
**Version**: 2.
|
|
313
|
+
**Version**: 2.24.0 | **Release Date**: January 2026 | **Maintainer**: CyberSoul Security
|