Target-info mode (menu 12) asked for company, industry, and location as three
blank prompts. Once the company name is known the local Ollama model can often
supply the other two, so ask it and offer the answers as editable defaults.
- llm.research_target(): TargetResearchInput/TargetResearchOutput schemas plus a
research SystemPromptGenerator that tells the model to return empty strings
when it does not genuinely recognize the organization, so an unknown small
client yields blank prompts instead of a confident hallucination.
APITimeoutError is translated to LLMTimeoutError like generate_candidates.
- clean_research_field(): strips, collapses whitespace, caps at 80 chars, and
turns anything non-string into "" so model output cannot be pasted unbounded
into a prompt default.
- main.hcatOllamaResearchTarget(): runs the call inside the existing spinner and
degrades to blank suggestions on timeout or any other failure, so research can
never block the attack.
- attacks.ollama_attack(): shows suggestions as "Industry (freight rail): "
defaults, labelled explicitly as the model's GUESSES rather than OSINT.
- New ollamaAutoResearch config toggle (default true) in both config examples.
Research uses only the configured local Ollama server; the client name is never
sent to a search engine or third-party company-data API.
Co-Authored-By: Claude <noreply@anthropic.com>
Adds a third LLM Attack generation mode ("cracked") that samples the
plaintexts already recovered this session from <hashfile>.out and asks the
model to infer the target organization's password conventions and emit new
candidates in the same style.
- llm.py: new _CRACKED_PROMPT (offensive candidate generation, explicitly
not the denylist-oriented _WORDLIST_PROMPT), a _PROMPTS mode->prompt map,
and a "cracked" branch in _build_request that tells the model not to
repeat already-cracked passwords.
- main.py: extract the wordlist sampling logic into the shared module-level
helper _sample_plaintext_file(path, cap, source_label) and call it from
both the wordlist and cracked branches of hcatOllama; guard missing and
empty .out files.
- attacks.py: offer mode 3 only when <hashfile>.out exists and is non-empty
(matching _markov_pick_training_source), with a clear message otherwise.
- README: document the three modes and the widened ollamaMaxSampleLines
scope.
Co-Authored-By: Claude <noreply@anthropic.com>
- Defect 1 (main.py): Replace floating-point stride loop with exact
floor(k * total / cap) index formula so sampling always returns
EXACTLY cap items for any 1 <= cap <= total_usable, including the
stride < 2 regime where the old approach returned cap-1 items.
- Defect 2 (main.py): Validate ollamaMaxSampleLines before computing
the stride; values <= 0 are nonsensical and fall back to 500
(same as the default) rather than raising ZeroDivisionError.
- Defect 3 (test_progress_spinner.py): Rewrite test_non_tty_no_extra_thread
to patch threading.Thread and assert it was never called, so the test
actually fails when the spinner starts a thread in the non-TTY path.
- Defect 4 (test_progress_spinner.py): Simplify test_non_tty_prints_message
to use monkeypatch.setattr instead of the dead mock.patch.object +
manual assign/restore layering; removes two # type: ignore comments.
- docs(README.md): Document ollamaMaxSampleLines alongside ollamaNumCtx /
ollamaTimeout in the Ollama Configuration section.
- tests: Add boundary-case tests for stride < 2 (total==cap, total==cap+1,
total=3/cap=2, cap=1) and zero-cap fallback; make
test_ollama_max_sample_lines_default exercise behaviour rather than
the ambient config value.
Co-Authored-By: Claude <noreply@anthropic.com>
The Atomic Agents client was built with no timeout, so an Ollama server that
accepted the TCP connection but never replied (most commonly a large model still
loading into VRAM) left the CLI blocked in agent.run() forever. The caller's
`except Exception` handler only ever fired on connection-refused.
- llm.generate_candidates() takes a `timeout` parameter (default
DEFAULT_TIMEOUT_SECONDS = 300.0) and forwards it to the OpenAI client.
- openai.APITimeoutError is translated into a new domain-level
llm.LLMTimeoutError so main.py need not import openai itself, keeping the
atomic-agents/instructor dependency isolated to llm.py as documented.
- hcatOllama passes the new `ollamaTimeout` config value and prints
timeout-specific guidance (elapsed seconds, VRAM-loading hint, the setting to
raise) instead of the misleading "ensure Ollama is running" message.
- Documented `ollamaTimeout` in config.json.example and README.
Also closes two test gaps: the defensive `except ValueError` handler in
hcatOllama now has coverage, and the misleadingly-named `test_unknown_mode_raises`
is split into explicit unit tests of _build_request's mode validation. Ticked the
completed steps in the LLM Atomic Agents plan doc.
Co-Authored-By: Claude <noreply@anthropic.com>
Shard Wordlist (option 7) now prompts for an output base path and a shard
count, then writes all N interleaved parts as base.001..base.00N in a single
pass instead of one file per modulus/offset invocation. Matches the intended
distributed-cracking workflow: split once, copy one part per node.
Updates tests and README usage docs; bumps CHANGELOG to 2.10.11.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidate the release notes into a single dedicated CHANGELOG.md
(Keep a Changelog format) and replace the README's inline Version
History with a pointer, so the two no longer drift.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add HashviewAPI.list_rules and download_rules wrapping the Hashview
rule endpoints. The server gzip-compresses plaintext rules on the
fly, so download_rules decompresses before saving, yielding a file
usable directly with hashcat -r. Wired into the interactive Hashview
menu (Download Rule) and the CLI (hashview download-rules
--rules-id/--output). Unknown rule ids surface the real HTTP 404.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Old clones made before the default-branch rename sit on a local `master`
whose upstream still points at the deleted refs/heads/master, so both a
bare `git pull` and `_run_upgrade()`'s `git checkout main` failed on stale
clones. The updater now fetches origin first, checks out main via
`git checkout -B main origin/main`, repairs the upstream to origin/main,
and pulls explicitly with `git pull origin main` so it never consults the
dangling branch.*.merge config. Existing dirty-branch and detached-HEAD
guards are unchanged.
Adds tests covering the renamed-clone migration and the fetch-failure bail.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The live Hashview tests previously could only run against a remote server
configured in config.json, and the CLI ignored env-var overrides, so the
suite always hit prod. Prod runs an older Hashview whose /v1/jobs/add 500s
on a notify_email kwarg, which masked real client/server drift.
Wire the suite up to a local Hashview docker stack:
- main.py: HASHVIEW_URL / HASHVIEW_API_KEY env vars now override config.json,
so the CLI can be pointed at a local instance without editing config.
- tests/_hashview_local.py + pytest_configure: when HASHVIEW_TEST_LOCAL=1,
bring up + seed the Hashview compose stack (HASHVIEW_REPO), verify
authenticated API access, export the HASHVIEW_* env, and tear down
(HASHVIEW_KEEP=1 keeps it). Runs in configure so collection-time skipif
on HASHVIEW_TEST_REAL sees the exported env.
- tests/hashview_local_seed.py: seed an admin api_key + non-default password
(else Hashview's setup guard redirects every request to /setup), a
customer, a hashfile, and cracked "effective task" data (else /v1/jobs/add
has no tasks to schedule).
- api.py: download_left_hashes now uses GET /v1/hashfiles/<id>; the old
/v1/hashfiles/<id>/left route 404s on current servers.
- subprocess tests: assert on the CLI's actual output ("Job ID:") rather than
the literal "Job created", and strip ambient HASHVIEW_* creds from the
no-key-configured check so the env override doesn't defeat it.
- README/CLAUDE: document HASHVIEW_TEST_LOCAL and the env overrides.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The hashfile-listing route (/v1/hashfiles/hash_type/<type>) only exists
on Hashview builds from 2026-06-08+ (v0.8.3-dev). On `main` and older
servers it 404s, which surfaced as "Could not list hashfiles" and a dead
end. The download flow is now version-adaptive:
- get_all_customer_hashfiles stops sweeping after the first 404 (the
listing endpoint is absent) instead of probing every common type.
- The menu treats listing as best-effort: when no listing is available
it prints a clear note and accepts a hashfile ID typed directly (read
from the web UI), with no list-membership check. The chosen file's
type is resolved via GET /v1/getHashType/<id> and downloaded via
GET /v1/hashfiles/<id> -- both present on all server versions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Downloading previously required knowing/typing the hash type because
Hashview only lists hashfiles per type. The download flow now sweeps
common hashcat modes (HashviewAPI.get_all_customer_hashfiles) and shows
all of the customer's uploaded hashfiles to pick from, deduped by id.
A manual hash-type prompt remains as a fallback only when nothing is
found among the common set (uncommon modes). The chosen file's real
hash type still drives the download.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The download-hashes flow scoped the customer hashfile listing to
hcatHashType, but that is None when the Hashview menu is entered
without a hashfile loaded this session -> "No hashfiles of type None
found". Since Hashview only lists hashfiles per type, prompt for a
hashcat hash-type when none is set (Q cancels back to the menu) and
use it for the listing. Downstream selection still derives the real
type from the chosen hashfile.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Continues the v0.8.3-dev API alignment beyond the hashfile-listing 404:
- download_left_hashes: "left" hashes are GET /v1/hashfiles/<id>
(returns uncracked ciphertexts), not the nonexistent /<id>/left.
- delete_job: DELETE /v1/jobs/<id>, not GET /v1/jobs/delete/<id>.
- start_job: POST /v1/jobs/start/<id>, not GET (route is POST-only).
- stop_job: Hashview has no stop route; raise NotImplementedError
pointing at delete_job rather than calling a phantom endpoint.
- found-hash bulk export: no such route exists (only single-hash
POST /v1/search); the best-effort merge is documented and degrades
gracefully on the expected 404.
Adds offline tests asserting start_job POSTs, delete_job uses DELETE,
and stop_job raises. Documents the release in README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Covers the two fingerprint-attack bugfixes shipping in this patch:
the empty-.expanded guard and the LC_ALL=C sort locale fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move simple-term-menu from the [tui] optional extra into the main
dependencies list so arrow-key menu navigation works out of the box.
Users can still opt into the plain numbered menu with
HATE_CRACK_PLAIN_MENU=1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Transmission's watch dir scanner runs every ~10s, so a 10s deadline
could expire before the first scan completes. 30s gives 2-3 scan
cycles of headroom.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prevent implicit None return when no data row matches the expected
transmission-remote --info-files format. Also clarify README macOS
install note.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Delete wordlists/kill_transmission.sh (replaced by Python extraction).
Update Makefile, Dockerfile.test, lima config, README, TESTING.md, and
test_dependencies.py to reference transmission-daemon/transmission-remote
instead of transmission-cli.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
Add menu option 21 (Combipow Passphrase Attack) using combipow.bin from
hashcat-utils. Generates all unique non-empty subset combinations from a
short wordlist and pipes them into hashcat stdin.
- hcatCombipow(hash_type, hash_file, wordlist, use_space_sep) in main.py
- combipow_crack handler in attacks.py validates file, enforces 63-line
limit, prompts for space separator, then delegates to hcatCombipow
- Wired into get_main_menu_items() and get_main_menu_options() in both
main.py and hate_crack.py
- 12 tests covering menu presence, handler delegation, line-count
enforcement, file validation, and subprocess flag construction
Extends the combinator submenu (option 6) with two new attacks using
hashcat-utils binaries that were already compiled but unused.
- hcatCombinator3: 3-way wordlist combination via combinator3.bin piped
to hashcat stdin
- hcatCombinatorX: 2-8 wordlist combination via combinatorX.bin with
optional --sepFill separator, piped to hashcat stdin
- combinator3_crack handler: prompts for 3 comma-separated wordlist paths
- combinatorX_crack handler: prompts for 2-8 paths plus optional separator
- combinator_submenu updated with options 5 and 6
Closes#84, closes#85
Adds Permutation Attack (menu option 19) that generates all character
permutations of each word in a targeted wordlist and pipes them to
hashcat via permute.bin from hashcat-utils.
- hcatPermute() in main.py: pipes permute.bin < wordlist | hashcat
- permute_crack() in attacks.py: prompts for single wordlist file with
factorial-growth warning, tab-autocomplete support
- Menu option 19 wired in both main.py and hate_crack.py
- hcatPermuteCount tracking alongside other count globals
- Tests: test_permute_attack.py (handler behavior) and
test_permute_wrapper.py (subprocess wiring)
- README: added entry in menu listing and attack descriptions
- Add hcatGenerateRules() in main.py: runs generate-rules.bin to
produce N random rules, writes them to a temp file, runs hashcat
with -r against a chosen wordlist, cleans up on exit
- Add generate_rules_crack() handler in attacks.py with count
prompt (default 65536), wordlist picker with tab-completion,
input validation, and abort on invalid input
- Add dispatcher generate_rules_crack() in main.py and key "20"
in both main.py and hate_crack.py get_main_menu_options()
- Add ("20", "Random Rules Attack") to get_main_menu_items()
- Add tests: test_random_rules_attack.py (menu presence, handler
wiring), test_random_rules_wrapper.py (subprocess behavior,
cleanup, count passing, count tracking)
- Add key "20" to MENU_OPTION_TEST_CASES in test_ui_menu_options.py
- Update README: add option 20 to menu listing, add attack
description, add version history entry
Adds Rule File Tools submenu (menu option 81) with three operations:
- Clean: removes invalid/duplicate rules via cleanup-rules.bin
- Optimize: consolidates redundant operations via rules_optimize.bin
- Clean and optimize: both in sequence with temp file handling
Wired through the standard three-layer pattern: main.py utility
functions + dispatcher, attacks.py handlers + submenu, root
hate_crack.py menu registration.
- Update menu listing to show combinator submenu and new attacks (17, 18)
- Remove keys 10/11/12 from main menu (consolidated into submenu)
- Add detailed descriptions for Combinator Attacks submenu, Ad-hoc Mask Attack, and Markov Brute Force Attack
- Document Markov training source selection and table persistence
- Update version history with new feature details
Config and assets were not found when running hate_crack from outside the
repo directory. The shim uses `uv run --directory` to always execute from
the repo root, and _candidate_roots() now includes _repo_root and
_package_path as fallback search locations.
- Remove vendor-assets and clean-vendor Makefile targets
- Lima VM test and Dockerfile.test now use make install (editable)
- Remove hate_crack/hashcat-utils from submodules-pre expander generation
- Update README to reflect no-vendoring install