Commit Graph

588 Commits

Author SHA1 Message Date
Justin Bollinger
e165e3dc65 docs: spec notifications submenu (option 82) and per-crack toggle
Design doc for consolidating Pushover menu options under a new
submenu at main-menu option 82, promoting notify_per_crack_enabled
to a runtime toggle, and guarding it so per-crack cannot be enabled
while global Pushover notifications are OFF.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 18:39:40 -04:00
Justin Bollinger
7842f631fc Merge branch 'feat/test-pushover-menu' into feat/pushover-notifications
Adds menu option 84 (Send Test Pushover Notification) so users can verify
their Pushover credentials without running an attack. Ignores the global
notify_enabled toggle by design (prints a note when OFF).
2026-04-22 18:29:05 -04:00
Justin Bollinger
757cd54a88 test(notify): cover option 84 in menu wiring parametrize 2026-04-22 18:15:09 -04:00
Justin Bollinger
b017b15cb6 feat(notify): wire option 84 into hate_crack.py proxy menu 2026-04-22 18:13:31 -04:00
Justin Bollinger
60b105fed3 feat(notify): wire option 84 into main.py menu 2026-04-22 18:11:35 -04:00
Justin Bollinger
ce625a7874 fix(tests): patch hate_crack.main._notify directly
tests/test_random_rules_attack.py purges and re-imports hate_crack.*
modules, which leaves main._notify pointing at a different notify
object than a top-level patch("hate_crack.notify._send_pushover")
would touch. Under the full suite that caused the test's mock to
miss and the production call to hit the real Pushover API.

Switch to patch.object(hc_main, "_notify") -- same pattern as
tests/test_run_hcat_cmd.py but anchored to the exact module object
already bound as hc_main, so it is immune to sys.modules churn
regardless of import order. Drop the now-redundant _install_settings
helper and _reset_notify_state fixture.
2026-04-22 18:07:52 -04:00
Justin Bollinger
316e9f3ec9 feat(notify): add test_pushover_notification helper
Canned send path so a user can verify Pushover credentials without
running an attack. Ignores the global notify_enabled toggle — the test's
purpose is to confirm the pipe is live, not that attack notifications
are enabled. Prints a note when the global toggle is OFF so the user is
not confused later.
2026-04-22 17:56:35 -04:00
Justin Bollinger
6d61b02c84 docs: add implementation plan for test pushover menu entry
Five-task TDD plan: unit tests + function, main.py menu wiring,
hate_crack.py proxy wiring, parametrized menu test, full verification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:53:43 -04:00
Justin Bollinger
e7ee258d2a docs: add spec for test pushover menu entry
Why: user needs a one-click way to verify Pushover credentials without
running an attack. This spec captures the menu wiring, credential/toggle
handling (option A — ignore global toggle), and test plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:47:42 -04:00
Justin Bollinger
c8507e8974 test: isolate notify state between tests
hate_crack.main calls notify.init() at import time with whatever
config.json is resolved from the developer's environment (often
~/.hate_crack/config.json). If that file has notify_enabled: true, the
per-attack prompt in attacks.py fires input() during tests and trips
pytest's capture fd, failing unrelated tests.

Add an autouse conftest fixture that clears notify module state before
and after every test so the suite is hermetic regardless of local
config. Notify-specific tests already use their own
clear_state_for_tests() fixture; this change covers the rest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:27:15 -04:00
Justin Bollinger
baeca07b70 feat(notify): wire Pushover notifications into attacks and config (WIP)
Adds notify_* keys to both config.json.example files, threads
notification calls through hashcat invocations in main.py, and
exposes menu/attack hooks. Pushed for manual testing — verification
and PR still pending.

Refs #106

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 15:16:12 -04:00
Justin Bollinger
f9926c0b41 feat(notify): add notification package with Pushover backend
Introduce hate_crack.notify package with a small functional public API
and a CrackTailer thread for polling hashcat output files. Package
layout keeps the HTTP call (_send_pushover) isolated so future backends
(Slack, generic webhooks) can be added as a sibling function rather
than a framework rewrite.

Core pieces:
- settings.py: NotifySettings dataclass plus atomic config persistence
  (save_enabled, add_to_allowlist) via read-modify-write + os.replace.
- pushover.py: single _send_pushover() that never raises; network
  errors, missing requests, and missing creds all funnel to False.
- _suppress.py: thread-local suppression context manager so
  orchestrator attacks can chain primitives without flooding
  notifications.
- tailer.py: CrackTailer(threading.Thread) that seeks to EOF on start,
  polls at a user-configurable interval, and collapses per-tick bursts
  into a single aggregate notification when they exceed the cap.
- __init__.py: public API (init, prompt_notify_for_attack,
  notify_job_done, notify_crack, start_tailer, stop_tailer,
  toggle_enabled, suppressed_notifications). Privacy guarantee:
  notification payloads contain only attack name, counts, and hash
  path, never plaintexts.

72 new tests cover dataclass defaults, atomic config writes, idempotent
allowlist updates, HTTP payload privacy, suppression nesting and
thread-locality, tailer EOF seek, burst cap, truncation recovery, and
the per-attack prompt's [y/n/always] flow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 14:41:38 -04:00
Justin Bollinger
158efdd5b5 feat: add auto-tagging workflow for semver bumps
Automatically creates a new git tag on push to main based on
conventional commit prefixes: feat: bumps minor (2.5.x → 2.6.0),
fix:/perf: bumps patch (2.5.1 → 2.5.2). The new tag triggers the
existing release workflow to create a GitHub release.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v2.6.0
2026-04-13 10:32:40 -04:00
Justin Bollinger
3fdff403fc fix: use no-local-version scheme for clean semver display
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>
2026-04-13 10:29:55 -04:00
Justin Bollinger
786df786b7 fix: correct convergence logic in hcatFingerprint loop
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>
2026-04-13 10:10:01 -04:00
Justin Bollinger
44489a0ce6 perf: combine d3ad0ne + T0XlC rules into single hashcat invocation
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>
2026-04-13 10:09:55 -04:00
Justin Bollinger
5311222297 perf: use binary chunk counting in lineCount for large files
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>
2026-04-13 10:09:47 -04:00
Justin Bollinger
b3f8aec0ba perf: replace hard 15s sleep with proper rate limiter for Hashmob downloads
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>
2026-04-13 10:09:39 -04:00
Justin Bollinger
94e957678d Merge pull request #105 from trustedsec/fix/output-path-symlinks
Fix output file path and remove symlink creation
v2.5.1
2026-04-08 13:11:49 -04:00
Justin Bollinger
abec43d5c4 test: add e2e test for output file path correctness
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>
2026-04-08 13:07:33 -04:00
Justin Bollinger
0a9549b15a fix: only report combined output file when it exists
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>
2026-04-08 13:05:08 -04:00
Justin Bollinger
13a1f9e28c Merge branch 'remove-symlink-logic' 2026-04-08 13:02:49 -04:00
Justin Bollinger
2f73289737 fix: remove symlink/copy from _ensure_hashfile_in_cwd
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>
2026-04-08 13:02:26 -04:00
Justin Bollinger
e7a4f3d441 fix: write output files to user's CWD, not install directory
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>
2026-04-08 11:43:51 -04:00
Justin Bollinger
717aa9e228 docs: update README for v2.5.0 changes 2026-03-20 11:15:29 -04:00
Justin Bollinger
c63154fb4e feat: restore hcatOptimizedWordlists config and use as quick crack default
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.
v2.5.0
2026-03-20 11:06:03 -04:00
Justin Bollinger
59f0052c0e feat: add tab autocomplete to wordlist menu file path prompts
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.
2026-03-20 10:30:08 -04:00
Justin Bollinger
600e21c034 fix: move dev tools to dependency-groups so uv sync installs them always
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.
v2.4.7
2026-03-20 10:16:53 -04:00
Justin Bollinger
0d59f0f203 fix: read version from package metadata instead of _version.py
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.
2026-03-20 10:16:04 -04:00
Justin Bollinger
95a6a59759 ci: auto-publish GitHub release on version tag push v2.4.5 2026-03-20 10:13:22 -04:00
Justin Bollinger
f1e63399d9 fix: force _version.py regeneration on install and 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.
v2.4.4
2026-03-20 10:08:56 -04:00
Justin Bollinger
82b3471d16 fix: remove hashcat preflight check from Makefile
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.
v2.4.3
2026-03-20 10:02:54 -04:00
Justin Bollinger
09302435b9 fix: search candidate config dirs in Makefile preflight hashcat check
Mirrors the Python runtime's _candidate_roots() logic so hcatPath is
honored even when config.json lives in ~/.hate_crack or another
non-cwd location.
v2.4.2
2026-03-20 09:55:28 -04:00
Justin Bollinger
ed6038f6e3 fix: honor config.json hcatPath/hcatBin in Makefile preflight check
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.
2026-03-20 09:37:34 -04:00
Justin Bollinger
8337471368 fix: resolve hashcat binary from hcatPath when not in PATH
- 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)
v2.4.1
2026-03-20 09:27:03 -04:00
Justin Bollinger
7ec1dba44e Merge pull request #103 from trustedsec/feat/release-v2.4.0
feat: release v2.4.0 - wordlist filtering, parallel rule downloads, dynamic optimized kernels
v2.4.0
2026-03-20 09:05:14 -04:00
Justin Bollinger
2ec3299859 chore: add pytest-timeout dev dependency 2026-03-19 23:43:49 -04:00
Justin Bollinger
9f7296c60c fix: add skip guards for missing runtime artifacts in e2e/integration tests 2026-03-19 23:42:43 -04:00
Justin Bollinger
304d105009 fix: preserve hate_crack.api in module reload to prevent test isolation hang 2026-03-19 23:40:24 -04:00
Justin Bollinger
a15b02bcdd test: add optimized kernel unit and integration tests (#82) 2026-03-19 21:37:58 -04:00
Justin Bollinger
8efed82ebc feat: add dynamic optimized kernel (-O) to 11 additional attack types (#82) 2026-03-19 21:11:59 -04:00
Justin Bollinger
9c3166af38 test: verify .7z wordlist filtering and parallel rule downloads (#80 #81) 2026-03-19 20:23:28 -04:00
Justin Bollinger
e2bc946afc fix: correct download_left_hashes potfile merge bugs
Merge fix/download-left-hashes-potfile-bugs
v2.3.6
2026-03-19 19:14:00 -04:00
Justin Bollinger
a014af5871 fix: correct download_left_hashes potfile merge bugs
- 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
2026-03-19 19:13:41 -04:00
Justin Bollinger
c8b18f9595 fix: locate uv binary before upgrade to handle non-standard PATH
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.
v2.3.5
2026-03-19 18:05:00 -04:00
Justin Bollinger
0aa61a4c7c fix: use git fetch --tags and uv sync --reinstall to fix version after upgrade
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.
v2.3.4
2026-03-19 18:02:54 -04:00
Justin Bollinger
bc166e0a4e fix: drop make clean from upgrade command to fix version detection
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.
v2.3.3
2026-03-19 17:57:09 -04:00
Justin Bollinger
240816ba33 feat: add --update flag to trigger in-place upgrade on demand
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.
2026-03-19 17:49:23 -04:00
Justin Bollinger
b808b389e5 feat: add upgrade prompt to version update check
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.
v2.3.2
2026-03-19 17:45:36 -04:00
Justin Bollinger
30728587bc Merge pull request #102 from trustedsec/bug/analyze-rules-tab-complete
fix: use tab-completion file selector for analyze hashcat rules
v2.3.1
2026-03-19 16:23:21 -04:00