railiance-platform/tests/test_nk_agent_status_rbac.py
codex 4e60f69f93 nk-agent-status: read-only status identity for the net-kingdom agent (NK-WP-0058-T05, decision 4af1ec01)
Founder decision 2026-11-06, option A, 1 h tokens. ServiceAccount sso/nk-agent-status without automount;
read-only Jobs, pods, Deployments and ExternalSecrets in sso; one named ConfigMap in informed-decision;
the informed-decision Application in argocd. No Secrets, pod logs, exec, writes or cluster scope.
Guard: tests/test_nk_agent_status_rbac.py. The Application follows in the next commit, pinned here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 943911@bnt-lap001
Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
2026-11-06 01:12:32 +01:00

86 lines
3.6 KiB
Python

"""Offline guard for the net-kingdom agent status identity (net-kingdom NK-WP-0058-T05, decision 4af1ec01).
Parses the GitOps manifests and asserts the founder's 2026-11-06 boundary (option A): read-only verbs on the curated
namespaced resources, named objects outside sso, no forbidden resources, no wildcards, no cluster scope, a
ServiceAccount without token automount, and a manual, pinned Application.
"""
from pathlib import Path
import re
import pytest
import yaml
ROOT = Path(__file__).resolve().parents[1]
ADDON = ROOT / "argocd/platform-addons/nk-agent-status"
APP = ROOT / "argocd/railiance01/applications/nk-agent-status.application.yaml"
READ_VERBS = {"get", "list", "watch"}
FORBIDDEN_RESOURCES = {
"secrets", "pods/log", "pods/exec", "pods/attach", "pods/portforward", "serviceaccounts/token",
"nodes/proxy", "services/proxy", "pods/proxy", "roles", "rolebindings", "clusterroles", "clusterrolebindings",
}
EXPECTED = {
("sso", "batch", "jobs"): None,
("sso", "", "pods"): None,
("sso", "apps", "deployments"): None,
("sso", "external-secrets.io", "externalsecrets"): None,
("informed-decision", "", "configmaps"): ["informed-decision-workload-registry"],
("argocd", "argoproj.io", "applications"): ["informed-decision"],
}
SUBJECT = [{"kind": "ServiceAccount", "name": "nk-agent-status", "namespace": "sso"}]
def _docs():
kust = yaml.safe_load((ADDON / "kustomization.yaml").read_text())
docs = []
for res in kust["resources"]:
docs.extend(d for d in yaml.safe_load_all((ADDON / res).read_text()) if d)
return docs
def test_only_namespaced_kinds_no_secret():
kinds = sorted(d["kind"] for d in _docs())
assert kinds == ["Role"] * 3 + ["RoleBinding"] * 3 + ["ServiceAccount"]
for path in ADDON.iterdir():
if path.is_file():
assert "kind: Secret" not in path.read_text(), path
def test_service_account_without_automount():
(sa,) = [d for d in _docs() if d["kind"] == "ServiceAccount"]
assert (sa["metadata"]["name"], sa["metadata"]["namespace"]) == ("nk-agent-status", "sso")
assert sa["automountServiceAccountToken"] is False
def test_roles_read_only_curated_named_outside_sso():
granted = {}
for role in (d for d in _docs() if d["kind"] == "Role"):
ns = role["metadata"]["namespace"]
for rule in role["rules"]:
values = [*rule["verbs"], *rule["apiGroups"], *rule["resources"]]
assert not any("*" in v for v in values), rule
assert set(rule["verbs"]) <= READ_VERBS, rule
for group in rule["apiGroups"]:
for resource in rule["resources"]:
assert resource not in FORBIDDEN_RESOURCES, resource
granted[(ns, group, resource)] = rule.get("resourceNames")
assert granted == EXPECTED
def test_bindings_target_only_the_agent():
for binding in (d for d in _docs() if d["kind"] == "RoleBinding"):
assert binding["subjects"] == SUBJECT
assert binding["roleRef"] == {"apiGroup": "rbac.authorization.k8s.io", "kind": "Role",
"name": "nk-agent-status"}
def test_application_manual_and_pinned():
if not APP.exists():
pytest.skip("Application not yet placed")
app = yaml.safe_load(APP.read_text())
spec = app["spec"]
assert spec["project"] == "railiance-platform-addons"
assert spec["source"]["path"] == "argocd/platform-addons/nk-agent-status"
assert re.fullmatch(r"[0-9a-f]{40}", spec["source"]["targetRevision"])
assert "automated" not in spec.get("syncPolicy", {})
assert "finalizers" not in app["metadata"]