Commands: - /sync-upstream [--dry-run] — casdoor-internal upstream sync with commit classification - /check-migrations [repo|all] — Alembic/Prisma/Drizzle consistency checker (focuses on xiaoshou pending migrations) Specialist agents: - migration-reviewer — Critical/High/Low severity review for DB schema changes across all 6 repos (Alembic, Prisma, Drizzle, xorm Sync2, raw SQL) Playbooks: - playbooks/casdoor-upstream-rebase.md — quarterly upstream rebase flow with commit classification, batched merging, cross-repo JWT compat check, rollback criteria Hooks (active by default via settings.json): - .claude/hooks/pre-commit-check.sh — PreToolUse on Bash: * blocks inline secrets in command strings (10+ patterns: sk-ant-, ghp_, AKIA, PEM, etc.) * on git commit, scans staged diff for same patterns * blocks diffs > 5000 lines (override with [huge-diff-ok] in commit msg) - settings.json: wire PreToolUse hook
77 lines
2.7 KiB
Bash
Executable File
77 lines
2.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Claude Code PreToolUse hook:
|
||
# - 扫描 Bash 命令字符串本身是否含疑似密钥
|
||
# - 对 git commit,追加扫描 staged diff
|
||
# - 拒绝超大 diff(防止 Agent 一次改太多)
|
||
# 通过方式:exit 0;阻断:exit 2(Claude 会把 stderr 作为拒绝理由展示)
|
||
|
||
set -uo pipefail
|
||
|
||
input=$(cat)
|
||
command=$(echo "$input" | jq -r '.tool_input.command // ""' 2>/dev/null || echo "")
|
||
|
||
# 空命令直接放行(其他工具触发时 command 为空)
|
||
if [[ -z "$command" ]]; then
|
||
exit 0
|
||
fi
|
||
|
||
# 疑似密钥 / 凭证 模式
|
||
patterns=(
|
||
'sk-ant-[A-Za-z0-9_-]{20,}'
|
||
'ghp_[A-Za-z0-9]{30,}'
|
||
'github_pat_[A-Za-z0-9_]{40,}'
|
||
'xox[pbar]-[A-Za-z0-9-]{20,}'
|
||
'AKIA[0-9A-Z]{16}'
|
||
'AIza[0-9A-Za-z_-]{30,}'
|
||
'-----BEGIN[[:space:]]+(RSA|OPENSSH|EC|PGP|DSA|PRIVATE)[[:space:]]+PRIVATE'
|
||
'DefaultEndpointsProtocol=https;AccountName=[^;]+;AccountKey=[A-Za-z0-9+/=]{20,}'
|
||
'SharedAccessKey=[A-Za-z0-9+/=]{20,}'
|
||
'mongodb(\+srv)?://[^:]+:[^@]+@'
|
||
'postgres(ql)?://[^:]+:[^@]+@'
|
||
)
|
||
|
||
# 1) 检查命令字符串本身(捕获内联密钥,如 git commit -m "my key sk-ant-xxx")
|
||
for p in "${patterns[@]}"; do
|
||
if echo "$command" | grep -qE -- "$p"; then
|
||
echo "[hook-block] Bash 命令字符串中检测到疑似密钥 / 连接串。已阻断。" >&2
|
||
echo "[hook-block] 匹配模式: $p" >&2
|
||
exit 2
|
||
fi
|
||
done
|
||
|
||
# 2) 如果是 git commit,追加扫描 staged diff
|
||
if [[ "$command" =~ (^|[[:space:];&|])git[[:space:]]+commit ]]; then
|
||
# 尝试提取 "cd <path> &&" 前缀,否则用 $PWD
|
||
cd_path=$(echo "$command" | grep -oE '^[[:space:]]*cd[[:space:]]+[^&;]+' | awk '{print $2}' | tr -d '\n' || true)
|
||
pushd_done=0
|
||
if [[ -n "$cd_path" && -d "$cd_path" ]]; then
|
||
pushd "$cd_path" >/dev/null 2>&1 && pushd_done=1
|
||
fi
|
||
|
||
diff_content=$(git diff --cached 2>/dev/null || true)
|
||
|
||
for p in "${patterns[@]}"; do
|
||
if echo "$diff_content" | grep -qE -- "$p"; then
|
||
echo "[hook-block] Staged diff 包含疑似密钥。请先 'git restore --staged <file>' 移除涉事文件。" >&2
|
||
echo "[hook-block] 匹配模式: $p" >&2
|
||
[[ $pushd_done -eq 1 ]] && popd >/dev/null 2>&1
|
||
exit 2
|
||
fi
|
||
done
|
||
|
||
# 超大 diff 保护
|
||
diff_lines=$(echo "$diff_content" | wc -l | tr -d ' ')
|
||
if [[ "$diff_lines" =~ ^[0-9]+$ ]] && [[ $diff_lines -gt 5000 ]]; then
|
||
echo "[hook-block] Staged diff 超过 5000 行 (实际 $diff_lines 行)。请拆分为更小粒度的提交。" >&2
|
||
echo "[hook-block] 若确实是合法的大改动,绕过:在 commit message 加 '[huge-diff-ok]'" >&2
|
||
if ! echo "$command" | grep -q '\[huge-diff-ok\]'; then
|
||
[[ $pushd_done -eq 1 ]] && popd >/dev/null 2>&1
|
||
exit 2
|
||
fi
|
||
fi
|
||
|
||
[[ $pushd_done -eq 1 ]] && popd >/dev/null 2>&1
|
||
fi
|
||
|
||
exit 0
|