SOP —— 图片读取放行(docs-read-guard.py)¶
变更日期:2026-10-10 | 风险等级:中(影响所有 Read 调用) | 回滚:覆盖回备份即退
1. 背景¶
用户上传截图后,Read 该图被 Hook 拦截一次,弹出「【大文件先定位·已拦截】该文件 >32 KB」,须原样重发才放行。实测两张真实截图:抖音截图 788 KB(被拦)、Trae 界面截图 27 KB(未被拦),确认与大小相关。
根因:docs-read-guard.py 的「大文件先定位」门禁只判断文件字节数,未排除图片。而 Read 读图忽略 offset/limit,无法「先定位再按段读」,截图与照片又天然超过 32 KB,故图片必然被拦一次,属误拦。
2. 目标¶
- 图片(
.png/.jpg/.jpeg/.gif/.webp,含大写扩展名)的Read直接放行,不再拦 - 文本大文件门禁行为不变:>32 KB 且不带
offset/limit仍拦一次,原样重发放行 - 第 ② 条功能(读
docs/*.md前注入技能提示)不受影响
3. 前置条件¶
- 脚本路径:
docs-read-hook/docs-read-guard.py(由~/.trae-cn/hooks.json的 PreToolUseReadmatcher 调用) python3可用;无需重启 Trae(Hook 每次调用即时读取脚本)- 已完成备份并登记台账,见第 7 章
4. 影响范围¶
| 项 | 说明 |
|---|---|
| 波及组件 | 全部 Read 调用(该 Hook 的 matcher 为 Read) |
| 是否需要停服 | 否 |
| 窗口期 | 无需窗口,改动即时生效 |
| 在跑作业 | 无关联 |
| 恢复方式 | 覆盖回备份文件,即时生效 |
5. 分步操作¶
步骤 1:备份¶
改动可能影响每次 Read,先做可回滚备份(已执行)。
cd ~/Documents/services/token_proxy
python3 docgen/backup_ledger.py docs-read-image-20261010 --backup docs-read-hook/docs-read-guard.py
预期:输出「备份 … → docs-read-image-20261010/backup/docs-read-hook/docs-read-guard.py.bak.20261010-034143」,并登记 backup/README.md。
步骤 2:加图片扩展名白名单¶
在门禁常量区新增一条正则。坑:re 模块在该文件已 import,不要重复 import;扩展名匹配用 re.IGNORECASE,否则 .PNG 会漏放行。
cd ~/Documents/services/token_proxy
grep -n "IMAGE_EXT_RE" docs-read-hook/docs-read-guard.py
预期:命中两行,定义处为
IMAGE_EXT_RE = re.compile(r"\.(?:png|jpe?g|gif|webp)$", re.IGNORECASE)
步骤 3:在门禁函数中放行图片¶
check_big_file() 内,取到 file_path 之后、判断 offset/limit 之前插入放行分支。
cd ~/Documents/services/token_proxy
grep -n -A2 "if IMAGE_EXT_RE.search(path)" docs-read-hook/docs-read-guard.py
预期:
if IMAGE_EXT_RE.search(path):
return False # 图片无法分段读,不适用大文件门禁
坑:放行位置必须在「文件是否存在 / 大小」判断之前,否则不存在的图片路径会走 os.path.isfile 分支(结果相同但多一次 syscall);更关键的是不要放到 main() 里,否则第 ② 条注入逻辑会被一起跳过。
步骤 4:文档头补记录¶
文件 docstring 的「修复记录」区追加 2026-10-10 一条,说明图片豁免的理由,避免后人误判为漏判。
6. 验证¶
判据实现体:docs-read-image-20261010/offline_verify.py(喂 PreToolUse JSON,断言 stdout 与退出码)。
cd ~/Documents/services/token_proxy
python3 docs-read-image-20261010/offline_verify.py # 生产脚本,预期 8/8
python3 docs-read-image-20261010/offline_verify.py --hook docs-read-image-20261010/backup/docs-read-hook/docs-read-guard.py.bak.20261010-034143 # 旧版,预期 6/8
| # | 用例 | 期望 | 新版实测 | 旧版实测 |
|---|---|---|---|---|
| 1 | 大图(.PNG,40 KB)无窗口 | allow | 通过 | FAIL(deny) |
| 2 | 真实截图 jpg(788 KB) | allow | 通过 | FAIL(deny) |
| 3 | 真实截图 png(27 KB) | allow | 通过 | 通过(旧版本就不拦) |
| 4 | 大 py(40 KB)无窗口 | deny | 通过 | 通过 |
| 5 | 大 py 带 offset |
allow | 通过 | 通过 |
| 6 | 小 py | allow | 通过 | 通过 |
| 7 | docs/SOP.md 带窗口 |
context | 通过 | 通过 |
| 8 | 非 Read 工具 |
allow | 通过 | 通过 |
要点:
- 用例 4 是反例:没有它,「图片放行」与「门禁失效」无法区分
- 旧版跑用例 1、2 必 FAIL,证明判据非空转
- 新版连跑两遍结果一致(幂等复验)
- 放行类用例同时要求退出码 0 与 stderr 为空;deny 用例按设计写 stderr,不适用该红线
7. 回滚¶
备份位置:docs-read-image-20261010/backup/docs-read-hook/docs-read-guard.py.bak.20261010-034143,台账见 docs-read-image-20261010/backup/README.md。
cd ~/Documents/services/token_proxy
cp -p docs-read-image-20261010/backup/docs-read-hook/docs-read-guard.py.bak.20261010-034143 docs-read-hook/docs-read-guard.py
回滚后验证:跑第 6 章命令,预期回到 6/8(用例 1、2 转为 FAIL)。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 图片进上下文后每轮重放,大图成本高于文本 | 无法规避:图片是用户主动提供的输入,无「分段读」替代;仅在确有需要时才读图 |
扩展名白名单可被伪装文件绕过(把大文本改名为 .png) |
影响可忽略:绕过者只是把一个读不出来的文件塞进上下文,不产生额外风险 |
| 放行分支位置写错,连带跳过第 ② 条注入 | 第 6 章用例 7 专门覆盖该路径 |
| 脚本异常导致 Hook 崩溃 | 脚本外层 try/except + hooks.json 的 [ -f ] 守卫兜底,异常不影响工具调用 |
附录:交付物清单¶
| 文件 | 用途 |
|---|---|
docs-read-image-20261010/SOP.md |
本变更档案 |
docs-read-image-20261010/offline_verify.py |
离线回归用例(8 项,含反例) |
docs-read-image-20261010/backup/README.md |
备份台账 |
docs-read-hook/docs-read-guard.py |
被改文件(就地修改,非交付物) |
未单独出 RUNBOOK:本次交付物是既有 Hook 的一行逻辑修正,其运行说明在脚本 docstring 的「设计约束」区与 hook-restore-20260923/SOP.md,无需另立手册。
来源:
/home/rain/Documents/services/token_proxy/docs-read-image-20261010/SOP.md| 最后更新:2026-10-10 03:43(由build/sync.py同步,请勿在站点侧修改)