新增 leo21c-mcp-guard.sh:擋下打向 leo 真庫的 Arcrun/KBDB MCP 呼叫(ISEP#69)

leo 2026-08-27:「我從頭的指令就叫你去打 youlin,stage 環境是它你不知道嗎?
為什麼打算用我的真庫測試?」

實況(總管當天實查):這台機器同時掛兩條 Arcrun MCP 連線,工具名是兩串 UUID——
mcp__00402d88-...__* → logged_in_as: leo21c(真庫),mcp__d0eaf65b-...__* →
logged_in_as: youlin(測試場)。總管整天用第一條,測試資料卻寫在第二條,
造成三次假結論(「wiki 沒送上去」「排在 1691 筆後面」「ingest 沒寫進去」——
全是查錯庫),而總管自己每張派工單都寫著「測試一律用 youlin」,卻一次都沒套用。

判準:任何打向 leo21c 座標的 kbdb_*/arcrun_* MCP 呼叫一律擋,除非明說
(touch /tmp/.leo21c-mcp-ok,單次 15 分鐘、會留痕到 .claude/hooks/
leo21c-mcp-guard.log)。讀寫都擋,但訊息不同:唯讀提示「你可能查錯地方」,
寫入提示「這會汙染真資料」。arcrun_whoami 本身永遠放行(唯一能確認身份的
工具,擋它會造成死結)。

已知限制(查過但查不到穩定識別法,不是猜測):MCP server 的 UUID 是這次連線
分配的,不是穩定識別碼,重裝/換 session 會換掉;~/.claude.json 等本機設定檔
裡找不到這兩條連線的來源設定,判斷是雲端 connector 層動態配的。因此這兩個
UUID 是寫死的(2026-08-27 由 arcrun_whoami 實測得到),且會過期。設計上不是
「表格以外的一律放行」,而是「不在表格裡的 UUID 一樣擋,且訊息明講清單可能
過期」——大聲失敗,不靜默放行到身份不明的目標(fail-closed)。

測試:hooks/tests/leo21c-mcp-guard.test.sh 19/19 通過,涵蓋擋(真庫讀/寫)、
放行(測試場讀/寫)、不擋(whoami 三種座標)、不擋(無關工具)、擋(未知UUID)、
戳記單次放行+留痕。既有測試套件(ask-user-question/dispatch-format/
factory-idle/main-and-prod-push/prod-write/stage-before-prod/sdd-guard)
全部重跑一遍,均維持原本通過狀態,未受本次改動影響。

hooks.json 新增一條 PreToolUse matcher:.*mcp__[0-9a-fA-F-]+__(kbdb_|arcrun_).*
docs/hooks-inventory.md 補上新閘一行,並記錄目前實際磁碟數(49 檔/60 條)
跟這份表既有列表(47/57)之間還有既存落差(isep-presence-beacon.sh/
milestone-due-guard.sh 疑似兩支舊閘未收錄)——不是本票動的閘,未一併查證,
留給總管或後續票核實。

plugin.json 0.5.0 → 0.5.1(改完不升版沒人吃得到;check-version-consistency.sh
在本分支照慣例是紅的,tag 於 merge 時打)。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 15:01:29 +08:00
parent 8e28041ba4
commit b8060ca263
5 changed files with 293 additions and 6 deletions
+71
View File
@@ -0,0 +1,71 @@
#!/usr/bin/env bash
# leo21c-mcp-guard.sh 的迴歸測試(ISEP#69)。
HOOK="$1"
rm -f /tmp/.leo21c-mcp-ok 2>/dev/null # 起手清乾淨,避免上一輪殘留戳記污染這輪
mk(){ python3 -c "import json,sys;print(json.dumps({'tool_name':sys.argv[1],'tool_input':{}}))" "$1"; }
PASS=0; FAIL=0
t(){ # t <期望 block|pass> <說明> <tool_name> —— 除了明說放行那幾格,其餘每格開跑前先確保沒有殘留戳記
mk "$3" | "$HOOK" >/dev/null 2>&1; rc=$?
got=$([ $rc -eq 2 ] && echo block || echo pass)
if [ "$got" = "$1" ]; then echo "$2"; PASS=$((PASS+1))
else echo "$2 —— 期望 $1,實得 $got"; FAIL=$((FAIL+1)); fi
}
LEO="00402d88-5e2f-4125-b971-8bdac50d7456"
YOULIN="d0eaf65b-583e-464e-aacc-00527ac05c4b"
UNKNOWN="11111111-2222-3333-4444-555555555555"
echo "── 該擋(打向 leo21c 真庫)──"
t block "唯讀查詢打 leo21c" "mcp__${LEO}__kbdb_search"
t block "get_map 打 leo21c" "mcp__${LEO}__kbdb_get_map"
t block "寫入打 leo21c" "mcp__${LEO}__kbdb_create_record"
t block "推工作流打 leo21c" "mcp__${LEO}__arcrun_push_workflow"
echo "── 該放行(打向 youlin 測試場)──"
t pass "唯讀查詢打 youlin" "mcp__${YOULIN}__kbdb_search"
t pass "寫入打 youlin" "mcp__${YOULIN}__kbdb_create_record"
t pass "查 template 打 youlin" "mcp__${YOULIN}__kbdb_list_templates"
t pass "arcrun 讀清單打 youlin" "mcp__${YOULIN}__arcrun_list_workflows"
echo "── 不該擋:arcrun_whoami 本身(不管打哪條線)──"
t pass "whoami 打 leo21c" "mcp__${LEO}__arcrun_whoami"
t pass "whoami 打 youlin" "mcp__${YOULIN}__arcrun_whoami"
t pass "whoami 打未知 UUID" "mcp__${UNKNOWN}__arcrun_whoami"
echo "── 不該擋:非 kbdb/arcrun 的 MCP 工具、非 MCP 工具 ──"
t pass "無關的 MCP 工具" "mcp__${LEO}__some_other_tool"
t pass "一般工具名" "Bash"
t pass "另一個一般工具" "kbdb_search"
echo "── 該擋(清單以外的未知 UUID —— 對照表可能過期,大聲失敗)──"
t block "未知 UUID 的 kbdb_search" "mcp__${UNKNOWN}__kbdb_search"
t block "未知 UUID 的寫入" "mcp__${UNKNOWN}__kbdb_create_record"
echo "── 明說要打真庫:戳記放行(單次)──"
rm -f /tmp/.leo21c-mcp-ok
touch /tmp/.leo21c-mcp-ok
t pass "戳記在,打 leo21c 放行一次" "mcp__${LEO}__kbdb_search"
# 上面 t() 已經在下一次呼叫前 rm -f 戳記,這裡驗證戳記確實是單次
touch /tmp/.leo21c-mcp-ok
mk "mcp__${LEO}__kbdb_search" | "$HOOK" >/dev/null 2>&1
mk "mcp__${LEO}__kbdb_search" | "$HOOK" >/dev/null 2>&1; rc2=$?
if [ $rc2 -eq 2 ]; then echo " ✅ 戳記用完即消失(第二次仍被擋)"; PASS=$((PASS+1))
else echo " ❌ 戳記用完即消失 —— 期望第二次被擋,實得 pass"; FAIL=$((FAIL+1)); fi
rm -f /tmp/.leo21c-mcp-ok 2>/dev/null
echo "── 明說放行要留痕 ──"
LOGDIR="${CLAUDE_PROJECT_DIR:-$(pwd)}/.claude/hooks"
mkdir -p "$LOGDIR"
rm -f "$LOGDIR/leo21c-mcp-guard.log"
touch /tmp/.leo21c-mcp-ok
mk "mcp__${LEO}__kbdb_search" | "$HOOK" >/dev/null 2>&1
if [ -f "$LOGDIR/leo21c-mcp-guard.log" ] && grep -q "明說放行" "$LOGDIR/leo21c-mcp-guard.log"; then
echo " ✅ 明說放行有寫進 log"; PASS=$((PASS+1))
else
echo " ❌ 明說放行沒有留痕"; FAIL=$((FAIL+1))
fi
rm -f "$LOGDIR/leo21c-mcp-guard.log" /tmp/.leo21c-mcp-ok 2>/dev/null
echo
echo "結果:通過 $PASS 失敗 $FAIL"
[ $FAIL -eq 0 ] || exit 1