fix(mcp): remediate B8, B6/B11, and B15 break-glass restart blockers (#664)
- B8: Correct redaction boundary for GITEA_TOKEN= and URI userinfo without destroying adjacent audit evidence or benign sec- text - B6/B11: Remove false restart execution claims from default executor when GITEA_SANCTIONED_RESTART_HOOK is non-empty - B15: Document deployable production grant set (runtime.break_glass_restart and gitea.issue.create) for prgs-controller - Preserve B13, B1, B14 and previously accepted corrections
This commit is contained in:
@@ -855,6 +855,128 @@ class TestBreakGlassRestart(unittest.TestCase):
|
||||
entry2 = TASK_CAPABILITY_MAP.get("break_glass_restart")
|
||||
self.assertEqual(entry2["permission"], "runtime.break_glass_restart")
|
||||
|
||||
# ── B8 / B6 / B15 remediation tests ─────────────────────────────────────
|
||||
|
||||
def test_b8_redaction_gitea_token_and_uri_credentials(self) -> None:
|
||||
"""B8: GITEA_TOKEN= and URI userinfo credentials redacted without erasing neighbours."""
|
||||
# GITEA_TOKEN= with underscore key
|
||||
out1 = gitea_audit._redact_str("failed with GITEA_TOKEN=synthetic_tok_123456789")
|
||||
self.assertIn("GITEA_TOKEN=[REDACTED]", out1)
|
||||
self.assertNotIn("synthetic_tok_123456789", out1)
|
||||
|
||||
# Connection string with URI userinfo
|
||||
out2 = gitea_audit._redact_str("conn postgres://user:[email protected]:5432/app")
|
||||
self.assertIn("postgres://[REDACTED_USER]:[REDACTED_PASS]@db.internal:5432/app", out2)
|
||||
self.assertNotIn("s3cr3tpw", out2)
|
||||
|
||||
# Value boundary preserving adjacent audit evidence (correlation_id, incident_number)
|
||||
raw_audit = "password=secret123;correlation_id=bg-7f2a1c;incident_number=4242"
|
||||
out3 = gitea_audit._redact_str(raw_audit)
|
||||
self.assertIn("password=[REDACTED]", out3)
|
||||
self.assertIn("correlation_id=bg-7f2a1c", out3)
|
||||
self.assertIn("incident_number=4242", out3)
|
||||
self.assertNotIn("secret123", out3)
|
||||
|
||||
# Query param boundary in URL preserving adjacent parameters
|
||||
raw_url = "token=abc-123&pr=908&issue=664&head=c67f39b4"
|
||||
out4 = gitea_audit._redact_str(raw_url)
|
||||
self.assertIn("token=[REDACTED]", out4)
|
||||
self.assertIn("pr=908", out4)
|
||||
self.assertIn("issue=664", out4)
|
||||
self.assertIn("head=c67f39b4", out4)
|
||||
|
||||
def test_b6_default_executor_environment_text_cannot_imply_execution(self) -> None:
|
||||
"""B6/B11: GITEA_SANCTIONED_RESTART_HOOK string alone returns break_glass_executed=False."""
|
||||
os.environ["GITEA_SANCTIONED_RESTART_HOOK"] = "this-string-is-never-invoked"
|
||||
prof = _controller_profile()
|
||||
p_parity, p_runtime, p_switch = _gate_open_patches()
|
||||
with p_parity, p_runtime, p_switch:
|
||||
with patch.object(gitea_mcp_server, "get_profile", return_value=prof), patch.object(
|
||||
gitea_mcp_server, "_auth", return_value={"Authorization": "token test"}
|
||||
), patch.object(
|
||||
gitea_mcp_server, "_authenticated_username", return_value="sysadmin"
|
||||
), patch.object(
|
||||
gitea_mcp_server, "gitea_request_mcp_restart",
|
||||
return_value={"affected_sessions": []},
|
||||
), patch.object(
|
||||
gitea_audit, "audit_enabled", return_value=True
|
||||
), patch.object(
|
||||
gitea_audit, "write_event", return_value=True
|
||||
), patch.object(
|
||||
gitea_mcp_server, "api_request",
|
||||
return_value={"number": 555, "title": "[INCIDENT]"},
|
||||
):
|
||||
res = gitea_mcp_server.gitea_break_glass_restart(
|
||||
reason="Privileged restart request with non-empty hook env var",
|
||||
confirmation="I_ACKNOWLEDGE_BREAK_GLASS_MCP_RESTART_DISRUPTION",
|
||||
impact_ack=True,
|
||||
dry_run=False,
|
||||
create_incident_issue=True,
|
||||
remote="prgs",
|
||||
)
|
||||
self.assertFalse(res["success"])
|
||||
self.assertFalse(res["performed"])
|
||||
self.assertFalse(res["break_glass_executed"])
|
||||
self.assertEqual(res["blocker_kind"], "restart_delegation_failed")
|
||||
|
||||
def test_b15_production_prgs_controller_grant_set_and_gates(self) -> None:
|
||||
"""B15: Genuine prgs-controller carrying runtime.break_glass_restart and gitea.issue.create passes real gates."""
|
||||
# Full production-shaped prgs-controller profile
|
||||
prod_profile = {
|
||||
"profile_name": "prgs-controller",
|
||||
"execution_profile": "prgs-controller",
|
||||
"role": "reconciler",
|
||||
"allowed_operations": [
|
||||
"gitea.read",
|
||||
"gitea.pr.close",
|
||||
"gitea.pr.comment",
|
||||
"gitea.issue.comment",
|
||||
"gitea.issue.create",
|
||||
"runtime.break_glass_restart",
|
||||
"gitea.branch.delete",
|
||||
],
|
||||
"forbidden_operations": [
|
||||
"gitea.pr.approve",
|
||||
"gitea.pr.merge",
|
||||
"gitea.pr.create",
|
||||
"gitea.branch.push",
|
||||
],
|
||||
}
|
||||
|
||||
# 1. Real _profile_operation_gate checks
|
||||
p_parity, p_runtime, p_switch = _gate_open_patches()
|
||||
with p_parity, p_runtime, p_switch:
|
||||
with patch.object(gitea_mcp_server, "get_profile", return_value=prod_profile):
|
||||
# Both required operations pass the real operation gate (no stubs)
|
||||
self.assertEqual(
|
||||
gitea_mcp_server._profile_operation_gate("runtime.break_glass_restart"),
|
||||
[],
|
||||
)
|
||||
self.assertEqual(
|
||||
gitea_mcp_server._profile_operation_gate("gitea.issue.create"),
|
||||
[],
|
||||
)
|
||||
|
||||
# 2. Missing gitea.issue.create fails incident creation gate
|
||||
no_issue_create = dict(prod_profile)
|
||||
no_issue_create["allowed_operations"] = [
|
||||
"gitea.read", "gitea.pr.close", "runtime.break_glass_restart"
|
||||
]
|
||||
with p_parity, p_runtime, p_switch:
|
||||
with patch.object(gitea_mcp_server, "get_profile", return_value=no_issue_create):
|
||||
res = gitea_mcp_server.gitea_break_glass_restart(
|
||||
reason="Restart testing missing gitea.issue.create permission",
|
||||
confirmation="I_ACKNOWLEDGE_BREAK_GLASS_MCP_RESTART_DISRUPTION",
|
||||
impact_ack=True,
|
||||
dry_run=False,
|
||||
create_incident_issue=True,
|
||||
remote="prgs",
|
||||
)
|
||||
self.assertFalse(res["success"])
|
||||
self.assertEqual(res["blocker_kind"], "permission_denied")
|
||||
self.assertIn("gitea.issue.create", " ".join(res.get("reasons") or []))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user