rotate replaces one declared KV field through the merge-safe patch path and never prints the value. Overlay states active/suspended/deactivated/ compromised live under the evidence directory. compromise/reactivate and successful suspend/deactivate/revoke update that overlay; exec/wrap/ handoff/provision refuse non-active lanes. Provider-side rotation stays with rotation.owner. Production remains fail-closed. Assistant: grok Assistant-Session: 01a05f07-ae72-7781-9fcb-19efd61add00
91 lines
2.9 KiB
Python
91 lines
2.9 KiB
Python
import copy
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
from secrets_engine.catalog import validate_entry
|
|
from secrets_engine.config import Config
|
|
from secrets_engine.errors import DecisionError, PolicyGuardError
|
|
from secrets_engine.lane_state import (
|
|
load_lane_state,
|
|
require_delivery_state,
|
|
require_provision_state,
|
|
save_lane_state,
|
|
)
|
|
from tests.test_catalog import VALID
|
|
|
|
|
|
def _cfg(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 test_missing_state_is_active(tmp_path):
|
|
record = load_lane_state(tmp_path / "evidence", "test-lane")
|
|
assert record.state == "active"
|
|
assert record.last_operation == ""
|
|
|
|
|
|
def test_compromise_blocks_delivery_and_provision(tmp_path):
|
|
evidence = tmp_path / "evidence"
|
|
save_lane_state(evidence, "test-lane", "compromised", operation="compromise")
|
|
with pytest.raises(DecisionError, match="compromised"):
|
|
require_delivery_state(evidence, "test-lane", "exec")
|
|
with pytest.raises(DecisionError, match="rotate"):
|
|
require_provision_state(evidence, "test-lane")
|
|
path = evidence / "lane-state" / "test-lane.yaml"
|
|
dumped = path.read_text(encoding="utf-8")
|
|
assert yaml.safe_load(dumped)["state"] == "compromised"
|
|
assert "npm_" not in dumped
|
|
|
|
|
|
def test_reason_rejects_secret_like_material(tmp_path):
|
|
with pytest.raises(PolicyGuardError, match="secret-like"):
|
|
save_lane_state(
|
|
tmp_path / "evidence",
|
|
"test-lane",
|
|
"compromised",
|
|
operation="compromise",
|
|
reason="npm_abcdefghijklmnop",
|
|
)
|
|
|
|
|
|
def test_exec_refuses_compromised_lane(tmp_path, monkeypatch):
|
|
from secrets_engine import cli
|
|
|
|
entry = validate_entry(copy.deepcopy(VALID))
|
|
save_lane_state(tmp_path / "evidence", entry.id, "compromised", operation="compromise")
|
|
monkeypatch.setattr(cli, "get_entry", lambda *_args: entry)
|
|
monkeypatch.setattr(cli, "_require_lane_approval", lambda *_args, **_kwargs: None)
|
|
monkeypatch.setattr(
|
|
cli.OpenBaoClient,
|
|
"resolve",
|
|
lambda *_args, **_kwargs: pytest.fail("backend must not be reached"),
|
|
)
|
|
args = SimpleNamespace(
|
|
catalog=entry.id,
|
|
field="api_token",
|
|
mode="exec-env",
|
|
command=["true"],
|
|
bootstrap_token_file=None,
|
|
auth="auto",
|
|
)
|
|
with pytest.raises(DecisionError, match="compromised"):
|
|
cli.cmd_exec(_cfg(tmp_path), args)
|
|
|
|
|
|
def test_state_show_defaults_active(tmp_path, monkeypatch):
|
|
from secrets_engine import cli
|
|
|
|
entry = validate_entry(copy.deepcopy(VALID))
|
|
monkeypatch.setattr(cli, "get_entry", lambda *_args: entry)
|
|
args = SimpleNamespace(catalog_id=entry.id, json=True)
|
|
rc = cli.cmd_state_show(_cfg(tmp_path), args)
|
|
assert rc == 0
|