souleyez 2.43.12__py3-none-any.whl → 2.43.14__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/docs/README.md +1 -1
- souleyez/main.py +1 -1
- souleyez/parsers/dnsrecon_parser.py +5 -3
- souleyez/ui/wordlist_browser.py +481 -0
- souleyez/wordlists.py +12 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.14.dist-info}/METADATA +1 -1
- {souleyez-2.43.12.dist-info → souleyez-2.43.14.dist-info}/RECORD +12 -11
- {souleyez-2.43.12.dist-info → souleyez-2.43.14.dist-info}/WHEEL +0 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.14.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.14.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.12.dist-info → souleyez-2.43.14.dist-info}/top_level.txt +0 -0
souleyez/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = '2.43.
|
|
1
|
+
__version__ = '2.43.14'
|
|
2
2
|
|
souleyez/docs/README.md
CHANGED
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.14')
|
|
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]
|
|
@@ -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.14
|
|
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=R24xs84NLN58y1uyVN2JXzH2DmSj7GoE1wyqZKEguVE,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=iQ6x_YjxxtE-DMdo9phpc1lbkHxGnHzLAl6OuQx2xOs,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
|
|
@@ -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=KZs_B3SZgW1TM9mlclkAw9fwFKlAr1DRhWPVOYwPQcc,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
|
|
@@ -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
|
|
@@ -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.14.dist-info/licenses/LICENSE,sha256=J7vDD5QMF4w2oSDm35eBgosATE70ah1M40u9W4EpTZs,1090
|
|
375
|
+
souleyez-2.43.14.dist-info/METADATA,sha256=fuUybTLHClWobzpKVjJ4-5VLNxXDzLYo7YdusfJ4VqU,10426
|
|
376
|
+
souleyez-2.43.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
377
|
+
souleyez-2.43.14.dist-info/entry_points.txt,sha256=bN5W1dhjDZJl3TKclMjRpfQvGPmyrJLwwDuCj_X39HE,48
|
|
378
|
+
souleyez-2.43.14.dist-info/top_level.txt,sha256=afAMzS9p4lcdBNxhGo6jl3ipQE9HUvvNIPOdjtPjr_Q,9
|
|
379
|
+
souleyez-2.43.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|