"""RBAC, Flux, and human-review contracts for the Hermes SCM broker.""" from __future__ import annotations import json import pytest import yaml from testing.tests.test_hermes_scm_broker_support import ROOT, _load_path def _can_i(role: dict, api_group: str, resource: str, verb: str) -> bool: return any( (api_group in rule["apiGroups"] or "*" in rule["apiGroups"]) and (resource in rule["resources"] or "*" in rule["resources"]) and (verb in rule["verbs"] or "*" in rule["verbs"]) for rule in role["rules"] ) def test_agent_rbac_preserves_read_diagnostics_without_administrator_authority(): documents = list( yaml.safe_load_all( (ROOT / "services/hermes-observer-rbac/rbac.yaml").read_text( encoding="utf-8" ) ) ) cluster_role, namespaced_role, binding = documents assert binding["roleRef"]["name"] == "hermes-agent-cluster-observer-v2" assert _can_i(cluster_role, "", "nodes", "get") assert _can_i(namespaced_role, "", "pods", "get") assert _can_i(namespaced_role, "", "pods/log", "get") assert _can_i( namespaced_role, "kustomize.toolkit.fluxcd.io", "kustomizations", "list", ) denied = ( ("", "secrets", "get"), ("", "serviceaccounts/token", "create"), ("", "pods", "create"), ("", "pods/exec", "create"), ("", "pods/attach", "create"), ("", "pods/portforward", "create"), ("rbac.authorization.k8s.io", "clusterrolebindings", "create"), ("authorization.k8s.io", "selfsubjectaccessreviews", "create"), ("apps", "deployments", "patch"), ) assert all( not _can_i(role, *request) for request in denied for role in (cluster_role, namespaced_role) ) bindings = yaml.safe_load( (ROOT / "services/hermes-observer-bindings/rolebindings.yaml").read_text( encoding="utf-8" ) )["items"] namespaces = {item["metadata"]["namespace"] for item in bindings} assert "hermes-scm" not in namespaces assert {"cassandra", "flux-system", "hermes", "kube-system"} <= namespaces assert all(item["roleRef"]["name"].endswith("namespaced-observer-v2") for item in bindings) def test_flux_boundary_keeps_broker_secret_and_network_separate_from_agent(): agent = yaml.safe_load( (ROOT / "services/hermes/agent-deployment.yaml").read_text(encoding="utf-8") ) annotations = agent["spec"]["template"]["metadata"]["annotations"] assert not any("gitea" in key.lower() for key in annotations) assert "GIT_ASKPASS" not in json.dumps(agent) broker = yaml.safe_load( (ROOT / "services/hermes-scm-broker/deployment.yaml").read_text( encoding="utf-8" ) ) assert broker["metadata"]["namespace"] == "hermes-scm" assert broker["spec"]["template"]["spec"]["serviceAccountName"] == ( "hermes-scm-broker" ) assert "gitea-token" in json.dumps(broker) container = broker["spec"]["template"]["spec"]["containers"][0] assert container["resources"]["limits"]["memory"] == "768Mi" assert container["livenessProbe"]["failureThreshold"] == 10 tmp = next( item for item in broker["spec"]["template"]["spec"]["volumes"] if item["name"] == "tmp" ) assert tmp["emptyDir"]["sizeLimit"] == "1Gi" policy = yaml.safe_load( (ROOT / "services/hermes-scm-broker/networkpolicy.yaml").read_text( encoding="utf-8" ) ) ingress = policy["spec"]["ingress"] assert ingress[0]["from"][0]["namespaceSelector"]["matchLabels"] == { "kubernetes.io/metadata.name": "hermes" } assert ingress[0]["from"][0]["podSelector"]["matchLabels"] == { "app": "hermes-agent" } vault = ( ROOT / "services/vault/scripts/vault_k8s_auth_configure.sh" ).read_text(encoding="utf-8") assert 'write_policy_and_role "hermes-scm-broker" "hermes-scm"' in vault assert '"hermes/developer-gitea hermes/scm-task-grant" ""' in vault agent_start = vault.index('write_policy_and_role "hermes-agent"') agent_end = vault.index("write_policy_and_role", agent_start + 1) assert "developer-gitea" not in vault[agent_start:agent_end] def test_flux_bootstrap_has_no_agent_namespace_dependency_cycle(): hermes_resources = yaml.safe_load( (ROOT / "services/hermes/kustomization.yaml").read_text(encoding="utf-8") )["resources"] assert "namespace.yaml" in hermes_resources assert "scm-common" in hermes_resources observer = yaml.safe_load( ( ROOT / "clusters/atlas/flux-system/applications/hermes-observer-rbac/kustomization.yaml" ).read_text(encoding="utf-8") ) assert "dependsOn" not in observer["spec"] observer_objects = [ item for item in yaml.safe_load_all( (ROOT / "services/hermes-observer-rbac/rbac.yaml").read_text( encoding="utf-8" ) ) if item ] assert {item["kind"] for item in observer_objects} <= { "ClusterRole", "ClusterRoleBinding", } hermes_flux = yaml.safe_load( ( ROOT / "clusters/atlas/flux-system/applications/hermes/kustomization.yaml" ).read_text(encoding="utf-8") ) assert "hermes-observer-rbac" in { item["name"] for item in hermes_flux["spec"]["dependsOn"] } bindings_flux = yaml.safe_load( ( ROOT / "clusters/atlas/flux-system/applications/hermes-observer-bindings/kustomization.yaml" ).read_text(encoding="utf-8") ) assert {item["name"] for item in bindings_flux["spec"]["dependsOn"]} == { "hermes", "hermes-observer-rbac", } assert bindings_flux["metadata"]["name"] not in { item["name"] for item in hermes_flux["spec"]["dependsOn"] } applications = ( ROOT / "clusters/atlas/flux-system/applications/kustomization.yaml" ).read_text(encoding="utf-8") assert "hermes-scm-agent-code" not in applications assert "hermes-scm-broker-code" not in applications broker_flux = yaml.safe_load( ( ROOT / "clusters/atlas/flux-system/applications/hermes-scm-broker/kustomization.yaml" ).read_text(encoding="utf-8") ) assert {item["name"] for item in broker_flux["spec"]["dependsOn"]} == { "vault", "gitea", "hermes-scm-namespace" } broker_kustomization = yaml.safe_load( (ROOT / "services/hermes-scm-broker/kustomization.yaml").read_text(encoding="utf-8") ) assert "../hermes/scm-common" in broker_kustomization["resources"] scm_common = yaml.safe_load( (ROOT / "services/hermes/scm-common/kustomization.yaml").read_text(encoding="utf-8") ) assert "options" not in scm_common["configMapGenerator"][0] def test_gitea_bootstrap_enforces_human_review_without_overwriting_drift(): script = ( ROOT / "services/gitea/scripts/gitea_atlas_identity_ensure.sh" ).read_text(encoding="utf-8") assert 'required_approvals\\\":1' in script assert 'enable_push_whitelist\\\":true' in script assert 'push_whitelist_usernames\\\":[\\\"${protected_reviewer}\\\"]' in script assert 'enable_merge_whitelist\\\":true' in script assert 'merge_whitelist_usernames\\\":[\\\"${protected_reviewer}\\\"]' in script assert "protection differs from the human-review policy" in script assert "api_request PATCH" not in script job = yaml.safe_load( (ROOT / "services/gitea/atlas-identity-bootstrap-job.yaml").read_text() ) init = job["spec"]["template"]["spec"]["initContainers"][0] assert init["image"] == ( "python@sha256:6d43704baacd1bfbe7c295d7f13079d5d8104ed33568873133f8fc69980419df" ) assert init["securityContext"]["runAsNonRoot"] is True assert init["securityContext"]["capabilities"]["drop"] == ["ALL"] assert "/opt/python/bin/python3" in script def _protection(rule_name: str, priority: int, helper, **overrides): value = { "rule_name": rule_name, "priority": priority, "created_at": "2026-01-01T00:00:00Z", **helper._required("bstein"), } value.update(overrides) return value def test_branch_protection_requires_exact_literal_rules_only(): helper = _load_path( "branch_protection_test", ROOT / "services/gitea/scripts/gitea_branch_protection_check.py", ) exact = _protection("main", 2, helper) assert ( helper.evaluate(json.dumps([exact]).encode(), "main", "bstein") == "PRESENT" ) drifted = _protection("main", 2, helper, required_approvals=0) with pytest.raises(helper.PolicyError, match="effective main protection differs"): helper.evaluate(json.dumps([drifted]).encode(), "main", "bstein") duplicate = _protection("main", 1, helper) with pytest.raises(helper.PolicyError, match="duplicate primary-branch"): helper.evaluate(json.dumps([exact, duplicate]).encode(), "main", "bstein") @pytest.mark.parametrize( "rule_name", [ "release/*", "release/**/v1.17", "*", "**", "M*", "m{ain,aster}", r"m\ain", "m?in", "m[ai]in", ], ) def test_branch_glob_rules_fail_closed_pending_human_review(rule_name: str): helper = _load_path( "branch_protection_glob_parity_test", ROOT / "services/gitea/scripts/gitea_branch_protection_check.py", ) rules = [_protection(rule_name, 1, helper)] with pytest.raises(helper.PolicyError, match="human review"): helper.evaluate(json.dumps(rules).encode(), "main", "bstein") @pytest.mark.parametrize( "rules", [ [{"rule_name": "main", "priority": 0, "created_at": "2026-01-01T00:00:00Z"}], [{"rule_name": "main", "priority": 1, "created_at": "not-a-time"}], [{"rule_name": "main"}], ], ) def test_branch_protection_rejects_ambiguous_rules_before_creation(rules): helper = _load_path( "branch_protection_adversarial_test", ROOT / "services/gitea/scripts/gitea_branch_protection_check.py", ) with pytest.raises(helper.PolicyError): helper.evaluate(json.dumps(rules).encode(), "main", "bstein") def test_branch_protection_reports_absent_only_when_no_rule_matches(): helper = _load_path( "branch_protection_absent_test", ROOT / "services/gitea/scripts/gitea_branch_protection_check.py", ) rules = [_protection("develop", 1, helper)] assert helper.evaluate(json.dumps(rules).encode(), "master", "bstein") == "ABSENT"