diff --git a/hate_crack/noninteractive.py b/hate_crack/noninteractive.py index 154e615..9790fdb 100644 --- a/hate_crack/noninteractive.py +++ b/hate_crack/noninteractive.py @@ -10,7 +10,7 @@ import os 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. 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): raise FileNotFoundError(name) chain = f"{chain} -r {path}".strip() + if not chain: + raise ValueError(f"Rule token {token!r} resolved to no rule files") chains.append(chain) return chains diff --git a/tests/test_noninteractive.py b/tests/test_noninteractive.py index 194e801..3fd50e8 100644 --- a/tests/test_noninteractive.py +++ b/tests/test_noninteractive.py @@ -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): ctx = _ctx_with_rules(tmp_path, "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): ctx = _ctx_with_rules(tmp_path) - with pytest.raises(FileNotFoundError): + with pytest.raises(FileNotFoundError, match="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, ["+"])