secrets-engine/tests/test_openbao_safe_write.py
tegwick 3a1bd4f1c8
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Harden secret provisioning and lifecycle controls
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0217e-8c4c-7383-be6b-f50a6e485306
2026-08-23 12:05:58 +02:00

171 lines
5.9 KiB
Python

import json
from pathlib import Path
from types import SimpleNamespace
import pytest
from secrets_engine.errors import BackendError
from secrets_engine.openbao import OpenBaoClient
def test_kv_patch_keeps_secret_out_of_argv_and_cleans_input(monkeypatch):
secret = "fake-SUPER-SECRET-value"
client = OpenBaoClient(addr="http://example.invalid", token="", bao_bin="bao")
monkeypatch.setattr(client, "kv_current_version", lambda _mount, _path: 7)
captured = {}
def fake_run_ok(args, *, stdin=None):
captured["args"] = list(args)
captured["stdin"] = stdin
input_path = Path(args[-1][1:])
captured["input_path"] = input_path
captured["mode"] = input_path.stat().st_mode & 0o777
captured["payload"] = json.loads(input_path.read_text(encoding="utf-8"))
return ""
monkeypatch.setattr(client, "_run_ok", fake_run_ok)
base = client.kv_patch_fields("secret", "prod/example", {"TOKEN": secret})
assert base == 7
assert secret not in " ".join(captured["args"])
assert captured["stdin"] is None
assert captured["mode"] == 0o600
assert captured["payload"] == {"TOKEN": secret}
assert not captured["input_path"].exists()
def test_new_path_uses_cas_zero_put_without_secret_in_argv(monkeypatch):
secret = "fake-new-path-secret"
client = OpenBaoClient(addr="http://example.invalid", token="", bao_bin="bao")
monkeypatch.setattr(client, "kv_current_version", lambda _mount, _path: 0)
captured = {}
def fake_run_ok(args, *, stdin=None):
captured["args"] = list(args)
return ""
monkeypatch.setattr(client, "_run_ok", fake_run_ok)
client.kv_patch_fields("secret", "build/example", {"TOKEN": secret})
assert captured["args"][:5] == [
"kv",
"put",
"-mount=secret",
"-cas=0",
"build/example",
]
assert secret not in " ".join(captured["args"])
assert not Path(captured["args"][-1][1:]).exists()
def test_kv_current_version_fails_closed_on_permission_error(monkeypatch):
client = OpenBaoClient(addr="http://example.invalid", token="", bao_bin="bao")
monkeypatch.setattr(
client,
"_run",
lambda _args: SimpleNamespace(
returncode=2, stdout="", stderr="permission denied"
),
)
with pytest.raises(BackendError, match="metadata get failed"):
client.kv_current_version("secret", "prod/example")
def test_kv_current_version_returns_zero_only_for_absent_path(monkeypatch):
client = OpenBaoClient(addr="http://example.invalid", token="", bao_bin="bao")
monkeypatch.setattr(
client,
"_run",
lambda _args: SimpleNamespace(
returncode=2, stdout="No value found at secret/metadata/example", stderr=""
),
)
assert client.kv_current_version("secret", "example") == 0
def test_approle_session_keeps_login_material_out_of_argv_and_revokes_self(monkeypatch):
client = OpenBaoClient(addr="http://example.invalid", token="parent", bao_bin="bao")
monkeypatch.setattr(client, "read_approle_role_id", lambda _role: "role-id-value")
monkeypatch.setattr(
client, "create_approle_secret_id", lambda _role: "secret-id-value"
)
captured = {}
def fake_json_call(args, payload):
captured["args"] = list(args)
captured["payload"] = dict(payload)
return json.dumps(
{"auth": {"client_token": "scoped-token", "accessor": "accessor-value"}}
)
monkeypatch.setattr(client, "_run_ok_with_json_file", fake_json_call)
session = client.create_approle_session("example-role")
revoke_calls = []
monkeypatch.setattr(
session.client,
"_run_ok",
lambda args, **_kwargs: revoke_calls.append(list(args)) or "",
)
assert "role-id-value" not in " ".join(captured["args"])
assert "secret-id-value" not in " ".join(captured["args"])
assert captured["payload"] == {
"role_id": "role-id-value",
"secret_id": "secret-id-value",
}
assert session.client.token == "scoped-token"
assert session.accessor_fingerprint
session.close()
session.close()
assert revoke_calls == [["token", "revoke", "-self"]]
assert session.client.token == ""
assert session.closed is True
assert session.evidence() == {
"session_handle": session.accessor_fingerprint,
"established": True,
"revocation_attempted": True,
"revocation_succeeded": True,
}
assert "accessor-value" not in json.dumps(session.evidence())
assert "scoped-token" not in json.dumps(session.evidence())
def test_failed_session_revocation_is_visible_and_credential_is_dropped(monkeypatch):
client = OpenBaoClient(addr="http://example.invalid", token="issued", bao_bin="bao")
from secrets_engine.openbao import ScopedTokenSession
session = ScopedTokenSession(client=client, accessor_fingerprint="safe-handle")
monkeypatch.setattr(
client,
"_run_ok",
lambda *_args, **_kwargs: (_ for _ in ()).throw(BackendError("revoke failed")),
)
with pytest.raises(BackendError, match="revoke failed"):
session.close()
assert session.client.token == ""
assert session.closed is True
assert session.evidence() == {
"session_handle": "safe-handle",
"established": True,
"revocation_attempted": True,
"revocation_succeeded": False,
}
def test_approle_session_context_revokes_on_failure(monkeypatch):
client = OpenBaoClient(addr="http://example.invalid", token="parent", bao_bin="bao")
session = SimpleNamespace(closed=False)
def close():
session.closed = True
session.close = close
monkeypatch.setattr(client, "create_approle_session", lambda _role: session)
with pytest.raises(RuntimeError, match="child failed"):
with client.approle_session("example-role"):
raise RuntimeError("child failed")
assert session.closed is True