diff --git a/tests/unit/test_cli_set_pin.py b/tests/unit/test_cli_set_pin.py new file mode 100644 index 0000000..9b6888e --- /dev/null +++ b/tests/unit/test_cli_set_pin.py @@ -0,0 +1,31 @@ +"""Tests for `vigilar config set-pin`.""" + +from click.testing import CliRunner + +from vigilar.alerts.pin import verify_pin +from vigilar.cli.cmd_config import config_cmd + + +def test_set_pin_outputs_pbkdf2_hash_under_security_section(): + """The CLI must emit a hash that alerts.pin.verify_pin can validate, + and direct the user to [security] pin_hash (not [system] arm_pin_hash).""" + runner = CliRunner() + result = runner.invoke(config_cmd, ["set-pin"], input="1234\n1234\n") + + assert result.exit_code == 0, result.output + # The output must direct the user to the [security] section + assert "[security]" in result.output + assert "arm_pin_hash" not in result.output + assert "pin_hash" in result.output + + # Extract the emitted hash (line starts with `pin_hash = "..."`) + hash_line = next( + line for line in result.output.splitlines() if line.strip().startswith("pin_hash") + ) + hash_value = hash_line.split('"')[1] + + # Round-trip: the emitted hash must accept the plaintext PIN + assert verify_pin("1234", hash_value) is True + assert verify_pin("0000", hash_value) is False + # And it must be in PBKDF2 format (not the legacy HMAC "secret:mac" format) + assert hash_value.startswith("pbkdf2_sha256$") diff --git a/vigilar/cli/cmd_config.py b/vigilar/cli/cmd_config.py index b4a0a9f..17bed2a 100644 --- a/vigilar/cli/cmd_config.py +++ b/vigilar/cli/cmd_config.py @@ -47,8 +47,8 @@ def show_cmd(config_path: str | None) -> None: # Redact sensitive fields if data.get("web", {}).get("password_hash"): data["web"]["password_hash"] = "***" - if data.get("system", {}).get("arm_pin_hash"): - data["system"]["arm_pin_hash"] = "***" + if data.get("security", {}).get("pin_hash"): + data["security"]["pin_hash"] = "***" if data.get("alerts", {}).get("webhook", {}).get("secret"): data["alerts"]["webhook"]["secret"] = "***" click.echo(json.dumps(data, indent=2)) @@ -82,14 +82,10 @@ def set_password_cmd(config_path: str | None) -> None: @config_cmd.command("set-pin") def set_pin_cmd() -> None: - """Generate an HMAC hash for the arm/disarm PIN.""" - import hashlib - import hmac - import os + """Generate a PBKDF2 hash for the arm/disarm PIN.""" + from vigilar.alerts.pin import hash_pin pin = click.prompt("Enter arm/disarm PIN", hide_input=True, confirmation_prompt=True) - secret = os.urandom(32) - mac = hmac.new(secret, pin.encode(), hashlib.sha256).hexdigest() - hash_str = secret.hex() + ":" + mac - click.echo(f"\nAdd this to your vigilar.toml [system] section:") - click.echo(f'arm_pin_hash = "{hash_str}"') + hash_str = hash_pin(pin) + click.echo("\nAdd this to your vigilar.toml [security] section:") + click.echo(f'pin_hash = "{hash_str}"')