Skip to content

feat(ext): #1075 REQ-138 用 sandbox-exec wrapper 围住引擎 shell,越界写入 fail-closed - #1126

Merged
jinjunnn merged 1 commit into
alphafrom
feat/1075-shell-sandbox-wrapper
Aug 25, 2026
Merged

feat(ext): #1075 REQ-138 用 sandbox-exec wrapper 围住引擎 shell,越界写入 fail-closed#1126
jinjunnn merged 1 commit into
alphafrom
feat/1075-shell-sandbox-wrapper

Conversation

@jinjunnn

Copy link
Copy Markdown
Owner

做了什么

C1 接缝(基线 docs/architecture/2026-08-23-shell-sandbox-seam.md §3):在 @alpha-code/extconfig(cfg) hook 里把 cfg.shell包住成一个 Alpha wrapper —— wrapper 内 exec /usr/bin/sandbox-exec -f <profile> -D WORKDIR="$(pwd)" <真shell> "$@"。收编上游 0 行(north-star 绿)。

咽喉点(全称否定式 → 有限判据)

「没有任何进程能写到工作区外」没有有限证据集。本票把它改写成:到达「工具派生子进程真正落盘」的唯一通路是 cfg.shell → wrapper → sandbox-exec 加载 profile → 真 shell;profile 的 (deny file-write*) 之后只再放行一个闭集前缀(WORKDIR 参数 + profile 内登记的固定前缀),其余写入在内核层 EPERM。判据于是有限、一次变异判得完。

不变量

  • I1cfg.shell 恒指向 wrapper;装不上时顶成 deny stub / /usr/bin/false,绝不回落裸 shell
  • I2 可写闭集唯一权威 = shell-sandbox.tsSEATBELT_PROFILE 常量(新增前缀改这里,可 diff;seam 测试逐 token 全等挡拓宽)。
  • I3 判据永远是「文件是否落盘」,不是子进程 exit code。
  • I4 wrapper 对 argv 完全透传("$@"),不解析、不重写。

落点(与基线 §2.4 的差异,已在架构文档 §7 校正)

wrapper 与 profile 落 ALPHA_GLOBAL_DIR/{bin/<basename>, sandbox/alpha-shell.sb},不落 <userData>:C1 的 config hook 跑在 ext 包内,拿不到 Electron main 的 app.getPath("userData");ALPHA_GLOBAL_DIR 是 ext 唯一能 canonical 校验的 alpha 自有可写根 —— 走它保持 ext-only、收编 0。

变异实验证据(本机 macOS,真 sandbox-exec)

越界写(写 $HOME 下、不在 profile 允许集里的目录)在围栏下 EPERM 且文件不落盘;同一语料撤围栏后全部落盘(正反两跑,基线 §2.6)。类边界探针 —— 重定向 > / >>sh / python3 / node 解释器、nohup 脱离、指向工作区外的 symlink —— 围栏下全拦。

FENCE ON · "echo pwned > $HOME/.../pwned.txt"
exit=1 stderr=zsh:1: operation not permitted: /Users/.../pwned.txt
file on disk? no — write was blocked at the kernel (EPERM)
FENCE OFF · same command, bare /bin/zsh, same cwd
file on disk? YES — the corpus can detect the known-bad

测试(AC1–AC3)

  • alpha-sandbox-seam.test.ts(已登记 gate-files.tsv,精确 11 条):接线 + profile 闭集(逐 token 全等)+ fail-closed(注入写失败 ⇒ deny stub;再失败 ⇒ /usr/bin/false)。platform 用 seam 强制 darwin、fs 用捕获式 mock ⇒ Linux CI 也跑
  • alpha-sandbox-escape.test.ts:真 sandbox-exec 正反语料,skipIf(!sandbox-exec)(CI/ubuntu skip、本机 macOS 真跑)。
  • alpha-sandbox-reassert.test.ts(AC3):每次加载重新包裹 + 双重包裹防线(cfg.shell 已是 wrapper ⇒ real 回落默认,不自嵌套)。
  • alpha-sandbox-shell-resolve.test.ts:resolveRealShell@opencode-ai/coreShell.acceptable+Shell.name 的等价性锚(独立基准,枚举输入形状域;darwin only)。

门(本机 CI 口径,实测全绿)

contracts-consumer/ext/ui-mac typecheck · check:vendor · ext floor 100(157 pass)· ui-mac floor 3000 known-fails(4479 pass / 0 fail,base fail-set 差 = 0)· bun-test-app(exit 0)· north-star(0 upstream edit)· assert-gate-files(新 seam 行 =11 通过)· 三个 opencode 真插件装载集成测试(7/5/10 pass)全绿。

Out of scope(基线 §4)

进程内 FS 工具 · MCP stdio · LSP · 网络(#1077)· Windows · pty。打包 Electron sidecar 复跑归 [REQ-138][VERIFY]

Fixes#1075
Refs #1074

🤖 Generated with Claude Code

https://claude.ai/code/session_01QHDS2BFKuzm9uNH1svU2oj

…closed
C1 接缝(基线 §3):在 @alpha-code/ext 的 config(cfg) hook 里把 cfg.shell **包住**成
一个 Alpha wrapper —— wrapper 内 `exec sandbox-exec -f <profile>` 再 exec 用户真 shell。
收编上游 0 行。
咽喉点(全称否定式落成有限判据):到达「工具派生子进程真正落盘」的唯一通路是
cfg.shell → wrapper → sandbox-exec 加载 profile → 真 shell;profile 的
`(deny file-write*)` 之后只再放行一个闭集前缀(WORKDIR 参数 + profile 内登记的固定前缀),
其余写入在内核层 EPERM。判据于是有限、一次变异判得完。
不变量:
- I1 cfg.shell 恒指向 wrapper;装不上时顶成 deny stub / /usr/bin/false,绝不回落裸 shell。
- I2 可写闭集唯一权威 = shell-sandbox.ts 的 SEATBELT_PROFILE 常量(新增前缀改这里,可 diff)。
- I3 判据永远是「文件是否落盘」,不是子进程 exit code。
- I4 wrapper 对 argv 完全透传(`"$@"`),不解析、不重写。
落点:wrapper 与 profile 落 ALPHA_GLOBAL_DIR/{bin/<basename>,sandbox/alpha-shell.sb} ——
基线 §2.4 的 `<userData>/bin` 在 spike 里成立,但 C1 的 config hook 在 ext 包内拿不到
Electron main 的 userData;ALPHA_GLOBAL_DIR 是 ext 唯一能 canonical 校验的 alpha 自有可写根,
走它保持 ext-only、收编 0。架构文档 §7 已校正该路径假设。
证据(本机 macOS,真 sandbox-exec):越界写(写 $HOME 外目录)在围栏下 EPERM 且文件不落盘;
同一语料撤围栏后全部落盘(正反两跑)。类边界探针(重定向 >/>>、sh/python3/node 解释器、
nohup 脱离、指向工作区外的 symlink)围栏下全拦。打包 Electron sidecar 复跑归 [REQ-138][VERIFY]。
Out of scope(基线 §4):进程内 FS 工具 · MCP stdio · LSP · 网络(#1077)· Windows · pty。
Fixes#1075
Refs #1074
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QHDS2BFKuzm9uNH1svU2oj
@jinjunnn
jinjunnn merged commit 831f3c4 into alphaAug 25, 2026
5 of 6 checks passed
Sign up for freeto 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.

[REQ-138][CODE] 用 sandbox-exec wrapper 包裹引擎 shell,越界写入失败

1 participant

@jinjunnn