SOP:glob-grep-guard 升级为「强制拦截」(2026-10-07)¶
1. 背景¶
规则写明「检索与列举:限定目录、排除依赖目录、探一级目录用 ls -1d */、结果设上限」,但复测证伪了条款效力:
**/*被.venv污染至少复现 2 次(其中一次撞 200 条结果上限);LS根目录「连犯两次」(4 万字符截断)。
根因与 merge-guard 同一条——缺必然触发点。该 Hook 原本只做提醒;本次按用户要求("能强制的强制")升级为强制拦截,与 merge-guard 用同一套机制。
2. 目标¶
- 判据 G(新):
LS且path为空或过浅(/、~、项目根及其上两级)→ 强制; - 判据 H(升级):
Glob/Grep的pattern/glob以**或*开头且未给path→ 强制(原为提醒); - 复用
merge-guard的 JSON deny + 指纹放行 + 每会话上限 3 次; - 误判只浪费一次往返,绝不瘫痪会话。
3. 前置条件¶
- 备份点:
backup/glob-grep-guard/glob-grep-guard.py.bak.20261007-005534、backup/trae-cn/hooks.json.bak.20261007-005745; - 已知:改脚本即时生效;改
hooks.json须重启 Trae。
4. 影响范围¶
| 对象 | 改动 | 生效方式 |
|---|---|---|
glob-grep-guard/glob-grep-guard.py |
重写(判据 G/H + 强制 + 放行) | 即时 |
~/.trae-cn/hooks.json |
PreToolUse 新增 matcher: "LS" 指向同一脚本 |
须重启 Trae |
影响面:Glob / Grep / LS 三类工具调用。不涉及服务与在跑作业。
5. 分步操作¶
步骤 1:备份¶
cd /home/rain/Documents/services/token_proxy
TS=$(date +%Y%m%d-%H%M%S)
mkdir -p glob-grep-enforce-20261007/backup/{glob-grep-guard,trae-cn}
cp -p glob-grep-guard/glob-grep-guard.py glob-grep-enforce-20261007/backup/glob-grep-guard/glob-grep-guard.py.bak.$TS
cp -p ~/.trae-cn/hooks.json glob-grep-enforce-20261007/backup/trae-cn/hooks.json.bak.$TS
chmod 600 glob-grep-enforce-20261007/backup/*/*.bak.*
预期:两份 .bak,权限 600。
步骤 2:改脚本¶
坑 1(关键):LS 判据写了也没用——hooks.json 里 glob-grep-guard 只注册了 matcher: "Glob" 与 "Grep",不含 LS。首轮真机验证因此"没被拦",排查 state 才发现 Hook 根本没被调用。判据能否生效取决于 matcher,不取决于脚本。
坑 2:放行分支必须单独处理。首版把"指纹命中的原样重发"落到了提醒分支 → 重发虽被放行,但会多注入一条提醒(回归用例 R1 失败)。
坑 3:缺 session_id 时绝不能 deny——无状态则放行机制失效,deny 会变成死锁。此时只提醒。
等价示意:
python3 - <<'PY'
src = open('glob-grep-guard/glob-grep-guard.py').read()
for token in ('TARGET_TOOLS = {"Glob", "Grep", "LS"}',
'SHALLOW_PATHS', 'def _emit_deny', 'if fp in denied'):
assert token in src, token
print('判据 G/H 与强制通道已就位')
PY
步骤 3:改 hooks.json(新增 LS matcher)¶
python3 - <<'PY'
import json, os
p = os.path.expanduser("~/.trae-cn/hooks.json")
h = json.load(open(p, encoding="utf-8"))
pre = h["hooks"]["PreToolUse"]
if not any(it.get("matcher") == "LS" for it in pre):
tpl = next(it["hooks"][0] for it in pre if it.get("matcher") == "Glob")
pre.append({"matcher": "LS", "hooks": [dict(tpl)]})
json.dump(h, open(p, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
print("已加入 LS matcher")
else:
print("已存在 LS matcher,跳过(幂等)")
PY
预期:已加入 LS matcher;复查 PreToolUse 应为 6 条(Read / Glob / Grep / WebFetch / * / LS)。
步骤 4:离线回归¶
见第 6 章用例表。
步骤 5:真机验证¶
- 判据 H 可立即验证(matcher 已在):
Glob(pattern="**/*")应被拦; - 判据 G 须重启 Trae 后验证:
LS(path=<项目根>)应被拦。
6. 验证¶
| # | 用例 | 判据 | 结果 |
|---|---|---|---|
| G1 | LS 项目根 |
deny | 通过 |
| G2 | LS 家目录 |
deny | 通过 |
| G3 | LS 无 path |
deny | 通过 |
| G4 | LS 具体子目录 |
不触发 | 通过 |
| H1 | Glob **/* 无 path |
deny | 通过 |
| H2 | Grep **/*.py 无 path |
deny | 通过 |
| H3 | Glob docs/**/*.md(已限定写法) |
不触发 | 通过 |
| H4 | Glob **/* 带 path |
不触发 | 通过 |
| R1 | deny 后原样重发 | 静默放行 | 通过 |
| R2 | 缺 session_id |
仅提醒、不拦 | 通过 |
| R3 | 非法 JSON | 静默 exit 0 | 通过 |
| R4 | 连续 4 次不同违规 | deny 恰 3 次(上限) | 通过 |
| R5 | 非目标工具(Read) |
静默 | 通过 |
离线合计 13/13 通过;merge-guard 同步改"静默放行"后回归 8/8。
真机:判据 H 已实测拦截成功;判据 G 待重启后验证。
7. 回滚¶
备份位置:glob-grep-enforce-20261007/backup/(台账见其 README.md)。
cd /home/rain/Documents/services/token_proxy
cp glob-grep-enforce-20261007/backup/glob-grep-guard/glob-grep-guard.py.bak.20261007-005534 glob-grep-guard/glob-grep-guard.py
cp glob-grep-enforce-20261007/backup/trae-cn/hooks.json.bak.20261007-005745 ~/.trae-cn/hooks.json # 改回后须重启 Trae
回滚后验证:Glob "**/*" 与 LS 项目根 均只提醒、不拦截(回到旧行为)。
回滚触发判据:误拦合法的定向检索、或拦截导致任务失败。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 误拦合法的全库检索 | 放行机制(原样重发即过)+ 每会话上限 3 次 |
SHALLOW_PATHS 判定过宽/过窄 |
仅含 /、~、项目根及其上两级;子目录一律不拦 |
缺 session_id 时 deny 会死锁 |
该分支只提醒、不 deny |
改了 hooks.json 却忘记重启 |
本 SOP 与 HANDOFF 均标注;重启前判据 G 静默不生效(无报错) |
| 判据静默失效(本次已踩) | matcher 与判据须成对核验;离线回归必须覆盖"该触发 / 不该触发"两侧 |
附录:交付物¶
| 文件 | 说明 |
|---|---|
glob-grep-guard/glob-grep-guard.py |
被改实现体(判据 G/H + 强制 + 放行) |
~/.trae-cn/hooks.json |
新增 LS matcher |
backup/ |
回滚点(2 份,600),台账见 backup/README.md |
docs/BASELINE.md §2 |
Hook 清单更新(glob-grep-guard 升强制) |
docs/HANDOFF.md §3 |
重启待办 + 待验证项 |
来源:
/home/rain/Documents/services/token_proxy/glob-grep-enforce-20261007/SOP.md| 最后更新:2026-10-07 00:58(由build/sync.py同步,请勿在站点侧修改)