跳转至

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 的 PreToolUse Read matcher 调用)
  • 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 同步,请勿在站点侧修改)