souleyez 2.43.12__py3-none-any.whl → 2.43.15__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/core/tool_chaining.py +103 -0
- souleyez/docs/README.md +1 -1
- souleyez/engine/result_handler.py +21 -0
- souleyez/main.py +1 -1
- souleyez/parsers/dnsrecon_parser.py +5 -3
- souleyez/plugins/gobuster.py +48 -1
- souleyez/ui/wordlist_browser.py +481 -0
- souleyez/wordlists.py +12 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.15.dist-info}/METADATA +1 -1
- {souleyez-2.43.12.dist-info → souleyez-2.43.15.dist-info}/RECORD +15 -14
- {souleyez-2.43.12.dist-info → souleyez-2.43.15.dist-info}/WHEEL +0 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.15.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.15.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.15.dist-info}/top_level.txt +0 -0
souleyez/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = '2.43.
|
|
1
|
+
__version__ = '2.43.15'
|
|
2
2
|
|
souleyez/core/tool_chaining.py
CHANGED
|
@@ -4451,6 +4451,58 @@ class ToolChaining:
|
|
|
4451
4451
|
return job_ids
|
|
4452
4452
|
# === END Wildcard Auto-Retry ===
|
|
4453
4453
|
|
|
4454
|
+
# === Host Redirect Auto-Retry Logic ===
|
|
4455
|
+
# If gobuster detected a host-level redirect (e.g., non-www to www), auto-retry with corrected target
|
|
4456
|
+
if parse_results.get('host_redirect_detected') and parse_results.get('redirect_target'):
|
|
4457
|
+
# Check if this is already a retry attempt (prevent loops)
|
|
4458
|
+
is_retry = job.get('metadata', {}).get('redirect_retry', False)
|
|
4459
|
+
|
|
4460
|
+
if not is_retry:
|
|
4461
|
+
redirect_target = parse_results['redirect_target']
|
|
4462
|
+
original_target = target
|
|
4463
|
+
logger.info(f"🔄 Host redirect detected ({original_target} → {redirect_target}), creating auto-retry with corrected target")
|
|
4464
|
+
|
|
4465
|
+
# Get original args and update the -u parameter with new target
|
|
4466
|
+
args = job.get('args', [])
|
|
4467
|
+
retry_args = []
|
|
4468
|
+
for i, arg in enumerate(args):
|
|
4469
|
+
if arg == '-u' and i + 1 < len(args):
|
|
4470
|
+
# Next arg is the URL, replace it
|
|
4471
|
+
retry_args.append(arg)
|
|
4472
|
+
retry_args.append(redirect_target)
|
|
4473
|
+
# Skip the next iteration since we handled it
|
|
4474
|
+
elif i > 0 and args[i - 1] == '-u':
|
|
4475
|
+
# This is the URL after -u, skip it (already handled)
|
|
4476
|
+
continue
|
|
4477
|
+
else:
|
|
4478
|
+
# Replace <target> placeholder if present
|
|
4479
|
+
retry_args.append(arg.replace(original_target, redirect_target) if original_target in arg else arg)
|
|
4480
|
+
|
|
4481
|
+
# Build retry job command with corrected target
|
|
4482
|
+
retry_command = {
|
|
4483
|
+
'tool': 'gobuster',
|
|
4484
|
+
'target': redirect_target,
|
|
4485
|
+
'args': retry_args,
|
|
4486
|
+
'reason': f'Auto-retry: gobuster (host redirect detected: {original_target} → {redirect_target})',
|
|
4487
|
+
'metadata': {
|
|
4488
|
+
'redirect_retry': True,
|
|
4489
|
+
'retry_parent_job_id': job.get('id'),
|
|
4490
|
+
'original_target': original_target,
|
|
4491
|
+
'redirect_target': redirect_target
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4494
|
+
|
|
4495
|
+
# Enqueue the retry job
|
|
4496
|
+
retry_job_ids = self._enqueue_commands([retry_command], tool, engagement_id, redirect_target, parent_job_id=job.get('id'))
|
|
4497
|
+
if retry_job_ids:
|
|
4498
|
+
job_ids.extend(retry_job_ids)
|
|
4499
|
+
logger.info(f"✓ Retry job created with corrected target: {redirect_target}")
|
|
4500
|
+
|
|
4501
|
+
# Skip further processing of this redirect scan
|
|
4502
|
+
# Let the retry job complete and process properly
|
|
4503
|
+
return job_ids
|
|
4504
|
+
# === END Host Redirect Auto-Retry ===
|
|
4505
|
+
|
|
4454
4506
|
# PHP/ASP files found → trigger SQLMap intelligently
|
|
4455
4507
|
# High-value targets get direct testing, others get base URL crawl
|
|
4456
4508
|
php_files = parse_results.get('php_files', [])
|
|
@@ -5041,6 +5093,57 @@ class ToolChaining:
|
|
|
5041
5093
|
return job_ids
|
|
5042
5094
|
# === END Gobuster wildcard retry ===
|
|
5043
5095
|
|
|
5096
|
+
# === Special handling for Gobuster host redirect retry ===
|
|
5097
|
+
if tool == 'gobuster' and parse_results.get('host_redirect_detected'):
|
|
5098
|
+
# Gobuster detected host-level redirect, auto-retry with corrected target
|
|
5099
|
+
from souleyez.engine.background import enqueue_job
|
|
5100
|
+
from souleyez.log_config import get_logger
|
|
5101
|
+
logger = get_logger(__name__)
|
|
5102
|
+
|
|
5103
|
+
# Check if this is already a redirect retry (prevent loops)
|
|
5104
|
+
is_redirect_retry = job.get('metadata', {}).get('redirect_retry', False)
|
|
5105
|
+
if is_redirect_retry:
|
|
5106
|
+
logger.info(f"Gobuster host redirect: Already attempted redirect retry, skipping")
|
|
5107
|
+
return job_ids
|
|
5108
|
+
|
|
5109
|
+
# Get the redirect target
|
|
5110
|
+
redirect_target = parse_results.get('redirect_target')
|
|
5111
|
+
if not redirect_target:
|
|
5112
|
+
logger.warning(f"Gobuster host redirect detected but no redirect_target found")
|
|
5113
|
+
return job_ids
|
|
5114
|
+
|
|
5115
|
+
original_target = target
|
|
5116
|
+
logger.info(f"Gobuster host redirect: Auto-retrying with corrected target {redirect_target}")
|
|
5117
|
+
|
|
5118
|
+
# Get original args and update the -u parameter with new target
|
|
5119
|
+
job_args = job.get('args', [])
|
|
5120
|
+
retry_args = []
|
|
5121
|
+
for i, arg in enumerate(job_args):
|
|
5122
|
+
if arg == '-u' and i + 1 < len(job_args):
|
|
5123
|
+
retry_args.append(arg)
|
|
5124
|
+
retry_args.append(redirect_target)
|
|
5125
|
+
elif i > 0 and job_args[i - 1] == '-u':
|
|
5126
|
+
continue
|
|
5127
|
+
else:
|
|
5128
|
+
retry_args.append(arg.replace(original_target, redirect_target) if original_target in arg else arg)
|
|
5129
|
+
|
|
5130
|
+
retry_job_id = enqueue_job(
|
|
5131
|
+
tool='gobuster',
|
|
5132
|
+
target=redirect_target,
|
|
5133
|
+
args=retry_args,
|
|
5134
|
+
label=f"Auto-retry: gobuster (redirect → {redirect_target})",
|
|
5135
|
+
engagement_id=engagement_id,
|
|
5136
|
+
parent_id=job.get('id'),
|
|
5137
|
+
reason=f"Auto-triggered by gobuster: Host redirect detected ({original_target} → {redirect_target})",
|
|
5138
|
+
metadata={'redirect_retry': True, 'retry_parent_job_id': job.get('id'), 'original_target': original_target}
|
|
5139
|
+
)
|
|
5140
|
+
|
|
5141
|
+
job_ids.append(retry_job_id)
|
|
5142
|
+
logger.info(f"Created gobuster redirect retry job #{retry_job_id}")
|
|
5143
|
+
|
|
5144
|
+
return job_ids
|
|
5145
|
+
# === END Gobuster host redirect retry ===
|
|
5146
|
+
|
|
5044
5147
|
# === Special handling for ffuf: Create SQLMap jobs and recursive ffuf scans ===
|
|
5045
5148
|
if tool == 'ffuf':
|
|
5046
5149
|
from souleyez.engine.background import enqueue_job
|
souleyez/docs/README.md
CHANGED
|
@@ -1218,12 +1218,27 @@ def parse_gobuster_job(engagement_id: int, log_path: str, job: Dict[str, Any]) -
|
|
|
1218
1218
|
exclude_length = length_match.group(1)
|
|
1219
1219
|
logger.info(f"Gobuster wildcard detected: Length {exclude_length}b")
|
|
1220
1220
|
|
|
1221
|
+
# Check for host-level redirect (for auto-retry with corrected target)
|
|
1222
|
+
host_redirect_detected = False
|
|
1223
|
+
redirect_target = None
|
|
1224
|
+
|
|
1225
|
+
if "HOST_REDIRECT_TARGET:" in log_content:
|
|
1226
|
+
host_redirect_detected = True
|
|
1227
|
+
import re
|
|
1228
|
+
redirect_match = re.search(r'HOST_REDIRECT_TARGET:\s*(\S+)', log_content)
|
|
1229
|
+
if redirect_match:
|
|
1230
|
+
redirect_target = redirect_match.group(1)
|
|
1231
|
+
logger.info(f"Gobuster host redirect detected: {redirect_target}")
|
|
1232
|
+
|
|
1221
1233
|
# Check for gobuster errors
|
|
1222
1234
|
gobuster_error = detect_tool_error(log_content, 'gobuster')
|
|
1223
1235
|
|
|
1224
1236
|
# Determine status based on results
|
|
1225
1237
|
if gobuster_error:
|
|
1226
1238
|
status = STATUS_ERROR # Tool failed to connect
|
|
1239
|
+
elif host_redirect_detected:
|
|
1240
|
+
# Host redirect detected - warning status (triggers auto-retry with corrected target)
|
|
1241
|
+
status = STATUS_WARNING
|
|
1227
1242
|
elif wildcard_detected:
|
|
1228
1243
|
# Wildcard detected - warning status (triggers auto-retry)
|
|
1229
1244
|
status = STATUS_WARNING
|
|
@@ -1255,6 +1270,12 @@ def parse_gobuster_job(engagement_id: int, log_path: str, job: Dict[str, Any]) -
|
|
|
1255
1270
|
if exclude_length:
|
|
1256
1271
|
result['exclude_length'] = exclude_length
|
|
1257
1272
|
|
|
1273
|
+
# Add host redirect info if detected
|
|
1274
|
+
if host_redirect_detected:
|
|
1275
|
+
result['host_redirect_detected'] = True
|
|
1276
|
+
if redirect_target:
|
|
1277
|
+
result['redirect_target'] = redirect_target
|
|
1278
|
+
|
|
1258
1279
|
return result
|
|
1259
1280
|
except Exception as e:
|
|
1260
1281
|
return {'error': str(e)}
|
souleyez/main.py
CHANGED
|
@@ -173,7 +173,7 @@ def _check_privileged_tools():
|
|
|
173
173
|
|
|
174
174
|
|
|
175
175
|
@click.group()
|
|
176
|
-
@click.version_option(version='2.43.
|
|
176
|
+
@click.version_option(version='2.43.15')
|
|
177
177
|
def cli():
|
|
178
178
|
"""SoulEyez - AI-Powered Pentesting Platform by CyberSoul Security"""
|
|
179
179
|
from souleyez.log_config import init_logging
|
|
@@ -57,7 +57,8 @@ def parse_dnsrecon_output(output: str, target: str = "") -> Dict[str, Any]:
|
|
|
57
57
|
continue
|
|
58
58
|
|
|
59
59
|
# Parse DNS records from dnsrecon output
|
|
60
|
-
#
|
|
60
|
+
# Format: [*] A cybersoulsecurity.com 198.185.159.144 (info)
|
|
61
|
+
# Format: [+] A www.vulnweb.com 44.228.249.3 (found records)
|
|
61
62
|
# New format: 2026-01-08T13:50:16.302153-1000 INFO SOA dns1.p01.nsone.net 198.51.44.1
|
|
62
63
|
# New format: 2026-01-08T13:50:17.112742-1000 INFO NS dns4.p01.nsone.net 198.51.45.65
|
|
63
64
|
|
|
@@ -65,8 +66,9 @@ def parse_dnsrecon_output(output: str, target: str = "") -> Dict[str, Any]:
|
|
|
65
66
|
hostname = None
|
|
66
67
|
ip = None
|
|
67
68
|
|
|
68
|
-
if line_stripped.startswith('[*]'):
|
|
69
|
-
#
|
|
69
|
+
if line_stripped.startswith('[*]') or line_stripped.startswith('[+]'):
|
|
70
|
+
# Format: [*] or [+] <type> <hostname> <ip>
|
|
71
|
+
# [*] = informational, [+] = found/success
|
|
70
72
|
parts = line_stripped.split()
|
|
71
73
|
if len(parts) >= 4:
|
|
72
74
|
record_type = parts[1]
|
souleyez/plugins/gobuster.py
CHANGED
|
@@ -239,18 +239,25 @@ class GobusterPlugin(PluginBase):
|
|
|
239
239
|
This causes gobuster to fail or produce false positives. We detect this
|
|
240
240
|
upfront and auto-add --exclude-length to filter them out.
|
|
241
241
|
|
|
242
|
+
Also detects host-level redirects (e.g., non-www to www) and warns the user.
|
|
243
|
+
|
|
242
244
|
Returns:
|
|
243
245
|
dict with keys:
|
|
244
246
|
- exclude_length: str or None (response length to exclude)
|
|
245
247
|
- exclude_status: str or None (status code detected)
|
|
246
248
|
- reason: str or None (explanation)
|
|
249
|
+
- redirect_host: str or None (suggested target if host redirect detected)
|
|
247
250
|
"""
|
|
248
|
-
result = {'exclude_length': None, 'exclude_status': None, 'reason': None}
|
|
251
|
+
result = {'exclude_length': None, 'exclude_status': None, 'reason': None, 'redirect_host': None}
|
|
249
252
|
|
|
250
253
|
# Generate random UUID path that definitely doesn't exist
|
|
251
254
|
test_path = str(uuid.uuid4())
|
|
252
255
|
test_url = f"{base_url.rstrip('/')}/{test_path}"
|
|
253
256
|
|
|
257
|
+
# Parse the original target host for comparison
|
|
258
|
+
original_parsed = urlparse(base_url)
|
|
259
|
+
original_host = original_parsed.netloc.lower()
|
|
260
|
+
|
|
254
261
|
try:
|
|
255
262
|
resp = requests.get(
|
|
256
263
|
test_url,
|
|
@@ -263,6 +270,46 @@ class GobusterPlugin(PluginBase):
|
|
|
263
270
|
if resp.status_code == 404:
|
|
264
271
|
return result
|
|
265
272
|
|
|
273
|
+
# Check for host-level redirects (301/302/307/308)
|
|
274
|
+
if resp.status_code in [301, 302, 307, 308]:
|
|
275
|
+
location = resp.headers.get('Location', '')
|
|
276
|
+
if location:
|
|
277
|
+
# Parse the redirect location
|
|
278
|
+
redirect_parsed = urlparse(location)
|
|
279
|
+
redirect_host = redirect_parsed.netloc.lower()
|
|
280
|
+
|
|
281
|
+
# If Location is relative, it's not a host redirect
|
|
282
|
+
if redirect_host and redirect_host != original_host:
|
|
283
|
+
# This is a host-level redirect (e.g., non-www to www)
|
|
284
|
+
suggested_url = f"{redirect_parsed.scheme or original_parsed.scheme}://{redirect_host}"
|
|
285
|
+
result['redirect_host'] = suggested_url
|
|
286
|
+
result['exclude_status'] = str(resp.status_code)
|
|
287
|
+
result['reason'] = (
|
|
288
|
+
f"Host redirect detected: {original_host} → {redirect_host}"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
if log_path:
|
|
292
|
+
with open(log_path, 'a') as f:
|
|
293
|
+
f.write(f"\n{'=' * 70}\n")
|
|
294
|
+
f.write("⚠️ HOST-LEVEL REDIRECT DETECTED\n")
|
|
295
|
+
f.write(f"{'=' * 70}\n")
|
|
296
|
+
f.write(f"Target: {base_url}\n")
|
|
297
|
+
f.write(f"Redirects to: {redirect_host}\n")
|
|
298
|
+
f.write(f"Status: {resp.status_code}\n\n")
|
|
299
|
+
f.write("The server redirects ALL requests to a different host.\n")
|
|
300
|
+
f.write("This causes unreliable results because:\n")
|
|
301
|
+
f.write(" - Response sizes vary based on path length in redirect URL\n")
|
|
302
|
+
f.write(" - Gobuster may report false positives or miss real paths\n\n")
|
|
303
|
+
# Parseable marker for auto-retry
|
|
304
|
+
f.write(f"HOST_REDIRECT_TARGET: {suggested_url}\n\n")
|
|
305
|
+
f.write("Auto-retrying with corrected target...\n")
|
|
306
|
+
f.write(f"{'=' * 70}\n\n")
|
|
307
|
+
|
|
308
|
+
# Still try to exclude the response length for this scan
|
|
309
|
+
content_length = len(resp.content)
|
|
310
|
+
result['exclude_length'] = str(content_length)
|
|
311
|
+
return result
|
|
312
|
+
|
|
266
313
|
# Any other status for a random UUID = false positive indicator
|
|
267
314
|
# Common: 403 (blocked), 401 (auth required), 200 (catch-all), 500 (error page)
|
|
268
315
|
if resp.status_code in [200, 301, 302, 400, 401, 403, 500, 503]:
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
souleyez.ui.wordlist_browser - Interactive wordlist browser with keyboard navigation
|
|
4
|
+
|
|
5
|
+
Discovers wordlists from common directories (SecLists, Kali, etc.) and provides
|
|
6
|
+
an interactive browser for selection.
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.table import Table
|
|
15
|
+
|
|
16
|
+
from souleyez.ui.design_system import DesignSystem
|
|
17
|
+
from souleyez.ui.interactive_selector import _get_key, KEY_UP, KEY_DOWN, KEY_ENTER, KEY_ESCAPE
|
|
18
|
+
|
|
19
|
+
# Common wordlist directories to scan
|
|
20
|
+
WORDLIST_DIRECTORIES: List[Tuple[str, str]] = [
|
|
21
|
+
# SoulEyez built-in (highest priority)
|
|
22
|
+
('SoulEyez', '~/.souleyez/data/wordlists'),
|
|
23
|
+
# Kali/Parrot Linux
|
|
24
|
+
('System', '/usr/share/wordlists'),
|
|
25
|
+
# SecLists - various install locations
|
|
26
|
+
('SecLists', '/usr/share/seclists'),
|
|
27
|
+
('SecLists', '/opt/SecLists'),
|
|
28
|
+
('SecLists', '/opt/seclists'),
|
|
29
|
+
('SecLists', '~/SecLists'),
|
|
30
|
+
# Metasploit
|
|
31
|
+
('Metasploit', '/usr/share/metasploit-framework/data/wordlists'),
|
|
32
|
+
# Dirb
|
|
33
|
+
('Dirb', '/usr/share/dirb/wordlists'),
|
|
34
|
+
# Dirbuster
|
|
35
|
+
('Dirbuster', '/usr/share/dirbuster/wordlists'),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Category detection patterns - ORDER MATTERS (more specific first)
|
|
39
|
+
# Each pattern is checked against the full path + filename
|
|
40
|
+
CATEGORY_PATTERNS = {
|
|
41
|
+
# DNS/Subdomain - check first since "subdomain" is specific
|
|
42
|
+
'dns': [
|
|
43
|
+
'subdomain', 'dns', 'vhost', 'hostname'
|
|
44
|
+
],
|
|
45
|
+
# Extensions
|
|
46
|
+
'extensions': [
|
|
47
|
+
'extension', 'ext.', 'suffix', 'filetype'
|
|
48
|
+
],
|
|
49
|
+
# Fuzzing
|
|
50
|
+
'fuzzing': [
|
|
51
|
+
'fuzz', 'injection', 'xss', 'sqli', 'lfi', 'rfi', 'traversal'
|
|
52
|
+
],
|
|
53
|
+
# Users - be specific to avoid matching "username" in paths
|
|
54
|
+
'users': [
|
|
55
|
+
'users.txt', 'usernames', 'user_', '_user', 'logins', 'accounts',
|
|
56
|
+
'/users/', '/usernames/'
|
|
57
|
+
],
|
|
58
|
+
# Passwords - check before dirs
|
|
59
|
+
'passwords': [
|
|
60
|
+
'password', 'pass.txt', 'passwd', 'credential', 'rockyou',
|
|
61
|
+
'darkweb', 'leaked', '/passwords/'
|
|
62
|
+
],
|
|
63
|
+
# Directories - last since patterns are more generic
|
|
64
|
+
'dirs': [
|
|
65
|
+
'directory', 'dirs', 'dir-', 'web-content', 'web_content',
|
|
66
|
+
'dirbuster', '/dirb/', 'raft-', 'apache.txt', 'iis.txt'
|
|
67
|
+
],
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def detect_category(path: str, name: str) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Detect wordlist category from path and filename.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
path: Full path to wordlist
|
|
77
|
+
name: Filename
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Category string: 'dirs', 'dns', 'passwords', 'users', 'fuzzing', 'extensions', or 'other'
|
|
81
|
+
"""
|
|
82
|
+
path_lower = path.lower()
|
|
83
|
+
name_lower = name.lower()
|
|
84
|
+
combined = f"{path_lower}/{name_lower}"
|
|
85
|
+
|
|
86
|
+
for category, patterns in CATEGORY_PATTERNS.items():
|
|
87
|
+
for pattern in patterns:
|
|
88
|
+
if pattern in combined:
|
|
89
|
+
return category
|
|
90
|
+
|
|
91
|
+
return 'other'
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def count_lines(filepath: str, max_count: int = 1000000) -> int:
|
|
95
|
+
"""
|
|
96
|
+
Count lines in a file efficiently.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
filepath: Path to file
|
|
100
|
+
max_count: Stop counting after this many lines
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Line count (or max_count if exceeded)
|
|
104
|
+
"""
|
|
105
|
+
try:
|
|
106
|
+
count = 0
|
|
107
|
+
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
|
108
|
+
for _ in f:
|
|
109
|
+
count += 1
|
|
110
|
+
if count >= max_count:
|
|
111
|
+
return max_count
|
|
112
|
+
return count
|
|
113
|
+
except Exception:
|
|
114
|
+
return 0
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def discover_all_wordlists(category_filter: Optional[str] = None) -> List[Dict[str, Any]]:
|
|
118
|
+
"""
|
|
119
|
+
Discover all wordlists from known directories.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
category_filter: Optional category to filter by ('dirs', 'dns', 'passwords', etc.)
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
List of wordlist dicts with: path, name, source, category, entries, size_mb
|
|
126
|
+
"""
|
|
127
|
+
wordlists = []
|
|
128
|
+
seen_paths = set()
|
|
129
|
+
|
|
130
|
+
for source, directory in WORDLIST_DIRECTORIES:
|
|
131
|
+
dir_path = os.path.expanduser(directory)
|
|
132
|
+
|
|
133
|
+
if not os.path.isdir(dir_path):
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
# Walk directory tree
|
|
137
|
+
for root, _, files in os.walk(dir_path):
|
|
138
|
+
for filename in files:
|
|
139
|
+
# Skip non-text files
|
|
140
|
+
if not filename.endswith(('.txt', '.lst', '.dic', '.wordlist')):
|
|
141
|
+
# Also include files without extension if they look like wordlists
|
|
142
|
+
if '.' in filename:
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
filepath = os.path.join(root, filename)
|
|
146
|
+
|
|
147
|
+
# Skip if already seen (handles duplicate paths)
|
|
148
|
+
if filepath in seen_paths:
|
|
149
|
+
continue
|
|
150
|
+
seen_paths.add(filepath)
|
|
151
|
+
|
|
152
|
+
# Get relative path from source directory for display
|
|
153
|
+
rel_path = os.path.relpath(filepath, dir_path)
|
|
154
|
+
|
|
155
|
+
# Detect category
|
|
156
|
+
category = detect_category(filepath, filename)
|
|
157
|
+
|
|
158
|
+
# Apply category filter
|
|
159
|
+
if category_filter and category != category_filter:
|
|
160
|
+
# Also check if filter matches 'other' for unmatched
|
|
161
|
+
if category_filter != 'all':
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
# Get file stats
|
|
165
|
+
try:
|
|
166
|
+
stat = os.stat(filepath)
|
|
167
|
+
size_mb = stat.st_size / (1024 * 1024)
|
|
168
|
+
except Exception:
|
|
169
|
+
size_mb = 0
|
|
170
|
+
|
|
171
|
+
# Count entries (limit to avoid slow scans on huge files)
|
|
172
|
+
if size_mb < 50: # Only count for files under 50MB
|
|
173
|
+
entries = count_lines(filepath)
|
|
174
|
+
else:
|
|
175
|
+
entries = -1 # Will display as "large"
|
|
176
|
+
|
|
177
|
+
wordlists.append({
|
|
178
|
+
'path': filepath,
|
|
179
|
+
'name': filename,
|
|
180
|
+
'rel_path': rel_path,
|
|
181
|
+
'source': source,
|
|
182
|
+
'category': category,
|
|
183
|
+
'entries': entries,
|
|
184
|
+
'size_mb': size_mb,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
# Sort: SoulEyez first, then by source, then by name
|
|
188
|
+
def sort_key(w):
|
|
189
|
+
source_order = {
|
|
190
|
+
'SoulEyez': 0,
|
|
191
|
+
'SecLists': 1,
|
|
192
|
+
'System': 2,
|
|
193
|
+
'Dirb': 3,
|
|
194
|
+
'Dirbuster': 4,
|
|
195
|
+
'Metasploit': 5,
|
|
196
|
+
}
|
|
197
|
+
return (source_order.get(w['source'], 99), w['name'].lower())
|
|
198
|
+
|
|
199
|
+
wordlists.sort(key=sort_key)
|
|
200
|
+
|
|
201
|
+
return wordlists
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class WordlistBrowser:
|
|
205
|
+
"""
|
|
206
|
+
Interactive single-select browser for wordlists.
|
|
207
|
+
|
|
208
|
+
Based on InteractiveSelector but for single selection:
|
|
209
|
+
- Enter selects highlighted item (no checkboxes)
|
|
210
|
+
- Tab cycles category filter
|
|
211
|
+
- / starts search mode
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
CURSOR = '>'
|
|
215
|
+
CATEGORIES = ['all', 'dirs', 'dns', 'passwords', 'users', 'fuzzing', 'other']
|
|
216
|
+
|
|
217
|
+
def __init__(
|
|
218
|
+
self,
|
|
219
|
+
category_filter: Optional[str] = None,
|
|
220
|
+
title: str = 'WORDLIST BROWSER'
|
|
221
|
+
):
|
|
222
|
+
"""
|
|
223
|
+
Initialize the wordlist browser.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
category_filter: Initial category filter
|
|
227
|
+
title: Title to display
|
|
228
|
+
"""
|
|
229
|
+
self.title = title
|
|
230
|
+
self.suggested_category = category_filter # Remember suggested filter
|
|
231
|
+
self.category_idx = 0 # Start with 'all' so users see everything
|
|
232
|
+
|
|
233
|
+
self.search_query = ''
|
|
234
|
+
self.search_mode = False
|
|
235
|
+
self.all_wordlists = discover_all_wordlists()
|
|
236
|
+
self.filtered = self._apply_filter()
|
|
237
|
+
self.cursor_pos = 0
|
|
238
|
+
self.page_start = 0
|
|
239
|
+
self.page_size = 15
|
|
240
|
+
self.console = Console()
|
|
241
|
+
self.running = True
|
|
242
|
+
|
|
243
|
+
def _apply_filter(self) -> List[Dict[str, Any]]:
|
|
244
|
+
"""Apply current category filter and search query."""
|
|
245
|
+
category = self.CATEGORIES[self.category_idx]
|
|
246
|
+
filtered = self.all_wordlists
|
|
247
|
+
|
|
248
|
+
# Apply category filter
|
|
249
|
+
if category != 'all':
|
|
250
|
+
filtered = [w for w in filtered if w['category'] == category]
|
|
251
|
+
|
|
252
|
+
# Apply search query
|
|
253
|
+
if self.search_query:
|
|
254
|
+
query = self.search_query.lower()
|
|
255
|
+
filtered = [w for w in filtered if query in w['name'].lower() or query in w['rel_path'].lower()]
|
|
256
|
+
|
|
257
|
+
return filtered
|
|
258
|
+
|
|
259
|
+
def run(self) -> Optional[str]:
|
|
260
|
+
"""
|
|
261
|
+
Run the interactive browser.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Selected wordlist path or None if cancelled
|
|
265
|
+
"""
|
|
266
|
+
if not self.all_wordlists:
|
|
267
|
+
click.echo(click.style(" No wordlists found in common directories.", fg='yellow'))
|
|
268
|
+
click.pause()
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
self.running = True
|
|
272
|
+
|
|
273
|
+
while self.running:
|
|
274
|
+
self._render()
|
|
275
|
+
key = _get_key()
|
|
276
|
+
result = self._handle_key(key)
|
|
277
|
+
if result is not None:
|
|
278
|
+
return result
|
|
279
|
+
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
def _render(self):
|
|
283
|
+
"""Render the browser UI."""
|
|
284
|
+
from rich.table import Table
|
|
285
|
+
from rich import box
|
|
286
|
+
|
|
287
|
+
DesignSystem.clear_screen()
|
|
288
|
+
width = DesignSystem.get_terminal_width()
|
|
289
|
+
|
|
290
|
+
# Title bar - box style like InteractiveSelector
|
|
291
|
+
click.echo()
|
|
292
|
+
click.echo("┌" + "─" * (width - 2) + "┐")
|
|
293
|
+
category = self.CATEGORIES[self.category_idx]
|
|
294
|
+
filter_text = f"Filter: {category}"
|
|
295
|
+
title_text = f" {self.title} "
|
|
296
|
+
padding = width - len(title_text) - len(filter_text) - 4
|
|
297
|
+
left_pad = padding // 2
|
|
298
|
+
right_pad = padding - left_pad
|
|
299
|
+
click.echo("│" + " " * left_pad + click.style(title_text, bold=True, fg='cyan') +
|
|
300
|
+
" " * right_pad + click.style(filter_text, fg='yellow') + " │")
|
|
301
|
+
click.echo("└" + "─" * (width - 2) + "┘")
|
|
302
|
+
click.echo()
|
|
303
|
+
|
|
304
|
+
# Search bar
|
|
305
|
+
if self.search_mode:
|
|
306
|
+
click.echo(f" Search: {click.style(self.search_query + '_', fg='cyan')}")
|
|
307
|
+
elif self.search_query:
|
|
308
|
+
click.echo(f" Search: {click.style(self.search_query, fg='cyan')} (press / to edit)")
|
|
309
|
+
|
|
310
|
+
# Stats
|
|
311
|
+
total = len(self.filtered)
|
|
312
|
+
click.echo(f" {click.style('Total:', bold=True)} {total} wordlists")
|
|
313
|
+
click.echo()
|
|
314
|
+
|
|
315
|
+
if not self.filtered:
|
|
316
|
+
click.echo(click.style(" No wordlists match current filter.", fg='yellow'))
|
|
317
|
+
click.echo()
|
|
318
|
+
else:
|
|
319
|
+
# Calculate visible items
|
|
320
|
+
page_end = min(self.page_start + self.page_size, len(self.filtered))
|
|
321
|
+
visible = self.filtered[self.page_start:page_end]
|
|
322
|
+
|
|
323
|
+
# Create table like InteractiveSelector
|
|
324
|
+
table = Table(
|
|
325
|
+
show_header=True,
|
|
326
|
+
header_style="bold cyan",
|
|
327
|
+
box=DesignSystem.TABLE_BOX,
|
|
328
|
+
padding=(0, 1),
|
|
329
|
+
expand=True
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# Add columns
|
|
333
|
+
table.add_column(" ", width=2, justify="center", no_wrap=True) # Cursor
|
|
334
|
+
table.add_column("Name", no_wrap=True)
|
|
335
|
+
table.add_column("Entries", width=12, justify="right")
|
|
336
|
+
table.add_column("Source", width=12)
|
|
337
|
+
table.add_column("Category", width=12)
|
|
338
|
+
|
|
339
|
+
# Add rows
|
|
340
|
+
for idx, wordlist in enumerate(visible):
|
|
341
|
+
absolute_idx = self.page_start + idx
|
|
342
|
+
is_cursor = (absolute_idx == self.cursor_pos)
|
|
343
|
+
|
|
344
|
+
# Cursor indicator
|
|
345
|
+
cursor = "▶" if is_cursor else " "
|
|
346
|
+
|
|
347
|
+
# Name - use rel_path if it has subdirs
|
|
348
|
+
name = wordlist['rel_path'] if '/' in wordlist['rel_path'] else wordlist['name']
|
|
349
|
+
if len(name) > 45:
|
|
350
|
+
name = '...' + name[-42:]
|
|
351
|
+
|
|
352
|
+
# Entry count
|
|
353
|
+
if wordlist['entries'] == -1:
|
|
354
|
+
entries_str = 'large'
|
|
355
|
+
elif wordlist['entries'] >= 1000000:
|
|
356
|
+
entries_str = f"{wordlist['entries'] / 1000000:.1f}M"
|
|
357
|
+
elif wordlist['entries'] >= 1000:
|
|
358
|
+
entries_str = f"{wordlist['entries'] / 1000:.1f}K"
|
|
359
|
+
else:
|
|
360
|
+
entries_str = str(wordlist['entries'])
|
|
361
|
+
|
|
362
|
+
# Source color
|
|
363
|
+
source = wordlist['source']
|
|
364
|
+
|
|
365
|
+
# Category
|
|
366
|
+
cat = wordlist['category']
|
|
367
|
+
|
|
368
|
+
# Add row with highlight for cursor
|
|
369
|
+
if is_cursor:
|
|
370
|
+
table.add_row(cursor, name, entries_str, source, cat, style="reverse")
|
|
371
|
+
else:
|
|
372
|
+
table.add_row(cursor, name, entries_str, source, cat)
|
|
373
|
+
|
|
374
|
+
self.console.print(table)
|
|
375
|
+
|
|
376
|
+
# Pagination
|
|
377
|
+
if len(self.filtered) > self.page_size:
|
|
378
|
+
page_num = (self.page_start // self.page_size) + 1
|
|
379
|
+
total_pages = (len(self.filtered) + self.page_size - 1) // self.page_size
|
|
380
|
+
click.echo()
|
|
381
|
+
click.echo(f" Page {page_num}/{total_pages}")
|
|
382
|
+
|
|
383
|
+
# Help bar
|
|
384
|
+
click.echo()
|
|
385
|
+
click.echo(DesignSystem.separator())
|
|
386
|
+
help_text = (
|
|
387
|
+
f" {click.style('↑↓/jk:', bold=True)} Navigate | "
|
|
388
|
+
f"{click.style('Enter:', bold=True)} Select | "
|
|
389
|
+
f"{click.style('/:', bold=True)} Search | "
|
|
390
|
+
f"{click.style('Tab:', bold=True)} Filter | "
|
|
391
|
+
f"{click.style('q:', bold=True)} Back"
|
|
392
|
+
)
|
|
393
|
+
click.echo(help_text)
|
|
394
|
+
click.echo(DesignSystem.separator())
|
|
395
|
+
|
|
396
|
+
def _handle_key(self, key: str) -> Optional[str]:
|
|
397
|
+
"""
|
|
398
|
+
Handle a keypress.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Selected path if Enter pressed, None otherwise
|
|
402
|
+
"""
|
|
403
|
+
# Search mode - capture text
|
|
404
|
+
if self.search_mode:
|
|
405
|
+
if key == KEY_ENTER or key == '\r' or key == '\n':
|
|
406
|
+
self.search_mode = False
|
|
407
|
+
self.filtered = self._apply_filter()
|
|
408
|
+
self.cursor_pos = 0
|
|
409
|
+
self.page_start = 0
|
|
410
|
+
elif key == KEY_ESCAPE:
|
|
411
|
+
self.search_mode = False
|
|
412
|
+
self.search_query = ''
|
|
413
|
+
self.filtered = self._apply_filter()
|
|
414
|
+
self.cursor_pos = 0
|
|
415
|
+
self.page_start = 0
|
|
416
|
+
elif key in ('\x7f', '\x08'): # Backspace
|
|
417
|
+
self.search_query = self.search_query[:-1]
|
|
418
|
+
self.filtered = self._apply_filter()
|
|
419
|
+
self.cursor_pos = 0
|
|
420
|
+
self.page_start = 0
|
|
421
|
+
elif len(key) == 1 and key.isprintable():
|
|
422
|
+
self.search_query += key
|
|
423
|
+
self.filtered = self._apply_filter()
|
|
424
|
+
self.cursor_pos = 0
|
|
425
|
+
self.page_start = 0
|
|
426
|
+
return None
|
|
427
|
+
|
|
428
|
+
# Navigation - Up
|
|
429
|
+
if key in (KEY_UP, 'k'):
|
|
430
|
+
if self.cursor_pos > 0:
|
|
431
|
+
self.cursor_pos -= 1
|
|
432
|
+
if self.cursor_pos < self.page_start:
|
|
433
|
+
self.page_start = max(0, self.page_start - self.page_size)
|
|
434
|
+
|
|
435
|
+
# Navigation - Down
|
|
436
|
+
elif key in (KEY_DOWN, 'j'):
|
|
437
|
+
if self.filtered and self.cursor_pos < len(self.filtered) - 1:
|
|
438
|
+
self.cursor_pos += 1
|
|
439
|
+
if self.cursor_pos >= self.page_start + self.page_size:
|
|
440
|
+
self.page_start += self.page_size
|
|
441
|
+
|
|
442
|
+
# Select - Enter
|
|
443
|
+
elif key in (KEY_ENTER, '\r', '\n'):
|
|
444
|
+
if self.filtered:
|
|
445
|
+
return self.filtered[self.cursor_pos]['path']
|
|
446
|
+
|
|
447
|
+
# Search mode
|
|
448
|
+
elif key == '/':
|
|
449
|
+
self.search_mode = True
|
|
450
|
+
|
|
451
|
+
# Cycle category filter - Tab
|
|
452
|
+
elif key == '\t':
|
|
453
|
+
self.category_idx = (self.category_idx + 1) % len(self.CATEGORIES)
|
|
454
|
+
self.filtered = self._apply_filter()
|
|
455
|
+
self.cursor_pos = 0
|
|
456
|
+
self.page_start = 0
|
|
457
|
+
|
|
458
|
+
# Exit
|
|
459
|
+
elif key in (KEY_ESCAPE, 'q', '\x03'): # \x03 is Ctrl+C
|
|
460
|
+
self.running = False
|
|
461
|
+
|
|
462
|
+
return None
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def browse_wordlists(category_filter: Optional[str] = None, title: str = 'WORDLIST BROWSER') -> Optional[str]:
|
|
466
|
+
"""
|
|
467
|
+
Launch the interactive wordlist browser.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
category_filter: Optional initial category filter
|
|
471
|
+
title: Browser title
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
Selected wordlist path or None if cancelled
|
|
475
|
+
"""
|
|
476
|
+
browser = WordlistBrowser(category_filter=category_filter, title=title)
|
|
477
|
+
return browser.run()
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
# Convenience export
|
|
481
|
+
__all__ = ['browse_wordlists', 'discover_all_wordlists', 'WordlistBrowser', 'WORDLIST_DIRECTORIES']
|
souleyez/wordlists.py
CHANGED
|
@@ -391,6 +391,11 @@ def display_wordlist_menu(tool_name, category, title="Wordlist Selection"):
|
|
|
391
391
|
idx += 1
|
|
392
392
|
click.echo()
|
|
393
393
|
|
|
394
|
+
# Browse all wordlists option
|
|
395
|
+
click.echo(f" {idx}. Browse all wordlists...")
|
|
396
|
+
browse_idx = idx
|
|
397
|
+
idx += 1
|
|
398
|
+
|
|
394
399
|
# Dynamic label based on category
|
|
395
400
|
single_label = "Enter single username" if category == 'users' else "Enter single password" if category == 'passwords' else "Enter single value"
|
|
396
401
|
|
|
@@ -411,6 +416,13 @@ def display_wordlist_menu(tool_name, category, title="Wordlist Selection"):
|
|
|
411
416
|
selected = choice_map[choice]
|
|
412
417
|
click.echo(click.style(f"✓ Selected: {selected}", fg='green'))
|
|
413
418
|
return selected
|
|
419
|
+
elif choice == browse_idx:
|
|
420
|
+
# Launch interactive wordlist browser
|
|
421
|
+
from souleyez.ui.wordlist_browser import browse_wordlists
|
|
422
|
+
selected = browse_wordlists(category_filter=category, title=f'SELECT {category.upper()} WORDLIST')
|
|
423
|
+
if selected:
|
|
424
|
+
click.echo(click.style(f"✓ Selected: {selected}", fg='green'))
|
|
425
|
+
return selected
|
|
414
426
|
elif choice == single_idx:
|
|
415
427
|
# Single value entry - return tuple to indicate it's not a file
|
|
416
428
|
value_prompt = "Enter username" if category == 'users' else "Enter password" if category == 'passwords' else "Enter value"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: souleyez
|
|
3
|
-
Version: 2.43.
|
|
3
|
+
Version: 2.43.15
|
|
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>
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
souleyez/__init__.py,sha256=
|
|
1
|
+
souleyez/__init__.py,sha256=slnfvvkWKYSrEzjlUbRNn3eLdpvlqHX0-gUtgkJAmx4,25
|
|
2
2
|
souleyez/config.py,sha256=av357I3GYRWAklv8Dto-9-5Db699Wq5znez7zo7241Q,11595
|
|
3
3
|
souleyez/devtools.py,sha256=rptmUY4a5eVvYjdEc6273MSagL-D9xibPOFgohVqUno,3508
|
|
4
4
|
souleyez/feature_flags.py,sha256=mo6YAq07lc6sR3lEFKmIwTKxXZ2JPxwa5X97uR_mu50,4642
|
|
5
5
|
souleyez/history.py,sha256=gzs5I_j-3OigIP6yfmBChdqxaFmyUIxvTpzWUPe_Q6c,2853
|
|
6
6
|
souleyez/log_config.py,sha256=MMhPAJOqgXDfuE-xm5g0RxAfWndcmbhFHvIEMm1a_Wo,5830
|
|
7
|
-
souleyez/main.py,sha256=
|
|
7
|
+
souleyez/main.py,sha256=MKgVu0VaYdNpZsq0MWFNz33uX3P8MWwennF_2gmDjPQ,129101
|
|
8
8
|
souleyez/scanner.py,sha256=U3IWHRrJ5aQ32dSHiVAHB60w1R_z0E0QxfM99msYNlw,3124
|
|
9
9
|
souleyez/security.py,sha256=S84m1QmnKz_6NgH2I6IBIAorMHxRPNYVFSnks5xjihQ,2479
|
|
10
10
|
souleyez/ui.py,sha256=15pfsqoDPnojAqr5S0TZHJE2ZkSHzkHpNVfVvsRj66A,34301
|
|
11
11
|
souleyez/utils.py,sha256=2IbIwzBmGm4_vVvPl9YV1ExS2dxhVS8SGVClvuWKuZI,2721
|
|
12
|
-
souleyez/wordlists.py,sha256=
|
|
12
|
+
souleyez/wordlists.py,sha256=xzWOOIPnfVlx3Zeq5dUYDZBW0gBxVX3BRxksTcYcinA,16336
|
|
13
13
|
souleyez/ai/__init__.py,sha256=7C4wIQX8gL5HksjKvyh3yp-XMiMnqIH8UWAF5zn6B2I,908
|
|
14
14
|
souleyez/ai/action_mapper.py,sha256=YdTNV1ylzovJKAROEhArlgGLsH6iyO6ll15My-ILVn8,17587
|
|
15
15
|
souleyez/ai/chain_advisor.py,sha256=3I94rKIG3yGsJ-16MWAnWPMjIvR0Tl57JZjzSf58xDg,16617
|
|
@@ -59,7 +59,7 @@ souleyez/core/network_utils.py,sha256=-4WgUE91RBzyXDFgGTxMa0zsWowJ47cEOAKXNeVa-W
|
|
|
59
59
|
souleyez/core/parser_handler.py,sha256=cyZtEDctqMdWgubsU0Jg6o4XqBgyfaJ_AeBHQmmv4hM,5564
|
|
60
60
|
souleyez/core/pending_chains.py,sha256=Dnka7JK7A8gTWCGpTu6qrIgIDIXprkZmwJ0Rm2oWqRE,10972
|
|
61
61
|
souleyez/core/templates.py,sha256=DzlXlAz8_lwAFjjUWPp3r81KCCzbNeK-bkN1IlgQBSU,18112
|
|
62
|
-
souleyez/core/tool_chaining.py,sha256=
|
|
62
|
+
souleyez/core/tool_chaining.py,sha256=LLrCUwVelZHP7N6yso-fcIJbDVbDJDcPKRQlFx6JIu0,287404
|
|
63
63
|
souleyez/core/version_utils.py,sha256=UOrOa3qfUdLKdzWT6GAGNV9TauwinXyLyelS8sOk0eE,11769
|
|
64
64
|
souleyez/core/vuln_correlation.py,sha256=U69MSI5I-AtiyOAbXohGDKMpEHRW9y4G_0M1ppRGX18,14765
|
|
65
65
|
souleyez/core/web_utils.py,sha256=f-Dqa6tH8ROnygn6-k7J1y8Qz2f1FmeJnPjPE0WRn34,4902
|
|
@@ -104,7 +104,7 @@ souleyez/detection/__init__.py,sha256=QIhvXjFdjrquQ6A0VQ7GZQkK_EXB59t8Dv9PKXhEUe
|
|
|
104
104
|
souleyez/detection/attack_signatures.py,sha256=akgWwiIkh6WYnghCuLhRV0y6FS0SQ0caGF8tZUc49oA,6965
|
|
105
105
|
souleyez/detection/mitre_mappings.py,sha256=xejE80YK-g8kKaeQoo-vBl8P3t8RTTItbfN0NaVZw6s,20558
|
|
106
106
|
souleyez/detection/validator.py,sha256=-AJ7QSJ3-6jFKLnPG_Rc34IXyF4JPyI82BFUgTA9zw0,15641
|
|
107
|
-
souleyez/docs/README.md,sha256=
|
|
107
|
+
souleyez/docs/README.md,sha256=hjhVK2ZaTUOWQWCum2agdKB_VL5O9FRLe74xMT2k-Xw,7188
|
|
108
108
|
souleyez/docs/api-reference/cli-commands.md,sha256=lTLFnILN3YRVdqCaag7WgsYXfDGglb1TuPexkxDsVdE,12917
|
|
109
109
|
souleyez/docs/api-reference/engagement-api.md,sha256=nd-EvQMtiJrobg2bzFEADp853HP1Uhb9dmgok0_-neE,11672
|
|
110
110
|
souleyez/docs/api-reference/integration-guide.md,sha256=c96uX79ukHyYotLa54wZ20Kx-EUZnrKegTeGkfLD-pw,16285
|
|
@@ -152,7 +152,7 @@ souleyez/engine/job_status.py,sha256=OAEf2rAzapm55m4tc3PSilotdA5ONX15JavUMLre0is
|
|
|
152
152
|
souleyez/engine/loader.py,sha256=ke6QQVVWozDnqGNBotajC3RBYOa2_DZmv5DAnDZVgIc,2769
|
|
153
153
|
souleyez/engine/log_sanitizer.py,sha256=QHF6zSms-wHo6SbL6fHXIh1GG-8G34lE7kl45nbPn70,7130
|
|
154
154
|
souleyez/engine/manager.py,sha256=aBQMoib-VWNXtIp5Qn34tRj1P1jiLpwAIoo1fexAaLU,3629
|
|
155
|
-
souleyez/engine/result_handler.py,sha256=
|
|
155
|
+
souleyez/engine/result_handler.py,sha256=jioWzfRrSdRP7vfrcGIujrhh6-H1POkyiBGDnwjoW9I,143455
|
|
156
156
|
souleyez/engine/worker_manager.py,sha256=B7b8RbkKTNofmiIyHTNgdikoZCLXpB-iIl1S4-U3q9o,6127
|
|
157
157
|
souleyez/export/__init__.py,sha256=2kFHftSqqrRUG6PhtfhCyhnkpkjc-8Zb4utGo-Nb6B4,61
|
|
158
158
|
souleyez/export/evidence_bundle.py,sha256=hqPn_h2CidhL-1VAT0qraZ8r1yfnUTnLZ3RfPPCK5Ds,9966
|
|
@@ -195,7 +195,7 @@ souleyez/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
|
|
|
195
195
|
souleyez/parsers/bloodhound_parser.py,sha256=r1f5ePsuPc90CJOySnZ6WkfYQoeVJWraTiE7dnI8BbI,3134
|
|
196
196
|
souleyez/parsers/crackmapexec_parser.py,sha256=HN3nCBMdCEoP1OuYPLmf080b2YmMphZ7Wtm1ByrKGu0,10849
|
|
197
197
|
souleyez/parsers/dalfox_parser.py,sha256=w_eCeyL-Eu9zGyXqYmq0XKbnOIR7lnDD-zbRT7SPco0,6856
|
|
198
|
-
souleyez/parsers/dnsrecon_parser.py,sha256=
|
|
198
|
+
souleyez/parsers/dnsrecon_parser.py,sha256=_j_xE94OAEffhH8YZWQ-lKXlcUVlhTaPHwGcwDBjY5I,8052
|
|
199
199
|
souleyez/parsers/enum4linux_parser.py,sha256=8C9VQ0f-VzKFBkRmScPM84Q8CYvPwidBuQFLH2TJ48c,17728
|
|
200
200
|
souleyez/parsers/ffuf_parser.py,sha256=xoLicqbwjgJqMrkGEyUFmBIx1Jn7t2OG7OtXkN-wvUE,1706
|
|
201
201
|
souleyez/parsers/gobuster_parser.py,sha256=-DS8fpA7uoSKhrGnoMdjowhl7OcitAPoEee-LXiogYs,5960
|
|
@@ -227,7 +227,7 @@ souleyez/plugins/dnsrecon.py,sha256=nxeVgwACUyw5VYEyD-5U277d1U72EkWBX9nR9_DMZrI,
|
|
|
227
227
|
souleyez/plugins/enum4linux.py,sha256=VHkKPs8PWX90RLsGdYt5Ieuc3Sz52fbeWvKCL1KquIY,10876
|
|
228
228
|
souleyez/plugins/ffuf.py,sha256=7c1-Q7xXTMmH_2wHXikjmZnSgZL13Hj5E_asBxZ6Y5U,11652
|
|
229
229
|
souleyez/plugins/firmware_extract.py,sha256=_hZXx6cHb9noM6uVgi3hwrJLw8hE9mDUelTEHwoIdCU,6460
|
|
230
|
-
souleyez/plugins/gobuster.py,sha256=
|
|
230
|
+
souleyez/plugins/gobuster.py,sha256=y8QeEjMR5_2tf-T63nxFRUtWmlCzrPez2I4nLqPNOfY,32114
|
|
231
231
|
souleyez/plugins/hashcat.py,sha256=aigfwBu9IorXKgbyEIWx0qOCEdr1wnZaPqdYwh0PITc,10381
|
|
232
232
|
souleyez/plugins/http_fingerprint.py,sha256=_D5UVAtDC0f-uy4pQcBI43c4jnsJ5wyvCIvttUiVurw,21202
|
|
233
233
|
souleyez/plugins/hydra.py,sha256=kfVJwgh3x1DC0wEtA-lkoY7qhQH1qKViYexUECZSPY4,29520
|
|
@@ -368,11 +368,12 @@ souleyez/ui/tool_setup.py,sha256=HkRTjzN7FHUs_XKtNYnphkdXb5kC4P6ghpI8TeCuEjU,363
|
|
|
368
368
|
souleyez/ui/tutorial.py,sha256=efDF6nWRek6fySppjmq3qMQ3J2WfW89LOcaeltqnWH4,14917
|
|
369
369
|
souleyez/ui/tutorial_state.py,sha256=Thf7_qCj4VKjG7UqgJqa9kjIqiFUU-7Q7kG4v-u2B4A,8123
|
|
370
370
|
souleyez/ui/wazuh_vulns_view.py,sha256=3vJJEmrjgS2wD6EDB7ZV7WxgytBHTm-1WqNDjp7lVEI,21830
|
|
371
|
+
souleyez/ui/wordlist_browser.py,sha256=iQ2YYxrVo8FGCfM-Bc0teVBijSAbd2rjbSQ2hOE7eiY,16110
|
|
371
372
|
souleyez/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
372
373
|
souleyez/utils/tool_checker.py,sha256=YzNajZpFyKJA5fp0Kq_gQ0YnKb7J1BaKJSZ8vP-IWj8,30868
|
|
373
|
-
souleyez-2.43.
|
|
374
|
-
souleyez-2.43.
|
|
375
|
-
souleyez-2.43.
|
|
376
|
-
souleyez-2.43.
|
|
377
|
-
souleyez-2.43.
|
|
378
|
-
souleyez-2.43.
|
|
374
|
+
souleyez-2.43.15.dist-info/licenses/LICENSE,sha256=J7vDD5QMF4w2oSDm35eBgosATE70ah1M40u9W4EpTZs,1090
|
|
375
|
+
souleyez-2.43.15.dist-info/METADATA,sha256=5Ax1maa8aJJjBmPh5hCgS1u6HQFmxRhn9DZ3wuf0IVQ,10426
|
|
376
|
+
souleyez-2.43.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
377
|
+
souleyez-2.43.15.dist-info/entry_points.txt,sha256=bN5W1dhjDZJl3TKclMjRpfQvGPmyrJLwwDuCj_X39HE,48
|
|
378
|
+
souleyez-2.43.15.dist-info/top_level.txt,sha256=afAMzS9p4lcdBNxhGo6jl3ipQE9HUvvNIPOdjtPjr_Q,9
|
|
379
|
+
souleyez-2.43.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|