"""Tests for Workload Security Posture descriptors + lattice (WP-0015 T2).""" from __future__ import annotations import json from pathlib import Path import pytest import yaml from typer.testing import CliRunner from warden.cli import app from warden.posture import PostureError, load_posture runner = CliRunner() def _repo_posture() -> Path: return Path(__file__).resolve().parents[1] / "registry" / "policy" / "security-posture.yaml" # --- real descriptors load + shape ----------------------------------------- def test_real_descriptors_load(): c = load_posture(_repo_posture()) assert {e.id for e in c.env_postures} == {"dev", "test", "prod"} assert {m.id for m in c.maturity_levels} == {"M0", "M1", "M2", "M3"} assert c.requires_env_posture == "prod" # YAML `on` gotcha must not have become a boolean assert c.env("test").audit == "on" # --- the secret-flow lattice ----------------------------------------------- def test_lattice_allows_matched_prod_workload(): c = load_posture(_repo_posture()) ok, why = c.can_deliver( workload_env="prod", workload_maturity="M3", secret_required_maturity="M3", secret_dataclass="restricted", ) assert ok and why == [] def test_lattice_denies_below_required_maturity(): c = load_posture(_repo_posture()) ok, why = c.can_deliver( workload_env="prod", workload_maturity="M1", secret_required_maturity="M3", secret_dataclass="restricted", ) assert not ok assert any("maturity M1 < required M3" in r for r in why) assert any("floor M3" in r for r in why) def test_lattice_denies_non_prod_posture(): c = load_posture(_repo_posture()) ok, why = c.can_deliver( workload_env="test", workload_maturity="M3", secret_required_maturity="M1", secret_dataclass="internal", ) assert not ok and any("env posture" in r for r in why) def test_lattice_unknown_maturity_raises(): c = load_posture(_repo_posture()) with pytest.raises(PostureError, match="unknown maturity"): c.can_deliver( workload_env="prod", workload_maturity="M9", secret_required_maturity="M1", ) # --- validation ------------------------------------------------------------ def _write(tmp_path, data) -> Path: p = tmp_path / "security-posture.yaml" p.write_text(yaml.dump(data)) return p def _valid_data() -> dict: return { "version": 1, "env_postures": [ {"id": "dev", "rank": 0, "backend": "m", "real_values": "f", "unseal": "n", "real_user_data": "never", "audit": "optional"}, {"id": "prod", "rank": 1, "backend": "b", "real_values": "g", "unseal": "s", "real_user_data": "allowed", "audit": "full"}, ], "maturity_levels": [ {"id": "M0", "rank": 0, "phase": "poc", "max_dataclass": "synthetic", "promotion_gate": []}, {"id": "M1", "rank": 1, "phase": "ga", "max_dataclass": "internal", "promotion_gate": ["x"]}, ], "dataclass_floor": {"synthetic": "M0", "internal": "M1"}, "lattice": {"requires_env_posture": "prod", "rule": "no-write-down"}, } def test_valid_minimal_loads(tmp_path): c = load_posture(_write(tmp_path, _valid_data())) assert c.requires_env_posture == "prod" def test_non_contiguous_ranks_rejected(tmp_path): data = _valid_data() data["maturity_levels"][1]["rank"] = 5 with pytest.raises(PostureError, match="contiguous"): load_posture(_write(tmp_path, data)) def test_dataclass_floor_unknown_level_rejected(tmp_path): data = _valid_data() data["dataclass_floor"]["internal"] = "M9" with pytest.raises(PostureError, match="not a known maturity level"): load_posture(_write(tmp_path, data)) def test_lattice_requires_known_env_posture(tmp_path): data = _valid_data() data["lattice"]["requires_env_posture"] = "staging" with pytest.raises(PostureError, match="not an env posture"): load_posture(_write(tmp_path, data)) # --- CLI ------------------------------------------------------------------- def test_cli_policy_list(monkeypatch): monkeypatch.setenv("WARDEN_POSTURE_CATALOG", str(_repo_posture())) r = runner.invoke(app, ["policy", "list"]) assert r.exit_code == 0 assert "environment posture" in r.stdout and "workload maturity" in r.stdout def test_cli_policy_list_json(monkeypatch): monkeypatch.setenv("WARDEN_POSTURE_CATALOG", str(_repo_posture())) r = runner.invoke(app, ["policy", "list", "--json"]) payload = json.loads(r.stdout) assert payload["requires_env_posture"] == "prod" assert len(payload["maturity_levels"]) == 4 def test_cli_policy_show_unknown_exits_1(monkeypatch): monkeypatch.setenv("WARDEN_POSTURE_CATALOG", str(_repo_posture())) r = runner.invoke(app, ["policy", "show", "nope"]) assert r.exit_code == 1