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)
93 lines
4.0 KiB
Markdown
93 lines
4.0 KiB
Markdown
# MCP daemon import and native-transport guard (#558 / #695)
|
|
|
|
## Problem
|
|
|
|
During deadlock debugging and the PR #694 incident (#695), agents imported
|
|
`gitea_mcp_server` or ran credential helpers from a raw shell / offline helper,
|
|
bypassing native MCP transport, preflight purity, and role gates. Contaminated
|
|
formal reviews then looked identical to native approvals.
|
|
|
|
## Rule
|
|
|
|
Mutation auth, keychain fill, and controller quarantine require a **production
|
|
native MCP transport runtime** established only by:
|
|
|
|
1. the **resolved absolute path** of the canonical entrypoint
|
|
(`mcp_server.py` / `gitea_mcp_server.py` next to `mcp_daemon_guard.py`), and
|
|
2. a live **transport bind** (`bind_native_mcp_transport(transport="stdio")`)
|
|
immediately before `mcp.run`.
|
|
|
|
Basename-only trust (a renamed file called `mcp_server.py`), caller-controlled
|
|
flags (there is **no** `allow_test_bootstrap`), environment variables, stack
|
|
frame spoofing, or import-only launch are insufficient.
|
|
|
|
| Context | Allowed |
|
|
|---------|---------|
|
|
| Official IDE-native MCP: resolved canonical entrypoint marks + binds stdio, holds process-local runtime token | yes |
|
|
| pytest (hermetic unit tests) via `is_pytest_runtime()` | yes for unit gates |
|
|
| `install_test_native_runtime()` under pytest (test-mode record) | unit-test transport gates only — **never** production Gitea mutations |
|
|
| `allow_test_bootstrap=True` (removed; must not exist) | **no** |
|
|
| Renamed runner basename `mcp_server.py` outside package root | **no** |
|
|
| Import/launch of real entrypoint without transport bind | **no** |
|
|
| `GITEA_MCP_SANCTIONED_DAEMON=1` alone (no process-local native runtime) | **no** (#695) |
|
|
| `GITEA_ALLOW_DIRECT_MCP_IMPORT=1` in LLM sessions | **no** — never set; never authorizes mutations (#695 AC1 / PR #701) |
|
|
| Override `GITEA_MCP_SESSION_STATE_DIR` mid-session | **no** — production bind pins state root; redirect cannot forge independent decision locks (#695 AC2 / PR #701) |
|
|
| `GITEA_ALLOW_KEYCHAIN_CLI=1` in LLM sessions | **no** — human operator only |
|
|
| bare `python -c 'import gitea_mcp_server; …'` or offline runners | **no** |
|
|
| keychain fill outside native/pytest | **no** |
|
|
|
|
Native runtime is **process-local**: a random token bound to the daemon PID and
|
|
transport phase. It is never reconstructed from environment variables,
|
|
session-state files, caller-controlled flags, or importing internals in a
|
|
fresh Python process.
|
|
|
|
## Contaminated review quarantine (#695 AC8)
|
|
|
|
Controller/reconciler/merger profiles may call
|
|
`gitea_quarantine_contaminated_review` with explicit confirmation:
|
|
|
|
```text
|
|
QUARANTINE CONTAMINATED REVIEW <review_id> PR <pr_number>
|
|
```
|
|
|
|
Quarantine records are durable under the MCP session-state root and are
|
|
**honored** by:
|
|
|
|
- `gitea_get_pr_review_feedback` (quarantined approvals do not authorize merge)
|
|
- `gitea_check_pr_eligibility` action=`merge`
|
|
- `gitea_merge_pr` (mutation)
|
|
- merger lease adoption paths that read `approval_at_current_head`
|
|
|
|
Forensic Gitea reviews and historical comments are **never deleted**.
|
|
|
|
## STOP after native MCP failure (AC10)
|
|
|
|
If the native MCP namespace dies (EOF, capability disconnect, session death):
|
|
|
|
1. **STOP.** State BLOCKED + DIAGNOSE.
|
|
2. Do **not** import `gitea_mcp_server` from a standalone process.
|
|
3. Do **not** run `offline_mcp_helper.py`, `offline_mcp_runner.py`,
|
|
`run_quarantine.py`, or any offline mutation helper.
|
|
4. Do **not** set direct-import, keychain-bypass, or raw-token environment
|
|
variables.
|
|
5. Reconnect / restart the official MCP daemon; resume only via native tools.
|
|
|
|
Any further native MCP failure is a hard stop. Do not construct another fallback.
|
|
|
|
## Canonical approval claims (AC7)
|
|
|
|
Comments that claim `approved` / `ready-to-merge` / `WHO_IS_NEXT: merger` /
|
|
`MERGE_READY: true` must include:
|
|
|
|
```text
|
|
NATIVE_REVIEW_PROOF: transport=native_mcp; …
|
|
```
|
|
|
|
Claims that cite offline/import helpers are rejected even if a proof line is
|
|
present.
|
|
|
|
## Operator note
|
|
|
|
LLM sessions must never set allow-direct-import, allow-keychain-cli, or raw
|
|
token overrides. Those are human-only escape hatches outside agent workflows.
|