chore: align CI and tooling with global development standards

- Remove 6 duplicate per-version pytest workflows (matrix build covers all)
- Pin GitHub Actions to SHA hashes with version comments
- Add persist-credentials: false to checkout steps
- Replace mypy with ty for type checking (faster, stricter)
- Pin dev deps to exact versions (ty==0.0.17, ruff==0.15.1, pytest==9.0.2, pytest-cov==7.0.0)
- Remove types-* stub packages (ty doesn't need them)
- Remove stale [dependency-groups] section from pyproject.toml
- Update shell scripts to use set -euo pipefail
- Add prek.toml for git hook management (pre-push, post-commit)
- Add lint-infra.yml workflow (shellcheck + actionlint)
- Fix actionlint warning: pass github.head_ref through env var
- Track CLAUDE.md and .claude/ scripts in git
- Update README.md and Makefile references from mypy to ty

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Justin Bollinger
2026-02-19 12:42:51 -05:00
parent e7f4ed815b
commit de2b400f6d
22 changed files with 693 additions and 275 deletions

152
CLAUDE.md Normal file
View File

@@ -0,0 +1,152 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What This Is
hate_crack is a menu-driven Python wrapper for hashcat that automates password cracking methodologies. It provides 16 attack modes, API integrations (Hashview, Weakpass, Hashmob), and utilities for wordlist/rule management.
## Commands
```bash
# Install (builds submodules, vendors assets, installs via uv)
make install
# Dev install (editable, with dev deps)
make dev-install
# Run tests (requires HATE_CRACK_SKIP_INIT=1 in worktrees without hashcat-utils)
HATE_CRACK_SKIP_INIT=1 uv run pytest -v
# Run a single test file
HATE_CRACK_SKIP_INIT=1 uv run pytest tests/test_ui_menu_options.py -v
# Lint
uv run ruff check hate_crack
uv run ty check hate_crack
# Both lint checks
make lint
# Format
uv run ruff format hate_crack
# Coverage
make coverage
```
**Test environment variables**: `HATE_CRACK_SKIP_INIT=1` skips binary/config validation (essential for CI and worktrees). `HASHMOB_TEST_REAL=1`, `HASHVIEW_TEST_REAL=1`, `WEAKPASS_TEST_REAL=1` enable live API tests.
## Git Hooks
Git hooks are managed by [prek](https://github.com/nicholasgasior/prek). Install with:
```bash
prek install
```
Hooks defined in `prek.toml`:
- **pre-push**: ruff check, ty check, pytest
- **post-commit**: documentation audit
## Documentation Auditing
Automatic documentation audits run after each commit via prek hooks.
```bash
# Manually audit a commit
bash .claude/audit-docs.sh HEAD
bash .claude/audit-docs.sh <commit_sha>
# Check the last N commits for documentation gaps
bash .claude/check-docs.sh 5
```
See `.claude/DOCUMENTATION-AUDIT.md` for details on the audit system.
## Worktree Policy
**Every agent MUST work in a dedicated git worktree** - never edit files directly in the main repo checkout. This prevents conflicts when multiple agents run in parallel.
### Setup
```bash
# Create a worktree under /tmp (keeps the parent directory clean)
git worktree add /tmp/hate_crack-<task-name> -b <branch-name>
cd /tmp/hate_crack-<task-name>
# Install dev dependencies in the new worktree
uv sync --dev
# Run tests in the worktree
HATE_CRACK_SKIP_INIT=1 uv run pytest -v
```
### Rules
1. **Always create a worktree** before making any file changes: `git worktree add /tmp/hate_crack-<task> -b <branch>`
2. **All file edits** happen inside the worktree directory, not the main repo
3. **Run tests and lint** inside the worktree before merging
4. **Merge back** via PR or `git merge` from the main worktree
5. **Clean up** when done: `git worktree remove /tmp/hate_crack-<task>`
## Architecture
### Module Map
- **`hate_crack.py`** (root) - Entry point, menu registration, proxy to `hate_crack.main`
- **`hate_crack/main.py`** - Core logic (~3700 lines): hashcat subprocess wrappers, config loading, menu display, global state
- **`hate_crack/attacks.py`** - 16 attack handler functions that receive `ctx` (main module ref)
- **`hate_crack/api.py`** - Hashmob, Hashview, Weakpass API integrations
- **`hate_crack/cli.py`** - Argparse helpers, path resolution, logging setup
- **`hate_crack/formatting.py`** - Terminal UI helpers (multi-column list printing)
### Three-Layer Attack Pattern
Every attack spans three files with a specific wiring pattern:
1. **`hate_crack/main.py`** - Low-level hashcat function (e.g., `hcatBruteForce(hcatHashType, hcatHashFile)`)
- Builds subprocess commands, manages `hcatProcess` global, handles KeyboardInterrupt
- All hashcat invocations follow: build cmd list -> `cmd.extend(shlex.split(hcatTuning))` -> `_append_potfile_arg(cmd)` -> `subprocess.Popen(cmd)`
2. **`hate_crack/attacks.py`** - Menu handler wrapper (e.g., `def brute_force_crack(ctx: Any)`)
- Receives `ctx` (the main module itself) via `_attack_ctx()`, which returns `sys.modules['hate_crack.main']`
- Handles user prompts, then calls `ctx.hcatBruteForce(ctx.hcatHashType, ctx.hcatHashFile)`
3. **`hate_crack.py`** (root) - Menu registration + dispatcher
- Has its own `get_main_menu_options()` that maps keys to `_attacks.<handler>`
- **Important**: `hate_crack.py` has a DUPLICATE menu mapping separate from `main.py`'s `get_main_menu_options()`. Both must be updated when adding attacks.
### Adding a New Attack - Checklist
1. Add the hashcat wrapper function in `main.py` (e.g., `hcatMyAttack(...)`)
2. Add the handler in `attacks.py` (e.g., `def my_attack(ctx: Any)`)
3. Add a dispatcher in `main.py`: `def my_attack(): return _attacks.my_attack(_attack_ctx())`
4. Add the print line in `main.py`'s menu display loop (~line 3807+)
5. Add the menu entry in `main.py`'s `get_main_menu_options()`
6. Add the menu entry in `hate_crack.py`'s `get_main_menu_options()` (the duplicate)
### hate_crack.py <-> main.py Proxy
`hate_crack.py` uses `__getattr__` to proxy attribute access to `hate_crack.main`. It syncs mutable globals via `_sync_globals_to_main()` and `_sync_callables_to_main()`. Tests load `hate_crack.py` as `CLI_MODULE` and exercise both the proxy and direct module paths.
### Config System
`config.json` is resolved from multiple candidate directories (repo root, cwd, `~/.hate_crack`, `/opt/hate_crack`, etc.) and auto-created from `config.json.example` if missing. Each config key is loaded via try/except with `default_config` fallback. New config vars need: entry in `config.json.example`, try/except loading block in `main.py` (~line 454 area), and path normalization via `_normalize_wordlist_setting()` if it's a wordlist path.
### Path Distinction
- **`hate_path`** - hate_crack assets directory (hashcat-utils, princeprocessor, masks, PACK). All bundled binaries use this.
- **`hcatPath`** - hashcat installation directory. Only used for the hashcat binary itself.
### External Binary Pattern
Binaries are verified at startup via `ensure_binary(path, build_dir, name)`. Non-critical binaries (princeprocessor, hcstat2gen) use try/except around `ensure_binary` with a warning message. The `SKIP_INIT` flag bypasses all binary checks.
## Testing Patterns
- Menu option tests in `test_ui_menu_options.py` use monkeypatching against `CLI_MODULE` (loaded from `hate_crack.py`)
- API tests mock `requests` responses; most are offline-first
- conftest.py provides `hc_module` fixture via `load_hate_crack_module()` which dynamically imports root `hate_crack.py` with SKIP_INIT enabled
- Python 3.9-3.14 supported in CI (requires-python >=3.13 in pyproject.toml but CI tests older versions)
- E2E tests (`test_e2e_local_install.py`, `test_docker_script_install.py`) are opt-in via `HATE_CRACK_RUN_E2E=1` and `HATE_CRACK_RUN_DOCKER_TESTS=1`