import os import pytest from cryptography.exceptions import InvalidTag from app import audit from app.constants import CHECKMK_SECRET, OLLAMA_URL from app.crypto import SecretBox from app.db import Database from app.settings import SettingsStore @pytest.fixture def box(): return SecretBox(os.urandom(32)) @pytest.fixture def store(box): return SettingsStore(Database(":memory:"), box) def test_roundtrip(box): token = box.encrypt("geheim", "ctx") assert "geheim" not in token assert box.decrypt(token, "ctx") == "geheim" def test_context_mismatch_fails(box): token = box.encrypt("geheim", "a") with pytest.raises(InvalidTag): box.decrypt(token, "b") def test_tampered_token_fails(box): token = box.encrypt("geheim", "ctx") other = SecretBox(os.urandom(32)) with pytest.raises(InvalidTag): other.decrypt(token, "ctx") def test_plain_setting_roundtrip(store): store.set(OLLAMA_URL, "http://x:11434", user="admin") assert store.get(OLLAMA_URL) == "http://x:11434" assert store.get("missing", "dflt") == "dflt" def test_secret_encrypted_at_rest(store): store.set(CHECKMK_SECRET, "topsecret", user="admin", secret=True) raw = store._db.query("SELECT value, is_secret FROM settings WHERE key=?", (CHECKMK_SECRET,))[0] assert raw["is_secret"] == 1 assert "topsecret" not in raw["value"] assert store.get(CHECKMK_SECRET) == "topsecret" def test_audit_masks_secrets(store): store.set(CHECKMK_SECRET, "one", user="admin", secret=True) store.set(CHECKMK_SECRET, "two", user="admin", secret=True) entries = audit.recent(store._db) dumped = str(entries) assert "one" not in dumped and "two" not in dumped assert entries[0]["old_value"] == "***" and entries[0]["new_value"] == "***" assert entries[1]["old_value"] is None and entries[1]["new_value"] == "***" def test_audit_records_plain_change(store): store.set(OLLAMA_URL, "http://a", user="admin") store.set(OLLAMA_URL, "http://b", user="admin") latest = audit.recent(store._db)[0] assert (latest["old_value"], latest["new_value"], latest["user"]) == ("http://a", "http://b", "admin")