diff --git a/.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml b/.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml index 463ecee62c..b13bee5e9a 100644 --- a/.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-06-sandbox.md -2026-07-06-sandbox.md: 79a42cbb62c73829d5ab2d0d7c12db6324e3e086 -2026-07-06-sandbox.zh.md: 800cd83cff57b304fdfcaf0628eb3f97f171304a +2026-07-06-sandbox.md: 6934691e9b3f37e50bb02dfec0240a03ec9b5f30 +2026-07-06-sandbox.zh.md: 37142ca452fb413426025505b7247d1d19aff9d9 diff --git a/.agents/notes/implemented/feature/2026-07-06-sandbox.md b/.agents/notes/implemented/feature/2026-07-06-sandbox.md index 79a42cbb62..6934691e9b 100644 --- a/.agents/notes/implemented/feature/2026-07-06-sandbox.md +++ b/.agents/notes/implemented/feature/2026-07-06-sandbox.md @@ -117,7 +117,7 @@ fs/web/todo execute in-process, so their sandbox semantics are policy at their s ### Testing -- **Unit:** pin platform selection and profiles, direct provider-argv handoff, spawn-level failures with invalid-workdir controls, missing/non-executable/missing-interpreter evidence, and malformed-runner negative controls, confined `BASH_ENV` ordering, structured runner classification (including partial-Landlock notice-only child outcomes, gated fatal evidence, child exits 126/127, and foreground/background parity), per-call mode/root resolution, per-process facts, escalation validation and outcomes, permission preset folding and write-through, and runtime-context ordering and materialization. +- **Unit:** pin platform selection and profiles, direct provider-argv handoff, spawn-level failures with invalid-workdir controls, missing/non-executable/missing-interpreter evidence, malformed-runner negative controls, confined `BASH_ENV` ordering, structured runner classification (including partial-Landlock notice-only child outcomes, gated fatal evidence, child exits 126/127, and foreground/background parity), per-call mode/root resolution, per-process facts, escalation validation and outcomes, permission preset folding and write-through, and runtime-context ordering and materialization. - **Keyless real-runner:** exercise bwrap, Landlock, and Seatbelt against real filesystem effects at provider and bash-consumer layers; one real Cordis context concurrently drives two project sessions through shipped bash and fs tools, proving own-root success and sibling-root denial. Packed-install coverage proves the registry launcher remains executable. CI rejects a silent all-skip. - **With-key:** start the real ACP composition in read-only mode, let a model-driven bash write hit the runner's denial marker, then drive the bridge answerer and disk effect through granted and rejected workspace-write retries; unavailable credentials or runners self-skip. - **Snapshot:** pin the atomic current-policy context and both scripted approval branches. A real ACP example scenario places its session under the user home while the deployment fallback points at `/tmp`, then pins both the workspace-write runtime-context message and a successful deployment-selected mutation; this distinguishes session-root resolution from the process fallback without depending on runner-specific denial text. A POSIX fake partial-Landlock provider pins direct bash `false` as an ordinary child result and a missing provider executable as foreground/background infrastructure failure through the assembled app. Other snapshots start unconfined so unrelated fixtures remain platform-independent. diff --git a/.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md b/.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md index 800cd83cff..37142ca452 100644 --- a/.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md +++ b/.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md @@ -117,7 +117,7 @@ fs/web/todo 在进程内执行,因此它们的沙箱语义是各自 seam 层 ### 测试 -- **单元测试:** 固定平台选择和 profile、直接交接提供方返回的 argv、带有无效 workdir 对照、runner 缺失/不可执行/解释器缺失证据以及格式错误 runner 阴性对照的 spawn 层失败、受约束的 `BASH_ENV` 求值顺序、结构化 runner 分类(包括只有部分强制执行通知的子进程结果、带门控的致命证据、子进程退出码 126/127,以及前台/后台一致性)、按调用的模式/根目录解析、按进程事实、升级验证和结果、权限 preset fold 和写入透传,以及运行时上下文排序与具体化。 +- **单元测试:** 固定平台选择和 profile、直接交接提供方返回的 argv、带有无效 workdir 对照的 spawn 层失败、runner 缺失/不可执行/解释器缺失证据、格式错误 runner 阴性对照、受约束的 `BASH_ENV` 求值顺序、结构化 runner 分类(包括只有部分强制执行通知的子进程结果、带门控的致命证据、子进程退出码 126/127,以及前台/后台一致性)、按调用的模式/根目录解析、按进程事实、升级验证和结果、权限 preset fold 和写入透传,以及运行时上下文排序与具体化。 - **Keyless 真实 runner:** 在提供方和 bash 消费方层面对 bwrap、Landlock 和 Seatbelt 执行真实文件系统效果测试;一个真实 Cordis 上下文通过已交付的 bash 和 fs 工具并发驱动两个项目会话,证明在自身根目录写入成功、在兄弟根目录写入被拒绝。Packed-install 覆盖率证明注册表 launcher 保持可执行。CI 拒绝静默全跳过。 - **With-key:** 以只读模式启动真实 ACP 组合,让模型驱动的 bash 写入命中 runner 的拒绝标记,再通过已授权与被拒绝的 workspace-write 重试驱动 bridge 应答器和磁盘效果;不可用的凭证或 runner 自动跳过。 - **快照:** 固定原子化的当前策略上下文和两个脚本化的 approval 分支。一个真实 ACP 示例场景把会话放在用户主目录下,同时让部署后备根目录指向 `/tmp`,然后固定 workspace-write 运行时上下文消息与一次成功的、由部署选定的变更;这能区分会话根目录解析与进程后备值,而不依赖 runner 特定的拒绝文本。一个模拟 Landlock 部分强制执行行为的 POSIX 提供方会在组装后的应用中固定直接执行 bash `false` 时仍得到普通子进程结果,并固定提供方可执行文件缺失时在前台/后台均为基础设施失败。其他快照以无约束启动,使无关 fixture(测试前置数据)保持平台无关。 diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 9e8de5e8fd..d9000fea89 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -1020,7 +1020,8 @@ export interface Config { * Override the runner argv; bwrap-shaped profile arguments are appended. A * non-empty override asserts full enforcement and skips built-in selection and * probing. A runner that starts but refuses its profile must be identifiable by - * {@link runnerFailureSignatures}; spawn rejection remains a consumer-owned + * {@link runnerFailureSignatures}. Consumers classify spawn rejection; only + * attributable `ENOENT` or `EACCES` with runner argv[0] provenance becomes an * infrastructure failure. */ runnerCommand?: string[] diff --git a/packages/bash/bash-sandbox/tests/partial-landlock.spec.ts b/packages/bash/bash-sandbox/tests/partial-landlock.spec.ts index b4582fa39d..23546e5d92 100644 --- a/packages/bash/bash-sandbox/tests/partial-landlock.spec.ts +++ b/packages/bash/bash-sandbox/tests/partial-landlock.spec.ts @@ -174,7 +174,7 @@ describe('partial Landlock runner-failure classification', () => { signal: null, sandbox: { mode: 'read-only', denied: false, enforcement: 'full' }, }) - expect((foreground as { stderr: { text: string } }).stderr.text).toMatch(/not.*not found/) + expect((foreground as { stderr: { text: string } }).stderr.text.length).toBeGreaterThan(0) const background = bash.start(bash.resolve(request)) await background.done @@ -183,7 +183,8 @@ describe('partial Landlock runner-failure classification', () => { expect(background.signal).toBeNull() expect(background.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'full' }) const output = background.readOutput().delta - expect(output).toMatch(/\[stderr\]\n[\s\S]*not.*not found/) + expect(output.startsWith('[stderr]\n')).toBe(true) + expect(output.length).toBeGreaterThan('[stderr]\n'.length) expect(output).not.toContain('spawn failed:') } diff --git a/packages/sandbox/sandbox-local/src/index.ts b/packages/sandbox/sandbox-local/src/index.ts index c4dd710b88..64e92d9bf2 100644 --- a/packages/sandbox/sandbox-local/src/index.ts +++ b/packages/sandbox/sandbox-local/src/index.ts @@ -26,7 +26,8 @@ export interface Config { * Override the runner argv; bwrap-shaped profile arguments are appended. A * non-empty override asserts full enforcement and skips built-in selection and * probing. A runner that starts but refuses its profile must be identifiable by - * {@link runnerFailureSignatures}; spawn rejection remains a consumer-owned + * {@link runnerFailureSignatures}. Consumers classify spawn rejection; only + * attributable `ENOENT` or `EACCES` with runner argv[0] provenance becomes an * infrastructure failure. */ runnerCommand?: string[]