Files
hate_crack/hate_crack.py
Justin Bollinger c51ae332c6 feat: add wordlist tools submenu (len, req-include, req-exclude, cutb, rli, rli2, splitlen, gate) (#90, #91, #92, #94)
Add Wordlist Tools submenu (key 80) with 7 preprocessing utilities backed by hashcat-utils binaries:

- Filter by length (len.bin) - #90
- Require/exclude character classes (req-include.bin, req-exclude.bin) - #90
- Extract substring (cutb.bin) - #90
- Remove matching lines (rli.bin, rli2.bin) - #91
- Split by length (splitlen.bin) - #92
- Shard wordlist (gate.bin) - #94

Three-layer pattern:
- main.py: low-level wrappers (wordlist_filter_len, wordlist_filter_req_include,
  wordlist_filter_req_exclude, wordlist_cutb, wordlist_splitlen, wordlist_subtract,
  wordlist_subtract_single, wordlist_gate) return bool for testability
- attacks.py: UI handlers with input validation and the wordlist_tools_submenu dispatcher
- hate_crack.py + main.py: menu item "80" wired in both get_main_menu_items and
  get_main_menu_options

Move interactive_menu import to attacks.py module level (was local import in
combinator_submenu) to support patching in tests.
2026-03-19 12:15:43 -04:00

110 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env -S uv run --project .
import sys
from hate_crack import main as _main
# Re-export symbols for tests and legacy imports.
for _name, _value in _main.__dict__.items():
if _name.startswith("__") and _name not in {
"__all__",
"__doc__",
"__name__",
"__package__",
"__loader__",
"__spec__",
}:
continue
globals().setdefault(_name, _value)
def __getattr__(name):
return getattr(_main, name)
def _sync_globals_to_main():
# Keep commonly-mutated globals aligned for tests and wrappers.
for name in (
"hcatHashType",
"pipal_count",
"hcatHashFile",
"hcatHashFileOrig",
"pipalPath",
"debug_mode",
):
if name in globals():
setattr(_main, name, globals()[name])
def _sync_callables_to_main():
for name in (
"weakpass_wordlist_menu",
"download_hashmob_wordlists",
"download_hashmob_rules",
"hashview_api",
"export_excel",
"show_results",
"show_readme",
"quit_hc",
):
if name in globals():
setattr(_main, name, globals()[name])
def cli_main():
_sync_globals_to_main()
_sync_callables_to_main()
return _main.main()
def main():
_sync_globals_to_main()
_sync_callables_to_main()
return _main.main()
def pipal():
_sync_globals_to_main()
return _main.pipal()
def get_main_menu_items():
return _main.get_main_menu_items()
def get_main_menu_options():
options = {
"1": _attacks.quick_crack,
"2": _attacks.extensive_crack,
"3": _attacks.brute_force_crack,
"4": _attacks.top_mask_crack,
"5": _attacks.fingerprint_crack,
"6": _attacks.combinator_submenu,
"7": _attacks.hybrid_crack,
"8": _attacks.pathwell_crack,
"9": _attacks.prince_attack,
"13": _attacks.bandrel_method,
"14": _attacks.loopback_attack,
"15": _attacks.ollama_attack,
"16": _attacks.omen_attack,
"17": _attacks.adhoc_mask_crack,
"18": _attacks.markov_brute_force,
"80": _attacks.wordlist_tools_submenu,
"90": download_hashmob_rules,
"91": weakpass_wordlist_menu,
"92": download_hashmob_wordlists,
"93": weakpass_wordlist_menu,
"95": pipal,
"96": export_excel,
"97": show_results,
"98": show_readme,
"99": quit_hc,
}
# Only show Hashview API when configured.
if globals().get("hashview_api_key"):
options["94"] = hashview_api
return options
if __name__ == "__main__":
sys.exit(main())