feat(read-isolation): 隔离 bot 附件按 appId 分桶可读自己上传,并补齐敏感数据 deny#387
Open
xu4wang wants to merge 4 commits into
Open
feat(read-isolation): 隔离 bot 附件按 appId 分桶可读自己上传,并补齐敏感数据 deny#387xu4wang 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>
whiteboards/ 之前整树 deny 且无 per-appId carve-out → 隔离 bot 连自己的白板都读不到 (list 返空 / read 报错),且 update 在读失败后再写会 clobber 目标板(和 schedules.json 同类的 read-modify-write 陷阱)。 按 owner 决定:白板是共享内容、本就该让协作 bot 看到,直接放开该 deny。注意白板 store 当前是全局的(无 per-chat 隔离),所以隔离 bot 能 list/read 到其它 chat 的板——已知并接受 的 tradeoff,后续可像附件那样按 chat/appId 分桶再收紧。放开读也顺带消除了上面的 clobber。 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.
改了什么
飞书上传的附件改按 appId 分桶存
data/attachments/<appId>/<messageId>/(原来是扁平的data/attachments/<messageId>/)。读隔离 Seatbelt profile 放行该 bot 自己的桶(carve-out)+ traverse shim,隔离 bot 从此能读用户在群里上传的文件;attachments/根目录仍整树 deny(盖住兄弟桶 + 历史扁平存量),ls枚举也挡。同轮盘点
~/.botmux里其它 per-bot / 跨 bot 敏感数据,补齐一批 deny(均核实 CLI 侧无合法读取):data/queues/(所有 bot 的完整入站消息内容)、data/schedules.json、data/read-isolation/~/.botmux/feishu-session.json(网页登录态,可开新 bot)~/.botmux/.dashboard-secret(loopback-HMAC 签名密钥)+.dashboard-token(bearer)data/identities-<appId>.json(对话者姓名 PII)、遗留data/.send-cred-<appId>downloadResources对 path-unsafe 的 appId 软失败(catch → 空附件),不再让畸形 appId 把整条消息文本一起丢。为什么
bots/、.lark-cli-bots/完全同一范式(不枚举兄弟、新增 bot 无需冷重启)。.dashboard-secret:它签 loopback-HMAC,门禁/__cli/rotate和 daemon-IPC 的 write-link 路由(dashboard-ipc-server.ts注释本就假设沙箱挡住它以阻止 sandboxed worker 铸造可写终端 token)。能触达它的是canTalk放行的所有人——oncall 群成员 / 团队群真人 / allowedChatGroup / 开放态,均非 owner,正是读隔离威胁模型里的「半可信对话者」→ 属跨 bot 提权,必须 deny。.dashboard-port是纯端口号无凭证价值,留可读。影响面
getAttachmentsDir是共用路径(core/session-manager),所有 CLI × macOS/Linux 两平台生效;附件路径经 prompt 绝对路径传给 CLI,CLI 侧无耦合。downloadResources软失败对合法 appId 是 no-op。验证
pnpm build绿;test/read-isolation.test.ts/download-resources-needlogin/claude-read-isolation/codex-read-isolation全绿。sandbox-exec14/14:own 附件桶可读、兄弟桶 + 旧扁平布局 +queues/+ 各类凭证 EPERM、.dashboard-secretDENY /.dashboard-portALLOW、ownsessions-<appId>.json不被新 regex 误伤、ls attachments/枚举仍被挡。