diff --git a/scripts/auth_role_watch_lane.py b/scripts/auth_role_watch_lane.py index 4e557fc..f6e23d5 100644 --- a/scripts/auth_role_watch_lane.py +++ b/scripts/auth_role_watch_lane.py @@ -137,8 +137,7 @@ def dry_run_payload(ccr: dict[str, Any], repo_dir: Path, warnings: list[str]) -> "source_artifacts": {"policy_file": openbao["policy_file"]}, "mutations": [ {"kind": "policy_write", "openbao_path": f"sys/policies/acl/{openbao['policy_name']}", - "payload": {"source": openbao["policy_file"], "watched_path": watched_path(ccr), - "capabilities": MODES[openbao["watch"]["mode"]]}}, + "source": openbao["policy_file"], "body": generated_policy_hcl(ccr)}, {"kind": "auth_role_write", "openbao_path": f"auth/{auth['mount']}/role/{auth['role']}", "payload": role_payload(ccr), "precondition": "auth/kubernetes exists (never enabled here)"}, {"kind": "readback_compare", "openbao_path": "policy and role", "payload": {"prints": "booleans only"}}, diff --git a/scripts/credential-change.py b/scripts/credential-change.py index 7714ce9..d257442 100755 --- a/scripts/credential-change.py +++ b/scripts/credential-change.py @@ -2304,7 +2304,7 @@ def command_decision(args: argparse.Namespace, status: str) -> int: "summary": ( f"CCR {ccr['id']} decision {status} by {args.reviewer}: " f"path={openbao.get('kv_path', '-')} policy={openbao['policy_name']} " - f"fields={','.join(openbao['fields'])} " + f"fields={','.join(openbao.get('fields') or []) or '-'} " f"auth_role=auth/{auth['mount']}/role/{auth['role']}; " f"{args.comment}" ), diff --git a/tests/test_auth_role_watch_lane.py b/tests/test_auth_role_watch_lane.py index fb4d385..e74dd5b 100644 --- a/tests/test_auth_role_watch_lane.py +++ b/tests/test_auth_role_watch_lane.py @@ -61,13 +61,27 @@ class AuthRoleWatchLaneTests(unittest.TestCase): def test_ccr_validates_and_blocks_until_approved_and_bound(self): _ccr, errors, _w = cc.validate_ccr(CCR_PATH) self.assertEqual(errors, []) - blockers = cc.applier_readiness_blockers(_ccr) + draft = copy.deepcopy(_ccr) # independent of the file's live review state + draft.update(status="proposed") + draft["openbao"]["auth"]["bound_claims_confirmed"] = False + blockers = cc.applier_readiness_blockers(draft) self.assertIn("applier requires confirmed OpenBao auth binding", blockers) ready = copy.deepcopy(_ccr) ready["status"] = "approved" ready["openbao"]["auth"]["bound_claims_confirmed"] = True self.assertEqual(cc.applier_readiness_blockers(ready), []) + def test_dry_run_and_apply_plan_render_through_the_cli(self): + """2026-11-06: the founder's dry run crashed on a policy_write without source and body.""" + ready = copy.deepcopy(ccr()) + ready.update(status="approved") + ready["openbao"]["auth"]["bound_claims_confirmed"] = True + dry = cc.render_applier_dry_run(cc.applier_dry_run_payload(ready, [])) + self.assertIn("policy_write: sys/policies/acl/net-kingdom-bounded-executor-window-watch", dry) + self.assertIn('path "%s" {' % WATCHED, dry) + self.assertIn("auth_role_write: auth/kubernetes/role/net-kingdom-bounded-executor-window-watch", dry) + self.assertIn("CCR-2026-0064", cc.render_applier_apply_plan(ready, [])) + def test_flag_policy_is_one_read_and_self_lifecycle(self): text = (ROOT / ccr()["openbao"]["policy_file"]).read_text() paths = [line.split('"')[1] for line in text.splitlines() if line.startswith("path ")]