agentsmithSmith
§ · Nested detail page

泄漏扫描与 Git guardrails 的闭环 深度解析

这个子页面拆安全面:secret-scan 看凭据形状,leak-gate 看人员和环境泄漏,install-git-hooks 把它们挂到 Git 生命周期。它们不是形式化安全证明,但能把最常见的公开仓库事故挡在提交前。

§ 00 · IMPORT CHAIN

导入链与契约

安全规则在 core 里写得很硬:core/20-principle-rules.md:54、core/20-principle-rules.md:60、core/20-principle-rules.md:64 规定 live secret 不进入 tracked file。脚本层用 scripts/secret-scan.sh:21、scripts/leak-gate.sh:97、scripts/install-git-hooks.sh:56 把这个规则落成扫描和 hook;这也呼应 core/20-principle-rules.md:65、core/20-principle-rules.md:66 的态度:发现 secret 要修复和清史,而不是用 allowlist 糊过去。

src/scripts/secret-scan.sh · L20-39Shell / Markdown
20# Generic high-signal secret patterns (shapes, not values).
21PATTERNS=(
22  'BEGIN [A-Z ]*PRIVATE KEY'                            # PEM private keys (RSA/OPENSSH/EC/bare/ENCRYPTED)
23  'AKIA[0-9A-Z]{16}'                                    # AWS access key id
24  'ASIA[0-9A-Z]{16}'                                    # AWS temp key id
25  'gh[pousr]_[A-Za-z0-9]{20,}'                          # GitHub tokens
26  'xox[baprs]-[A-Za-z0-9-]{10,}'                        # Slack tokens
27  'sk-[A-Za-z0-9]{20,}'                                 # OpenAI-style keys
28  'sk-ant-[A-Za-z0-9_-]{20,}'                           # Anthropic-style keys
29  'AIza[0-9A-Za-z_-]{35}'                               # Google API key
30  '(password|passwd|pwd|secret|token|api[_-]?key)["'"'"' ]*[:=][[:space:]]*["'"'"'][^"'"'"' ]{8,}'  # assigned secret literal
31)
32
33# Patterns go to a temp file so the whole scan is ONE grep pass (-f ORs every line of the file).
34# Matching per-line in a shell loop costs a subprocess per line per pattern — imperceptible on
35# Linux (~1ms spawns), minutes on Windows/Git Bash (~30ms). This script runs as a pre-commit
36# hook, and a hook slow enough to be disabled is a guardrail that isn't there (R8).
37PATTERN_FILE="$(mktemp)"
38trap 'rm -f "$PATTERN_FILE"' EXIT
39printf '%s\n' "${PATTERNS[@]}" > "$PATTERN_FILE"
§ 01 · CORE IMPL

核心实现,逐段拆

▸Secret scanner 的单 grep 策略

`secret-scan` 先把所有高信号 pattern 写进临时文件,再做一次 grep。scripts/secret-scan.sh:21、scripts/secret-scan.sh:37、scripts/secret-scan.sh:41、scripts/secret-scan.sh:65、scripts/secret-scan.sh:77 说明它为 pre-commit 性能做过取舍:一次扫描比逐行逐 pattern spawn 更适合 hooks。它不是单独存在的工具,而是在执行 core/20-principle-rules.md:54、core/20-principle-rules.md:56、core/20-principle-rules.md:57 的 no-secrets 规则。

src/scripts/secret-scan.sh · L41-85Shell / Markdown
41input_lines() {
42  if [ "${1:-}" = "--all" ]; then
43    # -n 64 batches files per shell instead of -I{} spawning one shell PER FILE. The trailing
44    # newline keeps a file that lacks one from welding its last line onto the next file's first.
45    git -C "$ROOT_DIR" ls-files -z \
46      | xargs -0 -n 64 sh -c 'for f in "$@"; do cat -- "$f"; printf "\n"; done' _ 2>/dev/null
47  elif [ "${1:-}" = "-" ]; then
48    cat
49  elif [ $# -gt 0 ]; then
50    cat "$@"
51  else
52    # staged diff: only ADDED lines (strip the leading +). The `|| true` keeps an empty or
53    # deletion-only staged diff from tripping `set -o pipefail` (grep exits 1 on no match),
54    # which previously produced a false BLOCKED when there was nothing to scan.
55    { git -C "$ROOT_DIR" diff --cached --no-color -U0 2>/dev/null | grep -E '^\+' | grep -Ev '^\+\+\+' | sed 's/^+//'; } || true
56  fi
57}
58
59# Drop allowlisted lines. Skipped entirely when the allowlist is absent or empty, so an empty
60# file can't turn into "match nothing / invert / drop everything" on a stricter grep.
61allow_filter() {
62  if [ -s "$ALLOW" ]; then grep -Evf "$ALLOW"; else cat; fi
63}
64
65scan() {
66  local hits line
67  # Blank lines out, allowlisted lines out, then every pattern matched in a single pass.
68  # `|| true` absorbs grep's exit-1-on-no-match, which pipefail would otherwise treat as failure.
69  hits="$(grep -Ev '^[[:space:]]*$' | allow_filter | grep -E -f "$PATTERN_FILE" || true)"
70  [ -n "$hits" ] || return 0
71  while IFS= read -r line; do
72    echo "  ✗ possible secret: $(echo "$line" | cut -c1-100)"
73  done <<<"$hits"
74  return 1
75}
76
77if input_lines "$@" | scan; then
78  echo "secret-scan: clean."
79  exit 0
80else
81  echo
82  echo "secret-scan: BLOCKED — a line looks like a live secret (Rule 8)."
83  echo "Move it to env/secret-manager, or (only for a genuine test fixture) add a regex to .harness/secret-scan.allow."
84  exit 1
85fi

▸Leak gate 为什么单独存在

`leak-gate` 补的是 secret shape 之外的环境泄漏。scripts/leak-gate.sh:31、scripts/leak-gate.sh:63、scripts/leak-gate.sh:73、scripts/leak-gate.sh:104、scripts/leak-gate.sh:110、scripts/leak-gate.sh:116 显示它显式处理姓名、IP、邮箱和 home path,同时拒绝把内部 hostname denylist 写进公开仓库。它还承担 core/20-principle-rules.md:68、core/20-principle-rules.md:72 的 research durability 约束:公开 surface 应该可分享,私有材料不能混进发布物。

src/scripts/leak-gate.sh · L73-129Shell / Markdown
73# check <label> <extended-regex> <exempt-regex|""> <hint>
74# Findings print as file:line:match, so <exempt-regex> can anchor on the match with $.
75check() {
76  local label="$1" ere="$2" exempt="$3" hint="$4" hits line
77  # -I skips binaries; -o prints just the match so exemptions test the match, not the whole
78  # line (a line holding both a safe and an unsafe value must still fail).
79  # `|| true` absorbs grep's exit-1-on-no-match, which pipefail would treat as a hard error.
80  hits="$(git grep -noIiE -- "$ere" -- . "${SCOPE[@]+"${SCOPE[@]}"}" 2>/dev/null || true)"
81  [ -n "$hits" ] || return 0
82  if [ -n "$exempt" ]; then
83    hits="$(printf '%s\n' "$hits" | grep -Evi "$exempt" || true)"
84    [ -n "$hits" ] || return 0
85  fi
86  hits="$(printf '%s\n' "$hits" | allow_filter || true)"
87  [ -n "$hits" ] || return 0
88  echo "  ✗ $label — $hint"
89  while IFS= read -r line; do
90    echo "      $line"
91  done <<<"$hits"
92  fail=1
93}
94
95# 1. The operator's identity. The evidenced failure: --operator-name "<author>" sat in the
96#    --help of both setup scripts, so every open-source user read a stranger's name.
97check "operator identity" \
98      "\\b(${TERMS})\\b" \
99      "" \
100      "a specific person's name — use the repo's placeholder convention (\"Your Name\", \"You\", \"you/...\")"
101
102# 2. Routable IPv4. Loopback/private/link-local/broadcast and the RFC 5737 documentation
103#    ranges are legitimate in docs and examples, so only a real, routable address fails.
104check "public IP address" \
105      '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' \
106      ':(0\.0\.0\.0|127\.[0-9.]+|10\.[0-9.]+|192\.168\.[0-9.]+|169\.254\.[0-9.]+|172\.(1[6-9]|2[0-9]|3[01])\.[0-9.]+|255\.255\.[0-9.]+|192\.0\.2\.[0-9]+|198\.51\.100\.[0-9]+|203\.0\.113\.[0-9]+)$' \
107      "a routable IP pins this to one environment — use a private/documentation range (192.0.2.x)"
108
109# 3. Real email addresses. example.com/.org/.net are the reserved documentation domains.
110check "email address" \
111      '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b' \
112      ':(noreply@|[A-Za-z0-9._%+-]+@(example\.(com|org|net)|host|localhost|domain\.com))' \
113      "a real address — use user@example.com"
114
115# 4. Absolute home directories. These pin a path to one machine and one username.
116check "absolute home path" \
117      '([A-Za-z]:\\Users\\[A-Za-z0-9._-]+|/home/[A-Za-z0-9._-]+|/Users/[A-Za-z0-9._-]+)' \
118      ':([A-Za-z]:\\Users\\(you|your-name|username|user|name)|/(home|Users)/(you|your-name|username|user|me|name))$' \
119      "a machine-specific path — use \$HOME or ~/"
120
121echo
122if [ "$fail" -eq 0 ]; then
123  echo "leak-gate: clean — the shipped surface names no operator and no environment."
124  exit 0
125fi
126echo "leak-gate: BLOCKED — the shipped surface leaks something specific to one operator/environment."
127echo "Replace it with the placeholder convention this repo already uses, or (only for a genuine"
128echo "false positive) add a regex to .harness/leak-gate.allow."
129exit 1
§ 02 · KEY FLOW

关键流程

▸Hook 写入如何避免覆盖用户脚本

staged diff / tracked tree → secret-scan patterns → leak-gate identity/env checks → install-git-hooks writes pre-commit/commit-msg/pre-push → verify can be wired into pre-push

hook writer 的关键在 scripts/install-git-hooks.sh:35、scripts/install-git-hooks.sh:45、scripts/install-git-hooks.sh:56、scripts/install-git-hooks.sh:64、scripts/install-git-hooks.sh:67:它备份 foreign hook,只写带 marker 的 managed hook,并按 flag 控制 pre-commit、commit-msg 和 pre-push。这个选择也符合 core/20-principle-rules.md:8、core/20-principle-rules.md:31、core/20-principle-rules.md:78:先理解已有东西、保持原子改动、控制工具表面积。

§ 03 · EDGE CASES

边界 / 失败模式

  • scripts/secret-scan.sh:30 的 assigned-secret 正则高信号但不可能覆盖所有语言和配置格式。
  • scripts/leak-gate.sh:36 解释了为什么不能把客户名清单提交进公开扫描器。
  • scripts/install-git-hooks.sh:13 允许 `--no-verify` 单次绕过,所以团队仍需要 code review 和 CI。
Sub-page · 2026-07-20 Source · PromptPartner/agentsmith ↑ agentsmith 主页面