Record privileged action failure evidence
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
This commit is contained in:
tegwick 2026-08-23 12:58:12 +02:00
parent c4504c6de9
commit f579f3761c
8 changed files with 517 additions and 189 deletions

View file

@ -112,7 +112,14 @@ def test_verify_defaults_to_every_declared_field_and_one_path_denial(
("webhook_secret", True, False),
("api_token", False, True),
]
assert len(records) == 3
assert len(records) == 5
assert [record[1]["result"] for record in records] == [
"attempt",
"positive:pass",
"positive:pass",
"negative:pass",
"pass",
]
def test_live_destroy_fails_before_approval_or_backend_until_action_contract(

View file

@ -0,0 +1,98 @@
import copy
import json
from types import SimpleNamespace
import pytest
from secrets_engine import cli
from secrets_engine.catalog import validate_entry
from secrets_engine.config import Config
from secrets_engine.errors import BackendError, DecisionError
from tests.test_catalog import VALID
def _config(tmp_path):
return Config(
catalog_dir=tmp_path,
policy_dir=tmp_path,
evidence_dir=tmp_path / "evidence",
hub_url="",
bao_addr="http://127.0.0.1:8200",
topic_id="test-topic",
)
def _records(tmp_path):
path = next((tmp_path / "evidence").glob("evidence-*.jsonl"))
return [json.loads(line) for line in path.read_text().splitlines()]
def _provision_args(entry):
return SimpleNamespace(
catalog_id=entry.id,
stage=entry.stage,
field="api_token",
generate=False,
from_file="/tmp/test-value-file",
bootstrap_token_file=None,
)
def test_provision_backend_exception_has_attempt_and_terminal_evidence(
tmp_path, monkeypatch
):
entry = validate_entry(copy.deepcopy(VALID))
monkeypatch.setattr(cli, "get_entry", lambda *_args: entry)
monkeypatch.setattr(cli, "_require_lane_approval", lambda *_args: None)
monkeypatch.setattr(cli.OpenBaoClient, "resolve", lambda *_args, **_kwargs: object())
monkeypatch.setattr(
cli,
"provision_from_file",
lambda *_args, **_kwargs: (_ for _ in ()).throw(
BackendError("fake-SUPER-SECRET-backend-message")
),
)
with pytest.raises(BackendError):
cli.cmd_provision(_config(tmp_path), _provision_args(entry))
records = _records(tmp_path)
assert [record["result"] for record in records] == [
"attempt",
"failed-BackendError",
]
assert records[-1]["detail"]["approval_status"] == "not-required"
assert "fake-SUPER-SECRET" not in json.dumps(records)
def test_provision_decision_rejection_is_recorded_before_backend(
tmp_path, monkeypatch
):
data = copy.deepcopy(VALID)
data["approval"] = {
"model": "decision",
"decision_ref": "CCR-2026-0001",
}
entry = validate_entry(data)
monkeypatch.setattr(cli, "get_entry", lambda *_args: entry)
monkeypatch.setattr(
cli,
"_require_lane_approval",
lambda *_args: (_ for _ in ()).throw(DecisionError("not approved")),
)
monkeypatch.setattr(
cli.OpenBaoClient,
"resolve",
lambda *_args, **_kwargs: pytest.fail("backend must not be reached"),
)
with pytest.raises(DecisionError):
cli.cmd_provision(_config(tmp_path), _provision_args(entry))
records = _records(tmp_path)
assert [record["result"] for record in records] == [
"attempt",
"failed-DecisionError",
]
assert records[-1]["detail"]["approval_status"] == "rejected"
assert records[-1]["detail"]["decision_ref"] == "CCR-2026-0001"

View file

@ -2,7 +2,10 @@ import json
import urllib.error
from types import SimpleNamespace
from secrets_engine.evidence import EvidenceWriter, _scrub
import pytest
from secrets_engine.evidence import EvidenceWriter, PrivilegedActionEvidence, _scrub
from secrets_engine.errors import BackendError, DecisionError
from secrets_engine.redact import looks_secret, redact_text
@ -86,3 +89,53 @@ def test_evidence_records_hub_failure_without_raising(tmp_path, monkeypatch):
]
assert lines[-1]["action"] == "evidence-delivery"
assert lines[-1]["result"] == "failed"
def test_privileged_evidence_records_approval_and_backend_failure(tmp_path):
writer = EvidenceWriter(evidence_dir=tmp_path, hub_url="")
evidence = PrivilegedActionEvidence(
writer,
"provision",
"lane",
"prod",
decision_ref="CCR-2026-0001",
approval_required=True,
)
with pytest.raises(BackendError, match="fake backend failure"):
with evidence:
evidence.mark_approved(SimpleNamespace(id="e6381a56-3e55-4fac-b22c-63ee1c152ce8"))
raise BackendError("fake backend failure")
lines = [
json.loads(line)
for line in next(tmp_path.glob("evidence-*.jsonl")).read_text().splitlines()
]
assert [line["result"] for line in lines] == ["attempt", "failed-BackendError"]
assert lines[-1]["detail"]["approval_status"] == "approved"
assert lines[-1]["detail"]["error_type"] == "BackendError"
assert "fake backend failure" not in json.dumps(lines)
def test_privileged_evidence_records_rejected_decision_without_message(tmp_path):
writer = EvidenceWriter(evidence_dir=tmp_path, hub_url="")
evidence = PrivilegedActionEvidence(
writer,
"apply",
"lane",
"prod",
decision_ref="CCR-2026-0001",
approval_required=True,
)
with pytest.raises(DecisionError):
with evidence:
raise DecisionError("operator prose must not enter evidence")
lines = [
json.loads(line)
for line in next(tmp_path.glob("evidence-*.jsonl")).read_text().splitlines()
]
assert lines[-1]["result"] == "failed-DecisionError"
assert lines[-1]["detail"]["approval_status"] == "rejected"
assert "operator prose" not in json.dumps(lines)