Add local_scheme = "no-local-version" to setuptools-scm config so
versions never include the +g<hash> suffix. Simplify the regex in
__init__.py to only strip .post/.dev suffixes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The old loop reassigned crackedBefore at the top of each iteration and
initialized crackedAfter to 0, which could cause the loop to enter
spuriously or skip entirely. Switch to while True / break to properly
detect when an iteration produces no new cracks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Merge both rule files into a temporary combined file so hashcat only
starts once per wordlist instead of twice, saving GPU initialization
overhead on each dictionary attack iteration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Read 1 MiB binary chunks and count newline bytes instead of iterating
text lines, which is significantly faster on large output files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces a shared _RateLimiter class (1 req/2s) instead of per-function
locks with a 15-second sleep. Also tunes backoff from 256s to 30s with
30s penalty increments for faster retry on rate-limited responses.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verifies that a pwdump file at /tmp/test_hashes.ntds produces output
at /tmp/test_hashes.ntds.out using real hashcat. Confirms no files
leak into the project directory. Gated behind HATE_CRACK_RUN_E2E=1.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cleanup() previously always printed 'Cracked passwords combined in
X.out' even when combine_ntlm_output() returned early (no cracked
hashes) or the hash type wasn't NTLM pwdump. Now checks file
existence first and falls back to pointing at the raw .out file.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Output files now land next to the original hashfile. resolve_path()
already resolves relative paths against HATE_CRACK_ORIG_CWD, so
relocating the hashfile into CWD was unnecessary and created
confusing symlinks in the working directory.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bash shim uses `uv run --directory <install_dir>` which changes the
process CWD to the install directory. _ensure_hashfile_in_cwd() and the
Hashview download path used os.getcwd() to determine the target directory
for output files (.out, .nt, etc.), causing them to land in the install
directory instead of where the user ran the command.
Add orig_cwd() helper that reads HATE_CRACK_ORIG_CWD (set by the shim)
and use it in _ensure_hashfile_in_cwd(), the Hashview download path, and
the potfile fallback path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Re-add hcatOptimizedWordlists config key (previously removed) with a
default of ./optimized_wordlists. Falls back to hcatWordlists if the
configured directory does not exist.
Update quick_crack to list files from and default to hcatOptimizedWordlists
instead of hcatWordlists when prompting for a wordlist or directory.
Replace input() with ctx.select_file_with_autocomplete() for all file
and directory path prompts in the 7 wordlist tools submenu functions.
Non-path prompts (lengths, masks, offsets, mode selection) remain as
plain input() calls.
Update tests to set ctx.select_file_with_autocomplete.side_effect for
file path values and leave builtins.input patches only for non-path
inputs.
ruff, ty, pytest, pytest-cov were in [project.optional-dependencies]
requiring --extra dev to install. Moved to [dependency-groups] which
uv sync includes by default, fixing the pre-push hook finding no ruff.
Replaces the _version.py import with importlib.metadata.version() so
the version is always read from the installed package, which setuptools-scm
writes correctly during uv sync. Removes the version_file config and
the stale-file workarounds from make install/clean.
_version.py is gitignored but persists on disk with a stale version.
Delete it before uv sync so setuptools-scm regenerates it from the
current git tag. Also remove it in make clean for consistency.
Hashcat path is configured via config.json at runtime. The build-time
check caused false failures when running as root (sudo) or before
config.json exists, and added no value since hashcat-utils builds
independently of the hashcat installation.
The submodules-pre hashcat check now reads hcatPath and hcatBin from
config.json (mirroring main.py resolution logic) before falling back to
PATH lookup and the vendored hate_crack/hashcat/hashcat binary.
- Apply os.path.expanduser() to hcatPath so tilde (~) paths work,
consistent with every other config path (hcatWordlists, rulesDirectory,
hcatPotfilePath, hcatDebugLogPath)
- Improve error message when binary not found to show what hcatPath was
checked, making it easier to diagnose misconfiguration
- Update optimizedKernelAttacks in config.json.example to include all 21
attacks from DEFAULT_OPTIMIZED_ATTACKS (13 were added in v2.4.0 but
config.json.example was not updated, causing the runtime override to
exclude them)
- Delete block that wrongly appended found hashes back into the left
(unsolved) file - found hashes belong only in the potfile
- Fix get_hcat_potfile_path() to return "" when config key is
explicitly set to "", respecting user intent to disable potfile override
- Fix get_hcat_potfile_path() to resolve relative paths relative to the
config file directory, matching main.py's hate_path resolution
- Add potfile_path parameter to download_left_hashes() and
download_hashes_from_hashview() so CLI --potfile-path and
--no-potfile-path overrides propagate to the API merge step
- Update main.py call sites to pass hcatPotfilePath through
- Add tests covering all four bug fixes
When running as root or via sudo, /root/.local/bin may not be in PATH.
Use shutil.which with fallback to ~/.local/bin/uv, and fail clearly
if uv can't be found.
uv's PEP 517 build isolation copies source to a temp dir without .git,
so setuptools-scm can't determine the version and falls back to the
existing _version.py. Forcing --reinstall-package hate_crack makes uv
rebuild from source in the actual repo dir (editable mode) where git
is accessible, so the correct version is generated.
make clean deinits submodules and wipes the uv cache, causing
setuptools-scm to compute a wrong version during the subsequent
uv sync. The clean step is unnecessary for an upgrade - just
git pull && make install is sufficient.
Refactors the upgrade logic into _run_upgrade() so it can be called
both from the startup version check prompt and directly via --update,
bypassing the version comparison entirely.
When a newer release is found, prompt the user to upgrade in-place.
Resolves the git repo root via `git rev-parse --show-toplevel` before
running `git pull && make clean && make && make install`, so the upgrade
works correctly whether hate_crack is run from source or installed into
site-packages. Ctrl-C and EOF at the prompt continue normally.
Resolves merge conflict with origin/main (keys 19-22 from main kept,
wordlist tools submenu added at key 80).
Fixes test isolation bug in test_random_rules_attack.py where
load_cli_module() nuked hate_crack.attacks from sys.modules, breaking
__globals__ references in wordlist_tools_submenu for downstream tests.
Also corrects wrong menu key assertion (21 not 20) for generate_rules_crack.
Resolves merge conflict with origin/main (keys 19-21 used by ngram,
permute, random-rules). Combipow takes key 22.
- gzip support: decompress .gz wordlists to a temp file before passing
path to combipow.bin (which requires a filename argument)
- UI line-counting uses gzip.open for .gz files
- Update tests to reference key 22 instead of 21