新增 Edit/Write/MultiEdit 事件攔截(原僅攔截 git commit Bash 指令), 補齊 getenv fallback 模式偵測,防止 hardcoded Token 透過工具直寫入檔案。 - .claude/hooks/commit-quality.js: 改寫為 PreToolUse JSON 格式,覆蓋 Edit/Write/MultiEdit - .claude/settings.json: 新增 Edit|Write|MultiEdit|Bash matcher 註冊 - .claude/hooks/__test__/commit-quality.test.sh: 4 case 自動化測試 - docs/guides/DISK_EXPANSION_GUIDE.md: 磁碟擴充 SOP 歸檔 - docs/p9_completion_report_*.md: P9-1 + P9-2 Sprint 完成報告 - docs/refactor/callback_prefix_proposal.md: 308 按鈕回呼前綴分析(Method C) - docs/refactor/openclaw_bot_routes_split_plan.md: 5999 行神檔拆分計畫 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
59 lines
2.4 KiB
Bash
Executable File
59 lines
2.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# commit-quality.js 單元測試
|
||
# 4 case:Bash git commit w/ staged token / Edit token / Edit getenv fallback / Edit 一般程式碼
|
||
set -u
|
||
HOOK="$(cd "$(dirname "$0")/.." && pwd)/commit-quality.js"
|
||
PASS=0; FAIL=0
|
||
|
||
# 真實格式 Telegram Token(測試字串,非活躍憑證)
|
||
TOKEN='8610496165:AAFOlcWV4oRUSC2TI-fYux7JV97fjNzsYR8'
|
||
|
||
run_case() {
|
||
local name="$1"; local input="$2"; local expect="$3" # expect: allow|deny
|
||
local out
|
||
out=$(printf '%s' "$input" | node "$HOOK" 2>/dev/null)
|
||
local decision
|
||
decision=$(printf '%s' "$out" | node -e "let s='';process.stdin.on('data',c=>s+=c).on('end',()=>{try{console.log(JSON.parse(s).hookSpecificOutput.permissionDecision)}catch(e){console.log('parse-error')}})")
|
||
if [[ "$decision" == "$expect" ]]; then
|
||
echo "PASS $name -> $decision"
|
||
PASS=$((PASS+1))
|
||
else
|
||
echo "FAIL $name -> got=$decision expect=$expect"
|
||
echo " raw=$out"
|
||
FAIL=$((FAIL+1))
|
||
fi
|
||
}
|
||
|
||
# ---- case1: Bash git commit,staged 含 token ----
|
||
# 先做一個暫存 repo
|
||
TMP=$(mktemp -d)
|
||
pushd "$TMP" >/dev/null
|
||
git init -q
|
||
git config user.email t@t; git config user.name t
|
||
printf "TOKEN=%s\n" "$TOKEN" > leak.py
|
||
git add leak.py
|
||
INPUT1=$(printf '{"tool_name":"Bash","tool_input":{"command":"git commit -m x"}}')
|
||
decision=$(printf '%s' "$INPUT1" | node "$HOOK" 2>/dev/null | node -e "let s='';process.stdin.on('data',c=>s+=c).on('end',()=>{console.log(JSON.parse(s).hookSpecificOutput.permissionDecision)})")
|
||
if [[ "$decision" == "deny" ]]; then echo "PASS case1 Bash git commit staged token -> deny"; PASS=$((PASS+1)); else echo "FAIL case1 -> $decision"; FAIL=$((FAIL+1)); fi
|
||
popd >/dev/null
|
||
rm -rf "$TMP"
|
||
|
||
# ---- case2: Edit new_string 有 token ----
|
||
run_case "case2 Edit new_string token" \
|
||
"{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"/x/y.py\",\"old_string\":\"a\",\"new_string\":\"TOKEN='${TOKEN}'\"}}" \
|
||
deny
|
||
|
||
# ---- case3: Edit 含 os.getenv fallback default(35 chars secret) ----
|
||
run_case "case3 Edit getenv fallback" \
|
||
"{\"tool_name\":\"Edit\",\"tool_input\":{\"file_path\":\"/x/y.py\",\"old_string\":\"a\",\"new_string\":\"TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', 'AAFOlcWV4oRUSC2TI-fYux7JV97fjNzsYR8')\"}}" \
|
||
deny
|
||
|
||
# ---- case4: Edit 一般程式碼 ----
|
||
run_case "case4 Edit plain code" \
|
||
'{"tool_name":"Edit","tool_input":{"file_path":"/x/y.py","old_string":"a","new_string":"def add(x, y):\n return x + y"}}' \
|
||
allow
|
||
|
||
echo "----"
|
||
echo "PASS=$PASS FAIL=$FAIL"
|
||
[[ $FAIL -eq 0 ]]
|