secrets-engine wrap writes a single-use OpenBao wrap token to a mode-0600 out-of-repo file and never prints it. KV reads and AppRole secret_ids are wrapped with a 15m TTL cap. Unwrapped secret payloads fail closed. Production wrap remains fail-closed. Assistant: grok Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
196 lines
7.8 KiB
Python
196 lines
7.8 KiB
Python
"""Layer-model conformance (security-layer-model_v0.7 §3.3, §6, §11).
|
|
|
|
The declaration must be this repository's own, machine-readable, and equal to
|
|
the shipped production fail-closed gate. A published stance map that may drift
|
|
from the code is worse than none.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
from secrets_engine.catalog import validate_entry
|
|
from secrets_engine.cli import _require_lane_approval
|
|
from secrets_engine.errors import DecisionError
|
|
from secrets_engine.evidence_class import SHIPPED_RULES, classify, load_classification_rules
|
|
from secrets_engine.pep_stance import SHIPPED_STANCE, load_pep_stance
|
|
|
|
from tests.test_catalog import VALID
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SCRIPT = ROOT / "scripts" / "check_layer_conformance.py"
|
|
DECL = ROOT / "layer.yaml"
|
|
STANCE = ROOT / "pep-stance.yaml"
|
|
INTENT = ROOT / "INTENT.md"
|
|
CLASSIFICATION = ROOT / "evidence-classification.yaml"
|
|
|
|
|
|
def _decl() -> dict:
|
|
return yaml.safe_load(DECL.read_text(encoding="utf-8"))
|
|
|
|
|
|
def _stance() -> dict:
|
|
return yaml.safe_load(STANCE.read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_declares_engine_lifecycle_in_its_own_voice():
|
|
assert DECL.exists(), "no layer.yaml — §11 requires a machine-readable declaration"
|
|
data = _decl()
|
|
assert data["repository"] == "secrets-engine"
|
|
assert data["layer"] == "engine"
|
|
assert data["role"] == "lifecycle"
|
|
assert data["declared_by"] == "INTENT.md"
|
|
assert data["decision_surfaces_exposed"] == "none"
|
|
assert data["pep_shaped"] is True
|
|
|
|
|
|
def test_intent_frontmatter_matches_declaration():
|
|
text = INTENT.read_text(encoding="utf-8")
|
|
assert text.startswith("---"), "companion §2 requires INTENT.md frontmatter"
|
|
end = text.find("\n---", 3)
|
|
front = yaml.safe_load(text[3:end])
|
|
assert front["layer"] == "Engine"
|
|
assert front["role"] == "Lifecycle"
|
|
assert front["declaration"] == "layer.yaml"
|
|
assert front["pep_stance"] == "pep-stance.yaml"
|
|
|
|
|
|
def test_checker_passes_on_the_real_tree():
|
|
result = subprocess.run(
|
|
[sys.executable, str(SCRIPT)],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
|
|
|
|
def test_proposed_capabilities_carry_gap_record_fields():
|
|
for cap in _decl()["proposed_capabilities"]:
|
|
for field in ("capability", "intended_owner", "blocked_on", "review", "state"):
|
|
assert cap.get(field), f"{cap.get('id')} missing {field}"
|
|
assert cap["state"] == "unowned-capability"
|
|
assert cap["owner_status"] == "proposed"
|
|
assert cap.get("contract"), f"{cap.get('id')} missing contract"
|
|
|
|
|
|
def test_stance_map_is_total_over_catalog_stages():
|
|
stance = _stance()["stance"]
|
|
required = {"build", "test", "prod", "unknown"}
|
|
assert required <= set(stance), f"stance not total; missing {required - set(stance)}"
|
|
assert set(stance.values()) <= {"fail_open", "fail_closed"}
|
|
assert stance["prod"] == "fail_closed"
|
|
assert stance["unknown"] == "fail_closed"
|
|
assert _stance()["verdict_caching"] == "none"
|
|
|
|
|
|
def test_published_map_equals_shipped_constant_and_loader():
|
|
"""Changing the YAML without changing SHIPPED_STANCE fails, and the reverse."""
|
|
published = _stance()["stance"]
|
|
loaded = load_pep_stance().stance
|
|
assert published == SHIPPED_STANCE
|
|
assert loaded == SHIPPED_STANCE
|
|
|
|
|
|
def test_published_prod_stance_equals_shipped_fail_closed(monkeypatch):
|
|
"""Runtime reads pep-stance.yaml; prod fail_closed must equal the gate."""
|
|
assert load_pep_stance().stance["prod"] == "fail_closed"
|
|
entry = validate_entry(dict(VALID, stage="prod", approval={"model": "bootstrap-only"}))
|
|
cfg = SimpleNamespace(hub_url="http://127.0.0.1:8000", bao_addr="http://127.0.0.1:8200")
|
|
monkeypatch.delenv("SECRETS_ENGINE_UNSAFE_DEMO", raising=False)
|
|
with pytest.raises(DecisionError, match="live production remains disabled") as raised:
|
|
_require_lane_approval(cfg, entry, "apply")
|
|
assert raised.value.stance["stance_stage"] == "prod"
|
|
assert raised.value.stance["stance_failure_mode"] == "fail_closed"
|
|
assert "stance_decision_id" not in raised.value.stance
|
|
|
|
|
|
def test_yaml_is_the_runtime_source(tmp_path, monkeypatch):
|
|
"""A published map the pin does not match is a test failure; runtime follows YAML."""
|
|
path = tmp_path / "pep-stance.yaml"
|
|
path.write_text(
|
|
yaml.safe_dump(
|
|
{
|
|
"stance": {
|
|
"build": "fail_open",
|
|
"test": "fail_open",
|
|
"prod": "fail_open",
|
|
"unknown": "fail_closed",
|
|
}
|
|
}
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
monkeypatch.setenv("SECRETS_ENGINE_PEP_STANCE", str(path))
|
|
entry = validate_entry(dict(VALID, stage="prod", approval={"model": "bootstrap-only"}))
|
|
cfg = SimpleNamespace(hub_url="http://127.0.0.1:8000", bao_addr="http://127.0.0.1:8200")
|
|
monkeypatch.delenv("SECRETS_ENGINE_UNSAFE_DEMO", raising=False)
|
|
# Runtime follows the YAML (prod is no longer the shipped fail_closed
|
|
# residue). GH-DEC-2026-003 still refuses OpenBao without CAS consume.
|
|
with pytest.raises(DecisionError, match="no durable consume binding"):
|
|
_require_lane_approval(cfg, entry, "apply")
|
|
|
|
|
|
def test_build_stage_is_not_the_production_fail_closed_gate(tmp_path, monkeypatch):
|
|
"""build is fail_open relative to access-engine: lane approval still applies."""
|
|
assert load_pep_stance().stance["build"] == "fail_open"
|
|
(tmp_path / ".decisions").mkdir()
|
|
(tmp_path / ".decisions" / "x.yaml").write_text(
|
|
"id: x\ntitle: approved\nstatus: resolved\nsuperseded_by: null\n"
|
|
)
|
|
import secrets_engine.cli as cli
|
|
|
|
monkeypatch.setattr(cli, "repo_root", lambda: tmp_path)
|
|
entry = validate_entry(
|
|
dict(
|
|
VALID,
|
|
stage="build",
|
|
path="build/team/thing",
|
|
approval={"model": "decision", "decision_ref": "x"},
|
|
)
|
|
)
|
|
cfg = SimpleNamespace(hub_url="", bao_addr="http://127.0.0.1:8200")
|
|
decision = _require_lane_approval(cfg, entry, "apply")
|
|
assert decision.id == "x"
|
|
|
|
|
|
def test_classification_yaml_equals_shipped_rules():
|
|
loaded = load_classification_rules()
|
|
assert tuple(rule["id"] for rule in loaded) == tuple(rule["id"] for rule in SHIPPED_RULES)
|
|
assert tuple(rule["kind"] for rule in loaded) == tuple(rule["kind"] for rule in SHIPPED_RULES)
|
|
|
|
|
|
def test_classify_does_not_grant_permission():
|
|
prod_provision = classify("provision", "prod")
|
|
test_provision = classify("provision", "test")
|
|
destroy = classify("lifecycle-destroy", "build")
|
|
apply_prod = classify("apply", "prod")
|
|
heartbeat = classify("evidence-heartbeat", "prod")
|
|
session_revoke = classify("session-revoke", "prod")
|
|
wrap = classify("wrap", "prod")
|
|
assert prod_provision.kind == "load-bearing"
|
|
assert test_provision.kind == "attributive"
|
|
assert destroy.kind == "load-bearing"
|
|
assert apply_prod.kind == "attributive"
|
|
assert heartbeat.kind == "heartbeat"
|
|
assert session_revoke.kind == "load-bearing"
|
|
assert wrap.kind == "load-bearing"
|
|
assert prod_provision.completeness_claimed is False
|
|
assert CLASSIFICATION.exists()
|
|
|
|
|
|
def test_proposed_contracts_exist_and_forbid_secret_material():
|
|
ssh = (ROOT / "docs/ssh-ca-signing-contract.md").read_text(encoding="utf-8")
|
|
secret_use = (ROOT / "docs/secret-use-evidence-contract.md").read_text(encoding="utf-8")
|
|
assert "proposed" in ssh.lower()
|
|
assert "proposed" in secret_use.lower()
|
|
assert "warden sign" in ssh
|
|
assert "private key" in ssh.lower() or "private keys" in ssh.lower()
|
|
assert "secret values" in secret_use.lower() or "secret value" in secret_use.lower()
|
|
assert "audit-core" in secret_use
|
|
assert "completeness is not claimed" in secret_use.lower()
|