Preserve special filesystem subpaths as wire strings (#33107)

## Why

Special filesystem subpaths can contain relative or foreign-platform path
spellings. Treating this wire text as a host-native path can reinterpret it.

## What changed

- Store special filesystem subpaths as strings in the core permissions model.
- Represent app-server subpaths as `LegacyAppPathString` and convert them at
  the protocol boundary without changing their spelling.
- Add `LegacyAppPathString::from_string` for callers that already own legacy
  app-server path text.

GitOrigin-RevId: 2214f03aa997fd0e6eb2ab60d425304bb4bd6132
This commit is contained in:
Adam Perry @ OpenAI
2026-07-14 16:40:13 +00:00
committed by copyberry
parent fca8c00f11
commit 0396f99cf1
19 changed files with 199 additions and 97 deletions
+8 -10
View File
@@ -274,7 +274,7 @@ fn insert_special_filesystem_permission_toml(
insert_scoped_filesystem_permission_toml(
entries,
":workspace_roots".to_string(),
subpath.unwrap_or_else(|| PathBuf::from(".")),
subpath.unwrap_or_else(|| ".".to_string()),
access,
);
}
@@ -303,7 +303,7 @@ fn insert_special_filesystem_permission_toml(
fn insert_scoped_filesystem_permission_toml(
entries: &mut BTreeMap<String, FilesystemPermissionToml>,
path: String,
subpath: PathBuf,
subpath: String,
access: FileSystemAccessMode,
) {
let permission = entries
@@ -311,13 +311,10 @@ fn insert_scoped_filesystem_permission_toml(
.or_insert_with(|| FilesystemPermissionToml::Scoped(BTreeMap::new()));
match permission {
FilesystemPermissionToml::Scoped(scoped_entries) => {
scoped_entries.insert(subpath.to_string_lossy().into_owned(), access);
scoped_entries.insert(subpath, access);
}
FilesystemPermissionToml::Access(_) => {
*permission = FilesystemPermissionToml::Scoped(BTreeMap::from([(
subpath.to_string_lossy().into_owned(),
access,
)]));
*permission = FilesystemPermissionToml::Scoped(BTreeMap::from([(subpath, access)]));
}
}
}
@@ -608,7 +605,9 @@ fn compile_scoped_filesystem_path(
}
if let Some(special) = parse_special_path(path) {
let subpath = parse_relative_subpath(subpath)?;
let subpath = parse_relative_subpath(subpath)?
.to_string_lossy()
.into_owned();
let special = match special {
FileSystemSpecialPath::ProjectRoots { .. } => Ok(FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(subpath)),
@@ -894,8 +893,7 @@ fn maybe_push_unknown_special_path_warning(
startup_warnings,
match subpath.as_deref() {
Some(subpath) => format!(
"Configured filesystem path `{path}` with nested entry `{}` is not recognized by this version of Codex and will be ignored. Upgrade Codex if this path is required.",
subpath.display()
"Configured filesystem path `{path}` with nested entry `{subpath}` is not recognized by this version of Codex and will be ignored. Upgrade Codex if this path is required."
),
None => format!(
"Configured filesystem path `{path}` is not recognized by this version of Codex and will be ignored. Upgrade Codex if this path is required."
@@ -6,7 +6,6 @@ use codex_protocol::permissions::FileSystemSandboxEntry;
use codex_protocol::permissions::FileSystemSpecialPath;
use codex_utils_path_uri::PathUri;
use std::collections::HashSet;
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct FileSystemContext {
@@ -183,9 +182,9 @@ fn render_special_path(value: &FileSystemSpecialPath) -> String {
}
}
fn render_special_path_with_subpath(base: &str, subpath: &Option<PathBuf>) -> String {
fn render_special_path_with_subpath(base: &str, subpath: &Option<String>) -> String {
match subpath {
Some(subpath) => format!("{base}/{}", subpath.display()),
Some(subpath) => format!("{base}/{subpath}"),
None => base.to_string(),
}
}
@@ -156,7 +156,7 @@ fn workspace_write_permission_profile_with_private_denials() -> PermissionProfil
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(Some(PathBuf::from("private"))),
value: FileSystemSpecialPath::project_roots(Some("private".to_string())),
},
access: FileSystemAccessMode::Deny,
},