Skip to content

WindowsSandbox测试与限制

汇总 Windows sandbox 的 backend、代理、TTY、取消、descendant、路径与环境依赖测试,并界定未证明范围。

基于rust-v0.150.0
CodexRustSecurityWindowsTesting

WindowsSandbox测试与限制 ​

Windows sandbox 的测试重点不是“命令返回 0”。当前测试覆盖 backend 选择、managed proxy 拒绝、restricted-token 进程、pipe/TTY、interrupt、cancellation、descendant 生命周期、setup roots 和 wrapper 参数。不同终止原因的断言也不同:正常 capture 结束允许 descendant 继续,取消或显式 terminate 则要求 Job Object 回收 descendants。

本文承接WindowsSandbox架构和Windows可读路径授权,面向理解平台集成测试、Job Object、ConPTY 和依赖探测的读者。范围是测试输入、关键断言、skip 条件和未证明边界;不重复 backend 实现细节。源码测试只能证明在满足 Windows、PowerShell、权限和 helper 条件的环境中观察到的行为。

1. Backend矩阵 ​

restricted-token request 携带 proxy_enforced=true 时测试断言返回错误,因为 managed networking 只允许 elevated backend。该测试证明选择器 fail closed,不证明 elevated backend 已成功配置 firewall。

源码位置:codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs :: proxy enforced restricted-token test

rust
let error = spawn_windows_sandbox_session_for_level(WindowsSandboxSessionRequest {
    windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
    proxy_enforced: true,
    network_proxy_restricting_sid: None,
    ..request
}).await.expect_err("restricted token should reject managed proxy");

2. 正常退出 ​

legacy_capture_emits_output_and_preserves_descendant_after_normal_exit 启动 parent 与 descendant,等待 parent capture 结束后再释放 descendant,并断言 descendant 仍存活。正常 top-level 退出不会总是 kill job 中所有 descendants。

源码位置:codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs :: legacy_capture_emits_output_and_preserves_descendant_after_normal_exit

rust
assert_eq!(result.exit_code, 0, "stdout={stdout:?} stderr={stderr:?}");
assert!(stdout.contains("LEGACY-CAPTURE-DIRECT"));
assert!(
    wait_for_path(&survival_marker, Duration::from_secs(10)),
    "sandbox descendant did not survive normal capture exit"
);
wait_for_process_exit(&descendant_process, Duration::from_secs(10))
    .expect("sandbox descendant did not exit after release");

源码位置:codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs :: legacy_capture_emits_output_and_preserves_descendant_after_normal_exit

3. 取消回收 ​

legacy_capture_cancellation_terminates_descendants_without_timeout 在 descendant 启动后触发 cancellation token,捕获 descendant handle,并等待其退出;survival marker 必须不存在。这里证明 cancellation 路径终止进程树,而不是只关闭 stdout reader。

源码位置:codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs :: legacy_capture_cancellation_terminates_descendants_without_timeout

rust
assert!(started_at.elapsed() < Duration::from_secs(10));
assert!(!result.timed_out, "cancellation should not be reported as a timeout");
assert_ne!(result.exit_code, 0);
wait_for_process_exit(&descendant_process, Duration::from_secs(10))
    .expect("sandbox descendant did not exit after cancellation");
assert!(!descendant_marker.exists(), "sandbox descendant survived cancellation");

源码位置:codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs :: legacy_capture_cancellation_terminates_descendants_without_timeout

4. TTY差异 ​

TTY 测试使用 PowerShell 7 和 ConPTY,分别验证 terminate 与 preserve descendant 两种生命周期。若 pwsh 未安装,测试打印 skip 并返回;因此测试通过数量不能被解释成所有 TTY 场景已执行。

源码位置:codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs :: legacy_tty_job_terminates_and_preserves_descendants

rust
let Some(pwsh) = find_pwsh() else {
    eprintln!("skipping sandbox ConPTY lifecycle test: PowerShell 7 is not installed");
    return;
};

等待辅助函数把退出、超时、Win32 API 失败和意外返回值分开,避免把“没有观察到退出”直接归因于 sandbox。

源码位置:codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs :: wait_for_process_exit

rust
match WaitForSingleObject(process.as_raw_handle() as _, timeout_ms) {
    WAIT_OBJECT_0 => Ok(()),
    WAIT_TIMEOUT => Err(std::io::Error::new(
        std::io::ErrorKind::TimedOut,
        "timed out waiting for process to exit",
    )),
    WAIT_FAILED => Err(std::io::Error::last_os_error()),
    result => Err(std::io::Error::other(format!("unexpected process wait result: {result}"))),
}

5. Setup与路径 ​

setup tests 验证 helper bin dir 总在 read roots、platform defaults 的开关、write root 从 read roots 移除、deny-read lexical/canonical 路径保留,以及 Codex sandbox/control directories 不被授予宽泛读写权限。

源码位置:codex-rs/windows-sandbox-rs/src/setup.rs :: tests

6. 环境依赖 ​

部分测试依赖 Windows API、PowerShell 7、ConPTY、可创建用户/token、ACL 权限和 helper binaries。非 Windows 平台无法动态运行;Windows CI 若缺少 pwsh 也会 skip。源码分支仍可静态复核,但不能写成跨平台实测通过。

7. 限制矩阵 ​

范围已证明未证明
Backend选择proxy enforced 拒绝 legacyelevated firewall 实际生效
Capturestdout/stderr 与正常退出任意程序输出行为
CancellationJob 终止 descendants外部脱离 Job 的进程
TTYConPTY 特定生命周期无 pwsh 时的动态行为
Pathsroots/deny aliases 组装ACL 与第三方 ACE 最终合并
Private desktop参数进入 backend桌面隔离的完整攻击面

8. 可执行验证 ​

输入一:restricted token + managed proxy,断言错误。输入二:normal capture root exit,断言 descendant 保留。输入三:cancellation,断言 descendant 在 10 秒内退出且 survival marker 不存在。输入四:TTY terminate/preserve,断言 Job 行为符合调用模式;若 pwsh 缺失则明确 skip。

这些测试能够证明实现中的进程与参数生命周期;不能证明所有 Windows 版本、企业安全软件、UAC 策略或第三方 ACL 下行为一致,也不能把 skip 当成 pass。

源码位置:

  • codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs
  • codex-rs/windows-sandbox-rs/src/setup.rs :: tests
  • codex-rs/windows-sandbox-rs/src/wrapper_tests.rs
text
cd codex-rs
cargo test -p codex-windows-sandbox -- --test-threads=1

9. 阅读闭环 ​

建议按 backend 拒绝 → normal exit → cancellation → TTY → setup/wrapper tests 阅读。读完后应能解释:为什么正常退出与取消对 descendants 的要求不同;为什么 skip 不等于通过;哪些测试只验证 payload;以及 Windows sandbox 的哪些系统效果仍需要真实平台集成环境。

下一篇将进入 NetworkPolicy 决策模型。