fix(read-isolation): botmux quoted 在隔离 bot 下可用 + 下载被引用消息的文件#402
Open
xu4wang wants to merge 4 commits into
Open
fix(read-isolation): botmux quoted 在隔离 bot 下可用 + 下载被引用消息的文件#402xu4wang wants to merge 4 commits into
xu4wang wants to merge 4 commits into
Conversation
问题:飞书上传的附件存 data/attachments/<messageId>/,路径无 spawn 时可知 的 key,Seatbelt profile 又是 spawn 时静态生成 → 只能整树 deny,隔离 bot 连自己收到的文件都 EPERM。 修复:附件改按 appId 分桶 data/attachments/<appId>/<messageId>/ (getAttachmentsDir),attachments/ 整目录保持 wholesale deny(盖住兄弟桶 +历史扁平存量),carve-out 只放行自己的桶 + traverse shim——与 bots/、 .lark-cli-bots/ 同一范式,不枚举兄弟 appId,新增 bot 无需冷重启。 同轮数据盘点补 deny(均核实 CLI 侧无读取): - data/queues/(所有 bot 的完整入站消息内容) - data/schedules.json、data/read-isolation/ - feishu-session.json(网页登录态,可开新 bot)、.dashboard-token - regex 整类:data/identities-<appId>.json(对话者姓名 PII,own 也不留)、 遗留 data/.send-cred-<appId>(旧版发送凭证,存量已删) 影响面:getAttachmentsDir 是共用路径(core/session-manager),所有 CLI、 两平台生效;附件路径经 prompt 绝对路径传递,CLI 侧无耦合;非隔离 bot 仅 落盘位置变化;历史存量附件留在旧布局(旧 prompt 引用的路径本来就 deny)。 验证:pnpm build 绿;vitest 相关文件 37/37 绿,全量 7221 过(唯一失败 sandbox.test.ts 在 clean master 同样挂,与本次无关);真机 sandbox-exec 12/12(own 桶可读/兄弟桶 EPERM/枚举仍挡/own sessions 不误伤);已部署 live daemon 并在飞书实测隔离 bot 读取新上传附件成功。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ppId 软失败 codex review(重点非隔离 bot 影响面)出的两条,承接上一 commit: 1) [blocker] deny ~/.botmux/.dashboard-secret —— 之前只挡了 .dashboard-token, 漏了签名密钥 .dashboard-secret。它签 loopback-HMAC,门禁 /__cli/rotate 和 daemon-IPC 的 write-link 路由;dashboard-ipc-server.ts 的注释本就假设「沙箱 挡住 secret」以阻止 sandboxed worker 给不属于自己的会话铸造可写终端 token ——这是半可信对话者(oncall 群成员/团队群真人/allowedChatGroup/开放态,均非 owner,见 canTalk)驱动隔离 agent 即可触达的跨 bot 提权。deny 之后隔离 agent 仍可正常 send/list/status(走 relay/send-cred,不签 HMAC);只有 owner 管理命令 dashboard/term-link 会失效,本就不该由半可信 agent 执行。.dashboard-port 是 纯端口号无凭证价值,留可读。 2) [minor] downloadResources 对 path-unsafe appId 软失败:getAttachmentsDir 内 assertSafeAppId 现在会抛(仅手编 bots.json 的畸形 appId 触发,真实 cli_xxx 恒 通过)。之前无校验、下载不会因 appId 抛;若直接穿到 event-dispatcher 会连整条 消息的文本一起丢。改为 catch→记 warn→返回空附件(与下载失败同 shape),文本照常 处理。 未修 codex 第 2 条(存活旧 pane 用旧 profile):部署 runbook 的 `suspend --isolated` 已是有效缓解(本次部署即用),隔离 bot 暂无外部用户,结构性 兜底(marker 存 profile hash)留待规则迭代变频/上外部用户时再做。 影响面:deny 仅作用于隔离 bot 的 Seatbelt profile,非隔离 bot 与 Linux daemon 无变化;downloadResources 软失败对合法 appId 是 no-op。 验证:pnpm build 绿;vitest 相关文件 32/32 绿(+软失败用例 +secret/port 断言), 全量 7222 过(唯一失败 sandbox.test.ts 在 clean master 同样挂,与本次无关);真机 sandbox-exec 14/14(secret DENY / port ALLOW / own 附件桶可读 / 兄弟桶+queues+ 凭证 EPERM / 枚举仍挡);已部署 live daemon + suspend --isolated 冷启动应用新规则。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
read-isolation.ts 把共享 schedules.json 加进读 deny,但 schedule-store 是 read-modify-write:sandbox 里读被 deny → load() 得空 map → save() 用只含新任务的 map 覆盖回写,静默抹掉其它所有 bot 的定时任务(read-modify-write 退化成 write-only)。 可达路径是正常业务(owner 让隔离 bot「明天提醒我」→ agent 跑 botmux schedule add)。 本次先放开该 deny(接受"隔离 bot 能读到别人定时提示词"的轻微泄露)解掉数据丢失 blocker。 更稳的长期解是让 schedule-store.load() 区分 ENOENT/EPERM、读失败 fail-closed 拒绝 save ——那时 deny 可保留又不丢数据,留待后续重构。加注释防回归;测试断言相应翻转。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
隔离 bot 的 sandbox 里 bots.json 被 read-isolation deny(含所有 bot 密钥,必须 deny), 导致两个问题: ① `botmux quoted` 走 resolveSessionAppId→loadBotConfigs() 读 bots.json→被 deny→注册静默 失败→getMessageDetail 抛 "Bot not registered"。cmdHistory/cmdSend 早有 send-cred 兜过, quoted 漏了。→ 开头补 `registerSelfFromCredFile()`(用 BOTMUX_LARK_APP_ID + 自己 BOT_HOME 的 send-cred.json 注册,不读 bots.json),与 history/send 一致。 ② 即便拿到被引用消息,其文件资源只是 key+name;隔离 bot 无凭证自己调 Lark resource API 下载。 → 渲染后对 rendered.resources 调 daemon 同款 downloadResources,用已注册 client(tenant token) 把字节下到自己 appId 桶 attachments/<appId>/<messageId>/(carve-out 可读;sandbox 只 deny 读、 不 deny 写),并把 attachments[].path 塞进输出,agent 直接读路径即可。 仅动 src/cli.ts 的 cmdQuoted。依赖本分支(deepcoldy#387)引入的 registerSelfFromCredFile / downloadResources / appId 分桶,故 stack 在 deepcoldy#387 之上。验证:codex review 两轮零 findings + 用户 live 实测隔离 bot 引用回复文件、quoted 输出含本地 path 并成功读取。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
本 PR 依赖 #387(read-isolation 附件分桶)引入的
registerSelfFromCredFile/downloadResources/ appId 分桶,基于其分支pr/read-isolation-attachments开出。请在 #387 之后 review/合并。在 #387 合入 master 前,本 PR 的 diff 会包含 #387 的改动;#387 合并后 GitHub 会自动收敛成仅剩本 PR 的增量(src/cli.tscmdQuoted)。问题
隔离 bot 的 sandbox 里
~/.botmux/bots.json(含所有 bot 的 app_id/app_secret)被 read-isolation deny(必须 deny,否则跨 bot 凭证泄露)。这导致botmux quoted对隔离 bot 两处不可用:Bot not registered:cmdQuoted → resolveSessionAppId → loadBotConfigs()读bots.json→ 被 deny → 注册静默失败 →getMessageDetail找不到已注册 bot → 抛Bot not registered: <appId>。cmdHistory/cmdSend早已用 send-cred 兜过这层,cmdQuoted漏了。key+name;隔离 bot 无凭证自己调 Lark resource API 下载,botmux quoted又没把字节落盘。改动(仅
src/cli.ts的cmdQuoted)await registerSelfFromCredFile();—— 用注入的BOTMUX_LARK_APP_ID+ 该 bot 自己 BOT_HOME 里的send-cred.json注册,不读 bots.json,与cmdHistory/cmdSend一致。rendered.resources调 daemon 同款downloadResources(appId, messageId, resources)—— 用上一步注册的 bot client(tenant token)把文件字节下到该 bot 自己的 appId 桶attachments/<appId>/<messageId>/(read-isolation carve-out 允许读自己桶;sandbox 只 deny 文件读、不 deny 写),并把attachments:[{type,path,name}]塞进 quoted 的 JSON 输出,agent 直接读path即可打开文件。影响面
cmdQuoted,对非隔离 bot 无行为变化:registerSelfFromCredFile()在无 send-cred 时 no-op,downloadResources与直发消息附件走完全相同的既有路径。downloadResources(daemon 直发附件同款),文件类型/失败处理/needLogin语义一致。测试验证
cli_aab4eaea…)在群里引用回复一条文件消息 →botmux quoted <msgId>先返回resources({type:file, key, name}),补下载后输出含attachments[].path,隔离 bot 直接读该本地路径成功拿到文件内容(此前Bot not registered+ 文件不落盘)。tsc通过。getMessageDetail/downloadResources);可按需补。🤖 Generated with Claude Code