diff --git a/Makefile b/Makefile index b4a7b67..1c63273 100644 --- a/Makefile +++ b/Makefile @@ -97,10 +97,20 @@ install: submodules fi @command -v uv >/dev/null 2>&1 || { echo "uv not found. Installing uv..."; curl -LsSf https://astral.sh/uv/install.sh | sh; } @UV_BIN=$$(command -v uv 2>/dev/null || echo "$$HOME/.local/bin/uv"); \ - "$$UV_BIN" tool install -e . + "$$UV_BIN" sync + @mkdir -p "$${XDG_BIN_HOME:-$$HOME/.local/bin}" + @printf '#!/usr/bin/env bash\nset -euo pipefail\nexec uv run --directory %s python -m hate_crack "$$@"\n' "$(CURDIR)" \ + > "$${XDG_BIN_HOME:-$$HOME/.local/bin}/hate_crack" + @chmod +x "$${XDG_BIN_HOME:-$$HOME/.local/bin}/hate_crack" + @echo "Installed hate_crack shim to $${XDG_BIN_HOME:-$$HOME/.local/bin}/hate_crack" update: submodules - @uv tool install -e . --force --reinstall + @uv sync + @mkdir -p "$${XDG_BIN_HOME:-$$HOME/.local/bin}" + @printf '#!/usr/bin/env bash\nset -euo pipefail\nexec uv run --directory %s python -m hate_crack "$$@"\n' "$(CURDIR)" \ + > "$${XDG_BIN_HOME:-$$HOME/.local/bin}/hate_crack" + @chmod +x "$${XDG_BIN_HOME:-$$HOME/.local/bin}/hate_crack" + @echo "Updated hate_crack shim at $${XDG_BIN_HOME:-$$HOME/.local/bin}/hate_crack" reinstall: uninstall install @@ -146,9 +156,6 @@ check: lint uninstall: @echo "Detecting OS and uninstalling dependencies..." - @uv tool uninstall hate_crack || true - @data_home="$${XDG_DATA_HOME:-$$HOME/.local/share}"; \ - rm -rf "$$data_home/uv/tools/hate-crack" "$$data_home/uv/tools/hate_crack" @bin_home="$${XDG_BIN_HOME:-$$HOME/.local/bin}"; \ rm -f "$$bin_home/hate_crack" @if [ "$(shell uname)" = "Darwin" ]; then \ diff --git a/README.md b/README.md index 8354482..689348f 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Then customize configuration in `config.json` if needed (wordlist paths, API key The easiest way is to run `make` (or `make install`), which auto-detects your OS and installs: - External dependencies (p7zip, transmission-cli) - Builds submodules (hashcat-utils, princeprocessor, and optionally omen) -- Python tool via uv +- Python dependencies via uv and a CLI shim at `~/.local/bin/hate_crack` ```bash make @@ -80,9 +80,12 @@ macOS (Homebrew): brew install p7zip transmission-cli ``` -Then install the Python tool: +Then install the Python dependencies and CLI shim: ```bash -uv tool install . +uv sync +mkdir -p ~/.local/bin +printf '#!/usr/bin/env bash\nset -euo pipefail\nexec uv run --directory %s python -m hate_crack "$@"\n' "$(pwd)" > ~/.local/bin/hate_crack +chmod +x ~/.local/bin/hate_crack ``` ------------------------------------------------------------------- @@ -133,9 +136,11 @@ make hate_crack ``` -The tool will automatically search for these assets in: -- The installed package (bundled by `make install`) +The `make install` command creates a bash shim at `~/.local/bin/hate_crack` that runs from the repo directory, so config and assets are always found regardless of your current working directory. + +Config is also searched in: - Current working directory and parent directory +- The repo root and package directory - `~/hate_crack`, `~/hate-crack`, or `~/.hate_crack` **Note:** The `hcatPath` in `config.json` is for the hashcat binary location only (optional if hashcat is in PATH). Hate_crack assets (hashcat-utils, princeprocessor, omen) are loaded from the repository directory and bundled automatically by `make install`. @@ -789,6 +794,8 @@ Interactive menu for downloading and managing wordlists from Weakpass.com via Bi ### Version History Version 2.0+ + - Replaced `uv tool install` with a bash shim for reliable config and asset resolution from any working directory + - Fixed config resolution to search the repo root and package directory in addition to CWD - Fixed bare NTLM hash detection failing when hash files contain leading blank lines, BOM characters, or null bytes from UTF-16 encoding - Improved error message for unrecognized hash formats to show the actual first-line content and list expected formats - Fixed rule file path construction in Quick Crack and Loopback Attack using `os.path.join()` instead of string concatenation diff --git a/hate_crack/main.py b/hate_crack/main.py index c02c091..1ff998d 100755 --- a/hate_crack/main.py +++ b/hate_crack/main.py @@ -110,6 +110,8 @@ def _candidate_roots(): candidates = [ cwd, os.path.abspath(os.path.join(cwd, os.pardir)), + _repo_root, + _package_path, "/opt/hate_crack", "/usr/local/share/hate_crack", ] diff --git a/tests/test_e2e_local_install.py b/tests/test_e2e_local_install.py index 5955e26..72ebf80 100644 --- a/tests/test_e2e_local_install.py +++ b/tests/test_e2e_local_install.py @@ -10,40 +10,47 @@ import pytest os.environ.get("HATE_CRACK_RUN_E2E") != "1", reason="Set HATE_CRACK_RUN_E2E=1 to run local end-to-end install tests.", ) -def test_local_uv_tool_install_and_help(tmp_path): +def test_local_make_install_and_help(tmp_path): if shutil.which("uv") is None: pytest.skip("uv not available") repo_root = Path(__file__).resolve().parents[1] home_dir = tmp_path / "home" home_dir.mkdir() + bin_dir = home_dir / ".local" / "bin" + bin_dir.mkdir(parents=True) env = os.environ.copy() env.update( { "HOME": str(home_dir), - "PATH": f"{home_dir / '.local' / 'bin'}:{env.get('PATH', '')}", + "PATH": f"{bin_dir}:{env.get('PATH', '')}", "HATE_CRACK_SKIP_INIT": "1", "XDG_CACHE_HOME": str(tmp_path / "cache"), "XDG_CONFIG_HOME": str(tmp_path / "config"), "XDG_DATA_HOME": str(tmp_path / "data"), + "XDG_BIN_HOME": str(bin_dir), } ) install = subprocess.run( - ["uv", "tool", "install", "."], + ["make", "install"], cwd=repo_root, env=env, capture_output=True, text=True, ) assert install.returncode == 0, ( - f"uv tool install failed. stdout={install.stdout} stderr={install.stderr}" + f"make install failed. stdout={install.stdout} stderr={install.stderr}" ) + shim_path = bin_dir / "hate_crack" + assert shim_path.exists(), "hate_crack shim was not created" + assert os.access(shim_path, os.X_OK), "hate_crack shim is not executable" + tool_help = subprocess.run( ["hate_crack", "--help"], - cwd=repo_root, + cwd=str(tmp_path), env=env, capture_output=True, text=True,