Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions codex-rs/exec-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,10 +352,17 @@ absolute path strings and normalize them to `file:` URIs:
- `fs/copy`

Each filesystem request accepts an optional `sandbox` object. When `sandbox`
contains a `ReadOnly` or `WorkspaceWrite` policy, the operation runs in a
hidden helper process launched from the top-level `codex` executable and
prepared through the shared sandbox transform path. Helper requests and
responses are passed over stdin/stdout.
contains a managed permission profile, concrete filesystem permission paths
are canonical `file:` URIs. Native absolute path strings and legacy
`file_system.read` / `file_system.write` roots remain accepted for compatibility
and are normalized to the canonical tagged profile with `file:` URIs when
serialized. The server converts those paths to native absolute paths before
invoking the local filesystem; a URI that is not native to the server host is
rejected as an invalid request.

Sandboxed operations run in a hidden helper process launched from the
top-level `codex` executable and prepared through the shared sandbox transform

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this second paragraph from this diff

path. Helper requests and responses are passed over stdin/stdout.

## Errors

Expand Down Expand Up @@ -384,6 +391,10 @@ The crate exports:
- `ExecServerClientConnectOptions`
- `RemoteExecServerConnectArgs`
- protocol request/response structs for process and filesystem RPCs
- exec-server-owned sandbox permission types, including
`ExecServerFileSystemSandboxContext`, `ExecServerPermissionProfile`, and the
filesystem path, entry, access, special-path, network, and Windows-level
types they contain
- `DEFAULT_LISTEN_URL` and `ExecServerListenUrlParseError`
- `ExecServerRuntimePaths`
- `run_main()` for embedding the websocket server
Expand Down
1 change: 1 addition & 0 deletions codex-rs/exec-server/src/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@ mod tests {
use crate::ProcessId;
use crate::environment_provider::EnvironmentDefault;
use crate::environment_provider::EnvironmentProviderSnapshot;
use codex_utils_path_uri::PathUri;
use pretty_assertions::assert_eq;

fn test_runtime_paths() -> ExecServerRuntimePaths {
Expand Down
10 changes: 10 additions & 0 deletions codex-rs/exec-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod remote_file_system;
mod remote_process;
mod rpc;
mod runtime_paths;
mod sandbox_permissions;
mod sandboxed_file_system;
mod server;

Expand Down Expand Up @@ -105,6 +106,15 @@ pub use protocol::WriteStatus;
pub use remote::RemoteEnvironmentConfig;
pub use remote::run_remote_environment;
pub use runtime_paths::ExecServerRuntimePaths;
pub use sandbox_permissions::ExecServerFileSystemAccessMode;
pub use sandbox_permissions::ExecServerFileSystemPath;
pub use sandbox_permissions::ExecServerFileSystemSandboxContext;
pub use sandbox_permissions::ExecServerFileSystemSandboxEntry;
pub use sandbox_permissions::ExecServerFileSystemSpecialPath;
pub use sandbox_permissions::ExecServerManagedFileSystemPermissions;
pub use sandbox_permissions::ExecServerNetworkSandboxPolicy;
pub use sandbox_permissions::ExecServerPermissionProfile;
pub use sandbox_permissions::ExecServerWindowsSandboxLevel;
pub use server::DEFAULT_LISTEN_URL;
pub use server::ExecServerListenUrlParseError;
pub use server::run_main;
26 changes: 14 additions & 12 deletions codex-rs/exec-server/src/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::path::PathBuf;

use crate::FileSystemSandboxContext;
use crate::ExecServerFileSystemSandboxContext;
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
use codex_protocol::config_types::ShellEnvironmentPolicyInherit;
use codex_utils_path_uri::PathUri;
Expand Down Expand Up @@ -198,7 +198,7 @@ pub struct TerminateResponse {
#[serde(rename_all = "camelCase")]
pub struct FsReadFileParams {
pub path: PathUri,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -212,7 +212,7 @@ pub struct FsReadFileResponse {
pub struct FsWriteFileParams {
pub path: PathUri,
pub data_base64: String,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -224,7 +224,7 @@ pub struct FsWriteFileResponse {}
pub struct FsCreateDirectoryParams {
pub path: PathUri,
pub recursive: Option<bool>,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -235,7 +235,7 @@ pub struct FsCreateDirectoryResponse {}
#[serde(rename_all = "camelCase")]
pub struct FsGetMetadataParams {
pub path: PathUri,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -253,7 +253,7 @@ pub struct FsGetMetadataResponse {
#[serde(rename_all = "camelCase")]
pub struct FsCanonicalizeParams {
pub path: PathUri,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -266,7 +266,7 @@ pub struct FsCanonicalizeResponse {
#[serde(rename_all = "camelCase")]
pub struct FsReadDirectoryParams {
pub path: PathUri,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -289,7 +289,7 @@ pub struct FsRemoveParams {
pub path: PathUri,
pub recursive: Option<bool>,
pub force: Option<bool>,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -302,7 +302,7 @@ pub struct FsCopyParams {
pub source_path: PathUri,
pub destination_path: PathUri,
pub recursive: bool,
pub sandbox: Option<FileSystemSandboxContext>,
pub sandbox: Option<ExecServerFileSystemSandboxContext>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -466,8 +466,10 @@ mod tests {
PermissionProfile::default(),
PathUri::from_path(&legacy_cwd).expect("cwd URI"),
);
let expected_exec_server_sandbox: crate::ExecServerFileSystemSandboxContext =
expected_sandbox.into();
let mut legacy_sandbox =
serde_json::to_value(&expected_sandbox).expect("sandbox should serialize");
serde_json::to_value(&expected_exec_server_sandbox).expect("sandbox should serialize");
legacy_sandbox["cwd"] = serde_json::json!(legacy_cwd.to_string_lossy());
let params: FsReadFileParams = serde_json::from_value(serde_json::json!({
"path": legacy_path.to_string_lossy(),
Expand All @@ -476,15 +478,15 @@ mod tests {
.expect("legacy absolute path should deserialize");
let expected = FsReadFileParams {
path: PathUri::from_path(legacy_path).expect("path URI"),
sandbox: Some(expected_sandbox.clone()),
sandbox: Some(expected_exec_server_sandbox.clone()),
};

assert_eq!(params, expected);
assert_eq!(
serde_json::to_value(params).expect("params should serialize"),
serde_json::json!({
"path": expected.path.to_string(),
"sandbox": serde_json::to_value(expected_sandbox)
"sandbox": serde_json::to_value(expected_exec_server_sandbox)
.expect("sandbox should serialize"),
})
);
Expand Down
4 changes: 3 additions & 1 deletion codex-rs/exec-server/src/remote_file_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use tracing::trace;
use crate::CopyOptions;
use crate::CreateDirectoryOptions;
use crate::ExecServerError;
use crate::ExecServerFileSystemSandboxContext;
use crate::ExecutorFileSystem;
use crate::ExecutorFileSystemFuture;
use crate::FileMetadata;
Expand Down Expand Up @@ -286,10 +287,11 @@ impl ExecutorFileSystem for RemoteFileSystem {

fn remote_sandbox_context(
sandbox: Option<&FileSystemSandboxContext>,
) -> Option<FileSystemSandboxContext> {
) -> Option<ExecServerFileSystemSandboxContext> {
sandbox
.cloned()
.map(FileSystemSandboxContext::drop_cwd_if_unused)
.map(Into::into)
}

fn map_remote_error(error: ExecServerError) -> io::Error {
Expand Down
27 changes: 20 additions & 7 deletions codex-rs/exec-server/src/remote_file_system_path_uri_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::FileSystemSpecialPath;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use futures::SinkExt;
use futures::StreamExt;
Expand All @@ -32,7 +33,7 @@ use crate::protocol::INITIALIZED_METHOD;
use crate::protocol::InitializeResponse;

#[tokio::test]
async fn remote_file_system_sends_path_and_sandbox_cwd_uris_without_native_conversion() {
async fn remote_file_system_sends_path_and_sandbox_uris_without_native_conversion() {
let (websocket_url, captured_params, server) =
record_read_file_params(/*expected_requests*/ 2).await;
let file_system = RemoteFileSystem::new(LazyRemoteExecServerClient::new(
Expand All @@ -43,12 +44,24 @@ async fn remote_file_system_sends_path_and_sandbox_cwd_uris_without_native_conve
PathUri::parse("file://server/share/src/main.rs").expect("valid UNC URI"),
];
let sandbox_cwd = non_native_cwd();
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
let permission_root = AbsolutePathBuf::from_absolute_path(
std::env::temp_dir().join("exec-server-permission-root"),
)
.expect("absolute permission root");
let policy = FileSystemSandboxPolicy::restricted(vec![
FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: permission_root,
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
},
access: FileSystemAccessMode::Write,
}]);
]);
let sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
sandbox_cwd,
Expand All @@ -68,7 +81,7 @@ async fn remote_file_system_sends_path_and_sandbox_cwd_uris_without_native_conve
.into_iter()
.map(|path| FsReadFileParams {
path,
sandbox: Some(sandbox.clone()),
sandbox: Some(sandbox.clone().into()),
})
.collect::<Vec<_>>();
assert_eq!(
Expand Down
Loading
Loading