fix: fix fs sandbox helper for apply_patch (#18296)

## Summary

- pass split filesystem sandbox policy/cwd through apply_patch contexts,
while omitting legacy-equivalent policies to keep payloads small
- keep the fs helper compatible with legacy Landlock by avoiding helper
read-root permission expansion in that mode and disabling helper network
access

## Root Cause

`d626dc38950fb40a1a5ad0a8ffab2485e3348c53` routed exec-server filesystem
operations through a sandboxed helper. That path forwarded legacy
Landlock into a helper policy shape that could require direct
split-policy enforcement. Sandboxed `apply_patch` hit that edge through
the filesystem abstraction.

The same 0.121 edit-regression path is consistent with #18354: normal
writes route through the `apply_patch` filesystem helper, fail under
sandbox, and then surface the generic retry-without-sandbox prompt.

Fixes #18069
Fixes #18354

## Validation

- `cd codex-rs && just fmt`
- earlier branch validation before merging current `origin/main` and
dropping the now-separate PATH fix:
  - `cd codex-rs && cargo test -p codex-exec-server`
- `cd codex-rs && cargo test -p codex-core file_system_sandbox_context`
  - `cd codex-rs && just fix -p codex-exec-server`
  - `cd codex-rs && just fix -p codex-core`
  - `git diff --check`
  - `cd codex-rs && cargo clean`

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
viyatb-oai
2026-04-17 15:39:07 -07:00
committed by GitHub
parent c9c4caafd8
commit f705f42ba8
5 changed files with 259 additions and 65 deletions

View File

@@ -1,6 +1,7 @@
use async_trait::async_trait;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use tokio::io;
@@ -41,6 +42,10 @@ pub struct ReadDirectoryEntry {
#[serde(rename_all = "camelCase")]
pub struct FileSystemSandboxContext {
pub sandbox_policy: SandboxPolicy,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub sandbox_policy_cwd: Option<AbsolutePathBuf>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_system_sandbox_policy: Option<FileSystemSandboxPolicy>,
pub windows_sandbox_level: WindowsSandboxLevel,
#[serde(default)]
pub windows_sandbox_private_desktop: bool,
@@ -53,6 +58,8 @@ impl FileSystemSandboxContext {
pub fn new(sandbox_policy: SandboxPolicy) -> Self {
Self {
sandbox_policy,
sandbox_policy_cwd: None,
file_system_sandbox_policy: None,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
use_legacy_landlock: false,