drscrewdriver
dsh-seatbelt-sandbox
dsh-seatbelt 方面沙箱增强尝试使用非exec工具直接对接系统seatbelt相关api
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-seatbelt-sandbox
macOS Seatbelt(libsandbox)沙箱适配器,为 DeepSeek Harness(dsh)提供 cordis 插件形态的 seatbelt rung 替换:部署时在 cordis.yml 里把 sandbox 插件从 @deepseek-ai/dsh-sandbox-local 换成 dsh-seatbelt-sandbox,即可让 dsh 的 macOS 沙箱从 Apple 弃用的 sandbox-exec CLI 切换到直接调用私有 libsandbox API 的自带 loader——无需修改 dsh 源码、无需提 PR。
当前状态(筹备阶段)
| 项 | 状态 |
|---|---|
| 单元测试(SBPL 方言 / argv 组装 / 缓存 / 探测 / fail-closed) | ✅ 12/12 通过(macOS arm64 实测) |
tsc 严格模式编译 | ✅ 通过 |
macOS 二进制构建(npm run build:native) | ✅ 已完成(macOS arm64;bin/darwin-arm64/seatbelt-run 34KB 有效 Mach-O) |
| macOS 真实内核 e2e(deny-write / workspace-write / 坏 profile 125) | ✅ 5/5 通过(macOS arm64 真实内核断言) |
dsh 装配集成(cordis.yml 替换后真实运行 + [sandbox: file access denied] 归因) | ⏳ 未验证 |
| npm 发布 | ⏳ 未发布(dsh-seatbelt-sandbox 尚未 publish) |
| darwin-x64 二进制 | ⏳ 未构建(arm64 优先,x64 需 Intel macOS 环境) |
代码与测试基于 dsh 源码逐行核实(landlock-run 先例形态、
confine()契约、SBPL 方言),macOS arm64 上已通过全部 17 项测试(12 单元 + 5 e2e 真实内核断言)。在完成下方「发布前验收清单」前,请勿用于生产部署。
快速开始(筹备阶段)
# 1. 获取源码(推送到 GitHub 后;当前阶段用本地目录亦可)
# git clone <your-org>/dsh-seatbelt-sandbox && cd dsh-seatbelt-sandbox
# 2. 安装依赖 + 构建二进制(须在 macOS 主机上)
npm install
npm run build:native # 生成 bin/darwin-<arch>/seatbelt-run(含 Mach-O 校验)
./bin/darwin-$(uname -m)/seatbelt-run --self-test && echo OK # 自检:libsandbox 可用
# 3. 完整测试(12 单元 + 5 macOS e2e,无 skip)
npm test
# 4. 接入 dsh:按 examples/cordis.yml 替换 sandbox 插件
# 验证点:confine 产出 [seatbelt-run, profileFile, '--', ...argv];
# 写入被拒时 bash 沙箱返回 [sandbox: file access denied …];
# loader 缺失时抛 SANDBOX_UNAVAILABLE(fail-closed)
为什么需要它
Apple 自 macOS 10.10 起弃用 sandbox-exec CLI,未来系统版本可能移除;但底层 libsandbox.1.dylib 的私有 API(sandbox_init / sandbox_free_error)仍是 macOS 安全架构核心(App Sandbox、sandboxd、Chromium/Firefox 都在生产环境直接调用)。任何需要 macOS 沙箱的语言生态都应基于该 API 构建 loader 模式:
sandbox_init(profile) ← 沙箱化当前进程(不可逆,仅一次)
execvp(cmd, argv) ← 替换进程映像;沙箱是进程属性,跨 exec 保留
核心不变量:沙箱必须作用于最终运行目标命令的进程,且必须先于目标启动应用。
纯 JS 无法自做 loader(无内建 FFI、无 execve 原语、V8 运行时窗口大),因此本包采用 C11 原生 loader 二进制 + JS seam 结构——与 dsh 仓库内 native/landlock-run 先例同构。
架构
dsh-seatbelt-sandbox/
├── native/seatbelt-run/src/main.c # C11 loader:dlsym libsandbox → execvp(唯一审计面)
├── src/
│ ├── sbpl.ts # SBPL profile 生成(复制 dsh roots.ts/profiles.ts 语义)
│ ├── loader.ts # launcherPath() / probe()(--self-test)
│ ├── provider.ts # SeatbeltSandboxProvider extends SandboxProvider
│ └── index.ts # 插件默认导出
├── bin/darwin-{arm64,x64}/ # 预编译二进制(macOS 上 npm run build:native 生成)
├── tests/ # node:test 单元(任意平台)+ macOS e2e
└── examples/cordis.yml # 部署装配示例
安装与构建
⚠️ 包尚未发布 npm。发布前请通过 git 安装或本地
npm pack(<github-owner>为你的 GitHub 用户名,推送后替换):npm install <github-owner>/dsh-seatbelt-sandbox # 或 npm pack 后安装 tarball
# macOS 主机上构建二进制(bin/ 不入库,clone 后必须先构建):
cd dsh-seatbelt-sandbox
npm run build:native # 生成 bin/darwin-<arch>/seatbelt-run
- 构建为 macOS-only(
scripts/build.sh在非 darwin 平台快速失败),使用系统自带 clang,无额外工具链。 - 二进制按架构分发:
bin/darwin-arm64/与bin/darwin-x64/,launcherPath()按process.platform + process.arch解析;缺平台包时探测失败 → fail-closed(SANDBOX_UNAVAILABLE),绝不无约束直通。
部署装配(自托管 macOS)
# cordis.yml —— 唯一改动:sandbox 插件替换
- id: sandbox
name: dsh-seatbelt-sandbox # 原为 @deepseek-ai/dsh-sandbox-local
# config 全部可选:
# loaderPath: /absolute/path/to/seatbelt-run
# probeTimeoutMs: 5000
# sandbox-policy / bash / fs-sandbox 等其余插件与配置不动
完整示例见 examples/cordis.yml。消费方(@deepseek-ai/dsh-bash-sandbox、@deepseek-ai/dsh-fs-sandbox 等)只依赖 ctx.sandbox 服务,对后端实现零感知。
loaderPath 仅当二进制未随包分发(例如单独拷贝到固定路径、或需要跨架构指定)时使用;缺省时 launcherPath() 按 process.platform + process.arch 解析包内 bin/darwin-<arch>/seatbelt-run。
loader CLI 契约
seatbelt-run <profile-file> [--] <cmd> <args...>
seatbelt-run --self-test
- profile 走文件传递(规避 ARG_MAX);可选
--分隔符与 dshconfine惯例对齐,且与 inferglowseatbelt-loader <profile-file> <cmd>契约兼容(无--形态同样可用,二进制可互换)。 - 失败统一退出 125(对齐 dsh landlock launcher 约定),stderr 前缀
seatbelt-run:;沙箱 denial 是内核方言operation not permitted,与 loader 失败可区分。 --self-test应用空 profile(version 1),退出 0 表示 libsandbox API 可用——插件的功能探测。
与官方 seatbelt rung 的行为差异
| 维度 | 官方 @deepseek-ai/dsh-sandbox-local | 本插件 |
|---|---|---|
| 执行器 | sandbox-exec CLI(已弃用) | seatbelt-run(直调 libsandbox,跨生态共享契约) |
| 探测 | 真实 read-only profile + true | --self-test(空 profile,与 inferglow/Python 生态一致) |
| denial 方言 | operation not permitted | 相同 |
| runner 失败签名 | sandbox-exec: (无退出码门控) | seatbelt-run: + allowedExitCodes: [125](exit-gated,更强) |
| enforcement | full | full(SBPL deny file-write* 按构造保证) |
| 探测失败 | fail-closed SANDBOX_UNAVAILABLE | 相同 |
| SBPL 生成 | seatbeltProfileArgs(dsh 内部) | 本包 sbpl.ts(复制上游语义,方言测试 pin 等价) |
| profile 文件 | 内联 -p 传递 | 临时文件(provider 级缓存 + dispose 清理) |
契约跟踪责任(升级注意)
本插件通过 peerDependencies 锁定 @deepseek-ai/cordis 与 @deepseek-ai/dsh-sandbox。dsh 上游若演进 ConfinedArgv / SandboxPolicy / SandboxProvider 契约,需同步更新 src/provider.ts(契约变化集中于该单文件);src/sbpl.ts 的 writableRoots / seatbeltProfileText 语义来自 dsh 的 roots.ts / profiles.ts(BSD-3-Clause,来源已标注),上游变更时同步镜像并由 tests/sbpl.test.mjs 的方言断言兜底。
测试
npm test # 单元测试任意平台可跑;macOS e2e 自动跳过
npm run build:native && npm test # macOS 上跑完整套件(真实内核双向断言)
- 单元(
tests/sbpl.test.mjs、tests/provider.test.mjs):SBPL 方言、argv 组装、profile 缓存/清理、探测缓存、fail-closed。 - macOS e2e(
tests/seatbelt.e2e.mjs):read-only deny-write 不落盘 +operation not permitted、/dev/null 可写、workspace-write 双向断言、temp 区授权、坏 profile → 125 +seatbelt-run:。 - 装配集成:
examples/cordis.yml替换后,ctx.sandbox.confine()走新 provider,bash 沙箱的[sandbox: file access denied …]归因基于本插件的 denial 方言正确工作。
发布前验收清单(macOS)
# 1. 构建二进制 + 自检
npm run build:native # bin/darwin-<arch>/seatbelt-run + Mach-O 校验
./bin/darwin-arm64/seatbelt-run --self-test && echo OK
# 2. 完整测试套件(真实内核断言,无 skip)
npm test # 12 单元 + 5 e2e 全部通过
# 3. dsh 装配集成(按 examples/cordis.yml 替换 sandbox 插件后)
# - ctx.sandbox.confine() 产出 [seatbelt-run, profileFile, '--', ...argv]
# - bash 沙箱写入被拒时出现 [sandbox: file access denied …] 归因
# - loader 缺失时抛 SANDBOX_UNAVAILABLE(fail-closed)
# 4. 发布
npm pack && npm publish # 确认 tarball 含 bin/darwin-*/seatbelt-run
推送到 GitHub
# 1. 在 GitHub 新建仓库(建议同名 dsh-seatbelt-sandbox)
# 2. 关联并推送(master 分支):
git remote add origin git@github.com:<your-org>/dsh-seatbelt-sandbox.git
git push -u origin master
# 3. 推送后替换 README/示例中的 <github-owner> 为实际用户名
# 4. 发布 npm 前:补 package.json 的 repository 字段,并完成「发布前验收清单」
参考
.trae/specs/seatbelt-ts-adapter/(工作区根,三件套 spec/checklist/tasks)——需求规格(本实现为"插件方式"落地)- dsh
native/landlock-run——C11 loader + JS seam 的包形态先例 - dsh
packages/sandbox/sandbox/src/{index.ts,roots.ts}、sandbox-local/src/profiles.ts——契约与 SBPL 语义来源 - inferglow
sandbox/seatbelt_loader/——Go 生态同契约实现