souleyez 2.43.29__py3-none-any.whl → 3.0.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.
Potentially problematic release.
This version of souleyez might be problematic. Click here for more details.
- souleyez/__init__.py +1 -2
- souleyez/ai/__init__.py +21 -15
- souleyez/ai/action_mapper.py +249 -150
- souleyez/ai/chain_advisor.py +116 -100
- souleyez/ai/claude_provider.py +29 -28
- souleyez/ai/context_builder.py +80 -62
- souleyez/ai/executor.py +158 -117
- souleyez/ai/feedback_handler.py +136 -121
- souleyez/ai/llm_factory.py +27 -20
- souleyez/ai/llm_provider.py +4 -2
- souleyez/ai/ollama_provider.py +6 -9
- souleyez/ai/ollama_service.py +44 -37
- souleyez/ai/path_scorer.py +91 -76
- souleyez/ai/recommender.py +176 -144
- souleyez/ai/report_context.py +74 -73
- souleyez/ai/report_service.py +84 -66
- souleyez/ai/result_parser.py +222 -229
- souleyez/ai/safety.py +67 -44
- souleyez/auth/__init__.py +23 -22
- souleyez/auth/audit.py +36 -26
- souleyez/auth/engagement_access.py +65 -48
- souleyez/auth/permissions.py +14 -3
- souleyez/auth/session_manager.py +54 -37
- souleyez/auth/user_manager.py +109 -64
- souleyez/commands/audit.py +40 -43
- souleyez/commands/auth.py +35 -15
- souleyez/commands/deliverables.py +55 -50
- souleyez/commands/engagement.py +47 -28
- souleyez/commands/license.py +32 -23
- souleyez/commands/screenshots.py +36 -32
- souleyez/commands/user.py +82 -36
- souleyez/config.py +52 -44
- souleyez/core/credential_tester.py +87 -81
- souleyez/core/cve_mappings.py +179 -192
- souleyez/core/cve_matcher.py +162 -148
- souleyez/core/msf_auto_mapper.py +100 -83
- souleyez/core/msf_chain_engine.py +294 -256
- souleyez/core/msf_database.py +153 -70
- souleyez/core/msf_integration.py +679 -673
- souleyez/core/msf_rpc_client.py +40 -42
- souleyez/core/msf_rpc_manager.py +77 -79
- souleyez/core/msf_sync_manager.py +241 -181
- souleyez/core/network_utils.py +22 -15
- souleyez/core/parser_handler.py +34 -25
- souleyez/core/pending_chains.py +114 -63
- souleyez/core/templates.py +158 -107
- souleyez/core/tool_chaining.py +9564 -2881
- souleyez/core/version_utils.py +79 -94
- souleyez/core/vuln_correlation.py +136 -89
- souleyez/core/web_utils.py +33 -32
- souleyez/data/wordlists/ad_users.txt +378 -0
- souleyez/data/wordlists/api_endpoints_large.txt +769 -0
- souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
- souleyez/data/wordlists/lfi_payloads.txt +82 -0
- souleyez/data/wordlists/passwords_brute.txt +1548 -0
- souleyez/data/wordlists/passwords_crack.txt +2479 -0
- souleyez/data/wordlists/passwords_spray.txt +386 -0
- souleyez/data/wordlists/subdomains_large.txt +5057 -0
- souleyez/data/wordlists/usernames_common.txt +694 -0
- souleyez/data/wordlists/web_dirs_large.txt +4769 -0
- souleyez/detection/__init__.py +1 -1
- souleyez/detection/attack_signatures.py +12 -17
- souleyez/detection/mitre_mappings.py +61 -55
- souleyez/detection/validator.py +97 -86
- souleyez/devtools.py +23 -10
- souleyez/docs/README.md +4 -4
- souleyez/docs/api-reference/cli-commands.md +2 -2
- souleyez/docs/developer-guide/adding-new-tools.md +562 -0
- souleyez/docs/user-guide/auto-chaining.md +30 -8
- souleyez/docs/user-guide/getting-started.md +1 -1
- souleyez/docs/user-guide/installation.md +26 -3
- souleyez/docs/user-guide/metasploit-integration.md +2 -2
- souleyez/docs/user-guide/rbac.md +1 -1
- souleyez/docs/user-guide/scope-management.md +1 -1
- souleyez/docs/user-guide/siem-integration.md +1 -1
- souleyez/docs/user-guide/tools-reference.md +1 -8
- souleyez/docs/user-guide/worker-management.md +1 -1
- souleyez/engine/background.py +1239 -535
- souleyez/engine/base.py +4 -1
- souleyez/engine/job_status.py +17 -49
- souleyez/engine/log_sanitizer.py +103 -77
- souleyez/engine/manager.py +38 -7
- souleyez/engine/result_handler.py +2200 -1550
- souleyez/engine/worker_manager.py +50 -41
- souleyez/export/evidence_bundle.py +72 -62
- souleyez/feature_flags/features.py +16 -20
- souleyez/feature_flags.py +5 -9
- souleyez/handlers/__init__.py +11 -0
- souleyez/handlers/base.py +188 -0
- souleyez/handlers/bash_handler.py +277 -0
- souleyez/handlers/bloodhound_handler.py +243 -0
- souleyez/handlers/certipy_handler.py +311 -0
- souleyez/handlers/crackmapexec_handler.py +486 -0
- souleyez/handlers/dnsrecon_handler.py +344 -0
- souleyez/handlers/enum4linux_handler.py +400 -0
- souleyez/handlers/evil_winrm_handler.py +493 -0
- souleyez/handlers/ffuf_handler.py +815 -0
- souleyez/handlers/gobuster_handler.py +1114 -0
- souleyez/handlers/gpp_extract_handler.py +334 -0
- souleyez/handlers/hashcat_handler.py +444 -0
- souleyez/handlers/hydra_handler.py +564 -0
- souleyez/handlers/impacket_getuserspns_handler.py +343 -0
- souleyez/handlers/impacket_psexec_handler.py +222 -0
- souleyez/handlers/impacket_secretsdump_handler.py +426 -0
- souleyez/handlers/john_handler.py +286 -0
- souleyez/handlers/katana_handler.py +425 -0
- souleyez/handlers/kerbrute_handler.py +298 -0
- souleyez/handlers/ldapsearch_handler.py +636 -0
- souleyez/handlers/lfi_extract_handler.py +464 -0
- souleyez/handlers/msf_auxiliary_handler.py +409 -0
- souleyez/handlers/msf_exploit_handler.py +380 -0
- souleyez/handlers/nikto_handler.py +413 -0
- souleyez/handlers/nmap_handler.py +821 -0
- souleyez/handlers/nuclei_handler.py +359 -0
- souleyez/handlers/nxc_handler.py +417 -0
- souleyez/handlers/rdp_sec_check_handler.py +353 -0
- souleyez/handlers/registry.py +292 -0
- souleyez/handlers/responder_handler.py +232 -0
- souleyez/handlers/service_explorer_handler.py +434 -0
- souleyez/handlers/smbclient_handler.py +344 -0
- souleyez/handlers/smbmap_handler.py +510 -0
- souleyez/handlers/smbpasswd_handler.py +296 -0
- souleyez/handlers/sqlmap_handler.py +1116 -0
- souleyez/handlers/theharvester_handler.py +601 -0
- souleyez/handlers/web_login_test_handler.py +327 -0
- souleyez/handlers/whois_handler.py +277 -0
- souleyez/handlers/wpscan_handler.py +554 -0
- souleyez/history.py +32 -16
- souleyez/importers/msf_importer.py +106 -75
- souleyez/importers/smart_importer.py +208 -147
- souleyez/integrations/siem/__init__.py +10 -10
- souleyez/integrations/siem/base.py +17 -18
- souleyez/integrations/siem/elastic.py +108 -122
- souleyez/integrations/siem/factory.py +207 -80
- souleyez/integrations/siem/googlesecops.py +146 -154
- souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
- souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
- souleyez/integrations/siem/sentinel.py +107 -109
- souleyez/integrations/siem/splunk.py +246 -212
- souleyez/integrations/siem/wazuh.py +65 -71
- souleyez/integrations/wazuh/__init__.py +5 -5
- souleyez/integrations/wazuh/client.py +70 -93
- souleyez/integrations/wazuh/config.py +85 -57
- souleyez/integrations/wazuh/host_mapper.py +28 -36
- souleyez/integrations/wazuh/sync.py +78 -68
- souleyez/intelligence/__init__.py +4 -5
- souleyez/intelligence/correlation_analyzer.py +309 -295
- souleyez/intelligence/exploit_knowledge.py +661 -623
- souleyez/intelligence/exploit_suggestions.py +159 -139
- souleyez/intelligence/gap_analyzer.py +132 -97
- souleyez/intelligence/gap_detector.py +251 -214
- souleyez/intelligence/sensitive_tables.py +266 -129
- souleyez/intelligence/service_parser.py +137 -123
- souleyez/intelligence/surface_analyzer.py +407 -268
- souleyez/intelligence/target_parser.py +159 -162
- souleyez/licensing/__init__.py +6 -6
- souleyez/licensing/validator.py +17 -19
- souleyez/log_config.py +79 -54
- souleyez/main.py +1505 -687
- souleyez/migrations/fix_job_counter.py +16 -14
- souleyez/parsers/bloodhound_parser.py +41 -39
- souleyez/parsers/crackmapexec_parser.py +178 -111
- souleyez/parsers/dalfox_parser.py +72 -77
- souleyez/parsers/dnsrecon_parser.py +103 -91
- souleyez/parsers/enum4linux_parser.py +183 -153
- souleyez/parsers/ffuf_parser.py +29 -25
- souleyez/parsers/gobuster_parser.py +301 -41
- souleyez/parsers/hashcat_parser.py +324 -79
- souleyez/parsers/http_fingerprint_parser.py +350 -103
- souleyez/parsers/hydra_parser.py +131 -111
- souleyez/parsers/impacket_parser.py +231 -178
- souleyez/parsers/john_parser.py +98 -86
- souleyez/parsers/katana_parser.py +316 -0
- souleyez/parsers/msf_parser.py +943 -498
- souleyez/parsers/nikto_parser.py +346 -65
- souleyez/parsers/nmap_parser.py +262 -174
- souleyez/parsers/nuclei_parser.py +40 -44
- souleyez/parsers/responder_parser.py +26 -26
- souleyez/parsers/searchsploit_parser.py +74 -74
- souleyez/parsers/service_explorer_parser.py +279 -0
- souleyez/parsers/smbmap_parser.py +180 -124
- souleyez/parsers/sqlmap_parser.py +434 -308
- souleyez/parsers/theharvester_parser.py +75 -57
- souleyez/parsers/whois_parser.py +135 -94
- souleyez/parsers/wpscan_parser.py +278 -190
- souleyez/plugins/afp.py +44 -36
- souleyez/plugins/afp_brute.py +114 -46
- souleyez/plugins/ard.py +48 -37
- souleyez/plugins/bloodhound.py +95 -61
- souleyez/plugins/certipy.py +303 -0
- souleyez/plugins/crackmapexec.py +186 -85
- souleyez/plugins/dalfox.py +120 -59
- souleyez/plugins/dns_hijack.py +146 -41
- souleyez/plugins/dnsrecon.py +97 -61
- souleyez/plugins/enum4linux.py +91 -66
- souleyez/plugins/evil_winrm.py +291 -0
- souleyez/plugins/ffuf.py +166 -90
- souleyez/plugins/firmware_extract.py +133 -29
- souleyez/plugins/gobuster.py +387 -190
- souleyez/plugins/gpp_extract.py +393 -0
- souleyez/plugins/hashcat.py +100 -73
- souleyez/plugins/http_fingerprint.py +913 -267
- souleyez/plugins/hydra.py +566 -200
- souleyez/plugins/impacket_getnpusers.py +117 -69
- souleyez/plugins/impacket_psexec.py +84 -64
- souleyez/plugins/impacket_secretsdump.py +103 -69
- souleyez/plugins/impacket_smbclient.py +89 -75
- souleyez/plugins/john.py +86 -69
- souleyez/plugins/katana.py +313 -0
- souleyez/plugins/kerbrute.py +237 -0
- souleyez/plugins/lfi_extract.py +541 -0
- souleyez/plugins/macos_ssh.py +117 -48
- souleyez/plugins/mdns.py +35 -30
- souleyez/plugins/msf_auxiliary.py +253 -130
- souleyez/plugins/msf_exploit.py +239 -161
- souleyez/plugins/nikto.py +134 -78
- souleyez/plugins/nmap.py +275 -91
- souleyez/plugins/nuclei.py +180 -89
- souleyez/plugins/nxc.py +285 -0
- souleyez/plugins/plugin_base.py +35 -36
- souleyez/plugins/plugin_template.py +13 -5
- souleyez/plugins/rdp_sec_check.py +130 -0
- souleyez/plugins/responder.py +112 -71
- souleyez/plugins/router_http_brute.py +76 -65
- souleyez/plugins/router_ssh_brute.py +118 -41
- souleyez/plugins/router_telnet_brute.py +124 -42
- souleyez/plugins/routersploit.py +91 -59
- souleyez/plugins/routersploit_exploit.py +77 -55
- souleyez/plugins/searchsploit.py +91 -77
- souleyez/plugins/service_explorer.py +1160 -0
- souleyez/plugins/smbmap.py +122 -72
- souleyez/plugins/smbpasswd.py +215 -0
- souleyez/plugins/sqlmap.py +301 -113
- souleyez/plugins/theharvester.py +127 -75
- souleyez/plugins/tr069.py +79 -57
- souleyez/plugins/upnp.py +65 -47
- souleyez/plugins/upnp_abuse.py +73 -55
- souleyez/plugins/vnc_access.py +129 -42
- souleyez/plugins/vnc_brute.py +109 -38
- souleyez/plugins/web_login_test.py +417 -0
- souleyez/plugins/whois.py +77 -58
- souleyez/plugins/wpscan.py +219 -69
- souleyez/reporting/__init__.py +2 -1
- souleyez/reporting/attack_chain.py +411 -346
- souleyez/reporting/charts.py +436 -501
- souleyez/reporting/compliance_mappings.py +334 -201
- souleyez/reporting/detection_report.py +126 -125
- souleyez/reporting/formatters.py +828 -591
- souleyez/reporting/generator.py +386 -302
- souleyez/reporting/metrics.py +72 -75
- souleyez/scanner.py +35 -29
- souleyez/security/__init__.py +37 -11
- souleyez/security/scope_validator.py +175 -106
- souleyez/security/validation.py +237 -149
- souleyez/security.py +22 -6
- souleyez/storage/credentials.py +247 -186
- souleyez/storage/crypto.py +296 -129
- souleyez/storage/database.py +73 -50
- souleyez/storage/db.py +58 -36
- souleyez/storage/deliverable_evidence.py +177 -128
- souleyez/storage/deliverable_exporter.py +282 -246
- souleyez/storage/deliverable_templates.py +134 -116
- souleyez/storage/deliverables.py +135 -130
- souleyez/storage/engagements.py +109 -56
- souleyez/storage/evidence.py +181 -152
- souleyez/storage/execution_log.py +31 -17
- souleyez/storage/exploit_attempts.py +93 -57
- souleyez/storage/exploits.py +67 -36
- souleyez/storage/findings.py +48 -61
- souleyez/storage/hosts.py +176 -144
- souleyez/storage/migrate_to_engagements.py +43 -19
- souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
- souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
- souleyez/storage/migrations/_003_add_execution_log.py +14 -8
- souleyez/storage/migrations/_005_screenshots.py +13 -5
- souleyez/storage/migrations/_006_deliverables.py +13 -5
- souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
- souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
- souleyez/storage/migrations/_010_evidence_linking.py +17 -10
- souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
- souleyez/storage/migrations/_012_team_collaboration.py +34 -21
- souleyez/storage/migrations/_013_add_host_tags.py +12 -6
- souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
- souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
- souleyez/storage/migrations/_016_add_domain_field.py +10 -4
- souleyez/storage/migrations/_017_msf_sessions.py +16 -8
- souleyez/storage/migrations/_018_add_osint_target.py +10 -6
- souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
- souleyez/storage/migrations/_020_add_rbac.py +36 -15
- souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
- souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
- souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
- souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
- souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
- souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
- souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
- souleyez/storage/migrations/__init__.py +26 -26
- souleyez/storage/migrations/migration_manager.py +19 -19
- souleyez/storage/msf_sessions.py +100 -65
- souleyez/storage/osint.py +17 -24
- souleyez/storage/recommendation_engine.py +269 -235
- souleyez/storage/screenshots.py +33 -32
- souleyez/storage/smb_shares.py +136 -92
- souleyez/storage/sqlmap_data.py +183 -128
- souleyez/storage/team_collaboration.py +135 -141
- souleyez/storage/timeline_tracker.py +122 -94
- souleyez/storage/wazuh_vulns.py +64 -66
- souleyez/storage/web_paths.py +33 -37
- souleyez/testing/credential_tester.py +221 -205
- souleyez/ui/__init__.py +1 -1
- souleyez/ui/ai_quotes.py +12 -12
- souleyez/ui/attack_surface.py +2439 -1516
- souleyez/ui/chain_rules_view.py +914 -382
- souleyez/ui/correlation_view.py +312 -230
- souleyez/ui/dashboard.py +2382 -1130
- souleyez/ui/deliverables_view.py +148 -62
- souleyez/ui/design_system.py +13 -13
- souleyez/ui/errors.py +49 -49
- souleyez/ui/evidence_linking_view.py +284 -179
- souleyez/ui/evidence_vault.py +393 -285
- souleyez/ui/exploit_suggestions_view.py +555 -349
- souleyez/ui/export_view.py +100 -66
- souleyez/ui/gap_analysis_view.py +315 -171
- souleyez/ui/help_system.py +105 -97
- souleyez/ui/intelligence_view.py +436 -293
- souleyez/ui/interactive.py +23034 -10679
- souleyez/ui/interactive_selector.py +75 -68
- souleyez/ui/log_formatter.py +47 -39
- souleyez/ui/menu_components.py +22 -13
- souleyez/ui/msf_auxiliary_menu.py +184 -133
- souleyez/ui/pending_chains_view.py +336 -172
- souleyez/ui/progress_indicators.py +5 -3
- souleyez/ui/recommendations_view.py +195 -137
- souleyez/ui/rule_builder.py +343 -225
- souleyez/ui/setup_wizard.py +678 -284
- souleyez/ui/shortcuts.py +217 -165
- souleyez/ui/splunk_gap_analysis_view.py +452 -270
- souleyez/ui/splunk_vulns_view.py +139 -86
- souleyez/ui/team_dashboard.py +498 -335
- souleyez/ui/template_selector.py +196 -105
- souleyez/ui/terminal.py +6 -6
- souleyez/ui/timeline_view.py +198 -127
- souleyez/ui/tool_setup.py +264 -164
- souleyez/ui/tutorial.py +202 -72
- souleyez/ui/tutorial_state.py +40 -40
- souleyez/ui/wazuh_vulns_view.py +235 -141
- souleyez/ui/wordlist_browser.py +260 -107
- souleyez/ui.py +464 -312
- souleyez/utils/tool_checker.py +427 -367
- souleyez/utils.py +33 -29
- souleyez/wordlists.py +134 -167
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/METADATA +2 -2
- souleyez-3.0.0.dist-info/RECORD +443 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/WHEEL +1 -1
- souleyez-2.43.29.dist-info/RECORD +0 -379
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/entry_points.txt +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {souleyez-2.43.29.dist-info → souleyez-3.0.0.dist-info}/top_level.txt +0 -0
souleyez/ui/wordlist_browser.py
CHANGED
|
@@ -14,56 +14,67 @@ from rich.console import Console
|
|
|
14
14
|
from rich.table import Table
|
|
15
15
|
|
|
16
16
|
from souleyez.ui.design_system import DesignSystem
|
|
17
|
-
from souleyez.ui.interactive_selector import
|
|
17
|
+
from souleyez.ui.interactive_selector import (
|
|
18
|
+
_get_key,
|
|
19
|
+
KEY_UP,
|
|
20
|
+
KEY_DOWN,
|
|
21
|
+
KEY_ENTER,
|
|
22
|
+
KEY_ESCAPE,
|
|
23
|
+
)
|
|
18
24
|
|
|
19
25
|
# Common wordlist directories to scan
|
|
20
26
|
WORDLIST_DIRECTORIES: List[Tuple[str, str]] = [
|
|
21
|
-
# SoulEyez built-in (highest priority)
|
|
22
|
-
(
|
|
23
|
-
#
|
|
24
|
-
(
|
|
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'),
|
|
27
|
+
# SoulEyez built-in (highest priority) - self-contained wordlists
|
|
28
|
+
("SoulEyez", "~/.souleyez/data/wordlists"),
|
|
29
|
+
# Package bundled wordlists (for installed package)
|
|
30
|
+
("SoulEyez-pkg", str(Path(__file__).parent.parent / "data" / "wordlists")),
|
|
34
31
|
# Dirbuster
|
|
35
|
-
(
|
|
32
|
+
("Dirbuster", "/usr/share/dirbuster/wordlists"),
|
|
36
33
|
]
|
|
37
34
|
|
|
38
35
|
# Category detection patterns - ORDER MATTERS (more specific first)
|
|
39
36
|
# Each pattern is checked against the full path + filename
|
|
40
37
|
CATEGORY_PATTERNS = {
|
|
41
38
|
# DNS/Subdomain - check first since "subdomain" is specific
|
|
42
|
-
|
|
43
|
-
'subdomain', 'dns', 'vhost', 'hostname'
|
|
44
|
-
],
|
|
39
|
+
"dns": ["subdomain", "dns", "vhost", "hostname"],
|
|
45
40
|
# Extensions
|
|
46
|
-
|
|
47
|
-
'extension', 'ext.', 'suffix', 'filetype'
|
|
48
|
-
],
|
|
41
|
+
"extensions": ["extension", "ext.", "suffix", "filetype"],
|
|
49
42
|
# Fuzzing
|
|
50
|
-
|
|
51
|
-
'fuzz', 'injection', 'xss', 'sqli', 'lfi', 'rfi', 'traversal'
|
|
52
|
-
],
|
|
43
|
+
"fuzzing": ["fuzz", "injection", "xss", "sqli", "lfi", "rfi", "traversal"],
|
|
53
44
|
# Users - be specific to avoid matching "username" in paths
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
"users": [
|
|
46
|
+
"users.txt",
|
|
47
|
+
"usernames",
|
|
48
|
+
"user_",
|
|
49
|
+
"_user",
|
|
50
|
+
"logins",
|
|
51
|
+
"accounts",
|
|
52
|
+
"/users/",
|
|
53
|
+
"/usernames/",
|
|
57
54
|
],
|
|
58
55
|
# Passwords - check before dirs
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
"passwords": [
|
|
57
|
+
"password",
|
|
58
|
+
"pass.txt",
|
|
59
|
+
"passwd",
|
|
60
|
+
"credential",
|
|
61
|
+
"rockyou",
|
|
62
|
+
"darkweb",
|
|
63
|
+
"leaked",
|
|
64
|
+
"/passwords/",
|
|
62
65
|
],
|
|
63
66
|
# Directories - last since patterns are more generic
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
"dirs": [
|
|
68
|
+
"directory",
|
|
69
|
+
"dirs",
|
|
70
|
+
"dir-",
|
|
71
|
+
"web-content",
|
|
72
|
+
"web_content",
|
|
73
|
+
"dirbuster",
|
|
74
|
+
"/dirb/",
|
|
75
|
+
"raft-",
|
|
76
|
+
"apache.txt",
|
|
77
|
+
"iis.txt",
|
|
67
78
|
],
|
|
68
79
|
}
|
|
69
80
|
|
|
@@ -88,7 +99,7 @@ def detect_category(path: str, name: str) -> str:
|
|
|
88
99
|
if pattern in combined:
|
|
89
100
|
return category
|
|
90
101
|
|
|
91
|
-
return
|
|
102
|
+
return "other"
|
|
92
103
|
|
|
93
104
|
|
|
94
105
|
def count_lines(filepath: str, max_count: int = 1000000) -> int:
|
|
@@ -104,7 +115,7 @@ def count_lines(filepath: str, max_count: int = 1000000) -> int:
|
|
|
104
115
|
"""
|
|
105
116
|
try:
|
|
106
117
|
count = 0
|
|
107
|
-
with open(filepath,
|
|
118
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
108
119
|
for _ in f:
|
|
109
120
|
count += 1
|
|
110
121
|
if count >= max_count:
|
|
@@ -114,7 +125,9 @@ def count_lines(filepath: str, max_count: int = 1000000) -> int:
|
|
|
114
125
|
return 0
|
|
115
126
|
|
|
116
127
|
|
|
117
|
-
def discover_all_wordlists(
|
|
128
|
+
def discover_all_wordlists(
|
|
129
|
+
category_filter: Optional[str] = None,
|
|
130
|
+
) -> List[Dict[str, Any]]:
|
|
118
131
|
"""
|
|
119
132
|
Discover all wordlists from known directories.
|
|
120
133
|
|
|
@@ -137,9 +150,9 @@ def discover_all_wordlists(category_filter: Optional[str] = None) -> List[Dict[s
|
|
|
137
150
|
for root, _, files in os.walk(dir_path):
|
|
138
151
|
for filename in files:
|
|
139
152
|
# Skip non-text files
|
|
140
|
-
if not filename.endswith((
|
|
153
|
+
if not filename.endswith((".txt", ".lst", ".dic", ".wordlist")):
|
|
141
154
|
# Also include files without extension if they look like wordlists
|
|
142
|
-
if
|
|
155
|
+
if "." in filename:
|
|
143
156
|
continue
|
|
144
157
|
|
|
145
158
|
filepath = os.path.join(root, filename)
|
|
@@ -158,7 +171,7 @@ def discover_all_wordlists(category_filter: Optional[str] = None) -> List[Dict[s
|
|
|
158
171
|
# Apply category filter
|
|
159
172
|
if category_filter and category != category_filter:
|
|
160
173
|
# Also check if filter matches 'other' for unmatched
|
|
161
|
-
if category_filter !=
|
|
174
|
+
if category_filter != "all":
|
|
162
175
|
continue
|
|
163
176
|
|
|
164
177
|
# Get file stats
|
|
@@ -174,27 +187,29 @@ def discover_all_wordlists(category_filter: Optional[str] = None) -> List[Dict[s
|
|
|
174
187
|
else:
|
|
175
188
|
entries = -1 # Will display as "large"
|
|
176
189
|
|
|
177
|
-
wordlists.append(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
190
|
+
wordlists.append(
|
|
191
|
+
{
|
|
192
|
+
"path": filepath,
|
|
193
|
+
"name": filename,
|
|
194
|
+
"rel_path": rel_path,
|
|
195
|
+
"source": source,
|
|
196
|
+
"category": category,
|
|
197
|
+
"entries": entries,
|
|
198
|
+
"size_mb": size_mb,
|
|
199
|
+
}
|
|
200
|
+
)
|
|
186
201
|
|
|
187
202
|
# Sort: SoulEyez first, then by source, then by name
|
|
188
203
|
def sort_key(w):
|
|
189
204
|
source_order = {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
"SoulEyez": 0,
|
|
206
|
+
"SecLists": 1,
|
|
207
|
+
"System": 2,
|
|
208
|
+
"Dirb": 3,
|
|
209
|
+
"Dirbuster": 4,
|
|
210
|
+
"Metasploit": 5,
|
|
196
211
|
}
|
|
197
|
-
return (source_order.get(w[
|
|
212
|
+
return (source_order.get(w["source"], 99), w["name"].lower())
|
|
198
213
|
|
|
199
214
|
wordlists.sort(key=sort_key)
|
|
200
215
|
|
|
@@ -209,15 +224,21 @@ class WordlistBrowser:
|
|
|
209
224
|
- Enter selects highlighted item (no checkboxes)
|
|
210
225
|
- Tab cycles category filter
|
|
211
226
|
- / starts search mode
|
|
227
|
+
- s enters single value mode (optional)
|
|
228
|
+
- c enters custom path mode (optional)
|
|
212
229
|
"""
|
|
213
230
|
|
|
214
|
-
CURSOR =
|
|
215
|
-
CATEGORIES = [
|
|
231
|
+
CURSOR = ">"
|
|
232
|
+
CATEGORIES = ["all", "dirs", "dns", "passwords", "users", "fuzzing", "other"]
|
|
216
233
|
|
|
217
234
|
def __init__(
|
|
218
235
|
self,
|
|
219
236
|
category_filter: Optional[str] = None,
|
|
220
|
-
title: str =
|
|
237
|
+
title: str = "WORDLIST BROWSER",
|
|
238
|
+
recommended_paths: Optional[List[str]] = None,
|
|
239
|
+
allow_single_value: bool = False,
|
|
240
|
+
allow_custom_path: bool = False,
|
|
241
|
+
single_value_label: str = "value",
|
|
221
242
|
):
|
|
222
243
|
"""
|
|
223
244
|
Initialize the wordlist browser.
|
|
@@ -225,12 +246,20 @@ class WordlistBrowser:
|
|
|
225
246
|
Args:
|
|
226
247
|
category_filter: Initial category filter
|
|
227
248
|
title: Title to display
|
|
249
|
+
recommended_paths: List of recommended wordlist paths to highlight with ★
|
|
250
|
+
allow_single_value: If True, 's' key allows entering a single value
|
|
251
|
+
allow_custom_path: If True, 'c' key allows entering a custom path
|
|
252
|
+
single_value_label: Label for single value entry (e.g., 'username', 'password')
|
|
228
253
|
"""
|
|
229
254
|
self.title = title
|
|
230
255
|
self.suggested_category = category_filter # Remember suggested filter
|
|
231
256
|
self.category_idx = 0 # Start with 'all' so users see everything
|
|
257
|
+
self.recommended_paths = set(recommended_paths or [])
|
|
258
|
+
self.allow_single_value = allow_single_value
|
|
259
|
+
self.allow_custom_path = allow_custom_path
|
|
260
|
+
self.single_value_label = single_value_label
|
|
232
261
|
|
|
233
|
-
self.search_query =
|
|
262
|
+
self.search_query = ""
|
|
234
263
|
self.search_mode = False
|
|
235
264
|
self.all_wordlists = discover_all_wordlists()
|
|
236
265
|
self.filtered = self._apply_filter()
|
|
@@ -246,13 +275,23 @@ class WordlistBrowser:
|
|
|
246
275
|
filtered = self.all_wordlists
|
|
247
276
|
|
|
248
277
|
# Apply category filter
|
|
249
|
-
if category !=
|
|
250
|
-
filtered = [w for w in filtered if w[
|
|
278
|
+
if category != "all":
|
|
279
|
+
filtered = [w for w in filtered if w["category"] == category]
|
|
251
280
|
|
|
252
281
|
# Apply search query
|
|
253
282
|
if self.search_query:
|
|
254
283
|
query = self.search_query.lower()
|
|
255
|
-
filtered = [
|
|
284
|
+
filtered = [
|
|
285
|
+
w
|
|
286
|
+
for w in filtered
|
|
287
|
+
if query in w["name"].lower() or query in w["rel_path"].lower()
|
|
288
|
+
]
|
|
289
|
+
|
|
290
|
+
# Sort recommended wordlists to top (preserving order within each group)
|
|
291
|
+
if self.recommended_paths:
|
|
292
|
+
recommended = [w for w in filtered if w["path"] in self.recommended_paths]
|
|
293
|
+
others = [w for w in filtered if w["path"] not in self.recommended_paths]
|
|
294
|
+
filtered = recommended + others
|
|
256
295
|
|
|
257
296
|
return filtered
|
|
258
297
|
|
|
@@ -264,7 +303,9 @@ class WordlistBrowser:
|
|
|
264
303
|
Selected wordlist path or None if cancelled
|
|
265
304
|
"""
|
|
266
305
|
if not self.all_wordlists:
|
|
267
|
-
click.echo(
|
|
306
|
+
click.echo(
|
|
307
|
+
click.style(" No wordlists found in common directories.", fg="yellow")
|
|
308
|
+
)
|
|
268
309
|
click.pause()
|
|
269
310
|
return None
|
|
270
311
|
|
|
@@ -296,8 +337,14 @@ class WordlistBrowser:
|
|
|
296
337
|
padding = width - len(title_text) - len(filter_text) - 4
|
|
297
338
|
left_pad = padding // 2
|
|
298
339
|
right_pad = padding - left_pad
|
|
299
|
-
click.echo(
|
|
300
|
-
|
|
340
|
+
click.echo(
|
|
341
|
+
"│"
|
|
342
|
+
+ " " * left_pad
|
|
343
|
+
+ click.style(title_text, bold=True, fg="cyan")
|
|
344
|
+
+ " " * right_pad
|
|
345
|
+
+ click.style(filter_text, fg="yellow")
|
|
346
|
+
+ " │"
|
|
347
|
+
)
|
|
301
348
|
click.echo("└" + "─" * (width - 2) + "┘")
|
|
302
349
|
click.echo()
|
|
303
350
|
|
|
@@ -305,7 +352,9 @@ class WordlistBrowser:
|
|
|
305
352
|
if self.search_mode:
|
|
306
353
|
click.echo(f" Search: {click.style(self.search_query + '_', fg='cyan')}")
|
|
307
354
|
elif self.search_query:
|
|
308
|
-
click.echo(
|
|
355
|
+
click.echo(
|
|
356
|
+
f" Search: {click.style(self.search_query, fg='cyan')} (press / to edit)"
|
|
357
|
+
)
|
|
309
358
|
|
|
310
359
|
# Stats
|
|
311
360
|
total = len(self.filtered)
|
|
@@ -313,12 +362,12 @@ class WordlistBrowser:
|
|
|
313
362
|
click.echo()
|
|
314
363
|
|
|
315
364
|
if not self.filtered:
|
|
316
|
-
click.echo(click.style(" No wordlists match current filter.", fg=
|
|
365
|
+
click.echo(click.style(" No wordlists match current filter.", fg="yellow"))
|
|
317
366
|
click.echo()
|
|
318
367
|
else:
|
|
319
368
|
# Calculate visible items
|
|
320
369
|
page_end = min(self.page_start + self.page_size, len(self.filtered))
|
|
321
|
-
visible = self.filtered[self.page_start:page_end]
|
|
370
|
+
visible = self.filtered[self.page_start : page_end]
|
|
322
371
|
|
|
323
372
|
# Create table like InteractiveSelector
|
|
324
373
|
table = Table(
|
|
@@ -326,7 +375,7 @@ class WordlistBrowser:
|
|
|
326
375
|
header_style="bold cyan",
|
|
327
376
|
box=DesignSystem.TABLE_BOX,
|
|
328
377
|
padding=(0, 1),
|
|
329
|
-
expand=True
|
|
378
|
+
expand=True,
|
|
330
379
|
)
|
|
331
380
|
|
|
332
381
|
# Add columns
|
|
@@ -339,35 +388,55 @@ class WordlistBrowser:
|
|
|
339
388
|
# Add rows
|
|
340
389
|
for idx, wordlist in enumerate(visible):
|
|
341
390
|
absolute_idx = self.page_start + idx
|
|
342
|
-
is_cursor =
|
|
391
|
+
is_cursor = absolute_idx == self.cursor_pos
|
|
392
|
+
is_recommended = wordlist["path"] in self.recommended_paths
|
|
343
393
|
|
|
344
|
-
# Cursor indicator
|
|
345
|
-
|
|
394
|
+
# Cursor indicator with recommended star
|
|
395
|
+
if is_cursor:
|
|
396
|
+
cursor = "▶"
|
|
397
|
+
elif is_recommended:
|
|
398
|
+
cursor = "★"
|
|
399
|
+
else:
|
|
400
|
+
cursor = " "
|
|
346
401
|
|
|
347
402
|
# Name - use rel_path if it has subdirs
|
|
348
|
-
name =
|
|
403
|
+
name = (
|
|
404
|
+
wordlist["rel_path"]
|
|
405
|
+
if "/" in wordlist["rel_path"]
|
|
406
|
+
else wordlist["name"]
|
|
407
|
+
)
|
|
349
408
|
if len(name) > 45:
|
|
350
|
-
name =
|
|
409
|
+
name = "..." + name[-42:]
|
|
351
410
|
|
|
352
411
|
# Entry count
|
|
353
|
-
if wordlist[
|
|
354
|
-
entries_str =
|
|
355
|
-
elif wordlist[
|
|
412
|
+
if wordlist["entries"] == -1:
|
|
413
|
+
entries_str = "large"
|
|
414
|
+
elif wordlist["entries"] >= 1000000:
|
|
356
415
|
entries_str = f"{wordlist['entries'] / 1000000:.1f}M"
|
|
357
|
-
elif wordlist[
|
|
416
|
+
elif wordlist["entries"] >= 1000:
|
|
358
417
|
entries_str = f"{wordlist['entries'] / 1000:.1f}K"
|
|
359
418
|
else:
|
|
360
|
-
entries_str = str(wordlist[
|
|
419
|
+
entries_str = str(wordlist["entries"])
|
|
361
420
|
|
|
362
421
|
# Source color
|
|
363
|
-
source = wordlist[
|
|
422
|
+
source = wordlist["source"]
|
|
364
423
|
|
|
365
424
|
# Category
|
|
366
|
-
cat = wordlist[
|
|
425
|
+
cat = wordlist["category"]
|
|
367
426
|
|
|
368
|
-
# Add row with highlight for cursor
|
|
427
|
+
# Add row with highlight for cursor, yellow for recommended
|
|
369
428
|
if is_cursor:
|
|
370
|
-
table.add_row(
|
|
429
|
+
table.add_row(
|
|
430
|
+
cursor, name, entries_str, source, cat, style="reverse"
|
|
431
|
+
)
|
|
432
|
+
elif is_recommended:
|
|
433
|
+
table.add_row(
|
|
434
|
+
f"[yellow]{cursor}[/yellow]",
|
|
435
|
+
f"[yellow]{name}[/yellow]",
|
|
436
|
+
f"[yellow]{entries_str}[/yellow]",
|
|
437
|
+
f"[yellow]{source}[/yellow]",
|
|
438
|
+
f"[yellow]{cat}[/yellow]",
|
|
439
|
+
)
|
|
371
440
|
else:
|
|
372
441
|
table.add_row(cursor, name, entries_str, source, cat)
|
|
373
442
|
|
|
@@ -376,44 +445,60 @@ class WordlistBrowser:
|
|
|
376
445
|
# Pagination
|
|
377
446
|
if len(self.filtered) > self.page_size:
|
|
378
447
|
page_num = (self.page_start // self.page_size) + 1
|
|
379
|
-
total_pages = (
|
|
448
|
+
total_pages = (
|
|
449
|
+
len(self.filtered) + self.page_size - 1
|
|
450
|
+
) // self.page_size
|
|
380
451
|
click.echo()
|
|
381
452
|
click.echo(f" Page {page_num}/{total_pages}")
|
|
382
453
|
|
|
383
454
|
# Help bar
|
|
384
455
|
click.echo()
|
|
385
456
|
click.echo(DesignSystem.separator())
|
|
386
|
-
|
|
387
|
-
f"
|
|
388
|
-
f"{click.style('Enter:', bold=True)} Select
|
|
389
|
-
f"{click.style('/:', bold=True)} Search
|
|
390
|
-
f"{click.style('Tab:', bold=True)} Filter
|
|
391
|
-
|
|
392
|
-
|
|
457
|
+
help_parts = [
|
|
458
|
+
f"{click.style('↑↓/jk:', bold=True)} Navigate",
|
|
459
|
+
f"{click.style('Enter:', bold=True)} Select",
|
|
460
|
+
f"{click.style('/:', bold=True)} Search",
|
|
461
|
+
f"{click.style('Tab:', bold=True)} Filter",
|
|
462
|
+
]
|
|
463
|
+
if self.allow_single_value:
|
|
464
|
+
help_parts.append(
|
|
465
|
+
f"{click.style('s:', bold=True)} Single {self.single_value_label}"
|
|
466
|
+
)
|
|
467
|
+
if self.allow_custom_path:
|
|
468
|
+
help_parts.append(f"{click.style('c:', bold=True)} Custom path")
|
|
469
|
+
help_parts.append(f"{click.style('q:', bold=True)} Back")
|
|
470
|
+
help_text = " " + " | ".join(help_parts)
|
|
393
471
|
click.echo(help_text)
|
|
472
|
+
# Show recommended legend if there are recommended wordlists
|
|
473
|
+
if self.recommended_paths:
|
|
474
|
+
click.echo(
|
|
475
|
+
f" {click.style('★', fg='yellow')} = Recommended for this tool/category"
|
|
476
|
+
)
|
|
394
477
|
click.echo(DesignSystem.separator())
|
|
395
478
|
|
|
396
|
-
def _handle_key(self, key: str) -> Optional[
|
|
479
|
+
def _handle_key(self, key: str) -> Optional[Any]:
|
|
397
480
|
"""
|
|
398
481
|
Handle a keypress.
|
|
399
482
|
|
|
400
483
|
Returns:
|
|
401
|
-
Selected path if Enter pressed
|
|
484
|
+
- Selected path (str) if Enter pressed
|
|
485
|
+
- ('single', value) tuple if single value entered
|
|
486
|
+
- None otherwise
|
|
402
487
|
"""
|
|
403
488
|
# Search mode - capture text
|
|
404
489
|
if self.search_mode:
|
|
405
|
-
if key == KEY_ENTER or key ==
|
|
490
|
+
if key == KEY_ENTER or key == "\r" or key == "\n":
|
|
406
491
|
self.search_mode = False
|
|
407
492
|
self.filtered = self._apply_filter()
|
|
408
493
|
self.cursor_pos = 0
|
|
409
494
|
self.page_start = 0
|
|
410
495
|
elif key == KEY_ESCAPE:
|
|
411
496
|
self.search_mode = False
|
|
412
|
-
self.search_query =
|
|
497
|
+
self.search_query = ""
|
|
413
498
|
self.filtered = self._apply_filter()
|
|
414
499
|
self.cursor_pos = 0
|
|
415
500
|
self.page_start = 0
|
|
416
|
-
elif key in (
|
|
501
|
+
elif key in ("\x7f", "\x08"): # Backspace
|
|
417
502
|
self.search_query = self.search_query[:-1]
|
|
418
503
|
self.filtered = self._apply_filter()
|
|
419
504
|
self.cursor_pos = 0
|
|
@@ -426,56 +511,124 @@ class WordlistBrowser:
|
|
|
426
511
|
return None
|
|
427
512
|
|
|
428
513
|
# Navigation - Up
|
|
429
|
-
if key in (KEY_UP,
|
|
514
|
+
if key in (KEY_UP, "k"):
|
|
430
515
|
if self.cursor_pos > 0:
|
|
431
516
|
self.cursor_pos -= 1
|
|
432
517
|
if self.cursor_pos < self.page_start:
|
|
433
518
|
self.page_start = max(0, self.page_start - self.page_size)
|
|
434
519
|
|
|
435
520
|
# Navigation - Down
|
|
436
|
-
elif key in (KEY_DOWN,
|
|
521
|
+
elif key in (KEY_DOWN, "j"):
|
|
437
522
|
if self.filtered and self.cursor_pos < len(self.filtered) - 1:
|
|
438
523
|
self.cursor_pos += 1
|
|
439
524
|
if self.cursor_pos >= self.page_start + self.page_size:
|
|
440
525
|
self.page_start += self.page_size
|
|
441
526
|
|
|
442
527
|
# Select - Enter
|
|
443
|
-
elif key in (KEY_ENTER,
|
|
528
|
+
elif key in (KEY_ENTER, "\r", "\n"):
|
|
444
529
|
if self.filtered:
|
|
445
|
-
return self.filtered[self.cursor_pos][
|
|
530
|
+
return self.filtered[self.cursor_pos]["path"]
|
|
446
531
|
|
|
447
532
|
# Search mode
|
|
448
|
-
elif key ==
|
|
533
|
+
elif key == "/":
|
|
449
534
|
self.search_mode = True
|
|
450
535
|
|
|
451
536
|
# Cycle category filter - Tab
|
|
452
|
-
elif key ==
|
|
537
|
+
elif key == "\t":
|
|
453
538
|
self.category_idx = (self.category_idx + 1) % len(self.CATEGORIES)
|
|
454
539
|
self.filtered = self._apply_filter()
|
|
455
540
|
self.cursor_pos = 0
|
|
456
541
|
self.page_start = 0
|
|
457
542
|
|
|
543
|
+
# Single value entry - 's'
|
|
544
|
+
elif key == "s" and self.allow_single_value:
|
|
545
|
+
# Clear screen and prompt for single value
|
|
546
|
+
DesignSystem.clear_screen()
|
|
547
|
+
click.echo()
|
|
548
|
+
click.echo(
|
|
549
|
+
click.style(
|
|
550
|
+
f" Enter single {self.single_value_label}", bold=True, fg="cyan"
|
|
551
|
+
)
|
|
552
|
+
)
|
|
553
|
+
click.echo()
|
|
554
|
+
try:
|
|
555
|
+
value = click.prompt(
|
|
556
|
+
f" {self.single_value_label.capitalize()}", type=str
|
|
557
|
+
)
|
|
558
|
+
if value.strip():
|
|
559
|
+
return ("single", value.strip())
|
|
560
|
+
except (KeyboardInterrupt, click.Abort):
|
|
561
|
+
pass
|
|
562
|
+
return None
|
|
563
|
+
|
|
564
|
+
# Custom path entry - 'c'
|
|
565
|
+
elif key == "c" and self.allow_custom_path:
|
|
566
|
+
# Clear screen and prompt for custom path
|
|
567
|
+
DesignSystem.clear_screen()
|
|
568
|
+
click.echo()
|
|
569
|
+
click.echo(
|
|
570
|
+
click.style(" Enter custom wordlist path", bold=True, fg="cyan")
|
|
571
|
+
)
|
|
572
|
+
click.echo()
|
|
573
|
+
try:
|
|
574
|
+
import os
|
|
575
|
+
|
|
576
|
+
custom = click.prompt(" Path", type=str)
|
|
577
|
+
if os.path.exists(custom):
|
|
578
|
+
return custom
|
|
579
|
+
else:
|
|
580
|
+
click.echo(click.style(f" File not found: {custom}", fg="red"))
|
|
581
|
+
click.pause()
|
|
582
|
+
except (KeyboardInterrupt, click.Abort):
|
|
583
|
+
pass
|
|
584
|
+
return None
|
|
585
|
+
|
|
458
586
|
# Exit
|
|
459
|
-
elif key in (KEY_ESCAPE,
|
|
587
|
+
elif key in (KEY_ESCAPE, "q", "\x03"): # \x03 is Ctrl+C
|
|
460
588
|
self.running = False
|
|
461
589
|
|
|
462
590
|
return None
|
|
463
591
|
|
|
464
592
|
|
|
465
|
-
def browse_wordlists(
|
|
593
|
+
def browse_wordlists(
|
|
594
|
+
category_filter: Optional[str] = None,
|
|
595
|
+
title: str = "WORDLIST BROWSER",
|
|
596
|
+
recommended_paths: Optional[List[str]] = None,
|
|
597
|
+
allow_single_value: bool = False,
|
|
598
|
+
allow_custom_path: bool = False,
|
|
599
|
+
single_value_label: str = "value",
|
|
600
|
+
) -> Optional[Any]:
|
|
466
601
|
"""
|
|
467
602
|
Launch the interactive wordlist browser.
|
|
468
603
|
|
|
469
604
|
Args:
|
|
470
605
|
category_filter: Optional initial category filter
|
|
471
606
|
title: Browser title
|
|
607
|
+
recommended_paths: List of recommended wordlist paths to highlight with ★
|
|
608
|
+
allow_single_value: If True, 's' key allows entering a single value
|
|
609
|
+
allow_custom_path: If True, 'c' key allows entering a custom path
|
|
610
|
+
single_value_label: Label for single value entry (e.g., 'username', 'password')
|
|
472
611
|
|
|
473
612
|
Returns:
|
|
474
|
-
Selected wordlist path
|
|
613
|
+
- Selected wordlist path (str)
|
|
614
|
+
- ('single', value) tuple if single value entered
|
|
615
|
+
- None if cancelled
|
|
475
616
|
"""
|
|
476
|
-
browser = WordlistBrowser(
|
|
617
|
+
browser = WordlistBrowser(
|
|
618
|
+
category_filter=category_filter,
|
|
619
|
+
title=title,
|
|
620
|
+
recommended_paths=recommended_paths,
|
|
621
|
+
allow_single_value=allow_single_value,
|
|
622
|
+
allow_custom_path=allow_custom_path,
|
|
623
|
+
single_value_label=single_value_label,
|
|
624
|
+
)
|
|
477
625
|
return browser.run()
|
|
478
626
|
|
|
479
627
|
|
|
480
628
|
# Convenience export
|
|
481
|
-
__all__ = [
|
|
629
|
+
__all__ = [
|
|
630
|
+
"browse_wordlists",
|
|
631
|
+
"discover_all_wordlists",
|
|
632
|
+
"WordlistBrowser",
|
|
633
|
+
"WORDLIST_DIRECTORIES",
|
|
634
|
+
]
|