Merge pull request #117 from trustedsec/fix/cleanup-rules-mode

fix(rules): pass required mode arg to cleanup-rules.bin (2.11.3)
This commit is contained in:
Justin Bollinger
2026-07-24 11:29:20 -04:00
committed by GitHub
3 changed files with 35 additions and 3 deletions
+10
View File
@@ -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
+8 -3
View File
@@ -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
+17
View File
@@ -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()