diff --git a/CHANGELOG.md b/CHANGELOG.md index 40d5b77..bfdc581 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Dates are omitted for releases predating this file; see the git tags for exact timing. +## [2.11.3] - 2026-07-24 + +### Fixed + +- **Rule file cleanup no longer errors out.** `rules_cleanup` (Rule File Tools → "Clean + rule file" / "Clean and optimize") invoked `cleanup-rules.bin` with no arguments, but the + binary requires a `mode` argument (`1` = CPU, `2` = GPU) and exits with usage text + otherwise — so cleanup always failed. It now passes the mode (defaulting to GPU) so the + cleanup actually runs. + ## [2.11.2] - 2026-07-23 ### Added diff --git a/hate_crack/main.py b/hate_crack/main.py index 53a6dbf..7734aec 100755 --- a/hate_crack/main.py +++ b/hate_crack/main.py @@ -4261,11 +4261,16 @@ def wordlist_tools_submenu(): return _attacks.wordlist_tools_submenu(_attack_ctx()) -def rules_cleanup(infile: str, outfile: str) -> bool: - """Clean a rule file using cleanup-rules.bin. Returns True on success.""" +def rules_cleanup(infile: str, outfile: str, mode: int = 2) -> bool: + """Clean a rule file using cleanup-rules.bin. Returns True on success. + + cleanup-rules.bin requires a ``mode`` argument (1 = CPU, 2 = GPU) and exits + with usage text if it is omitted. Defaults to GPU (2), which strips rules + hashcat cannot run on the GPU. + """ cleanup_path = os.path.join(hate_path, "hashcat-utils", "bin", "cleanup-rules.bin") with open(infile, "rb") as fin, open(outfile, "wb") as fout: - result = subprocess.run([cleanup_path], stdin=fin, stdout=fout) + result = subprocess.run([cleanup_path, str(mode)], stdin=fin, stdout=fout) return result.returncode == 0 diff --git a/tests/test_rule_wrappers.py b/tests/test_rule_wrappers.py index e7be6c4..0f6eff1 100644 --- a/tests/test_rule_wrappers.py +++ b/tests/test_rule_wrappers.py @@ -34,6 +34,23 @@ class TestRulesCleanupWrapper: call_args = mock_run.call_args cmd = call_args[0][0] assert cmd[0].endswith("cleanup-rules.bin") + # cleanup-rules.bin requires a mode arg (1=CPU, 2=GPU) or it errors out + assert cmd[1] == "2" + + def test_passes_custom_mode(self, tmp_path): + main = _load_main() + infile = tmp_path / "input.rule" + infile.write_text("l\n") + outfile = tmp_path / "output.rule" + + fake_result = MagicMock() + fake_result.returncode = 0 + + with patch("subprocess.run", return_value=fake_result) as mock_run: + main.rules_cleanup(str(infile), str(outfile), mode=1) + + cmd = mock_run.call_args[0][0] + assert cmd[1] == "1" def test_returns_false_on_nonzero_exit(self, tmp_path): main = _load_main()