- 新增 scripts/check-secrets.sh 与 scripts/hooks/pre-commit(与 api/flutter 同构,规则单一来源) - ci.yml 在 checkout 后新增 Secret scan step;mkdocs build --strict 通过 - git-workflow.md 新增「凭证防泄漏检查」节:两层机制、启用命令、允许清单边界、真凭证轮换优先原则 - 验收:全仓 74 个已跟踪文件扫描零误报 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,10 @@ jobs:
|
||||
git remote add origin "$AUTH_URL/${{ github.repository }}.git"
|
||||
git fetch -q --depth 1 origin "+${{ github.ref }}:refs/ci-head"
|
||||
git checkout -q refs/ci-head
|
||||
# 凭证防泄漏兜底(ADR-021):与本地 pre-commit 同一脚本、同一规则表,
|
||||
# 扫全部已跟踪文件(覆盖本次 push 变更的超集),纯 shell 零外部依赖。
|
||||
- name: Secret scan
|
||||
run: sh scripts/check-secrets.sh --all
|
||||
- name: Install mkdocs
|
||||
run: |
|
||||
apt-get update -qq
|
||||
|
||||
@@ -33,6 +33,26 @@ feat: 迁移珊瑚橙主题体系并新增认证基础组件(ADR-005)
|
||||
- **不修改已推送的 Flyway 迁移**(呼应开发计划 4.3 节):`V1__*.sql` 等已进入 `dev` 的版本化迁移视为不可变,schema 变更一律新增 `V<n+1>__*.sql`。
|
||||
- 不改写已推送的提交历史(rebase/amend 仅限未推送内容)。
|
||||
|
||||
## 凭证防泄漏检查(ADR-021)
|
||||
|
||||
两层检查共用同一规则表,单一来源为各仓入库的 `scripts/check-secrets.sh`(纯 shell,零外部依赖;三仓副本内容同构,调整规则时三仓同步提交):
|
||||
|
||||
1. **本地 pre-commit(推荐,每人每仓启用一次)**:
|
||||
|
||||
```bash
|
||||
cd <你的工作区>/<仓名>
|
||||
git config core.hooksPath scripts/hooks
|
||||
```
|
||||
|
||||
之后每次 `git commit` 自动扫描暂存区内容与文件名。注意 `core.hooksPath` 会整体接管 hooks 目录(当前三仓无其他自定义 hook)。`git commit --no-verify` 可绕过,但仅限确认误报时使用——CI 兜底仍会拦。
|
||||
2. **CI 兜底(强制)**:三仓 `ci.yml` 在 checkout 后的首个 step 运行同一脚本的 `--all` 模式,对全部已跟踪文件扫描(本次 push 变更文件的超集),命中即红,禁止合入。
|
||||
|
||||
规则覆盖(细节以脚本内规则表为准,不在文档重复维护,避免两处漂移):云厂商 AccessKey 形态(AWS/腾讯云/阿里云前缀)、MinIO 默认凭证、独立成行的私钥 PEM 头、access/secret key 与 JWT/签名密钥的实值赋值、配置类文件中非 `${}` 注入形态的数据库口令、`.env`/credentials/密钥导出 CSV 文件本体误入版本库。允许清单:`${}` 注入形态、占位值(changeme、your-xxx、`<占位>` 等)与明显示例值——配置真实值仍只允许存在于被 gitignore 的文件中,占位只进 `.sample`。
|
||||
|
||||
手动全量自查:`sh scripts/check-secrets.sh --all`(在仓库根目录执行)。
|
||||
|
||||
**拦下真实云凭证后的第一动作是去云控制台轮换/禁用该密钥**,之后才是清理提交历史——只清历史不轮换等于没有处理。
|
||||
|
||||
## 提交前本地门禁(未来 CI 将执行同一清单)
|
||||
|
||||
| 仓库 | 必跑命令 | 通过标准 |
|
||||
|
||||
Executable
+104
@@ -0,0 +1,104 @@
|
||||
#!/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}"
|
||||
|
||||
# 允许清单:行内出现任一形态即放行(${} 注入、占位值、明显示例值)
|
||||
ALLOW='\$\{[^}]*\}|\{\{[^}]*\}\}|changeme|change[-_]me|your[-_][a-zA-Z0-9_-]+|<[a-zA-Z0-9 ,_.-]+>|placeholder|example|sample|dummy|fake|redacted|\*\*\*'
|
||||
|
||||
# 内容扫描跳过:本脚本与 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,}
|
||||
MINIO-DEFAULT i - minio[-_.]?admin
|
||||
PRIVATE-KEY - - ^[[:space:]]*-----BEGIN [A-Z ]*PRIVATE KEY-----[[:space:]]*$
|
||||
KEY-ASSIGN i - (access[-_]?key(_?id)?|secret[-_]?(access[-_]?)?key)["']?[[: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
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
# pre-commit —— 凭证防泄漏(ADR-021)。启用(每人每仓一次):
|
||||
# git config core.hooksPath scripts/hooks
|
||||
# 注意:core.hooksPath 会整体接管 hooks 目录;本仓无其他自定义 hook。
|
||||
repo_root=$(git rev-parse --show-toplevel) || exit 1
|
||||
exec sh "$repo_root/scripts/check-secrets.sh" --staged
|
||||
Reference in New Issue
Block a user