feat(dashboard): Bot 配置加「读隔离(macOS)」toggle,切换后自动重启会话#407
Open
xu4wang wants to merge 5 commits into
Open
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>
在 dashboard Bot 配置的「文件沙盒」下面新增读隔离开关,存到 bots.json 的 readIsolation 字段 (镜像沙盒 toggle:/api/bot-read-isolation + rmwBotEntry 原子写 + 内存同步)。 安全护栏(worker 对不可执行的 readIsolation 会 fail-close): - 只有真能强制执行(macOS + 适配器 supportsReadIsolation + 无 wrapperCli)时才可开;否则前端禁用 toggle、后端拒绝 → 绝不 brick;已开却变不可执行的显警告且保持可点以恢复;改 CLI/wrapper 让已开 失效时自动清除并即时反映到 UI。全程不放开 bots.json(密钥受保护)。 - 切换后自动 suspend 该 bot 活跃会话逼冷启动(读隔离仅 cold-spawn 生效),PTY 兜底 closeSession, 已 idle-suspend 的不动;提示明说切换隔离模式会重置对话上下文(owner 定为可接受)。 依赖本分支(deepcoldy#387)的 read-isolation。codex review 多轮零 findings + 本机真沙箱验证。 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.
用到 #387(read-isolation 附件分桶)引入的
readIsolation字段 + worker 侧evaluateReadIsolationGate+provisionIsolatedBotHome,基于其分支开出。请在 #387 之后 review/合并;#387 合入 master 前,本 PR diff 含 #387 的改动。背景
read-isolation(macOS Seatbelt 读隔离)此前只能改
bots.json手动开启。本 PR 在 dashboard「Bot 配置」的「文件沙盒」区块下面加一个可视化开关。改动
bot-defaults.ts+i18n.ts):「开启读隔离(macOS)」toggle + 说明。sandbox-store.ts+dashboard-ipc-server.ts+dashboard.ts+bot-payload.ts):镜像沙盒 toggle ——PUT /api/bots/:appId/read-isolation→ daemon/api/bot-read-isolation→updateBotReadIsolation(rmwBotEntry原子写 bots.json + 内存同步,下个新会话生效)。安全护栏
worker 对「配置了 readIsolation 但无法强制执行」的会话会 fail-close(拒绝启动)。为避免 toggle 把 bot 弄 brick:
evaluateReadIsolationGate:macOS + 适配器supportsReadIsolation+ 无wrapperCli):不满足 → 前端禁用 toggle + 后端拒绝写;/api/bot-agent会自动清除失效的readIsolation并即时反映到 UI;bots.json(密钥始终受保护)。切换后自动重启会话(逼冷启动生效)
读隔离只在 cold-spawn 时应用(
provisionIsolatedBotHome+ Seatbelt 包裹)。若只落盘、用户close+resume现有会话会 reattach 旧进程 → 隔离静默不生效。故 toggle 落盘后自动suspendActiveSessionsForBot:suspendWorker杀 CLI、会话保持 active、下条消息冷启动;suspendWorkerno-op)且有活 worker → 兜底closeSession;提示明说:切换隔离模式会换 Claude 数据根目录,这些会话的对话上下文将重置为新会话(owner 权衡后定为可接受,符合"把 bot 转给半信任者"的隔离语义)。
测试
tsc+dashboard:bundle通过;模块加载无循环依赖。bots list出脱敏名单、白板/schedules 可读。🤖 Generated with Claude Code