fix(guard): pin session-state authority and reject direct-import mutation (#695)

Close remaining AC1/AC2/AC6-8 gaps after REQUEST_CHANGES and the PR #701
recurrence: GITEA_ALLOW_DIRECT_MCP_IMPORT never authorizes mutations;
production transport bind pins GITEA_MCP_SESSION_STATE_DIR so redirected
dirs cannot forge independent decision locks; mark/submit fail closed
offline; quarantine continues to void contaminated merge eligibility.

Regression tests reproduce the PR #701 direct-import + state-dir override
sequence. Full suite: 2665 passed, 6 skipped.

Closes #695 (remediation on PR #696)
This commit is contained in:
2026-07-13 21:48:28 -04:00
parent ae6f0b74db
commit 576349d545
6 changed files with 334 additions and 3 deletions
+31
View File
@@ -3305,6 +3305,18 @@ def _save_review_decision_lock(data):
payload["profile_identity"] = binding["profile_identity"]
if binding.get("remote") and not payload.get("remote"):
payload["remote"] = binding["remote"]
# #695 AC6: stamp native transport provenance on durable decision locks.
try:
payload.update(
{
k: v
for k, v in mcp_daemon_guard.mutation_provenance_fields().items()
if v is not None
}
)
except Exception:
payload.setdefault("transport", "untrusted")
payload.setdefault("native_mcp_transport", False)
persisted = mcp_session_state.save_state(
kind=mcp_session_state.KIND_DECISION_LOCK,
payload=payload,
@@ -4041,6 +4053,15 @@ def _evaluate_pr_review_submission(
reasons.extend(review_workflow_load.recovery_handoff_without_replay())
return result
if live:
# #695 AC1/AC2: offline direct-import submit (PR #701 run_submit.py) fails closed.
try:
mcp_daemon_guard.assert_sanctioned_mutation_runtime(
"gitea_submit_pr_review"
)
mcp_daemon_guard.assert_no_direct_import_bypass("gitea_submit_pr_review")
except mcp_daemon_guard.UnsanctionedRuntimeError as exc:
reasons.append(str(exc))
return result
ns_gate = _live_namespace_health_gate("review_pr")
if ns_gate:
reasons.extend(ns_gate)
@@ -4228,6 +4249,16 @@ def gitea_mark_final_review_decision(
repo: str | None = None,
) -> dict:
"""Mark validation complete; the final review decision is ready to submit."""
# #695 AC1/AC2: direct import / redirected session state cannot mark final.
try:
mcp_daemon_guard.assert_sanctioned_mutation_runtime(
"gitea_mark_final_review_decision"
)
mcp_daemon_guard.assert_no_direct_import_bypass(
"gitea_mark_final_review_decision"
)
except mcp_daemon_guard.UnsanctionedRuntimeError as exc:
return {"marked_ready": False, "reasons": [str(exc)]}
action = (action or "").strip().lower()
lock = _load_review_decision_lock()
if lock is None: