fix(harness): guard hook 區分「執行 acr push」vs「展示指令」(壓測 §9.5)
舊版 grep "acr push" 連「在 echo/heredoc 裡提到 push 字串」都擋, 導致 CC 連「把可貼上的指令印給使用者看」都做不到 → 反而違反 user-cc-harness §0.5「擋下必須印出正路」自身鐵則(連選②都印不出)。 修法(啟發式,誠實標明非完美,mindset §7): - awk 抽掉 heredoc 主體 → 剩下「實際執行命令列」才看 push 是否在命令位置 (行首 / ; & | && ||)。真執行擋、純展示放行。 - block 訊息補上選②可貼指令(符合 §0.5)。 - shell 層無法 100% 區分執行/展示,偷渡(bash<<EOF / $(...))由上游安全分類器擋。 驗證:8/8 案例正確(真執行全 BLOCK、純展示全 ALLOW)+ 邊角;bash -n OK。 SDD: .agents/specs/user-cc-harness/tasks.md A6 + C9。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -28,10 +28,46 @@ block() {
|
|||||||
|
|
||||||
# ── 硬擋:未經人類同意的暴露動作(明確越界,mindset §5)──────────────
|
# ── 硬擋:未經人類同意的暴露動作(明確越界,mindset §5)──────────────
|
||||||
# 非互動環境下 CC 自己跑「部署對外 webhook / push recipe」= 替人類決定公開。
|
# 非互動環境下 CC 自己跑「部署對外 webhook / push recipe」= 替人類決定公開。
|
||||||
|
#
|
||||||
|
# 壓測 §9.5 修正(A6):舊版 grep "acr push" 連「在 echo/heredoc 裡**提到** push 字串」
|
||||||
|
# 都擋 → CC 連「把可貼上的指令印給使用者看」都做不到,反而違反 §0.5「擋下必指正路」鐵則。
|
||||||
|
# 故先判斷此命令是「展示指令」還是「真的執行 push」,只擋後者。
|
||||||
|
#
|
||||||
|
# 誠實限制(mindset §7):shell 命令層**無法 100% 乾淨區分**「執行」與「展示」
|
||||||
|
# (例:`echo "跑 acr push"` 與 `acr push` 對單純 grep 都命中)。以下是**啟發式**,
|
||||||
|
# 目的是降低誤殺、讓「印出正路指令」這條合法用途能通,不是完美防護。邊角情況可能漏判,
|
||||||
|
# 但漏判方向是「偏向放行展示」——而真正的暴露(執行)仍受 TTY/env 把關。
|
||||||
if echo "$CMD" | grep -qE "acr (push|recipe push)\b"; then
|
if echo "$CMD" | grep -qE "acr (push|recipe push)\b"; then
|
||||||
|
# 目標(壓測 §9.5):擋「真的執行 push」,放行「把 push 指令印給使用者看」。
|
||||||
|
#
|
||||||
|
# 作法:把 heredoc 主體(cat/印出用的多行文字)先抽掉,剩下的才是「實際會被 shell 執行的命令列」,
|
||||||
|
# 再看 push 是否出現在那裡的「命令位置」(行首 / ; & | && || 之後)。
|
||||||
|
# 執行 → 命中:`acr push x`、`cd f && acr push x`、`echo done; acr push x`
|
||||||
|
# 展示 → 不命中:`echo "跑 acr push x"`、`cat <<EOF\n acr push x \nEOF`(push 在 heredoc 主體內)
|
||||||
|
#
|
||||||
|
# 抽掉 heredoc 主體:刪掉從 `<<EOF`(或任何 <<TOKEN)那行的下一行起、到單獨一行 TOKEN 為止的內容。
|
||||||
|
# 用 awk 做簡單狀態機(只處理最常見的 `<<TOKEN` / `<<-TOKEN`,不含複雜巢狀——夠用且誠實)。
|
||||||
|
EXEC_PART=$(printf '%s\n' "$CMD" | awk '
|
||||||
|
BEGIN{inhd=0}
|
||||||
|
{
|
||||||
|
if (inhd) { if ($0 ~ "^[[:space:]]*" term "[[:space:]]*$") { inhd=0 }; next }
|
||||||
|
line=$0
|
||||||
|
if (match(line, /<<-?[[:space:]]*"?'"'"'?[A-Za-z_][A-Za-z0-9_]*"?'"'"'?/)) {
|
||||||
|
t=substr(line, RSTART, RLENGTH); gsub(/^<<-?[[:space:]]*["'"'"']?/, "", t); gsub(/["'"'"']$/, "", t)
|
||||||
|
term=t; inhd=1
|
||||||
|
}
|
||||||
|
print line
|
||||||
|
}')
|
||||||
|
|
||||||
|
# 誠實限制(mindset §7):shell 層無法 100% 乾淨區分「執行」與「展示」。awk heredoc 抽取只覆蓋
|
||||||
|
# 常見形式(`<<EOF` / `<<-EOF` / 引號 token);`bash <<EOF ... acr push` 這種「heredoc 內容其實
|
||||||
|
# 會被執行」的偷渡,這裡會誤放——但它已被上游安全分類器擋下(壓測 §9.6 實證)。取捨上偏向
|
||||||
|
# 「放行展示」以保住 §0.5「擋下也要能印出正路指令」這條合法用途,不假裝此啟發式完美。
|
||||||
|
if echo "$EXEC_PART" | grep -qE "(^|[;&|][[:space:]]*)acr[[:space:]]+(push|recipe[[:space:]]+push)\b"; then
|
||||||
if [ ! -t 0 ] && [ "${ARCRUN_HUMAN_CONFIRMED:-}" != "1" ]; then
|
if [ ! -t 0 ] && [ "${ARCRUN_HUMAN_CONFIRMED:-}" != "1" ]; then
|
||||||
block "在非互動環境自動執行暴露動作(acr push / recipe push 會讓東西可被外部呼叫)" \
|
block "在非互動環境自動執行暴露動作(acr push / recipe push 會讓東西可被外部呼叫)" \
|
||||||
"把這動作交給人類在終端機執行,或先讓使用者明示同意。不要替使用者決定公開。"
|
"交人類在終端機執行(真 TTY 會自動放行)。可把指令完整複製給使用者貼上自己跑:\`acr push <你的 workflow.yaml>\`。或使用者先在對話明示同意後親自於終端機執行。不要替使用者決定公開。"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user