mirror of
https://github.com/openai/codex.git
synced 2026-04-30 09:26:44 +00:00
exec-server: carry filesystem sandbox profiles (#18276)
## Why The exec-server still needs platform sandbox inputs, but the migration should preserve the `PermissionProfile` that produced them. Keeping only the derived legacy sandbox map would keep `SandboxPolicy` as the effective abstraction and would make full-disk vs. restricted profiles harder to preserve as the permissions stack starts round-tripping profiles. `PermissionProfile` entries can also be cwd-sensitive (`:cwd`, `:project_roots`, relative globs), so the exec-server must carry the request sandbox cwd instead of resolving those entries against the long-lived exec-server process cwd. ## What changed `FileSystemSandboxContext` now carries `permissions: PermissionProfile` plus an optional `cwd`: - removed `sandboxPolicy`, `sandboxPolicyCwd`, `fileSystemSandboxPolicy`, and `additionalPermissions` - added `permissions` and `cwd` - kept the platform knobs `windowsSandboxLevel`, `windowsSandboxPrivateDesktop`, and `useLegacyLandlock` Core turn and apply-patch paths populate the context from the active runtime permissions and request cwd. Exec-server derives platform `SandboxPolicy`/`FileSystemSandboxPolicy` at the filesystem boundary, adds helper runtime reads there, and rejects cwd-dependent profiles that arrive without a cwd. The legacy `FileSystemSandboxContext::new(SandboxPolicy)` constructor now preserves the old workspace-write conversion semantics for compatibility tests/callers. ## Verification - `cargo test -p codex-exec-server` - `cargo test -p codex-exec-server sandbox_cwd -- --nocapture` - `cargo test -p codex-exec-server sandbox_context_new_preserves_legacy_workspace_write_read_only_subpaths -- --nocapture` - `cargo test -p codex-core --lib file_system_sandbox_context_uses_active_attempt -- --nocapture`
This commit is contained in:
@@ -25,6 +25,7 @@ use codex_protocol::models::FileSystemPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::protocol::ReadOnlyAccess;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
use codex_sandboxing::policy_transforms::merge_permission_profiles;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::TempDir;
|
||||
@@ -101,6 +102,31 @@ fn workspace_write_sandbox(writable_root: std::path::PathBuf) -> FileSystemSandb
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sandbox_context_new_preserves_legacy_workspace_write_read_only_subpaths() -> Result<()> {
|
||||
let tmp = TempDir::new()?;
|
||||
let writable_dir = tmp.path().join("writable");
|
||||
let git_dir = writable_dir.join(".git");
|
||||
std::fs::create_dir_all(&git_dir)?;
|
||||
|
||||
let sandbox = workspace_write_sandbox(writable_dir.clone());
|
||||
let cwd = sandbox.cwd.as_ref().expect("sandbox cwd");
|
||||
let policy = sandbox.permissions.file_system_sandbox_policy();
|
||||
let writable_roots = policy.get_writable_roots_with_cwd(cwd.as_path());
|
||||
let writable_dir = absolute_path(std::fs::canonicalize(writable_dir)?);
|
||||
let git_dir = absolute_path(std::fs::canonicalize(git_dir)?);
|
||||
let Some(writable_root) = writable_roots
|
||||
.iter()
|
||||
.find(|writable_root| writable_root.root == writable_dir)
|
||||
else {
|
||||
panic!("writable root should be preserved");
|
||||
};
|
||||
|
||||
assert!(writable_root.read_only_subpaths.contains(&git_dir));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assert_sandbox_denied(error: &std::io::Error) {
|
||||
match error.kind() {
|
||||
std::io::ErrorKind::InvalidInput | std::io::ErrorKind::PermissionDenied => {
|
||||
@@ -567,13 +593,19 @@ async fn file_system_sandboxed_write_allows_additional_write_root(use_remote: bo
|
||||
std::fs::create_dir_all(&writable_dir)?;
|
||||
|
||||
let mut sandbox = read_only_sandbox(readable_dir);
|
||||
sandbox.additional_permissions = Some(PermissionProfile {
|
||||
let additional_permissions = PermissionProfile {
|
||||
network: None,
|
||||
file_system: Some(FileSystemPermissions::from_read_write_roots(
|
||||
/*read*/ None,
|
||||
Some(vec![absolute_path(writable_dir)]),
|
||||
)),
|
||||
});
|
||||
};
|
||||
let Some(permissions) =
|
||||
merge_permission_profiles(Some(&sandbox.permissions), Some(&additional_permissions))
|
||||
else {
|
||||
panic!("merged permissions should not be empty");
|
||||
};
|
||||
sandbox.permissions = permissions;
|
||||
|
||||
file_system
|
||||
.write_file(
|
||||
|
||||
Reference in New Issue
Block a user