b8060ca263
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>
72 lines
3.4 KiB
Bash
Executable File
72 lines
3.4 KiB
Bash
Executable File
#!/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
|