"""Tests for killswitch — verifies emergency purge destroys all sensitive data.""" from __future__ import annotations import hashlib from pathlib import Path import pytest from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.serialization import ( Encoding, NoEncryption, PrivateFormat, PublicFormat, ) @pytest.fixture() def populated_soosef(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: """Create a populated ~/.soosef directory with identity, chain, attestations, etc.""" import soosef.paths as paths data_dir = tmp_path / ".soosef" data_dir.mkdir() monkeypatch.setattr(paths, "BASE_DIR", data_dir) # Create identity identity_dir = data_dir / "identity" identity_dir.mkdir() key = Ed25519PrivateKey.generate() priv_pem = key.private_bytes(Encoding.PEM, PrivateFormat.PKCS8, NoEncryption()) (identity_dir / "private.pem").write_bytes(priv_pem) pub_pem = key.public_key().public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo) (identity_dir / "public.pem").write_bytes(pub_pem) # Create channel key stegasoo_dir = data_dir / "stegasoo" stegasoo_dir.mkdir() (stegasoo_dir / "channel.key").write_text("test-channel-key") # Create chain data from soosef.federation.chain import ChainStore chain_dir = data_dir / "chain" store = ChainStore(chain_dir) for i in range(3): store.append(hashlib.sha256(f"c-{i}".encode()).digest(), "test/plain", key) # Create attestation dir with a dummy file att_dir = data_dir / "attestations" att_dir.mkdir() (att_dir / "log.bin").write_bytes(b"dummy attestation data") # Create other dirs (data_dir / "auth").mkdir() (data_dir / "auth" / "soosef.db").write_bytes(b"dummy db") (data_dir / "temp").mkdir() (data_dir / "temp" / "file.tmp").write_bytes(b"tmp") (data_dir / "instance").mkdir() (data_dir / "instance" / ".secret_key").write_bytes(b"secret") (data_dir / "config.json").write_text("{}") return data_dir def test_purge_all_destroys_chain_data(populated_soosef: Path): """CRITICAL: execute_purge(ALL) must destroy chain directory.""" from soosef.fieldkit.killswitch import PurgeScope, execute_purge chain_dir = populated_soosef / "chain" assert chain_dir.exists() assert (chain_dir / "chain.bin").exists() result = execute_purge(PurgeScope.ALL, reason="test") assert not chain_dir.exists(), "Chain directory must be destroyed by killswitch" assert "destroy_chain_data" in result.steps_completed def test_purge_all_destroys_identity(populated_soosef: Path): """execute_purge(ALL) must destroy identity keys.""" from soosef.fieldkit.killswitch import PurgeScope, execute_purge assert (populated_soosef / "identity" / "private.pem").exists() result = execute_purge(PurgeScope.ALL, reason="test") assert not (populated_soosef / "identity").exists() assert "destroy_identity_keys" in result.steps_completed def test_purge_all_destroys_attestation_log(populated_soosef: Path): """execute_purge(ALL) must destroy the Verisoo attestation log.""" from soosef.fieldkit.killswitch import PurgeScope, execute_purge result = execute_purge(PurgeScope.ALL, reason="test") assert not (populated_soosef / "attestations").exists() assert "destroy_attestation_log" in result.steps_completed def test_purge_keys_only_preserves_chain(populated_soosef: Path): """KEYS_ONLY purge destroys keys but preserves chain and attestation data.""" from soosef.fieldkit.killswitch import PurgeScope, execute_purge result = execute_purge(PurgeScope.KEYS_ONLY, reason="test") # Keys gone assert not (populated_soosef / "identity").exists() assert "destroy_identity_keys" in result.steps_completed # Chain and attestations preserved (KEYS_ONLY doesn't touch data) assert (populated_soosef / "chain" / "chain.bin").exists() assert (populated_soosef / "attestations" / "log.bin").exists() def test_purge_reports_all_steps(populated_soosef: Path): """execute_purge(ALL) reports all expected steps including chain.""" from soosef.fieldkit.killswitch import PurgeScope, execute_purge result = execute_purge(PurgeScope.ALL, reason="test") expected_steps = [ "destroy_identity_keys", "destroy_channel_key", "destroy_flask_secret", "destroy_auth_db", "destroy_attestation_log", "destroy_chain_data", "destroy_temp_files", "destroy_config", ] for step in expected_steps: assert step in result.steps_completed, f"Missing purge step: {step}"