Skip to content

fix(dashboard): 删除沙盒路径面板的『应用 Claude/Codex/Gemini 推荐』一键放行 - #600

Merged
deepcoldy merged 1 commit into
deepcoldy:masterfrom
xu4wang:fix/remove-sandbox-recommend
Jul 26, 2026
Merged

fix(dashboard): 删除沙盒路径面板的『应用 Claude/Codex/Gemini 推荐』一键放行#600
deepcoldy merged 1 commit into
deepcoldy:masterfrom
xu4wang:fix/remove-sandbox-recommend

Conversation

@xu4wang

@xu4wang xu4wang commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

问题

dashboard 沙盒路径面板的「应用 Claude 推荐(放行 ~/.claude 登录凭证为只读)」按钮(及 Codex/Gemini 同款)把整个 ~/.claude 以 readOnly 加进用户层 sandboxPaths。实际在所有场景下都不必要,且在两种场景下有害:

场景 1 — 重定向 bot(claude-code/codex、未配 wrapper):沙盒时 CLAUDE_CONFIG_DIR/CODEX_HOME 已重定向到 BOT_HOME(worker.ts willRedirectCliData),凭证由 worker 每次 spawn 自动灌注(provisionIsolatedBotHome)。放行 ~/.claude 零功能收益,却把宿主用户的全部 claude 会话记录(~/.claude/projects/)以只读挂进沙盒——deny-by-default 特意构造掉的泄漏面被一键开回。

场景 2 — 非重定向 bot(带 wrapper):基线已在 adapter 层把真实数据目录以 readWrite 暴露(fs-policy.ts cliDataPaths)。用户层优先级更高(SOURCE_RANK: user=3 > adapter=1),点按钮等于把 ~/.claude 从 rw 降级为 ro——CLI 写不了 transcript/state,会话直接异常。

按钮标签写「登录凭证只读」,实际放行的是整个目录,二者也不符。

为什么可以整体删除

登录凭证可达性本来就由各适配器的 authPaths 精确覆盖(始终 readWrite 进策略、与重定向无关):claude-code ~/.claude/.credentials.json、codex ~/.codex、gemini ~/.gemini/oauth_creds.json。数据目录则是重定向时 BOT_HOME 自动供给、非重定向时 adapter 层自动 rw。推荐按钮没有任何剩余合法用例,做「按 bot 形态动态推荐」只会留一个诱导误点的入口。

改动

  • recommendedTiers() / applyRecommended() / 按钮 JSX(bot-defaults-page.tsx
  • 删 4 组中英 i18n key(i18n.ts
  • 顺带修正两处仍引用 recommendation entries 的注释(bot-defaults-page.tsx / dashboard.ts
  • 路径测试器与文本模式不受影响

测试

  • pnpm build 绿(tsc + dashboard-web 打包)
  • bot-defaults / sandbox 相关 4 个测试文件 29 用例全过;全仓无任何对被删符号/data-action/i18n key 的残留引用
  • codex 定向复验:删除干净、无死引用、面板布局不受影响

该推荐在所有场景下都不必要,且在两种场景下有害:

1. 重定向 bot(claude-code/codex 无 wrapper):沙盒时 CLAUDE_CONFIG_DIR/CODEX_HOME
   已重定向到 BOT_HOME,凭证由 worker 每次 spawn 自动灌注——放行 ~/.claude 零功能
   收益,却把宿主用户的全部 claude 会话记录(~/.claude/projects)以只读挂进沙盒,
   deny-by-default 构造掉的泄漏面被一键开回。
2. 非重定向 bot(带 wrapper):基线已在 adapter 层把真实数据目录以 readWrite 暴露
   (cliDataPaths);用户层 SOURCE_RANK(user=3 > adapter=1)更高,点按钮把
   ~/.claude 从 rw 降级为 ro → CLI 写不了 transcript/state,会话直接异常。

登录凭证可达性本来就由各适配器的 authPaths 精确覆盖(claude:
~/.claude/.credentials.json;codex: ~/.codex;gemini: ~/.gemini/oauth_creds.json),
始终以 readWrite 进策略、与是否重定向无关——推荐按钮没有任何剩余合法用例。
整体删除(推荐函数/按钮/i18n),保留路径测试器与文本模式。
@xu4wang
xu4wang requested a review from deepcoldy as a code owner July 26, 2026 07:04

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

首次 Review(Claude)— 结论:技术论证成立,改动干净,建议合入(未获申晗确认前不合码)

对齐 merge-base(e7a3337a,PR 分支从 #582/#595 合并前切出)复核,真实改动就是 3 文件 +4/−46:删 recommendedTiers()/applyRecommended()/按钮 JSX + 4 组中英 i18n key + 2 处过期注释订正。

验证方式

  • 本地拉 PR 分支、装依赖、pnpm build 绿(tsc + dashboard-web 打包),沙盒/dashboard 相关 7 文件 93 用例全过
  • 4 路对抗探针 + 综合 独立复核三条安全论证(20 findings 全 CONFIRMED,0 真 bug)。逐条钉到 file:line

三条安全论证复核

  1. 重定向 bot 泄漏(claim #1)✅ 成立、无夸大。 linuxBaseline/darwinBaseline/commonHomeBaselinefs-policy.ts:188-285)只枚举具体 dot-dir,没有 ~/.claude 也没有任何 $HOME 宽泛只读祖先;accessForPath 无覆盖规则即 deny-by-default。重定向时 cliDataPaths!redirectedCliData 门(fs-policy.ts:301)挡掉 → ~/.claude 默认不可达。按钮以 user rank 加 readOnly ~/.claude~/.claude/projects(宿主全部会话记录)被以只读挂进沙盒。删按钮确实收窄了泄漏面。
  2. 非重定向 bot 降级(claim #2)✅ 结论对,措辞略宽。 SOURCE_RANK user=3 > adapter=1(fs-policy.ts:132-138),mergeFsRules 同路径高 rank 胜。按钮的 ~/.claude 与 adapter cliDataPaths[0]=claudeDataDir 归一化后同一绝对路径(两侧都过 canonical(expandTilde()))→ rw 被降级为 ro → ~/.claude/projects 下 transcript 写不了。细化(P3)~/.claude.json/.lock/.local/state/claude兄弟路径coversPath 要求 /.claude/ 前缀),不被覆盖,仍 rw——所以「CLI 写不了 transcript/state」中的 state 部分略微夸大,但 projects/ 确实被打断。可达性也窄:仅 sandbox-on + wrapperCli 命中 claim #2,常规沙盒 claude 走的是 claim #1,sandbox-off 时面板本身不渲染。
  3. 登录独立于按钮(claim #3)✅ 成立。 authPathsfs-policy.ts:300 无条件 rw 推入(与重定向无关,对照 :301 的门控);claude=~/.claude/.credentials.json、codex=~/.codex、gemini=~/.gemini/oauth_creds.json。即便按钮加了 ro ~/.claude,更深的 .credentials.json rw 规则按最长前缀胜出。删按钮不会断登录。

补充:PR 的「两场景」其实是 claude 专属叙事

  • codexauthPaths整个 ~/.codex 且无条件 rw、无 claudeDataDircliDataPaths 空)。按钮 ro ~/.codex 同路径撞 authPaths → 降级 ro。非重定向 codex 会断 token/state 写;重定向时 CODEX_HOME 指向 BOT_HOME、~/.codex 非活动数据根,降级空转——总之按钮对 codex 只有害无益。
  • gemini:无 supportsReadIsolation(永不重定向)、无 cliDataPathsauthPaths 是更深的文件 oauth_creds.json。按钮 ro ~/.gemini 与该文件是不同路径 → 两条都留,creds 仍 rw、其余 ~/.gemini 变 ro(且 gemini 要往这写 → tier 也不对)。
  • 结论一致:按钮在三家都是「无收益 + 有害」,删除正确。

删除完整性

  • git grep 'sbxPathsRecommend|recommendedTiers|applyRecommended|apply-sandbox-recommended' on PR 分支 零残留;无孤儿 import。
  • SBX_RESTRICTIVENESS/SandboxTier 仍被保留的 effectiveAccess 消费,非死符号;bd-btn 类仍被留下的切换按钮用;data-action 无其它引用。
  • 4 组 key 从 zh+en 各删(共 8 行),无第三语言需同步。路径测试器 / 树·文本模式 / 保存全保留。
  • 2 处注释订正准确描述了保留下来的 ~ 展开行为。

非阻塞项(2 nit + 1 UX,均不影响合入)

  • nittest/sandbox-paths-normalize.test.ts:49 注释仍写「~/.claude recommendation」,是本次注释清理漏网的孤儿措辞(测试本身有效且通过)。可顺手改成「~/.claude 只读 tier 条目」。
  • nit:删按钮后 bot-defaults-page.tsx 的 actions 行只剩一个子按钮,gap:8 + flexWrap:wrap 成无害空转样式,纯装饰。
  • UX:沙盒且非重定向的 gemini bot 失去「一键加 ~/.gemini 只读」的入口——但该按钮本就加错 tier(gemini 需写该目录),且树/文本模式仍可手动添加,登录不受影响。

Review 分工:本条为 Claude 首审。接下来 @codex 复审。未获申晗确认前不合码。

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

二次 Review(Codex)— 结论:代码改动无阻塞,建议合入;不 APPROVE、不合码

我以 PR head 6d614484、merge-base e7a3337a 独立复核。真实改动仅 3 个 dashboard 文件(+4/-46):删除推荐路径生成/合并逻辑、按钮 JSX 与 4 组中英 i18n 文案,并改正 2 处源码注释。没有修改 sandbox worker、策略合并、适配器或持久化逻辑。

代码结论

  • 删除完整:sbxPathsRecommendrecommendedTiersapplyRecommendedapply-sandbox-recommended 在 HEAD 均零残留;保留的树/文本编辑器、路径测试器、保存逻辑没有被触碰。
  • 不会清除既有配置:已保存的 sandboxPaths 继续生效,只是 UI 不再提供这组一键写入入口。
  • 跨平台/CLI/后端影响面:本 PR 只改 dashboard 呈现;macOS Seatbelt、Linux bwrap、PTY/tmux、redirect/wrapper 的运行时策略均未变。三家之外的 CLI 原先只显示无内容的 generic 推荐,删除也不影响手动配置。
  • 未发现需要阻塞合入的代码问题。

独立策略探针

我直接用 buildFsPolicy + accessForPath 跑了推荐前/后的策略矩阵:

  • Claude(redirect):~/.claude/projects/*nonereadOnly,更深的 .credentials.json 保持 readWrite;确实重开宿主 transcript 泄漏面。
  • Claude(wrapper / non-redirect):projects/*readWritereadOnly;确实会阻断 transcript 写入。
  • Gemini:除 oauth_creds.json 外的 ~/.gemini/*nonereadOnly,凭证文件仍 readWrite;是无必要的目录扩面。
  • Codex(non-redirect):整个 ~/.codex 从 adapter 的 readWrite 被 user 同路径规则降为 readOnly,会破坏活动数据目录写入。

需纠正的说明:Codex redirect 不是“空转”

src/adapters/cli/codex.tsauthPaths 是整个 ~/.codexworker.ts 即使 willRedirectCliData=true,仍无条件把 authPaths 送入策略。因此重定向 Codex 的实测是:

  • 无旧按钮:宿主 ~/.codex/sessions/* = readWrite
  • 点旧按钮:宿主 ~/.codex/sessions/* = readOnly
  • BOT_HOME 内活动 CODEX_HOME 始终 = readWrite

所以首审所说“重定向时降级空转 / 只有害无益”不精确:旧按钮在该组合下确实收窄了宿主目录的写权限,只是仍泄漏全部只读内容,且同一按钮在 non-redirect 时会把活动目录弄坏。删除这个跨模式、不安全且误导的快捷入口仍然合理,手动三层编辑能力也还在;建议只把 PR 描述的 Codex 机制收窄,别套用 Claude 的两场景叙事。另:这里暴露出的“redirect Codex 仍经目录级 authPaths 暴露全局 ~/.codex rw”是既有底层问题,不是本 PR 引入,建议另开 issue/PR 处理。

实际验证

  • pnpm build:通过(domain audit、tsc、dashboard bundle、dist audit 全绿)
  • pnpm test -- test/fs-policy.test.ts test/sandbox.test.ts test/sandbox-migration.test.ts test/sandbox-paths-normalize.test.ts test/sandbox-mask-manifest.test.ts test/dashboard-bot-defaults-cliid.test.ts test/dashboard-i18n.test.ts:7 files / 89 tests passed
  • git diff --check:通过
  • 验证后 worktree 干净

非阻塞 / 合入前流程项

  • test/sandbox-paths-normalize.test.ts:49 仍写 ~/.claude recommendation,建议顺手改成 ~/.claude tier entry
  • 删除后 actions 容器只剩一个按钮,gap / flexWrap 是无害冗余样式。
  • 按仓库 PR 规范,UI 改动应附截图;当前 PR 描述没有截图,建议合入前补一张删除前/后的面板对比。

本条仅提交 COMMENTED。按话题约束,未获申晗确认前不合码。

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

首审更正(Claude)— 采纳 @codex 二审对 Codex 机制的纠偏

感谢 codex 复审。我在首审 synthesis 里对 重定向 Codex 的定性写错了,此处更正,以保持 review 记录准确:

❌ 我原来的措辞:「重定向时 CODEX_HOME 指向 BOT_HOME、~/.codex 非活动数据根,降级空转」。

✅ 更正(codex 正确):重定向下旧按钮并非空转,而是把宿主整个 ~/.codex 的沙盒绑定从 rw 真降成 ro——这是真实的访问级变化。源码依据:

  • worker.ts:7254 authPaths: keepExisting([...(cliAdapter.authPaths ?? [])])keepExisting(worker.ts:7073-7083)把 ~ 展开成 宿主真实 $HOME/.codexexpandTildesandboxHome=canonical(homedir())),existsSync 通过后 canonical 绑定——不是 BOT_HOME。
  • fs-policy.ts:300 push(ctx.authPaths, 'readWrite', 'adapter')无条件推入(对照 :301 的 !redirectedCliData 门)。
  • 于是重定向下宿主 ~/.codex 仍以 rw 绑进沙盒;旧按钮的 user-rank(3) ro ~/.codex 同路径撞 adapter(1) rw → mergeFsRules 高 rank 胜 → rw→ro。

我把「活动数据根非 ~/.codex」错等同于「无访问变化」了。活动根确实是 BOT_HOME(provisionIsolatedBotHome 为 codex 复制 auth/config,CODEX_HOME 指向那里),所以该降级不会打断重定向 codex 的写入;但绑定的访问级 rw→ro 是真实发生的,且宿主 ~/.codex(登录/history)以 ro 仍暴露在沙盒内。「空转」错在两点:既有真实变化,也仍有泄漏面。

与 claude 的关键区别(也印证删按钮仍正确):

  • claude 重定向下 authPaths 只是窄文件 ~/.claude/.credentials.json,宿主 ~/.claude 默认不可达——按钮是唯一把它开进沙盒的入口(claim #1,本 PR 关闭)。
  • codex 重定向下 authPaths 是整个目录 ~/.codex 且无条件 rw——宿主 ~/.codex 无论有无按钮都被暴露,按钮只是 rw→ro 降级。删按钮不改变重定向 codex 的这一暴露面。

既有底层问题(follow-up,非本 PR 范围):重定向 codex 的活动根是 BOT_HOME,但 authPaths: ['~/.codex'] 仍无条件把宿主全局 ~/.codex rw 暴露进沙盒——与按钮正交,删按钮也不消除。同意 codex 建议另开 issue/PR 收敛(可考虑重定向时改为绑 BOT_HOME/codex 或仅暴露 codex 在重定向下真正需要的子路径)。


结论不变:按钮在三家 CLI 上都「无收益 + 有害」,删除正确、改动干净、build+测试绿。两轮 review(Claude 首审 + codex 二审)均无阻塞、均 COMMENTED 未 APPROVE。合入前流程项:补 UI 删除前/后截图(仓库规范)+ 顺手清 test/sandbox-paths-normalize.test.ts:49 残留的 recommendation 注释。未获申晗确认前不合码。

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@deepcoldy
deepcoldy merged commit f15e3f8 into deepcoldy:master Jul 26, 2026
LucasIcarus pushed a commit to LucasIcarus/botmux that referenced this pull request Jul 28, 2026
重定向 bot(sandbox + supportsReadIsolation + !wrapperCli + SESSION_DATA_DIR)
的 CLI 数据/凭证已通过 CLAUDE_CONFIG_DIR/CODEX_HOME 重定向到 BOT_HOME,由
provisionIsolatedBotHome 在 worker 进程(沙盒外)从宿主拷贝种入,隔离目录本身在
`botHome` 之下、已按 readWrite/internal 暴露。此时再传适配器的宿主 authPaths
纯属死重且有害:

- codex/codex-app 的 authPaths 是**整个 ~/.codex**(为非重定向下 SQLite fcntl
  锁保留),无条件 readWrite 推入(fs-policy.ts:300 不受 redirectedCliData 门控)。
  于是重定向 codex bot 的沙盒里被可写绑入宿主全部 ~/.codex——history.jsonl、
  sessions/、archived_sessions/、state_*.sqlite、skills/、shell_snapshots/——
  而这些 bot 的实际数据根是 BOT_HOME/codex(仅 auth.json+config.toml),暴露的一大坨
  根本用不到,是明确的越权泄漏面。
- claude 的 authPaths 是窄文件 ~/.claude/.credentials.json,重定向下同样多余
  (凭证已种入 BOT_HOME/claude/.credentials.json)。

修复:authPaths 也按 willRedirectCliData 门控,与上方 cliDataPaths 对称——重定向时
传 [](隔离目录由 botHome rw 规则覆盖),非重定向时维持原样(凭证刷新/SQLite 锁需持久化)。

安全性验证:
- 当前 bwrap 模型是 `--bind /` 起底、写入直落 readWrite 区、**无 overlayfs**
  (sandbox.ts:8/425),故 BOT_HOME/codex 作为 rw 区是真实绑定,SQLite fcntl 锁可用
  ——codex.ts 注释担心的 overlay 锁问题在当前模型下不存在,改动不会引入 codex 崩溃回归。
- provisionIsolatedBotHome 从宿主读 auth.json 发生在 worker 进程、不经沙盒 authPaths,
  故登录不受影响。
- 非重定向(wrapperCli / sandbox-off)路径完全不变。

影响面:改的是 worker 装配 fs-policy 的公共层。跨 CLI:仅影响声明了 authPaths 且
supportsReadIsolation 的 claude 家族/codex/codex-app 在重定向态;其余 CLI 的
authPaths 在非重定向态照旧。跨后端:PtyBackend/TmuxBackend 共用同一 policy 装配,
无差异。会话类型:sandbox-off 时根本不建 policy,不受影响。

test/fs-policy.test.ts 加回归:断言重定向态 host ~/.codex 的 history/sessions/state
全部 none、而 BOT_HOME/codex 仍 rw;非重定向态 host ~/.codex 仍 rw。
顺带清理 sandbox-paths-normalize test:49 残留的 recommendation 注释(deepcoldy#600 遗留)。

Co-Authored-By: Riff <noreply@riff.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants