39 lines
1.4 KiB
Python
39 lines
1.4 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib
|
|
|
|
|
|
def test_redaction_removes_nested_secret_fields_and_canaries() -> None:
|
|
redaction = importlib.import_module("backup_tool.security.redaction")
|
|
canary = "canary-a96f"
|
|
value = {
|
|
"username": "operator",
|
|
"password": canary,
|
|
"nested": {"token": canary, "safe": "visible"},
|
|
"items": [{"secret": canary}, canary],
|
|
}
|
|
result = redaction.redact(value, canaries=(canary,))
|
|
rendered = repr(result)
|
|
assert canary not in rendered
|
|
assert result["username"] == "operator"
|
|
assert result["nested"]["safe"] == "visible"
|
|
assert result["password"] == "[REDACTED]"
|
|
|
|
|
|
def test_envelope_cipher_round_trips_with_purpose_binding(tmp_path) -> None:
|
|
secrets = importlib.import_module("backup_tool.security.secrets")
|
|
key_path = tmp_path / "master.key"
|
|
key_path.write_bytes(b"x" * 32)
|
|
cipher = secrets.EnvelopeCipher.from_file(key_path)
|
|
ciphertext, key_id = cipher.encrypt("sensitive", purpose="ssh", version=1)
|
|
assert b"sensitive" not in ciphertext
|
|
assert cipher.decrypt(ciphertext, purpose="ssh", version=1) == "sensitive"
|
|
assert key_id
|
|
|
|
try:
|
|
cipher.decrypt(ciphertext, purpose="database", version=1)
|
|
except Exception as error:
|
|
assert error.__class__.__name__ == "InvalidTag"
|
|
else: # pragma: no cover - required safety assertion
|
|
raise AssertionError("ciphertext accepted under a different purpose")
|