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:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user