souleyez 2.43.28__py3-none-any.whl → 2.43.32__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. souleyez/__init__.py +1 -2
  2. souleyez/ai/__init__.py +21 -15
  3. souleyez/ai/action_mapper.py +249 -150
  4. souleyez/ai/chain_advisor.py +116 -100
  5. souleyez/ai/claude_provider.py +29 -28
  6. souleyez/ai/context_builder.py +80 -62
  7. souleyez/ai/executor.py +158 -117
  8. souleyez/ai/feedback_handler.py +136 -121
  9. souleyez/ai/llm_factory.py +27 -20
  10. souleyez/ai/llm_provider.py +4 -2
  11. souleyez/ai/ollama_provider.py +6 -9
  12. souleyez/ai/ollama_service.py +44 -37
  13. souleyez/ai/path_scorer.py +91 -76
  14. souleyez/ai/recommender.py +176 -144
  15. souleyez/ai/report_context.py +74 -73
  16. souleyez/ai/report_service.py +84 -66
  17. souleyez/ai/result_parser.py +222 -229
  18. souleyez/ai/safety.py +67 -44
  19. souleyez/auth/__init__.py +23 -22
  20. souleyez/auth/audit.py +36 -26
  21. souleyez/auth/engagement_access.py +65 -48
  22. souleyez/auth/permissions.py +14 -3
  23. souleyez/auth/session_manager.py +54 -37
  24. souleyez/auth/user_manager.py +109 -64
  25. souleyez/commands/audit.py +40 -43
  26. souleyez/commands/auth.py +35 -15
  27. souleyez/commands/deliverables.py +55 -50
  28. souleyez/commands/engagement.py +47 -28
  29. souleyez/commands/license.py +32 -23
  30. souleyez/commands/screenshots.py +36 -32
  31. souleyez/commands/user.py +82 -36
  32. souleyez/config.py +52 -44
  33. souleyez/core/credential_tester.py +87 -81
  34. souleyez/core/cve_mappings.py +179 -192
  35. souleyez/core/cve_matcher.py +162 -148
  36. souleyez/core/msf_auto_mapper.py +100 -83
  37. souleyez/core/msf_chain_engine.py +294 -256
  38. souleyez/core/msf_database.py +153 -70
  39. souleyez/core/msf_integration.py +679 -673
  40. souleyez/core/msf_rpc_client.py +40 -42
  41. souleyez/core/msf_rpc_manager.py +77 -79
  42. souleyez/core/msf_sync_manager.py +241 -181
  43. souleyez/core/network_utils.py +22 -15
  44. souleyez/core/parser_handler.py +34 -25
  45. souleyez/core/pending_chains.py +114 -63
  46. souleyez/core/templates.py +158 -107
  47. souleyez/core/tool_chaining.py +9592 -2879
  48. souleyez/core/version_utils.py +79 -94
  49. souleyez/core/vuln_correlation.py +136 -89
  50. souleyez/core/web_utils.py +33 -32
  51. souleyez/data/wordlists/ad_users.txt +378 -0
  52. souleyez/data/wordlists/api_endpoints_large.txt +769 -0
  53. souleyez/data/wordlists/home_dir_sensitive.txt +39 -0
  54. souleyez/data/wordlists/lfi_payloads.txt +82 -0
  55. souleyez/data/wordlists/passwords_brute.txt +1548 -0
  56. souleyez/data/wordlists/passwords_crack.txt +2479 -0
  57. souleyez/data/wordlists/passwords_spray.txt +386 -0
  58. souleyez/data/wordlists/subdomains_large.txt +5057 -0
  59. souleyez/data/wordlists/usernames_common.txt +694 -0
  60. souleyez/data/wordlists/web_dirs_large.txt +4769 -0
  61. souleyez/detection/__init__.py +1 -1
  62. souleyez/detection/attack_signatures.py +12 -17
  63. souleyez/detection/mitre_mappings.py +61 -55
  64. souleyez/detection/validator.py +97 -86
  65. souleyez/devtools.py +23 -10
  66. souleyez/docs/README.md +4 -4
  67. souleyez/docs/api-reference/cli-commands.md +2 -2
  68. souleyez/docs/developer-guide/adding-new-tools.md +562 -0
  69. souleyez/docs/user-guide/auto-chaining.md +30 -8
  70. souleyez/docs/user-guide/getting-started.md +1 -1
  71. souleyez/docs/user-guide/installation.md +26 -3
  72. souleyez/docs/user-guide/metasploit-integration.md +2 -2
  73. souleyez/docs/user-guide/rbac.md +1 -1
  74. souleyez/docs/user-guide/scope-management.md +1 -1
  75. souleyez/docs/user-guide/siem-integration.md +1 -1
  76. souleyez/docs/user-guide/tools-reference.md +1 -8
  77. souleyez/docs/user-guide/worker-management.md +1 -1
  78. souleyez/engine/background.py +1238 -535
  79. souleyez/engine/base.py +4 -1
  80. souleyez/engine/job_status.py +17 -49
  81. souleyez/engine/log_sanitizer.py +103 -77
  82. souleyez/engine/manager.py +38 -7
  83. souleyez/engine/result_handler.py +2198 -1550
  84. souleyez/engine/worker_manager.py +50 -41
  85. souleyez/export/evidence_bundle.py +72 -62
  86. souleyez/feature_flags/features.py +16 -20
  87. souleyez/feature_flags.py +5 -9
  88. souleyez/handlers/__init__.py +11 -0
  89. souleyez/handlers/base.py +188 -0
  90. souleyez/handlers/bash_handler.py +277 -0
  91. souleyez/handlers/bloodhound_handler.py +243 -0
  92. souleyez/handlers/certipy_handler.py +311 -0
  93. souleyez/handlers/crackmapexec_handler.py +486 -0
  94. souleyez/handlers/dnsrecon_handler.py +344 -0
  95. souleyez/handlers/enum4linux_handler.py +400 -0
  96. souleyez/handlers/evil_winrm_handler.py +493 -0
  97. souleyez/handlers/ffuf_handler.py +815 -0
  98. souleyez/handlers/gobuster_handler.py +1114 -0
  99. souleyez/handlers/gpp_extract_handler.py +334 -0
  100. souleyez/handlers/hashcat_handler.py +444 -0
  101. souleyez/handlers/hydra_handler.py +563 -0
  102. souleyez/handlers/impacket_getuserspns_handler.py +343 -0
  103. souleyez/handlers/impacket_psexec_handler.py +222 -0
  104. souleyez/handlers/impacket_secretsdump_handler.py +426 -0
  105. souleyez/handlers/john_handler.py +286 -0
  106. souleyez/handlers/katana_handler.py +425 -0
  107. souleyez/handlers/kerbrute_handler.py +298 -0
  108. souleyez/handlers/ldapsearch_handler.py +636 -0
  109. souleyez/handlers/lfi_extract_handler.py +464 -0
  110. souleyez/handlers/msf_auxiliary_handler.py +408 -0
  111. souleyez/handlers/msf_exploit_handler.py +380 -0
  112. souleyez/handlers/nikto_handler.py +413 -0
  113. souleyez/handlers/nmap_handler.py +821 -0
  114. souleyez/handlers/nuclei_handler.py +359 -0
  115. souleyez/handlers/nxc_handler.py +371 -0
  116. souleyez/handlers/rdp_sec_check_handler.py +353 -0
  117. souleyez/handlers/registry.py +288 -0
  118. souleyez/handlers/responder_handler.py +232 -0
  119. souleyez/handlers/service_explorer_handler.py +434 -0
  120. souleyez/handlers/smbclient_handler.py +344 -0
  121. souleyez/handlers/smbmap_handler.py +510 -0
  122. souleyez/handlers/smbpasswd_handler.py +296 -0
  123. souleyez/handlers/sqlmap_handler.py +1116 -0
  124. souleyez/handlers/theharvester_handler.py +601 -0
  125. souleyez/handlers/whois_handler.py +277 -0
  126. souleyez/handlers/wpscan_handler.py +554 -0
  127. souleyez/history.py +32 -16
  128. souleyez/importers/msf_importer.py +106 -75
  129. souleyez/importers/smart_importer.py +208 -147
  130. souleyez/integrations/siem/__init__.py +10 -10
  131. souleyez/integrations/siem/base.py +17 -18
  132. souleyez/integrations/siem/elastic.py +108 -122
  133. souleyez/integrations/siem/factory.py +207 -80
  134. souleyez/integrations/siem/googlesecops.py +146 -154
  135. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  136. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  137. souleyez/integrations/siem/sentinel.py +107 -109
  138. souleyez/integrations/siem/splunk.py +246 -212
  139. souleyez/integrations/siem/wazuh.py +65 -71
  140. souleyez/integrations/wazuh/__init__.py +5 -5
  141. souleyez/integrations/wazuh/client.py +70 -93
  142. souleyez/integrations/wazuh/config.py +85 -57
  143. souleyez/integrations/wazuh/host_mapper.py +28 -36
  144. souleyez/integrations/wazuh/sync.py +78 -68
  145. souleyez/intelligence/__init__.py +4 -5
  146. souleyez/intelligence/correlation_analyzer.py +309 -295
  147. souleyez/intelligence/exploit_knowledge.py +661 -623
  148. souleyez/intelligence/exploit_suggestions.py +159 -139
  149. souleyez/intelligence/gap_analyzer.py +132 -97
  150. souleyez/intelligence/gap_detector.py +251 -214
  151. souleyez/intelligence/sensitive_tables.py +266 -129
  152. souleyez/intelligence/service_parser.py +137 -123
  153. souleyez/intelligence/surface_analyzer.py +407 -268
  154. souleyez/intelligence/target_parser.py +159 -162
  155. souleyez/licensing/__init__.py +6 -6
  156. souleyez/licensing/validator.py +17 -19
  157. souleyez/log_config.py +79 -54
  158. souleyez/main.py +1505 -687
  159. souleyez/migrations/fix_job_counter.py +16 -14
  160. souleyez/parsers/bloodhound_parser.py +41 -39
  161. souleyez/parsers/crackmapexec_parser.py +178 -111
  162. souleyez/parsers/dalfox_parser.py +72 -77
  163. souleyez/parsers/dnsrecon_parser.py +103 -91
  164. souleyez/parsers/enum4linux_parser.py +183 -153
  165. souleyez/parsers/ffuf_parser.py +29 -25
  166. souleyez/parsers/gobuster_parser.py +301 -41
  167. souleyez/parsers/hashcat_parser.py +324 -79
  168. souleyez/parsers/http_fingerprint_parser.py +350 -103
  169. souleyez/parsers/hydra_parser.py +131 -111
  170. souleyez/parsers/impacket_parser.py +231 -178
  171. souleyez/parsers/john_parser.py +98 -86
  172. souleyez/parsers/katana_parser.py +316 -0
  173. souleyez/parsers/msf_parser.py +943 -498
  174. souleyez/parsers/nikto_parser.py +346 -65
  175. souleyez/parsers/nmap_parser.py +262 -174
  176. souleyez/parsers/nuclei_parser.py +40 -44
  177. souleyez/parsers/responder_parser.py +26 -26
  178. souleyez/parsers/searchsploit_parser.py +74 -74
  179. souleyez/parsers/service_explorer_parser.py +279 -0
  180. souleyez/parsers/smbmap_parser.py +180 -124
  181. souleyez/parsers/sqlmap_parser.py +434 -308
  182. souleyez/parsers/theharvester_parser.py +75 -57
  183. souleyez/parsers/whois_parser.py +135 -94
  184. souleyez/parsers/wpscan_parser.py +278 -190
  185. souleyez/plugins/afp.py +44 -36
  186. souleyez/plugins/afp_brute.py +114 -46
  187. souleyez/plugins/ard.py +48 -37
  188. souleyez/plugins/bloodhound.py +95 -61
  189. souleyez/plugins/certipy.py +303 -0
  190. souleyez/plugins/crackmapexec.py +186 -85
  191. souleyez/plugins/dalfox.py +120 -59
  192. souleyez/plugins/dns_hijack.py +146 -41
  193. souleyez/plugins/dnsrecon.py +97 -61
  194. souleyez/plugins/enum4linux.py +91 -66
  195. souleyez/plugins/evil_winrm.py +291 -0
  196. souleyez/plugins/ffuf.py +166 -90
  197. souleyez/plugins/firmware_extract.py +133 -29
  198. souleyez/plugins/gobuster.py +387 -190
  199. souleyez/plugins/gpp_extract.py +393 -0
  200. souleyez/plugins/hashcat.py +100 -73
  201. souleyez/plugins/http_fingerprint.py +854 -267
  202. souleyez/plugins/hydra.py +566 -200
  203. souleyez/plugins/impacket_getnpusers.py +117 -69
  204. souleyez/plugins/impacket_psexec.py +84 -64
  205. souleyez/plugins/impacket_secretsdump.py +103 -69
  206. souleyez/plugins/impacket_smbclient.py +89 -75
  207. souleyez/plugins/john.py +86 -69
  208. souleyez/plugins/katana.py +313 -0
  209. souleyez/plugins/kerbrute.py +237 -0
  210. souleyez/plugins/lfi_extract.py +541 -0
  211. souleyez/plugins/macos_ssh.py +117 -48
  212. souleyez/plugins/mdns.py +35 -30
  213. souleyez/plugins/msf_auxiliary.py +253 -130
  214. souleyez/plugins/msf_exploit.py +239 -161
  215. souleyez/plugins/nikto.py +134 -78
  216. souleyez/plugins/nmap.py +275 -91
  217. souleyez/plugins/nuclei.py +180 -89
  218. souleyez/plugins/nxc.py +285 -0
  219. souleyez/plugins/plugin_base.py +35 -36
  220. souleyez/plugins/plugin_template.py +13 -5
  221. souleyez/plugins/rdp_sec_check.py +130 -0
  222. souleyez/plugins/responder.py +112 -71
  223. souleyez/plugins/router_http_brute.py +76 -65
  224. souleyez/plugins/router_ssh_brute.py +118 -41
  225. souleyez/plugins/router_telnet_brute.py +124 -42
  226. souleyez/plugins/routersploit.py +91 -59
  227. souleyez/plugins/routersploit_exploit.py +77 -55
  228. souleyez/plugins/searchsploit.py +91 -77
  229. souleyez/plugins/service_explorer.py +1160 -0
  230. souleyez/plugins/smbmap.py +122 -72
  231. souleyez/plugins/smbpasswd.py +215 -0
  232. souleyez/plugins/sqlmap.py +301 -113
  233. souleyez/plugins/theharvester.py +127 -75
  234. souleyez/plugins/tr069.py +79 -57
  235. souleyez/plugins/upnp.py +65 -47
  236. souleyez/plugins/upnp_abuse.py +73 -55
  237. souleyez/plugins/vnc_access.py +129 -42
  238. souleyez/plugins/vnc_brute.py +109 -38
  239. souleyez/plugins/whois.py +77 -58
  240. souleyez/plugins/wpscan.py +173 -69
  241. souleyez/reporting/__init__.py +2 -1
  242. souleyez/reporting/attack_chain.py +411 -346
  243. souleyez/reporting/charts.py +436 -501
  244. souleyez/reporting/compliance_mappings.py +334 -201
  245. souleyez/reporting/detection_report.py +126 -125
  246. souleyez/reporting/formatters.py +828 -591
  247. souleyez/reporting/generator.py +386 -302
  248. souleyez/reporting/metrics.py +72 -75
  249. souleyez/scanner.py +35 -29
  250. souleyez/security/__init__.py +37 -11
  251. souleyez/security/scope_validator.py +175 -106
  252. souleyez/security/validation.py +223 -149
  253. souleyez/security.py +22 -6
  254. souleyez/storage/credentials.py +247 -186
  255. souleyez/storage/crypto.py +296 -129
  256. souleyez/storage/database.py +73 -50
  257. souleyez/storage/db.py +58 -36
  258. souleyez/storage/deliverable_evidence.py +177 -128
  259. souleyez/storage/deliverable_exporter.py +282 -246
  260. souleyez/storage/deliverable_templates.py +134 -116
  261. souleyez/storage/deliverables.py +135 -130
  262. souleyez/storage/engagements.py +109 -56
  263. souleyez/storage/evidence.py +181 -152
  264. souleyez/storage/execution_log.py +31 -17
  265. souleyez/storage/exploit_attempts.py +93 -57
  266. souleyez/storage/exploits.py +67 -36
  267. souleyez/storage/findings.py +48 -61
  268. souleyez/storage/hosts.py +176 -144
  269. souleyez/storage/migrate_to_engagements.py +43 -19
  270. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  271. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  272. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  273. souleyez/storage/migrations/_005_screenshots.py +13 -5
  274. souleyez/storage/migrations/_006_deliverables.py +13 -5
  275. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  276. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  277. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  278. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  279. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  280. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  281. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  282. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  283. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  284. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  285. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  286. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  287. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  288. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  289. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  290. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  291. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  292. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  293. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  294. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  295. souleyez/storage/migrations/__init__.py +26 -26
  296. souleyez/storage/migrations/migration_manager.py +19 -19
  297. souleyez/storage/msf_sessions.py +100 -65
  298. souleyez/storage/osint.py +17 -24
  299. souleyez/storage/recommendation_engine.py +269 -235
  300. souleyez/storage/screenshots.py +33 -32
  301. souleyez/storage/smb_shares.py +136 -92
  302. souleyez/storage/sqlmap_data.py +183 -128
  303. souleyez/storage/team_collaboration.py +135 -141
  304. souleyez/storage/timeline_tracker.py +122 -94
  305. souleyez/storage/wazuh_vulns.py +64 -66
  306. souleyez/storage/web_paths.py +33 -37
  307. souleyez/testing/credential_tester.py +221 -205
  308. souleyez/ui/__init__.py +1 -1
  309. souleyez/ui/ai_quotes.py +12 -12
  310. souleyez/ui/attack_surface.py +2439 -1516
  311. souleyez/ui/chain_rules_view.py +914 -382
  312. souleyez/ui/correlation_view.py +312 -230
  313. souleyez/ui/dashboard.py +2382 -1130
  314. souleyez/ui/deliverables_view.py +148 -62
  315. souleyez/ui/design_system.py +13 -13
  316. souleyez/ui/errors.py +49 -49
  317. souleyez/ui/evidence_linking_view.py +284 -179
  318. souleyez/ui/evidence_vault.py +393 -285
  319. souleyez/ui/exploit_suggestions_view.py +555 -349
  320. souleyez/ui/export_view.py +100 -66
  321. souleyez/ui/gap_analysis_view.py +315 -171
  322. souleyez/ui/help_system.py +105 -97
  323. souleyez/ui/intelligence_view.py +436 -293
  324. souleyez/ui/interactive.py +23142 -10430
  325. souleyez/ui/interactive_selector.py +75 -68
  326. souleyez/ui/log_formatter.py +47 -39
  327. souleyez/ui/menu_components.py +22 -13
  328. souleyez/ui/msf_auxiliary_menu.py +184 -133
  329. souleyez/ui/pending_chains_view.py +336 -172
  330. souleyez/ui/progress_indicators.py +5 -3
  331. souleyez/ui/recommendations_view.py +195 -137
  332. souleyez/ui/rule_builder.py +343 -225
  333. souleyez/ui/setup_wizard.py +678 -284
  334. souleyez/ui/shortcuts.py +217 -165
  335. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  336. souleyez/ui/splunk_vulns_view.py +139 -86
  337. souleyez/ui/team_dashboard.py +498 -335
  338. souleyez/ui/template_selector.py +196 -105
  339. souleyez/ui/terminal.py +6 -6
  340. souleyez/ui/timeline_view.py +198 -127
  341. souleyez/ui/tool_setup.py +264 -164
  342. souleyez/ui/tutorial.py +202 -72
  343. souleyez/ui/tutorial_state.py +40 -40
  344. souleyez/ui/wazuh_vulns_view.py +235 -141
  345. souleyez/ui/wordlist_browser.py +260 -107
  346. souleyez/ui.py +464 -312
  347. souleyez/utils/tool_checker.py +427 -367
  348. souleyez/utils.py +33 -29
  349. souleyez/wordlists.py +134 -167
  350. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/METADATA +1 -1
  351. souleyez-2.43.32.dist-info/RECORD +441 -0
  352. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/WHEEL +1 -1
  353. souleyez-2.43.28.dist-info/RECORD +0 -379
  354. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/entry_points.txt +0 -0
  355. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/licenses/LICENSE +0 -0
  356. {souleyez-2.43.28.dist-info → souleyez-2.43.32.dist-info}/top_level.txt +0 -0
@@ -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 _get_key, KEY_UP, KEY_DOWN, KEY_ENTER, KEY_ESCAPE
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
- ('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'),
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
- ('Dirbuster', '/usr/share/dirbuster/wordlists'),
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
- 'dns': [
43
- 'subdomain', 'dns', 'vhost', 'hostname'
44
- ],
39
+ "dns": ["subdomain", "dns", "vhost", "hostname"],
45
40
  # Extensions
46
- 'extensions': [
47
- 'extension', 'ext.', 'suffix', 'filetype'
48
- ],
41
+ "extensions": ["extension", "ext.", "suffix", "filetype"],
49
42
  # Fuzzing
50
- 'fuzzing': [
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
- 'users': [
55
- 'users.txt', 'usernames', 'user_', '_user', 'logins', 'accounts',
56
- '/users/', '/usernames/'
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
- 'passwords': [
60
- 'password', 'pass.txt', 'passwd', 'credential', 'rockyou',
61
- 'darkweb', 'leaked', '/passwords/'
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
- 'dirs': [
65
- 'directory', 'dirs', 'dir-', 'web-content', 'web_content',
66
- 'dirbuster', '/dirb/', 'raft-', 'apache.txt', 'iis.txt'
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 'other'
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, 'r', encoding='utf-8', errors='ignore') as f:
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(category_filter: Optional[str] = None) -> List[Dict[str, Any]]:
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(('.txt', '.lst', '.dic', '.wordlist')):
153
+ if not filename.endswith((".txt", ".lst", ".dic", ".wordlist")):
141
154
  # Also include files without extension if they look like wordlists
142
- if '.' in filename:
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 != 'all':
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
- '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
- })
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
- 'SoulEyez': 0,
191
- 'SecLists': 1,
192
- 'System': 2,
193
- 'Dirb': 3,
194
- 'Dirbuster': 4,
195
- 'Metasploit': 5,
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['source'], 99), w['name'].lower())
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 = ['all', 'dirs', 'dns', 'passwords', 'users', 'fuzzing', 'other']
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 = 'WORDLIST BROWSER'
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 != 'all':
250
- filtered = [w for w in filtered if w['category'] == category]
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 = [w for w in filtered if query in w['name'].lower() or query in w['rel_path'].lower()]
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(click.style(" No wordlists found in common directories.", fg='yellow'))
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("│" + " " * left_pad + click.style(title_text, bold=True, fg='cyan') +
300
- " " * right_pad + click.style(filter_text, fg='yellow') + " │")
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(f" Search: {click.style(self.search_query, fg='cyan')} (press / to edit)")
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='yellow'))
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 = (absolute_idx == self.cursor_pos)
391
+ is_cursor = absolute_idx == self.cursor_pos
392
+ is_recommended = wordlist["path"] in self.recommended_paths
343
393
 
344
- # Cursor indicator
345
- cursor = "▶" if is_cursor else " "
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 = wordlist['rel_path'] if '/' in wordlist['rel_path'] else wordlist['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 = '...' + name[-42:]
409
+ name = "..." + name[-42:]
351
410
 
352
411
  # Entry count
353
- if wordlist['entries'] == -1:
354
- entries_str = 'large'
355
- elif wordlist['entries'] >= 1000000:
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['entries'] >= 1000:
416
+ elif wordlist["entries"] >= 1000:
358
417
  entries_str = f"{wordlist['entries'] / 1000:.1f}K"
359
418
  else:
360
- entries_str = str(wordlist['entries'])
419
+ entries_str = str(wordlist["entries"])
361
420
 
362
421
  # Source color
363
- source = wordlist['source']
422
+ source = wordlist["source"]
364
423
 
365
424
  # Category
366
- cat = wordlist['category']
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(cursor, name, entries_str, source, cat, style="reverse")
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 = (len(self.filtered) + self.page_size - 1) // self.page_size
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
- 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
- )
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[str]:
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, None otherwise
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 == '\r' or key == '\n':
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 ('\x7f', '\x08'): # Backspace
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, 'k'):
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, 'j'):
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, '\r', '\n'):
528
+ elif key in (KEY_ENTER, "\r", "\n"):
444
529
  if self.filtered:
445
- return self.filtered[self.cursor_pos]['path']
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 == '\t':
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, 'q', '\x03'): # \x03 is Ctrl+C
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(category_filter: Optional[str] = None, title: str = 'WORDLIST BROWSER') -> Optional[str]:
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 or None if cancelled
613
+ - Selected wordlist path (str)
614
+ - ('single', value) tuple if single value entered
615
+ - None if cancelled
475
616
  """
476
- browser = WordlistBrowser(category_filter=category_filter, title=title)
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__ = ['browse_wordlists', 'discover_all_wordlists', 'WordlistBrowser', 'WORDLIST_DIRECTORIES']
629
+ __all__ = [
630
+ "browse_wordlists",
631
+ "discover_all_wordlists",
632
+ "WordlistBrowser",
633
+ "WORDLIST_DIRECTORIES",
634
+ ]