Files
net-kingdom/tools/security-bootstrap-console/tests/test_security_bootstrap_console.py
tegwick f625dd0681 feat: OpenBao unseal custody models — automation-first with blocked alternatives
Document three init/unseal custody paths; default sops-held-automation for
fast rebuild cycles. Security bootstrap console lists models, blocks planned
attended-ceremony and auto-unseal-transit with hints, and gates init ceremony
on implemented selection. NET-WP-0020 tracks downstream SSH automation.
2026-06-18 00:51:48 +02:00

119 lines
4.7 KiB
Python

import importlib.util
import json
import sys
from pathlib import Path
import pytest
# Dynamically import the console module (like other conformance tests)
TOOL_PATH = Path(__file__).resolve().parents[1] / "security_bootstrap_console.py"
SPEC = importlib.util.spec_from_file_location("security_bootstrap_console", TOOL_PATH)
console = importlib.util.module_from_spec(SPEC)
assert SPEC.loader is not None
sys.modules[SPEC.name] = console
SPEC.loader.exec_module(console)
def test_metadata_template_has_core_fields():
tmpl = console.metadata_template()
assert isinstance(tmpl, dict)
core = [
"approval_scope",
"bootstrap_mode",
"custody_mode",
"openbao_unseal_custody_model",
"review_date",
]
for f in core:
assert f in tmpl
assert tmpl["openbao_unseal_custody_model"] == console.DEFAULT_OPENBAO_UNSEAL_CUSTODY_MODEL
def test_openbao_unseal_custody_model_gate_automation_default():
data = console.metadata_template()
gate = console.openbao_unseal_custody_model_gate(data)
assert gate.status == "done"
init_gate = console.openbao_init_ceremony_gate(data)
assert init_gate.status == "automation"
def test_openbao_unseal_custody_planned_models_blocked():
for model in ("attended-ceremony", "auto-unseal-transit"):
data = console.metadata_template()
data["openbao_unseal_custody_model"] = model
gate = console.openbao_unseal_custody_model_gate(data)
assert gate.status == "blocked"
assert "not yet implemented" in gate.reason.lower()
init_gate = console.openbao_init_ceremony_gate(data)
assert init_gate.status == "blocked"
def test_onboarding_dry_run_template_has_required_fields():
tmpl = console.onboarding_dry_run_template()
assert isinstance(tmpl, dict)
required = [
"dry_run_date", "operator", "subject_reference", "actor_class",
"groups", "effective_access_summary", "lock_offboard_result",
"lldap_identity_verified", "keycape_oidc_claims_verified",
"no_secret_material_recorded"
]
for f in required:
assert f in tmpl
assert tmpl["actor_class"] != "king credential" # per guard
# cross-check lifecycle template has the preview guards
flow = console.lifecycle_flow_template()
assert "shows_effective_access_before_save" in flow
assert "prevents_platform_root_grant" in flow
def test_lifecycle_flow_template():
tmpl = console.lifecycle_flow_template()
assert "flow_version" in tmpl
assert "onboard_user_supported" in tmpl
assert tmpl["onboard_user_supported"] is True
def test_runbook_payloads_includes_dry_run():
# minimal data
data = {"openbao_initialized": True}
payloads = console.runbook_payloads(data)
names = [p["name"] for p in payloads]
assert "User lifecycle dry-run (T06)" in names
dry = next(p for p in payloads if "dry-run" in p["name"].lower())
assert "NET-WP-0019" in dry.get("location", "") or "dry-run-nonroot-user.sh" in dry.get("location", "")
def test_audit_core_posture_ready_with_bootstrap_risk():
data = {
"audit_core_production_sink_ready": False,
"audit_core_bootstrap_risk_accepted": True,
"audit_core_risk_owner": "role:platform-custodian",
"audit_core_risk_review_date": "2026-07-02",
"audit_core_risk_note": "Temporary bootstrap exception"
}
assert console.audit_core_posture_ready(data) is True
reason = console.audit_core_posture_reason(data)
assert "Temporary bootstrap audit-retention risk exception" in reason
def test_audit_core_posture_ready_false_without_fields():
data = {"audit_core_production_sink_ready": False}
assert console.audit_core_posture_ready(data) is False
def test_runbook_payloads_taints():
data = {
"openbao_initialized": True,
"openbao_trial_material_exposed": True,
"openbao_compromise_response_complete": False,
"openbao_unseal_keys_rotated": False
}
payloads = console.runbook_payloads(data)
# should have taint entries for compromise etc.
taint_names = [p.get("name") for p in payloads if p.get("state") == "tainted" or "taint" in str(p).lower()]
assert len([p for p in payloads if "compromised" in p["name"].lower() or "Emergency" in p["name"]]) > 0
def test_console_has_dry_run_commands_in_parser():
# crude check that subparsers include the 0019 commands
# by looking at source or assuming from dispatch
source = open(TOOL_PATH).read()
assert "onboarding-dry-run" in source
assert "lifecycle-cleanup-dryrun-users" in source
assert "onboarding-dry-run-claims" in source
# Note: full CLI dispatch and web-ui harder to unit without mocks; covered by fixture/acceptance in T08 later.
# Syntax for sh covered in Makefile target.