Sandboxed apply_patch falls back to vendored bwrap because exec-server fs helper clears PATH

Resolved 💬 2 comments Opened Apr 16, 2026 by dfaerch Closed Apr 20, 2026

What version of Codex CLI is running?

0.120.0

What subscription do you have?

plus

Which model were you using?

gpt-5.4

What platform is your computer?

Ubuntu 24.04

What terminal emulator and version are you using (if applicable)?

wezterm

What issue are you seeing?

Summary

Sandboxed apply_patch can fall back to vendored bubblewrap when it runs through the exec-server filesystem sandbox helper. In environments where the system bwrap binary must be used, for example Ubuntu setups relying on AppArmor policy for /usr/bin/bwrap, this breaks sandboxed filesystem operations such as apply_patch.

What I observed

On my setup:

  • sandboxed shell command execution works
  • apply_patch fails under sandbox
  • AppArmor logs show the helper going through the codex-linux-sandbox path instead of /usr/bin/bwrap
  • the helper then attempts to create a user namespace
  • AppArmor rejects the namespace setup under the unprivileged_userns profile

What steps can reproduce the bug?

If you run codex sandboxed with bwrap from system (/usr/bin/bwrap) and ask it to use apply_patch, it should fall back to using the vendored bwrap for that.

I can see it because it fails and asks for permission to run it outside the sandbox. And then in kern.log, i see that apparmor rejects the vendored tools attempt to create a user namespace.

I dont know how else you could see this, except if you change the code to throw an error when it tries to use the vendored one (which would be a nice setting to have. reject_vendored_bwrap=true).

What is the expected behavior?

Codex started under /usr/bin/bwrap, should keep using that.

Additional information

Root cause

The filesystem sandbox helper path introduced in d626dc3 launches the helper with an empty env map in SandboxCommand { env: HashMap::new(), ... }.

Later, spawn_command(...) in codex-rs/exec-server/src/fs_sandbox.rs does:

  • command.env_clear()
  • command.envs(env)

So the helper process gets no PATH.

Later, Linux sandbox launcher selection in preferred_bwrap_launcher() uses find_system_bwrap_in_path(). Since PATH is missing in this helper process, system bwrap lookup fails and the code falls back to vendored bubblewrap.

Proposed solution

Preserve only PATH from the parent process when spawning the exec-server fs sandbox helper, while keeping the rest of the helper environment cleared.

diff --git a/codex-rs/exec-server/src/fs_sandbox.rs b/codex-rs/exec-server/src/fs_sandbox.rs
index 7b0ac25..3ed6c57 100644
--- a/codex-rs/exec-server/src/fs_sandbox.rs
+++ b/codex-rs/exec-server/src/fs_sandbox.rs
@@ -81,11 +81,14 @@ impl FileSystemSandboxRunner {
             sandbox_context.windows_sandbox_level,
             /*has_managed_network_requirements*/ false,
         );
+        let path_env = std::env::var_os("PATH")
+            .map(|path| HashMap::from([("PATH".to_string(), path.to_string_lossy().into_owned())]))
+            .unwrap_or_default();
         let command = SandboxCommand {
             program: helper.as_path().as_os_str().to_owned(),
             args: vec![CODEX_FS_HELPER_ARG1.to_string()],
             cwd: cwd.clone(),
-            env: HashMap::new(),
+            env: path_env,
             additional_permissions: Some(
                 self.helper_permissions(sandbox_context.additional_permissions.as_ref()),
             ),

With that patch:

  • sandboxed apply_patch succeeds again
  • writes outside the allowed sandbox roots still trigger the normal sandbox and approval behavior
  • targeted tests pass:
  • cargo test -p codex-exec-server
  • cargo test -p codex-linux-sandbox

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗