import copy import os import pytest from secrets_engine.catalog import validate_entry from secrets_engine.errors import ProvisioningError from secrets_engine.openbao import read_strict_token_file from secrets_engine.verify import verify_negative from tests.test_catalog import VALID def _entry(): return validate_entry(copy.deepcopy(VALID)) class NegativeClient: def __init__(self, can_read): self.can_read = can_read self.tokens = [] def kv_can_read(self, _mount, _path, *, token): self.tokens.append(token) return self.can_read def test_negative_check_fails_closed_without_real_unrelated_token(): client = NegativeClient(can_read=False) result = verify_negative(client, _entry(), unrelated_token=None) assert result.passed is False assert "not proven" in result.detail["reason"] assert client.tokens == [] def test_negative_check_uses_real_token_and_detects_policy_overlap(): denied = NegativeClient(can_read=False) assert verify_negative( denied, _entry(), unrelated_token="test-unrelated-token" ).passed assert denied.tokens == ["test-unrelated-token"] overlapping = NegativeClient(can_read=True) result = verify_negative( overlapping, _entry(), unrelated_token="test-overlapping-token" ) assert result.passed is False assert "LEAK RISK" in result.detail["reason"] def test_negative_token_file_requires_mode_0600(tmp_path): path = tmp_path / "unrelated.token" path.write_text("test-unrelated-token", encoding="utf-8") os.chmod(path, 0o644) with pytest.raises(ProvisioningError, match="must be 0600"): read_strict_token_file(path, purpose="negative verification token") os.chmod(path, 0o600) assert ( read_strict_token_file(path, purpose="negative verification token") == "test-unrelated-token" )