fix(linux-sandbox): avoid panic on bwrap build failures (#21127)

## Summary

- Propagate Linux bubblewrap argument-construction failures instead of
panicking in the helper
- Keep mutable-symlink carveouts fail-closed while reporting them as
ordinary sandbox build failures
- Add regression coverage for a protected `.codex` symlink inside a
writable workspace root

## Root cause

Linux bubblewrap intentionally rejects read-only carveouts that cross a
symlink the sandboxed process can still rewrite. That is the correct
security behavior for protected metadata paths such as `.codex`.

The bug was one layer higher: `linux_run_main` treated the expected
build failure as impossible and panicked while constructing the
bubblewrap argv. For issue #20716, that turned a normal fail-closed
sandbox outcome into a noisy panic in the transcript.

## User impact

Users with a project-local `.codex` symlink inside a writable workspace
still get the conservative sandbox decision, but they no longer see a
Rust panic for that condition. The helper now exits with the concise
sandbox-build error so the normal denial / escalation path can handle
it.


Fixes #20716
This commit is contained in:
viyatb-oai
2026-05-05 13:34:08 -07:00
committed by GitHub
Unverified
parent 3b2ebb368e
commit 8b95d5467e
3 changed files with 75 additions and 10 deletions
@@ -61,6 +61,7 @@ fn inserts_bwrap_argv0_before_command_separator() {
..Default::default()
},
)
.expect("build bwrap argv")
.args;
apply_inner_command_argv0_for_launcher(
&mut argv,
@@ -104,6 +105,7 @@ fn rewrites_inner_command_path_when_bwrap_lacks_argv0() {
..Default::default()
},
)
.expect("build bwrap argv")
.args;
apply_inner_command_argv0_for_launcher(
&mut argv,
@@ -172,6 +174,7 @@ fn inserts_unshare_net_when_network_isolation_requested() {
..Default::default()
},
)
.expect("build bwrap argv")
.args;
assert!(argv.contains(&"--unshare-net".to_string()));
}
@@ -190,6 +193,7 @@ fn inserts_unshare_net_when_proxy_only_network_mode_requested() {
..Default::default()
},
)
.expect("build bwrap argv")
.args;
assert!(argv.contains(&"--unshare-net".to_string()));
}
@@ -265,6 +269,7 @@ fn managed_proxy_preflight_argv_is_wrapped_for_full_access_policy() {
&FileSystemSandboxPolicy::unrestricted(),
mode,
)
.expect("build preflight argv")
.args;
assert!(argv.iter().any(|arg| arg == "--"));
}