souleyez 2.43.29__py3-none-any.whl → 2.43.34__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 (358) 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 +9526 -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 +1239 -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 +2200 -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 +292 -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/web_login_test_handler.py +327 -0
  126. souleyez/handlers/whois_handler.py +277 -0
  127. souleyez/handlers/wpscan_handler.py +554 -0
  128. souleyez/history.py +32 -16
  129. souleyez/importers/msf_importer.py +106 -75
  130. souleyez/importers/smart_importer.py +208 -147
  131. souleyez/integrations/siem/__init__.py +10 -10
  132. souleyez/integrations/siem/base.py +17 -18
  133. souleyez/integrations/siem/elastic.py +108 -122
  134. souleyez/integrations/siem/factory.py +207 -80
  135. souleyez/integrations/siem/googlesecops.py +146 -154
  136. souleyez/integrations/siem/rule_mappings/__init__.py +1 -1
  137. souleyez/integrations/siem/rule_mappings/wazuh_rules.py +8 -5
  138. souleyez/integrations/siem/sentinel.py +107 -109
  139. souleyez/integrations/siem/splunk.py +246 -212
  140. souleyez/integrations/siem/wazuh.py +65 -71
  141. souleyez/integrations/wazuh/__init__.py +5 -5
  142. souleyez/integrations/wazuh/client.py +70 -93
  143. souleyez/integrations/wazuh/config.py +85 -57
  144. souleyez/integrations/wazuh/host_mapper.py +28 -36
  145. souleyez/integrations/wazuh/sync.py +78 -68
  146. souleyez/intelligence/__init__.py +4 -5
  147. souleyez/intelligence/correlation_analyzer.py +309 -295
  148. souleyez/intelligence/exploit_knowledge.py +661 -623
  149. souleyez/intelligence/exploit_suggestions.py +159 -139
  150. souleyez/intelligence/gap_analyzer.py +132 -97
  151. souleyez/intelligence/gap_detector.py +251 -214
  152. souleyez/intelligence/sensitive_tables.py +266 -129
  153. souleyez/intelligence/service_parser.py +137 -123
  154. souleyez/intelligence/surface_analyzer.py +407 -268
  155. souleyez/intelligence/target_parser.py +159 -162
  156. souleyez/licensing/__init__.py +6 -6
  157. souleyez/licensing/validator.py +17 -19
  158. souleyez/log_config.py +79 -54
  159. souleyez/main.py +1505 -687
  160. souleyez/migrations/fix_job_counter.py +16 -14
  161. souleyez/parsers/bloodhound_parser.py +41 -39
  162. souleyez/parsers/crackmapexec_parser.py +178 -111
  163. souleyez/parsers/dalfox_parser.py +72 -77
  164. souleyez/parsers/dnsrecon_parser.py +103 -91
  165. souleyez/parsers/enum4linux_parser.py +183 -153
  166. souleyez/parsers/ffuf_parser.py +29 -25
  167. souleyez/parsers/gobuster_parser.py +301 -41
  168. souleyez/parsers/hashcat_parser.py +324 -79
  169. souleyez/parsers/http_fingerprint_parser.py +350 -103
  170. souleyez/parsers/hydra_parser.py +131 -111
  171. souleyez/parsers/impacket_parser.py +231 -178
  172. souleyez/parsers/john_parser.py +98 -86
  173. souleyez/parsers/katana_parser.py +316 -0
  174. souleyez/parsers/msf_parser.py +943 -498
  175. souleyez/parsers/nikto_parser.py +346 -65
  176. souleyez/parsers/nmap_parser.py +262 -174
  177. souleyez/parsers/nuclei_parser.py +40 -44
  178. souleyez/parsers/responder_parser.py +26 -26
  179. souleyez/parsers/searchsploit_parser.py +74 -74
  180. souleyez/parsers/service_explorer_parser.py +279 -0
  181. souleyez/parsers/smbmap_parser.py +180 -124
  182. souleyez/parsers/sqlmap_parser.py +434 -308
  183. souleyez/parsers/theharvester_parser.py +75 -57
  184. souleyez/parsers/whois_parser.py +135 -94
  185. souleyez/parsers/wpscan_parser.py +278 -190
  186. souleyez/plugins/afp.py +44 -36
  187. souleyez/plugins/afp_brute.py +114 -46
  188. souleyez/plugins/ard.py +48 -37
  189. souleyez/plugins/bloodhound.py +95 -61
  190. souleyez/plugins/certipy.py +303 -0
  191. souleyez/plugins/crackmapexec.py +186 -85
  192. souleyez/plugins/dalfox.py +120 -59
  193. souleyez/plugins/dns_hijack.py +146 -41
  194. souleyez/plugins/dnsrecon.py +97 -61
  195. souleyez/plugins/enum4linux.py +91 -66
  196. souleyez/plugins/evil_winrm.py +291 -0
  197. souleyez/plugins/ffuf.py +166 -90
  198. souleyez/plugins/firmware_extract.py +133 -29
  199. souleyez/plugins/gobuster.py +387 -190
  200. souleyez/plugins/gpp_extract.py +393 -0
  201. souleyez/plugins/hashcat.py +100 -73
  202. souleyez/plugins/http_fingerprint.py +854 -267
  203. souleyez/plugins/hydra.py +566 -200
  204. souleyez/plugins/impacket_getnpusers.py +117 -69
  205. souleyez/plugins/impacket_psexec.py +84 -64
  206. souleyez/plugins/impacket_secretsdump.py +103 -69
  207. souleyez/plugins/impacket_smbclient.py +89 -75
  208. souleyez/plugins/john.py +86 -69
  209. souleyez/plugins/katana.py +313 -0
  210. souleyez/plugins/kerbrute.py +237 -0
  211. souleyez/plugins/lfi_extract.py +541 -0
  212. souleyez/plugins/macos_ssh.py +117 -48
  213. souleyez/plugins/mdns.py +35 -30
  214. souleyez/plugins/msf_auxiliary.py +253 -130
  215. souleyez/plugins/msf_exploit.py +239 -161
  216. souleyez/plugins/nikto.py +134 -78
  217. souleyez/plugins/nmap.py +275 -91
  218. souleyez/plugins/nuclei.py +180 -89
  219. souleyez/plugins/nxc.py +285 -0
  220. souleyez/plugins/plugin_base.py +35 -36
  221. souleyez/plugins/plugin_template.py +13 -5
  222. souleyez/plugins/rdp_sec_check.py +130 -0
  223. souleyez/plugins/responder.py +112 -71
  224. souleyez/plugins/router_http_brute.py +76 -65
  225. souleyez/plugins/router_ssh_brute.py +118 -41
  226. souleyez/plugins/router_telnet_brute.py +124 -42
  227. souleyez/plugins/routersploit.py +91 -59
  228. souleyez/plugins/routersploit_exploit.py +77 -55
  229. souleyez/plugins/searchsploit.py +91 -77
  230. souleyez/plugins/service_explorer.py +1160 -0
  231. souleyez/plugins/smbmap.py +122 -72
  232. souleyez/plugins/smbpasswd.py +215 -0
  233. souleyez/plugins/sqlmap.py +301 -113
  234. souleyez/plugins/theharvester.py +127 -75
  235. souleyez/plugins/tr069.py +79 -57
  236. souleyez/plugins/upnp.py +65 -47
  237. souleyez/plugins/upnp_abuse.py +73 -55
  238. souleyez/plugins/vnc_access.py +129 -42
  239. souleyez/plugins/vnc_brute.py +109 -38
  240. souleyez/plugins/web_login_test.py +417 -0
  241. souleyez/plugins/whois.py +77 -58
  242. souleyez/plugins/wpscan.py +173 -69
  243. souleyez/reporting/__init__.py +2 -1
  244. souleyez/reporting/attack_chain.py +411 -346
  245. souleyez/reporting/charts.py +436 -501
  246. souleyez/reporting/compliance_mappings.py +334 -201
  247. souleyez/reporting/detection_report.py +126 -125
  248. souleyez/reporting/formatters.py +828 -591
  249. souleyez/reporting/generator.py +386 -302
  250. souleyez/reporting/metrics.py +72 -75
  251. souleyez/scanner.py +35 -29
  252. souleyez/security/__init__.py +37 -11
  253. souleyez/security/scope_validator.py +175 -106
  254. souleyez/security/validation.py +223 -149
  255. souleyez/security.py +22 -6
  256. souleyez/storage/credentials.py +247 -186
  257. souleyez/storage/crypto.py +296 -129
  258. souleyez/storage/database.py +73 -50
  259. souleyez/storage/db.py +58 -36
  260. souleyez/storage/deliverable_evidence.py +177 -128
  261. souleyez/storage/deliverable_exporter.py +282 -246
  262. souleyez/storage/deliverable_templates.py +134 -116
  263. souleyez/storage/deliverables.py +135 -130
  264. souleyez/storage/engagements.py +109 -56
  265. souleyez/storage/evidence.py +181 -152
  266. souleyez/storage/execution_log.py +31 -17
  267. souleyez/storage/exploit_attempts.py +93 -57
  268. souleyez/storage/exploits.py +67 -36
  269. souleyez/storage/findings.py +48 -61
  270. souleyez/storage/hosts.py +176 -144
  271. souleyez/storage/migrate_to_engagements.py +43 -19
  272. souleyez/storage/migrations/_001_add_credential_enhancements.py +22 -12
  273. souleyez/storage/migrations/_002_add_status_tracking.py +10 -7
  274. souleyez/storage/migrations/_003_add_execution_log.py +14 -8
  275. souleyez/storage/migrations/_005_screenshots.py +13 -5
  276. souleyez/storage/migrations/_006_deliverables.py +13 -5
  277. souleyez/storage/migrations/_007_deliverable_templates.py +12 -7
  278. souleyez/storage/migrations/_008_add_nuclei_table.py +10 -4
  279. souleyez/storage/migrations/_010_evidence_linking.py +17 -10
  280. souleyez/storage/migrations/_011_timeline_tracking.py +20 -13
  281. souleyez/storage/migrations/_012_team_collaboration.py +34 -21
  282. souleyez/storage/migrations/_013_add_host_tags.py +12 -6
  283. souleyez/storage/migrations/_014_exploit_attempts.py +22 -10
  284. souleyez/storage/migrations/_015_add_mac_os_fields.py +15 -7
  285. souleyez/storage/migrations/_016_add_domain_field.py +10 -4
  286. souleyez/storage/migrations/_017_msf_sessions.py +16 -8
  287. souleyez/storage/migrations/_018_add_osint_target.py +10 -6
  288. souleyez/storage/migrations/_019_add_engagement_type.py +10 -6
  289. souleyez/storage/migrations/_020_add_rbac.py +36 -15
  290. souleyez/storage/migrations/_021_wazuh_integration.py +20 -8
  291. souleyez/storage/migrations/_022_wazuh_indexer_columns.py +6 -4
  292. souleyez/storage/migrations/_023_fix_detection_results_fk.py +16 -6
  293. souleyez/storage/migrations/_024_wazuh_vulnerabilities.py +26 -10
  294. souleyez/storage/migrations/_025_multi_siem_support.py +3 -5
  295. souleyez/storage/migrations/_026_add_engagement_scope.py +31 -12
  296. souleyez/storage/migrations/_027_multi_siem_persistence.py +32 -15
  297. souleyez/storage/migrations/__init__.py +26 -26
  298. souleyez/storage/migrations/migration_manager.py +19 -19
  299. souleyez/storage/msf_sessions.py +100 -65
  300. souleyez/storage/osint.py +17 -24
  301. souleyez/storage/recommendation_engine.py +269 -235
  302. souleyez/storage/screenshots.py +33 -32
  303. souleyez/storage/smb_shares.py +136 -92
  304. souleyez/storage/sqlmap_data.py +183 -128
  305. souleyez/storage/team_collaboration.py +135 -141
  306. souleyez/storage/timeline_tracker.py +122 -94
  307. souleyez/storage/wazuh_vulns.py +64 -66
  308. souleyez/storage/web_paths.py +33 -37
  309. souleyez/testing/credential_tester.py +221 -205
  310. souleyez/ui/__init__.py +1 -1
  311. souleyez/ui/ai_quotes.py +12 -12
  312. souleyez/ui/attack_surface.py +2439 -1516
  313. souleyez/ui/chain_rules_view.py +914 -382
  314. souleyez/ui/correlation_view.py +312 -230
  315. souleyez/ui/dashboard.py +2382 -1130
  316. souleyez/ui/deliverables_view.py +148 -62
  317. souleyez/ui/design_system.py +13 -13
  318. souleyez/ui/errors.py +49 -49
  319. souleyez/ui/evidence_linking_view.py +284 -179
  320. souleyez/ui/evidence_vault.py +393 -285
  321. souleyez/ui/exploit_suggestions_view.py +555 -349
  322. souleyez/ui/export_view.py +100 -66
  323. souleyez/ui/gap_analysis_view.py +315 -171
  324. souleyez/ui/help_system.py +105 -97
  325. souleyez/ui/intelligence_view.py +436 -293
  326. souleyez/ui/interactive.py +22827 -10678
  327. souleyez/ui/interactive_selector.py +75 -68
  328. souleyez/ui/log_formatter.py +47 -39
  329. souleyez/ui/menu_components.py +22 -13
  330. souleyez/ui/msf_auxiliary_menu.py +184 -133
  331. souleyez/ui/pending_chains_view.py +336 -172
  332. souleyez/ui/progress_indicators.py +5 -3
  333. souleyez/ui/recommendations_view.py +195 -137
  334. souleyez/ui/rule_builder.py +343 -225
  335. souleyez/ui/setup_wizard.py +678 -284
  336. souleyez/ui/shortcuts.py +217 -165
  337. souleyez/ui/splunk_gap_analysis_view.py +452 -270
  338. souleyez/ui/splunk_vulns_view.py +139 -86
  339. souleyez/ui/team_dashboard.py +498 -335
  340. souleyez/ui/template_selector.py +196 -105
  341. souleyez/ui/terminal.py +6 -6
  342. souleyez/ui/timeline_view.py +198 -127
  343. souleyez/ui/tool_setup.py +264 -164
  344. souleyez/ui/tutorial.py +202 -72
  345. souleyez/ui/tutorial_state.py +40 -40
  346. souleyez/ui/wazuh_vulns_view.py +235 -141
  347. souleyez/ui/wordlist_browser.py +260 -107
  348. souleyez/ui.py +464 -312
  349. souleyez/utils/tool_checker.py +427 -367
  350. souleyez/utils.py +33 -29
  351. souleyez/wordlists.py +134 -167
  352. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/METADATA +1 -1
  353. souleyez-2.43.34.dist-info/RECORD +443 -0
  354. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/WHEEL +1 -1
  355. souleyez-2.43.29.dist-info/RECORD +0 -379
  356. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/entry_points.txt +0 -0
  357. {souleyez-2.43.29.dist-info → souleyez-2.43.34.dist-info}/licenses/LICENSE +0 -0
  358. {souleyez-2.43.29.dist-info → souleyez-2.43.34.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
+ ]