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.
Files changed (35) hide show
  1. souleyez/__init__.py +1 -1
  2. souleyez/assets/__init__.py +1 -0
  3. souleyez/assets/souleyez-icon.png +0 -0
  4. souleyez/core/msf_sync_manager.py +15 -5
  5. souleyez/core/tool_chaining.py +126 -26
  6. souleyez/detection/validator.py +4 -2
  7. souleyez/docs/README.md +2 -2
  8. souleyez/docs/user-guide/installation.md +14 -1
  9. souleyez/engine/background.py +17 -1
  10. souleyez/engine/result_handler.py +89 -0
  11. souleyez/main.py +103 -4
  12. souleyez/parsers/crackmapexec_parser.py +101 -43
  13. souleyez/parsers/dnsrecon_parser.py +50 -35
  14. souleyez/parsers/enum4linux_parser.py +101 -21
  15. souleyez/parsers/http_fingerprint_parser.py +319 -0
  16. souleyez/parsers/hydra_parser.py +56 -5
  17. souleyez/parsers/impacket_parser.py +123 -44
  18. souleyez/parsers/john_parser.py +47 -14
  19. souleyez/parsers/msf_parser.py +20 -5
  20. souleyez/parsers/nmap_parser.py +48 -27
  21. souleyez/parsers/smbmap_parser.py +39 -23
  22. souleyez/parsers/sqlmap_parser.py +18 -9
  23. souleyez/parsers/theharvester_parser.py +21 -13
  24. souleyez/plugins/http_fingerprint.py +592 -0
  25. souleyez/plugins/nuclei.py +41 -17
  26. souleyez/ui/interactive.py +99 -7
  27. souleyez/ui/setup_wizard.py +93 -5
  28. souleyez/ui/tool_setup.py +52 -52
  29. souleyez/utils/tool_checker.py +45 -5
  30. {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/METADATA +16 -3
  31. {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/RECORD +35 -31
  32. {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/WHEEL +0 -0
  33. {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/entry_points.txt +0 -0
  34. {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/licenses/LICENSE +0 -0
  35. {souleyez-2.22.0.dist-info → souleyez-2.26.0.dist-info}/top_level.txt +0 -0
@@ -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
- if not show_raw_logs and job.get('tool') == 'nmap' and job.get('status') in ['done', 'completed'] and log_path and os.path.exists(log_path):
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
- desc = (alert.get('description') or 'No description')[:45]
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
- description = alert.get('description') or rule.get('description', 'N/A')
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()
@@ -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 - let it output directly to terminal
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(" You can install manually from https://ollama.ai")
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 _add_paths_to_bashrc():
86
- """Add tool paths to ~/.bashrc if not already present."""
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
- if not bashrc.exists():
94
- return
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
- try:
97
- content = bashrc.read_text()
98
- additions = []
99
-
100
- for line, marker in paths_to_add:
101
- if marker not in content:
102
- additions.append(line)
103
-
104
- if additions:
105
- with open(bashrc, "a") as f:
106
- f.write("\n# Added by souleyez setup\n")
107
- for line in additions:
108
- f.write(line + "\n")
109
- except Exception:
110
- pass # Don't fail on PATH configuration issues
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(" All tools should be available via apt install.")
405
+ console.print(" Most tools are available via apt. Some may use pipx or direct download.")
398
406
  console.print()
399
- _show_tool_status(console)
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 bashrc
532
- _add_paths_to_bashrc()
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
- if distro in ('ubuntu', 'debian'):
558
- console.print()
559
- console.print(" [yellow]Important:[/yellow] To use newly installed tools, either:")
560
- console.print(" 1. Restart your terminal, OR")
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
- console.print()
562
+ console.print()
563
563
 
564
564
 
565
565
  def _show_tool_status(console):
@@ -223,9 +223,9 @@ EXTERNAL_TOOLS = {
223
223
  },
224
224
  'dalfox': {
225
225
  'command': 'dalfox',
226
- 'install_kali': 'ARCH=$(uname -m | sed "s/x86_64/amd64/;s/aarch64/arm64/") && wget -q https://github.com/hahwul/dalfox/releases/download/v2.12.0/dalfox-linux-${ARCH}.tar.gz -O /tmp/dalfox.tar.gz && tar -xzf /tmp/dalfox.tar.gz -C /tmp && sudo mv /tmp/dalfox-linux-${ARCH} /usr/local/bin/dalfox && sudo chmod +x /usr/local/bin/dalfox && rm /tmp/dalfox.tar.gz',
227
- 'install_ubuntu': 'ARCH=$(uname -m | sed "s/x86_64/amd64/;s/aarch64/arm64/") && wget -q https://github.com/hahwul/dalfox/releases/download/v2.12.0/dalfox-linux-${ARCH}.tar.gz -O /tmp/dalfox.tar.gz && tar -xzf /tmp/dalfox.tar.gz -C /tmp && sudo mv /tmp/dalfox-linux-${ARCH} /usr/local/bin/dalfox && sudo chmod +x /usr/local/bin/dalfox && rm /tmp/dalfox.tar.gz',
228
- 'install_method': 'kali_only',
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.22.0
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.22.0
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.22.0 | **Release Date**: January 2026 | **Maintainer**: CyberSoul Security
313
+ **Version**: 2.24.0 | **Release Date**: January 2026 | **Maintainer**: CyberSoul Security