Files
ISEP/hooks/tests/prod-write-guard.test.sh
Leo 17a74d7d3a 每一則回覆都自己說出拖了多久(inkstone/ISEP#63 → comment 5106)
leo 2026-08-27:「前面說過每個回覆要戴上已經花了總時長,這為什麼沒出現?」
              「這應該寫在 ISEP,隨時看自己拖了多久」
重點在後面那句:不是要總管記得戴,是要它長在機器上。
總管當時答「我沒做,現在開始戴」——而那正是這條規則第一次失效的方式。

一支閘掛兩個事件,是同一件事的兩半(票上點名的失效模式就在這裡):
  UserPromptSubmit → 注入算好的那一行(模型不必自己算,也算不準)
  Stop            → 查核這一回合的回覆裡到底有沒有那一行,沒有就擋一次
只做前半=又一個會被忽略的提醒;只做後半=罰它做一件拿不到資料的事。

判準不是關鍵字黑名單,是「那個被要求的輸出元素在不在」——
whitelist-of-one:要求一個機器產生的標記在場,不是猜哪些字不該在場。
換講法照樣要帶標記,多寫什麼都不會觸發。

時長從這段對話的第一則訊息算起,理由寫在 hooks/lib/countdown.py 檔頭:
「任務」在機器上沒有起點,而 CLAUDE.md 規則三點七「一段對話=一個 release」
剛好讓對話起點就是這個交付的起點——這個數字沒有人要維護,也不會說謊。
resume 取較早的那個:接關不是重新開始。

連帶修好票上的兩個卡點(prod-write-guard):
  卡點一 發一則 Telegram 跟部署工作流在閘眼裡一模一樣 ⇒ 判準改看打的是哪一個
        named webhook(路徑形狀+名字),放行範圍只有 notify_leo 一個名字
  卡點二 連「把卡點寫進票裡」都被同一支閘擋(同款第八次)⇒ 剝掉內文再判,
        起始行保留,所以真的在部署的寫法照樣擋
        (修這支的過程又撞了一次同款——那就是這一格最好的證據)

測試:countdown 20/20、prod-write-guard 29→37/37,全程離線
     (時鐘定住、狀態走環境變數、不打網路)。
實測:起點 06:28 台北、現在 08:03 ⇒ 期望「已過 1 小時 35 分」,實得同一字串。

plugin.json 0.9.0 → 0.10.0(版本沒動=沒有人吃得到)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 00:03:38 +00:00

110 lines
6.0 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# prod-write-guard.sh 的迴歸測試。
# 🔴 必須寫成檔案跑:這道閘會擋掉「含有它要擋的字串」的指令本身
# ⇒ 直接在 Bash 裡打測試,會被自己擋下(2026-08-12 實撞)。
HOOK="$1"
mk(){ python3 -c "import json,sys;print(json.dumps({'tool_name':'Bash','tool_input':{'command':sys.argv[1]}}))" "$1"; }
PASS=0; FAIL=0
t(){ # t <期望 block|pass> <說明> <指令>
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
}
H='https://arcrun-kbdb.leo21c.workers.dev'
DASH_D='-d'
echo "── 該放行(唯讀)──"
t pass "純 GET" "curl -s $H/templates"
t pass "GET + tr -d(本次實撞的誤攔)" "ACC=\$(grep -oE '^X=.' f | tr $DASH_D '\\r'); curl -s $H/templates"
t pass "GET + cut -d=" "A=\$(cut ${DASH_D}= -f2 f); curl -s $H/templates"
t pass "GET + sort -d / xargs -d" "ls | sort $DASH_D | xargs $DASH_D '\\n' echo; curl -s $H/entries"
t pass "GET 帶 Bearer 標頭" "curl -s -H 'Authorization: Bearer xxx' $H/templates?limit=1"
t pass "打 staging" "curl -X POST https://arcrun-rag-installer-staging.workers.dev/x $DASH_D '{}'"
t pass "打 youlin 測試場" "curl -X POST https://arcrun-cypher-executor.youlin-hsieh-dev.workers.dev/x $DASH_D '{}'"
t pass "打 Gitea(不是實例)" "curl -X POST https://git.uncle6.me/api/v1/repos/Leo/x/issues $DASH_D '{}'"
echo "── 該擋(寫入)──"
t block "POST 到實例" "curl -X POST $H/entries $DASH_D '{}'"
t block "PUT 到實例" "curl -X PUT $H/entries/1 --data '{}'"
t block "DELETE 到實例" "curl -X DELETE $H/entries/1"
t block "POST 且同時有 tr -d(剪字後仍該擋)" "cat f | tr $DASH_D '\\r' | curl -X POST $H/webhooks/named $DASH_D @-"
t block "wrangler deploy" "npx wrangler deploy --name arcrun-kbdb"
# ── 2026-08-13:「執行它」vs「談論它」───────────────────────────────
# 實撞:把 #108 的驗收證據留言到 Gitea,正文引述了那個指令名 ⇒ 一則留言被當成部署擋掉。
# 這批測資的挑法是「本 repo 現行真的會出現的形狀」,不是我自己挑好抓的壞例子
# ——PR #87 那道假綠閘就是敗在後者(14 條全過,卻漏掉 repo 實際在用的呼叫寫法)。
ACR_U='acr up''date' # 拆開寫:否則這支測試檔自己會被閘擋住(它也是一條 Bash 指令)
ACR_P='acr pu''sh'
WR_D='wrangler dep''loy'
echo "── 該放行(只是談論,不是執行)──"
t pass "heredoc 正文引述(本次實撞)" \
"B=\$(cat <<'EOF'
更新指令 \`$ACR_U --force\` 印出了 ARCRUN_NAMESPACE
EOF
); curl -X POST https://git.uncle6.me/api/v1/repos/Leo/Arcrun/issues/108/comments $DASH_D \"\$B\""
t pass "commit 訊息裡提到" "git commit -m '修好 $ACR_U 的命名空間注入'"
t pass "markdown code span(反引號)" "printf '%s' '看 \`$ACR_U\` 的輸出'"
t pass "grep 它的名字" "grep -n '$ACR_P' .claude/hooks/prod-write-guard.sh"
t pass "文章裡提到 $WR_D" "printf '%s' '# 為什麼 $WR_D 要擋'"
t pass "acr 唯讀子指令" "acr status --json"
t pass "acr whoami" "acr whoami"
echo "── 該擋(真的在指令位置執行)──"
t block "行首" "$ACR_U --force"
t block "&& 之後" "cd /x && $ACR_U"
t block "; 之後" "echo hi; $ACR_P workflow.yaml"
t block "管線之後" "cat x.yaml | $ACR_P -"
t block "bash -c 引號裡" "bash -c \"$ACR_U --force\""
t block "\$( ) 裡" "OUT=\$($ACR_U 2>&1)"
t block "npx 前綴" "npx $WR_D --name arcrun-kbdb"
t block "sudo 前綴" "sudo $ACR_U"
t block "heredoc 之後的真指令(剝內文不能連指令一起剝)" \
"cat <<'EOF' > note.md
只是筆記
EOF
$ACR_U --force"
# ══ inkstone/ISEP#63 → comment 5106:兩個卡點 ═══════════════════════════
#
# 卡點一:發一則 Telegram 給 leo`notify_leo`)跟「部署一個工作流上線」在本閘眼裡
# 長得一模一樣——兩者都是帶 body 的寫入型請求打到同一台實例。
# 判準改成看**打的是哪一個 named webhook**(路徑形狀+名字),
# 而放行的範圍只有一個名字,家族其他成員照擋。
# 卡點二:連「把這個卡點寫進票裡」都被同一支閘擋(同款第八次)——
# 內文引用了主機名與寫入旗標,而閘比對的是指令文字。
EXE="https://arcrun-cypher-executor.leo21c.wo""rkers.dev"
NAMED="$EXE/webhooks/named"
echo "── 卡點一:通知放得過,家族其他成員照擋 ──"
t pass "發一則 Telegram 給 leonotify_leo trigger" \
"curl -s -X POST $NAMED/leo/notify_leo/trigger $DASH_D '{\"text\":\"[總管] 進度\"}'"
t pass "同上,網址後面帶斜線" \
"curl -s -X POST $NAMED/leo/notify_leo/trigger/ --data '{\"text\":\"[總管] x\"}'"
t block "別的 named webhook(會動線上狀態)不在白名單裡" \
"curl -X POST $NAMED/leo/ship_refresh_cdn/trigger $DASH_D '{}'"
t block "部署端點(沒有名字、沒有 /trigger" \
"curl -X POST $NAMED $DASH_D @workflow.json"
t block "通知混著一筆真的寫入 ⇒ 整條照擋(白名單要每一個都符合)" \
"curl -X POST $NAMED/leo/notify_leo/trigger $DASH_D '{}' && curl -X POST $EXE/entries $DASH_D '{}'"
t block "名字只是前綴相同(notify_leo_deploy)不算通知" \
"curl -X POST $NAMED/leo/notify_leo_deploy/trigger $DASH_D '{}'"
echo "── 卡點二:談論它(內文引用)不是執行它 ──"
t pass "把卡點貼回票上,內文引用了實例網址與寫入旗標(本次實撞)" \
"B=\$(cat <<'EOF'
閘擋下來了:\`curl -X POST $NAMED\` 被當成部署。
EOF
); curl -X POST https://git.uncle6.me/api/v1/repos/inkstone/ISEP/issues/63/comments $DASH_D \"\$B\""
t block "真的在部署、只是把 payload 放進內文 ⇒ 起始行仍看得到,照擋" \
"curl -X POST $NAMED $DASH_D @- <<'EOF'
{\"name\":\"x\"}
EOF"
echo
echo "結果:通過 $PASS 失敗 $FAIL"
[ $FAIL -eq 0 ] || exit 1