secrets-engine/tests/test_dry_run_never_gates.py
tegwick 083bee7333
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
test: enforce that dry runs never reach the authorization gate
flex-auth's published policy package encodes an explicit limit: apply and
apply --dry-run are indistinguishable to a PDP, since both arrive as action
`apply`. What separates them is a property of this repo - the PEP does not
call the gate for a dry run - and flex-auth recorded that as a limit rather
than implying a control they do not have, asking to be told if it stops
holding.

Verified it holds across all five dry-run handlers (apply, revoke, lifecycle
suspend/deactivate/destroy) and locked it with a regression test that fails
with the instruction to notify flex-auth. A paired test proves the hook
under test is actually load-bearing, so the guard cannot pass vacuously.

Also records the FLEX-WP-0021-T01/T02 and GH-DEC-2026-005 outcomes in
SECRETS-WP-0007-T04, including the revisit trigger flex-auth flagged: with
one calling identity the denial ladder has no action_not_granted branch, so
a second identity or a per-lane/stage split is the trigger to add it.

allow_ttl 15m needed no change - normalize_wrap_ttl already caps wrap at 15m.
The policy pin stays unset; publishing is not deploying. 257 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M65ovP3eiiPHubibvWs9mD

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 393550@bnt-lap001
Assistant-Session: 4bb359f9-1f12-4410-9e76-079cf23c82e4
2026-09-06 08:09:42 +02:00

67 lines
2.7 KiB
Python

"""Dry runs must never reach the authorization gate.
flex-auth's `secrets-engine.catalog-lane.lifecycle` v1 encodes an explicit
limit: `apply` and `apply --dry-run` are indistinguishable to a PDP, because
both would arrive as action `apply`. What keeps them apart is a property of
THIS repo -- the PEP does not call the gate for a dry run -- and flex-auth asked
to be told if that assumption ever stops holding (FLEX-WP-0021-T02).
This test is that notification. If a dry-run path ever starts calling
`_require_lane_approval`, it fails here rather than silently widening what the
published package is understood to cover.
"""
import pytest
from secrets_engine import cli
DRY_RUN_COMMANDS = [
("apply", ["apply", "whynot-design-npm-publish", "--stage", "prod", "--dry-run"]),
("revoke", ["revoke", "whynot-design-npm-publish", "--dry-run"]),
("lifecycle suspend", ["lifecycle", "suspend", "whynot-design-npm-publish", "--dry-run"]),
("lifecycle deactivate", ["lifecycle", "deactivate", "whynot-design-npm-publish", "--dry-run"]),
(
"lifecycle destroy",
[
"lifecycle", "destroy", "whynot-design-npm-publish", "--dry-run",
"--confirm-destroy", "whynot-design-npm-publish",
],
),
]
@pytest.mark.parametrize(("label", "argv"), DRY_RUN_COMMANDS, ids=[c[0] for c in DRY_RUN_COMMANDS])
def test_dry_run_does_not_reach_the_authorization_gate(label, argv, monkeypatch, capsys):
def _forbidden(*_args, **_kwargs):
pytest.fail(
f"'{label} --dry-run' reached the authorization gate. flex-auth's "
"policy package assumes dry runs never do, because a PDP cannot "
"distinguish them from the live action. Tell flex-auth before "
"changing this."
)
monkeypatch.setattr(cli, "_require_lane_approval", _forbidden)
monkeypatch.setattr(
cli.OpenBaoClient,
"resolve",
lambda *_a, **_k: pytest.fail(f"'{label} --dry-run' opened a backend"),
)
cli.main(argv)
# A dry run must still render something for the operator to inspect.
assert capsys.readouterr().out.strip()
def test_live_paths_do_still_gate(monkeypatch):
"""Guard the guard: prove the hook under test is actually load-bearing."""
calls = []
monkeypatch.setattr(
cli,
"_require_lane_approval",
lambda *a, **k: calls.append(a[2] if len(a) > 2 else "?"),
)
monkeypatch.setattr(
cli.OpenBaoClient, "resolve", lambda *_a, **_k: pytest.fail("backend opened")
)
with pytest.raises(BaseException):
cli.main(["apply", "whynot-design-npm-publish", "--stage", "prod"])
assert calls == ["apply"], "live apply must pass action 'apply' to the gate"