雲端要拿得到票、主線與通知:白名單住 ISEP、主線檔隨 repo 走、leo21c 讀放寫擋(inkstone/ISEP#130)

- docs/permissions-allow.json + scripts/settings-allow-sync:四個 Gitea 正門工具的權限白名單一份,
  setup script 裝完 plugin 寫一次、SessionStart 每次再對一次(只加不減、冪等)
- hooks/lib/mainline.py/scripts/mainline:家目錄沒主線就讀 InkStoneCo/system-dev/mainline.json;
  set/adopt/clear 兩份一起寫,refresh 只寫家目錄
- hooks/leo21c-write-guard.sh:唯讀 -d(tr/cut/sort…)先剪掉再判、notify_leo trigger 放行
  (與 prod-write-guard 同一份白名單)、改法段改印 09-02 起的 youlin 子網域;補第一支測試(26 條)
- prod-write-guard/main-and-prod-push-guard/kbdb-live-exam:認得 youlin 新子網域 arcrun-yuga3bse
- scripts/ticket:收件 repo 寫 inkstone/ISEP 不再 404(org 寫錯當場講)
- scripts/isep-notify:有 TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID 先走 Bot API 直送(雲端唯一通的路)
- 測試:A31–A35 共 79 條;README/plugin.json/hooks-inventory 數字實數(61 支、85 條、53 支腳本)

假設(記在這裡等 review):權限規則的形狀沿用 leo 09-07 親手加、實測有效的那四條;
「分類器真的不擋」要雲端一趟 run 的 permission_denials 才驗得到,本 PR 驗不了。
版本:待總管定版(plugin.json 仍 0.22.0)。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DTZ9QtvjY7MNxjfQbexAm7
This commit is contained in:
isep-hand
2026-09-07 01:17:15 +00:00
parent 605f1fe5a6
commit 65120a1c65
27 changed files with 1141 additions and 34 deletions
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env bash
# leo21c-write-guard.sh 的迴歸測試(inkstone/ISEP#130
#
# ── 這支在守什麼 ──────────────────────────────────────────────────────
# 閘的判準是「**讀可以,寫不行**」(leo 2026-08-20leo21c 是他本人在用的知識庫)。
# 2026-09-04 雲端 Routine 的 run log**讀 notify_leo 的定義被擋** ——
# 讀都讀不到,雲端就什麼都拿不到(inkstone/ISEP#130 開票的第一件)。
#
# 這支之前**沒有任何測試**,所以「讀會不會被誤攔」從來沒被驗過。
# 下面的測資是 Routine 文件(cloud-worker.mdprogress-guard.md)裡**真的會下的那幾條指令**,
# 不是想像出來的例子。
#
# 🔴 三個方向都要驗,缺一個就是假綠:
# A 讀 ⇒ 放行(含 `tr -d``cut -d=` 這種唯讀的 -d,那正是 09-04 被擋的形狀)
# B 寫 ⇒ 擋(心跳 POST/PATCH 收件匣/部署端點/別的 trigger)
# C 閘印出來的「改法」指的是**現役**的 youlin 網址,不是 09-02 已死的舊名
#
# 這支閘不寫任何檔案,直接跑真跡即可;全程離線。
# 用法:bash hooks/tests/leo21c-write-guard.test.sh
set -u
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
HOOK="$HERE/../leo21c-write-guard.sh"
PASS=0; FAIL=0
mk(){ python3 -c "import json,sys;print(json.dumps({'tool_name':'Bash','tool_input':{'command':sys.argv[1]}}))" "$1"; }
t(){ # t <期望 pass|block> <說明> <指令>
LAST_ERR=$(mk "$3" | bash "$HOOK" 2>&1 >/dev/null); rc=$?
got=$([ "$rc" -eq 2 ] && echo block || echo pass)
if [ "$got" = "$1" ]; then echo "$2"; PASS=$((PASS+1))
else echo "$2 —— 期望 $1,實得 $gotrc=$rc"; FAIL=$((FAIL+1)); fi
}
L='https://arcrun-cypher-executor.leo21c.workers.dev'
K='-H "X-Arcrun-API-Key: leo"'
D='-d'
echo "── A. 讀 ⇒ 放行(雲端 Routine 真的會下的形狀)──"
t pass "① 讀收件匣(progress-guard.md 步驟 0 原句)" "curl -s \"$L/kbdb/entries?entry_type=inbox&limit=20\" $K"
t pass "② 讀 notify_leo 定義(09-04 被擋的那一條)" "curl -s $L/webhooks/named/leo/notify_leo $K"
t pass "③ 讀 tr -d(唯讀的 -d,不是 body" "curl -s $L/workflows/notify_leo $K | tr $D '\\r'"
t pass "④ 讀 cut -d=" "NS=\$(grep ns cfg | cut ${D}= -f2); curl -s $L/kbdb/entries?limit=1 $K"
t pass "⑤ 讀 sort -d date -d" "curl -s $L/health | sort $D; date $D yesterday"
t pass "⑥ 明寫 -X GET" "curl -s -X GET $L/kbdb/entries?entry_type=dash_beat $K"
t pass "⑦ 讀心跳(progress-guard 2.6 原句)" "curl -s \"$L/kbdb/entries?entry_type=dash_beat&limit=30\" $K"
t pass "⑧ git commit 訊息提到 leo21c 與 POSTgit 寫不到那台)" "git commit -m 'fix: curl -X POST $L/kbdb/entries 會被擋'"
t pass "⑨ 修這道閘自己" "sed -i 's/a/b/' hooks/leo21c-write-guard.sh"
t pass "⑩ 打 youlin09-02 起的新子網域)POST,不是 leo21c" "curl -X POST https://arcrun-cypher-executor.arcrun-yuga3bse.workers.dev/webhooks/named $D '{}'"
t pass "⑪ 用 youlin 的 CF 帳號部署" "CLOUDFLARE_ACCOUNT_ID=1129efd7df2e8899d537e9c8fbabb6cb npx wrangler deploy"
t pass "⑫ 發一則 notify_leo(通知型 trigger,什麼都沒改;同 prod-write-guard 白名單)" "curl -s -X POST $L/webhooks/named/leo/notify_leo/trigger -H 'Content-Type: application/json' $D '{\"text\":\"[總管] hi\"}'"
echo "── B. 寫 ⇒ 擋 ──"
t block "⑬ 打心跳 POSTcloud-worker.md 步驟 0 原句——它寫進 leo 的庫)" "curl -s -X POST \"$L/kbdb/entries\" $K -H 'Content-Type: application/json' $D '{\"entry_type\":\"dash_beat\"}'"
t block "⑭ PATCH 收件匣" "curl -s -X PATCH \"$L/kbdb/entries/123\" $K $D '{\"content\":\"x\"}'"
t block "⑮ DELETE" "curl -X DELETE $L/kbdb/entries/1 $K"
t block "⑯ 只帶 -d 的隱式 POST" "curl $L/kbdb/entries $D '{}'"
t block "⑰ --data-urlencode" "curl $L/kbdb/entries --data-urlencode text=hi"
t block "⑱ --request PUT" "curl --request PUT $L/kbdb/entries/1 $D '{}'"
t block "⑲ 別的工作流 triggership_refresh_cdn 會動線上狀態)" "curl -X POST $L/webhooks/named/leo/ship_refresh_cdn/trigger $D '{}'"
t block "⑳ 部署端點(無名字的 named webhook" "curl -X POST $L/webhooks/named $D @def.yaml"
t block "㉑ notify_leo 旁邊夾一個部署端點" "curl -X POST $L/webhooks/named/leo/notify_leo/trigger $D '{}' && curl -X POST $L/webhooks/named $D @x"
t block "㉒ 寫 + tr -d(剪掉唯讀 -d 之後仍該擋)" "cat f | tr $D '\\r' | curl -X POST $L/kbdb/entries $D @-"
t block "㉓ 用 leo21c 的 CF 帳號部署" "CLOUDFLARE_ACCOUNT_ID=51a01bfa2665bd7bc3fd080dc40cf3e1 npx wrangler deploy"
echo "── C. 擋下時印的改法要指到活著的 youlin ──"
mk "curl -X POST $L/kbdb/entries $D '{}'" | bash "$HOOK" 2>"$HERE/.leo21c-msg.$$" >/dev/null
MSG=$(cat "$HERE/.leo21c-msg.$$"); rm -f "$HERE/.leo21c-msg.$$"
if printf '%s' "$MSG" | grep -q 'arcrun-cypher-executor.arcrun-yuga3bse.workers.dev'; then
echo " ✅ ㉔ 改法段印的是現役子網域 arcrun-yuga3bse"; PASS=$((PASS+1))
else echo " ❌ ㉔ 改法段沒有現役子網域 arcrun-yuga3bse"; FAIL=$((FAIL+1)); fi
if printf '%s' "$MSG" | grep -q 'cypher : https://arcrun-cypher-executor.youlin-hsieh-dev'; then
echo " ❌ ㉕ 改法段還教人打 09-02 已死的 youlin-hsieh-dev"; FAIL=$((FAIL+1))
else echo " ✅ ㉕ 改法段不再把死的舊名當網址教人"; PASS=$((PASS+1)); fi
if printf '%s' "$MSG" | grep -q '讀可以,寫不行'; then
echo " ✅ ㉖ 訊息講得出判準(讀可以,寫不行)"; PASS=$((PASS+1))
else echo " ❌ ㉖ 訊息沒講判準"; FAIL=$((FAIL+1)); fi
echo
echo "通過 $PASS 條,失敗 $FAIL"
[ "$FAIL" -eq 0 ]