Files
patbond-doc/scripts/check-secrets.sh
T
lixi 8f97e34900
CI / docs-build (push) Successful in 47s
fix(security): 凭证扫描补 AI 服务商密钥形态与 api_key 赋值(ADR-021)
M4 将引入 AI 服务商凭证,而现行规则表对其完全不覆盖。夹具实测:
5 种真实形态(ANTHROPIC_API_KEY=sk-ant-…、OPENAI_API_KEY=sk-proj-…、
openai_api_key: "sk-…"、api_key = "…"、api-token: "…")修复前全部
漏网退出 0,修复后全部拦下。

- 新增 AK-ANTHROPIC(sk-ant-)、AK-OPENAI(sk- / sk-proj- / sk-svcacct-)
- KEY-ASSIGN 扩至 api[-_]?(key|secret|token) 与 auth[-_]?token
  (原模式只认 access_key / secret_key 两族)
- ALLOW 补 x{3,}:使「讲解规则的文档」不被规则自身拦下
- git-workflow.md 覆盖面概述同步(细节仍以脚本规则表为唯一来源)

验证:占位夹具仍放行;三仓 --all 全绿零误报;三仓副本 md5 同构
640994c12bfa78b4512e872d709ed685;mkdocs --strict exit 0。

依据 iteration-4/08 §漏检清单(决策 G),须先于任何 AI key 落地。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 15:57:05 +08:00

109 lines
4.3 KiB
Bash
Executable File
Raw 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.
#!/bin/sh
# check-secrets.sh —— 凭证防泄漏检查(ADR-021,规范见 patbond-doc docs/development/git-workflow.md
#
# 规则单一来源:本地 pre-commit 与 CI 兜底跑的是同一个脚本、同一张规则表。
# 三仓(patbond-api / patbond-flutter / patbond-doc)各存一份同构副本,改规则时三仓同步。
#
# 用法:
# sh scripts/check-secrets.sh --staged # pre-commit:扫暂存区内容(经 scripts/hooks/pre-commit 调用)
# sh scripts/check-secrets.sh --all # CI 兜底 / 手动自查:扫全部已跟踪文件(缺省模式)
# sh scripts/check-secrets.sh <文件...> # 扫指定文件
#
# 拦下真实凭证时的第一动作:去云控制台轮换/禁用该密钥,然后才是清理提交。
set -u
mode="${1:---all}"
# 允许清单:行内出现任一形态即放行(${} 注入、占位值、明显示例值)
# 注:x{3,}xxx / xxxx…)是本项目文档描述凭证形态时的既有占位约定,
# 放行它使「讲解规则的文档」不会被规则自身拦下(如 iteration-4/08 §漏检清单)。
ALLOW='\$\{[^}]*\}|\{\{[^}]*\}\}|changeme|change[-_]me|your[-_][a-zA-Z0-9_-]+|<[a-zA-Z0-9 ,_.-]+>|placeholder|example|sample|dummy|fake|redacted|\*\*\*|x{3,}'
# 内容扫描跳过:本脚本与 hook 自身(含规则文本,非凭证)
SKIP_PATHS='(^|/)scripts/(check-secrets\.sh|hooks/pre-commit)$'
# 文件名黑名单:凭证载体文件本体禁止入库(.sample/.example 除外)
DENY_NAME='(^|/)\.env(\.[^/]+)?$|(^|/)credentials[^/]*$|[Aa]ccess[Kk]eys?[^/]*\.csv$|(^|/)rootkey\.csv$'
DENY_NAME_OK='\.(sample|example)$'
# 规则表:ID<TAB>大小写旗标(i=忽略大小写,-=敏感)<TAB>文件范围ERE(-=全部文件)<TAB>行模式ERE
RULES=$(cat <<'EOF'
AK-AWS - - AKIA[0-9A-Z]{16}
AK-QCLOUD - - AKID[0-9A-Za-z]{16,}
AK-ALIYUN - - LTAI[0-9A-Za-z]{12,}
AK-ANTHROPIC - - sk-ant-[A-Za-z0-9_-]{16,}
AK-OPENAI - - sk-(proj-|svcacct-)?[A-Za-z0-9]{20,}
MINIO-DEFAULT i - minio[-_.]?admin
PRIVATE-KEY - - ^[[:space:]]*-----BEGIN [A-Z ]*PRIVATE KEY-----[[:space:]]*$
KEY-ASSIGN i - (access[-_]?key(_?id)?|secret[-_]?(access[-_]?)?key|api[-_]?(key|secret|token)|auth[-_]?token)["']?[[:space:]]*[:=][[:space:]]*["']?[A-Za-z0-9+/=_-]{8,}
JWT-SECRET i - (jwt[-_.]?secret|signing[-_]?key|token[-_]?secret|hmac[-_]?(key|secret))["']?[[:space:]]*[:=][[:space:]]*["']?[A-Za-z0-9+/=_-]{8,}
DB-PASSWORD i \.(ya?ml|properties|toml|conf|ini)(\.sample|\.example)?$ (password|passwd|pwd)["']?[[:space:]]*[:=][[:space:]]*["']?[^[:space:]"'$]{6,}
EOF
)
case "$mode" in
--staged)
files=$(git diff --cached --name-only --diff-filter=ACM)
src=index
;;
--all)
files=$(git ls-files)
src=worktree
;;
-*)
echo "用法: $0 [--staged|--all|<文件...>]" >&2
exit 2
;;
*)
files=$(printf '%s\n' "$@")
src=worktree
;;
esac
[ -n "$files" ] || exit 0
tmp=$(mktemp) || exit 2
viol=$(mktemp) || exit 2
trap 'rm -f "$tmp" "$viol"' EXIT
# 第一道:文件名黑名单
printf '%s\n' "$files" | grep -E "$DENY_NAME" | grep -vE "$DENY_NAME_OK" |
sed 's/^/[NAME-DENY] /' >>"$viol" || true
# 第二道:逐文件逐规则内容扫描(二进制文件经 grep -I 自然跳过)
IFS='
'
for f in $files; do
printf '%s' "$f" | grep -qE "$SKIP_PATHS" && continue
if [ "$src" = index ]; then
git show ":$f" >"$tmp" 2>/dev/null || continue
else
[ -f "$f" ] || continue
cat -- "$f" >"$tmp"
fi
printf '%s\n' "$RULES" | while IFS="$(printf '\t')" read -r id flag scope pat; do
[ -n "$id" ] || continue
if [ "$scope" != "-" ]; then
printf '%s' "$f" | grep -qE "$scope" || continue
fi
ci=""
[ "$flag" = "i" ] && ci="-i"
grep -InE $ci -e "$pat" "$tmp" 2>/dev/null | grep -viE "$ALLOW" |
sed "s|^|[$id] $f:|" >>"$viol" || true
done
done
if [ -s "$viol" ]; then
echo "凭证防泄漏检查未通过(ADR-021)——以下内容疑似真实凭证:" >&2
cat "$viol" >&2
cat >&2 <<'MSG'
处置:
1. 若是真实凭证:先去云控制台轮换/禁用该密钥,再从提交中移除;
2. 若是误报:改用 ${} 注入或占位值(changeme / your-xxx / <占位>),
或与团队确认后调整三仓同构的 scripts/check-secrets.sh 规则表。
敏感信息只允许存在于被 gitignore 的文件或 .sample 占位中(git-workflow.md)。
MSG
exit 1
fi
exit 0