Preserve filesystem permission path conventions (#39084)

## Why

Filesystem permission paths can use a convention that differs from the host
running Codex. Converting them immediately to native absolute paths can change
the meaning of ambiguous paths such as `/C:/secret` or Windows UNC paths.

## What changed

- Store literal filesystem permission paths as `PathUri` values through the
  runtime policy and execution protocol.
- Keep legacy string-based serialization at explicit protocol boundaries and
  reject conversions that cannot be represented losslessly.
- Encode native paths as opaque URIs when a normal file URI would imply the
  wrong path convention.

## Testing

Added coverage for cross-platform and ambiguous path round trips, UNC path
variants, permission-profile serialization, and deny-policy enforcement.

GitOrigin-RevId: 5247713796d1f2bb4e02f94eb9fc82d4698060f0
This commit is contained in:
iceweasel-oai
2026-08-17 21:42:42 +00:00
committed by copyberry
parent a4f37a5b7f
commit 2013e04354
46 changed files with 765 additions and 260 deletions

View File

@@ -236,8 +236,12 @@ fn add_helper_runtime_permissions(
fn normalize_file_system_policy_root_aliases(file_system_policy: &mut FileSystemSandboxPolicy) {
for entry in &mut file_system_policy.entries {
if let FileSystemPath::Path { path } = &mut entry.path {
*path = normalize_top_level_alias(path.clone());
// Alias normalization uses this executor's filesystem; leave foreign
// or opaque PathUris unchanged.
if let FileSystemPath::Path { path } = &mut entry.path
&& let Ok(native_path) = path.to_abs_path()
{
*path = normalize_top_level_alias(native_path).into();
}
}
}

View File

@@ -452,7 +452,7 @@ mod tests {
fn remote_sandbox_context_drops_unused_cwd() {
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: absolute_test_path("remote-root"),
path: absolute_test_path("remote-root").into(),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,

View File

@@ -15,8 +15,6 @@ use codex_protocol::models::PermissionProfile;
#[cfg(unix)]
use codex_protocol::permissions::FileSystemAccessMode;
#[cfg(unix)]
use codex_protocol::permissions::FileSystemPath;
#[cfg(unix)]
use codex_protocol::permissions::FileSystemSandboxEntry;
#[cfg(unix)]
use codex_protocol::permissions::FileSystemSandboxPolicy;
@@ -214,7 +212,7 @@ async fn sandboxed_discovery_follows_only_permitted_external_symlinks() -> anyho
let root_path = AbsolutePathBuf::from_absolute_path(root.path())?;
let external_root = AbsolutePathBuf::from_absolute_path(external.path())?;
let path_entry =
|path, access| FileSystemSandboxEntry::new(FileSystemPath::Path { path }, access);
|path: AbsolutePathBuf, access| FileSystemSandboxEntry::new(path.into(), access);
let read_root = path_entry(root_path, FileSystemAccessMode::Read);
let read_external = path_entry(external_root.clone(), FileSystemAccessMode::Read);
let deny_external_skill = path_entry(external_root.join("skill"), FileSystemAccessMode::Deny);

View File

@@ -364,7 +364,6 @@ fn read_only_sandbox(path: std::path::PathBuf) -> codex_exec_server::FileSystemS
use codex_exec_server::FileSystemSandboxContext;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemAccessMode;
use codex_protocol::permissions::FileSystemPath;
use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
@@ -374,7 +373,7 @@ fn read_only_sandbox(path: std::path::PathBuf) -> codex_exec_server::FileSystemS
.unwrap_or_else(|err| panic!("sandbox path should be absolute: {err}"));
FileSystemSandboxContext::from_permission_profile(PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path { path },
path: path.into(),
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
}]),

View File

@@ -85,7 +85,7 @@ pub(crate) fn read_only_sandbox(readable_root: std::path::PathBuf) -> FileSystem
let readable_root = absolute_path(readable_root);
sandbox_context(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: readable_root,
path: readable_root.into(),
},
access: FileSystemAccessMode::Read,
missing_path_behavior: None,
@@ -98,7 +98,7 @@ pub(crate) fn workspace_write_sandbox(
let writable_root = absolute_path(writable_root);
sandbox_context(vec![FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: writable_root,
path: writable_root.into(),
},
access: FileSystemAccessMode::Write,
missing_path_behavior: None,