refactor: harden rule-chain builder (types, empty-token guard, stronger tests)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Justin Bollinger
2026-07-24 19:59:22 -04:00
co-authored by Claude Sonnet 4.6
parent d2e45afb4a
commit db374edda2
2 changed files with 13 additions and 3 deletions
+3 -1
View File
@@ -10,7 +10,7 @@ import os
from typing import Any from typing import Any
def build_rule_chains(ctx: Any, rule_tokens): def build_rule_chains(ctx: Any, rule_tokens: list[str] | None) -> list[str]:
"""Convert CLI ``--rules`` tokens into hashcat ``-r`` chain strings. """Convert CLI ``--rules`` tokens into hashcat ``-r`` chain strings.
Each token becomes one attack pass. A token may chain multiple rule files Each token becomes one attack pass. A token may chain multiple rule files
@@ -34,5 +34,7 @@ def build_rule_chains(ctx: Any, rule_tokens):
if not os.path.isfile(path): if not os.path.isfile(path):
raise FileNotFoundError(name) raise FileNotFoundError(name)
chain = f"{chain} -r {path}".strip() chain = f"{chain} -r {path}".strip()
if not chain:
raise ValueError(f"Rule token {token!r} resolved to no rule files")
chains.append(chain) chains.append(chain)
return chains return chains
+10 -2
View File
@@ -38,10 +38,18 @@ def test_build_rule_chains_chained_token(tmp_path):
def test_build_rule_chains_multiple_tokens_are_separate_passes(tmp_path): def test_build_rule_chains_multiple_tokens_are_separate_passes(tmp_path):
ctx = _ctx_with_rules(tmp_path, "best64.rule", "d3ad0ne.rule") ctx = _ctx_with_rules(tmp_path, "best64.rule", "d3ad0ne.rule")
chains = ni.build_rule_chains(ctx, ["best64.rule", "d3ad0ne.rule"]) chains = ni.build_rule_chains(ctx, ["best64.rule", "d3ad0ne.rule"])
assert len(chains) == 2 a = os.path.join(ctx.rulesDirectory, "best64.rule")
b = os.path.join(ctx.rulesDirectory, "d3ad0ne.rule")
assert chains == [f"-r {a}", f"-r {b}"]
def test_build_rule_chains_missing_file_raises(tmp_path): def test_build_rule_chains_missing_file_raises(tmp_path):
ctx = _ctx_with_rules(tmp_path) ctx = _ctx_with_rules(tmp_path)
with pytest.raises(FileNotFoundError): with pytest.raises(FileNotFoundError, match="nope.rule"):
ni.build_rule_chains(ctx, ["nope.rule"]) ni.build_rule_chains(ctx, ["nope.rule"])
def test_build_rule_chains_empty_token_raises(tmp_path):
ctx = _ctx_with_rules(tmp_path, "best64.rule")
with pytest.raises(ValueError):
ni.build_rule_chains(ctx, ["+"])