Avoid creating metadata paths in the Windows sandbox (#33430)

## Why

Elevated Windows sandbox setup could turn the built-in read-only protections
for workspace metadata into deny-write paths, creating metadata directories
that did not already exist.

## What changed

- Track whether filesystem permission entries come from a built-in profile or
  were explicitly configured.
- Exclude generated metadata defaults from direct Windows deny-write overrides
  while continuing to enforce explicit read-only carveouts for the same paths.
- Preserve the distinction while materializing and normalizing permission
  paths, without exposing generated defaults as user configuration.

## Testing

Added Windows coverage confirming that elevated sandbox setup leaves missing
workspace metadata absent and still produces overrides for explicit metadata
carveouts.

GitOrigin-RevId: 701bcb229f631b18ab5ced22f3edf6f303d932f3
This commit is contained in:
iceweasel-oai
2026-07-15 21:19:14 +00:00
committed by copyberry
parent 79177c3e20
commit cb79b3cdf9
11 changed files with 340 additions and 60 deletions
@@ -312,11 +312,13 @@ pub enum FileSystemPath {
impl From<CoreFileSystemPath> for FileSystemPath {
fn from(value: CoreFileSystemPath) -> Self {
match value {
CoreFileSystemPath::Path { path } => Self::Path {
CoreFileSystemPath::Path { path }
| CoreFileSystemPath::GeneratedDefaultPath { path } => Self::Path {
path: LegacyAppPathString::from_abs_path(&path),
},
CoreFileSystemPath::GlobPattern { pattern } => Self::GlobPattern { pattern },
CoreFileSystemPath::Special { value } => Self::Special {
CoreFileSystemPath::Special { value }
| CoreFileSystemPath::GeneratedDefaultSpecial { value } => Self::Special {
value: value.into(),
},
}
+1 -1
View File
@@ -4263,7 +4263,7 @@ exclude_slash_tmp = true
file_system_policy
.entries
.contains(&FileSystemSandboxEntry {
path: FileSystemPath::Path {
path: FileSystemPath::GeneratedDefaultPath {
path: AbsolutePathBuf::resolve_path_against_base(
subpath,
cwd.path()
+2
View File
@@ -249,6 +249,8 @@ fn insert_filesystem_permission_toml(
FileSystemPath::Special { value } => {
insert_special_filesystem_permission_toml(entries, value, entry.access);
}
FileSystemPath::GeneratedDefaultPath { .. }
| FileSystemPath::GeneratedDefaultSpecial { .. } => {}
}
}
@@ -153,13 +153,13 @@ fn render_file_system_entry(rendered: &mut String, entry: &FileSystemSandboxEntr
}
rendered.push_str("\">");
match &entry.path {
FileSystemPath::Path { path } => {
FileSystemPath::Path { path } | FileSystemPath::GeneratedDefaultPath { path } => {
push_text_element(rendered, "path", path.to_string_lossy().as_ref());
}
FileSystemPath::GlobPattern { pattern } => {
push_text_element(rendered, "glob", pattern);
}
FileSystemPath::Special { value } => {
FileSystemPath::Special { value } | FileSystemPath::GeneratedDefaultSpecial { value } => {
let value = render_special_path(value);
push_text_element(rendered, "special", &value);
}
+45
View File
@@ -797,6 +797,51 @@ fn windows_elevated_supports_split_write_read_carveouts() {
);
}
#[cfg(target_os = "windows")]
#[test]
fn windows_workspace_defaults_do_not_hide_explicit_metadata_carveouts() {
let temp_dir = tempfile::TempDir::new().expect("tempdir");
let cwd = temp_dir.path().canonicalize().expect("canonical cwd").abs();
let default_profile = PermissionProfile::workspace_write();
let default_overrides = resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&default_profile,
&cwd,
/*use_windows_elevated_backend*/ true,
)
.expect("resolve workspace defaults");
assert!(
default_overrides.is_none_or(|overrides| overrides.additional_deny_write_paths.is_empty())
);
for name in codex_protocol::permissions::PROTECTED_METADATA_PATH_NAMES {
let (mut explicit_policy, network_policy) = default_profile.to_runtime_permissions();
explicit_policy
.entries
.push(codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
value: codex_protocol::permissions::FileSystemSpecialPath::project_roots(Some(
(*name).into(),
)),
},
access: codex_protocol::permissions::FileSystemAccessMode::Read,
});
let explicit_profile =
PermissionProfile::from_runtime_permissions(&explicit_policy, network_policy);
let overrides = resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&explicit_profile,
&cwd,
/*use_windows_elevated_backend*/ true,
)
.expect("resolve explicit metadata carveout")
.expect("explicit metadata carveout needs an override");
assert_eq!(overrides.additional_deny_write_paths, vec![cwd.join(name)]);
}
}
#[test]
fn windows_elevated_supports_unreadable_split_carveouts() {
let temp_dir = tempfile::TempDir::new().expect("tempdir");
@@ -197,6 +197,59 @@ async fn windows_restricted_token_rejects_exact_and_glob_deny_read_policy() -> a
Ok(())
}
#[tokio::test]
#[serial(codex_home)]
async fn windows_elevated_does_not_create_missing_workspace_metadata() -> anyhow::Result<()> {
let codex_home =
codex_home_for_windows_sandbox_test("windows-elevated-missing-metadata-codex-home")?;
let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str());
stage_windows_sandbox_helpers()?;
let workspace = TempDir::new()?;
let cwd = dunce::canonicalize(workspace.path())?.abs();
let permission_profile = PermissionProfile::workspace_write()
.materialize_project_roots_with_workspace_roots(std::slice::from_ref(&cwd));
let output = process_exec_tool_call(
ExecParams {
command: vec![
"cmd.exe".to_string(),
"/D".to_string(),
"/C".to_string(),
"echo sandbox-ok".to_string(),
],
cwd: cwd.clone(),
expiration: 10_000.into(),
capture_policy: ExecCapturePolicy::ShellTool,
env: HashMap::new(),
network: None,
network_environment_id: None,
sandbox_permissions: SandboxPermissions::UseDefault,
windows_sandbox_level: WindowsSandboxLevel::Elevated,
windows_sandbox_private_desktop: false,
justification: None,
arg0: None,
},
&permission_profile,
&cwd,
std::slice::from_ref(&cwd),
&None,
/*use_legacy_landlock*/ false,
/*stdout_stream*/ None,
)
.await?;
assert_eq!(output.exit_code, 0, "sandboxed command should complete");
for name in codex_protocol::permissions::PROTECTED_METADATA_PATH_NAMES {
let path = cwd.join(name);
assert!(
!path.exists(),
"elevated setup should not create missing workspace metadata: {}",
path.display()
);
}
Ok(())
}
#[tokio::test]
#[serial(codex_home)]
async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyhow::Result<()> {
+21 -1
View File
@@ -126,8 +126,10 @@ pub struct WalkOutcome {
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ExecFileSystemPath {
Path { path: PathUri },
GeneratedDefaultPath { path: PathUri },
GlobPattern { pattern: String },
Special { value: FileSystemSpecialPath },
GeneratedDefaultSpecial { value: FileSystemSpecialPath },
}
impl From<FileSystemPath> for ExecFileSystemPath {
@@ -136,8 +138,14 @@ impl From<FileSystemPath> for ExecFileSystemPath {
FileSystemPath::Path { path } => Self::Path {
path: PathUri::from_abs_path(&path),
},
FileSystemPath::GeneratedDefaultPath { path } => Self::GeneratedDefaultPath {
path: PathUri::from_abs_path(&path),
},
FileSystemPath::GlobPattern { pattern } => Self::GlobPattern { pattern },
FileSystemPath::Special { value } => Self::Special { value },
FileSystemPath::GeneratedDefaultSpecial { value } => {
Self::GeneratedDefaultSpecial { value }
}
}
}
}
@@ -150,8 +158,14 @@ impl TryFrom<ExecFileSystemPath> for FileSystemPath {
ExecFileSystemPath::Path { path } => Self::Path {
path: path.to_abs_path()?,
},
ExecFileSystemPath::GeneratedDefaultPath { path } => Self::GeneratedDefaultPath {
path: path.to_abs_path()?,
},
ExecFileSystemPath::GlobPattern { pattern } => Self::GlobPattern { pattern },
ExecFileSystemPath::Special { value } => Self::Special { value },
ExecFileSystemPath::GeneratedDefaultSpecial { value } => {
Self::GeneratedDefaultSpecial { value }
}
})
}
}
@@ -350,8 +364,14 @@ impl FileSystemSandboxContext {
ExecFileSystemPath::GlobPattern { pattern } => !Path::new(pattern).is_absolute(),
ExecFileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots { .. },
}
| ExecFileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::ProjectRoots { .. },
} => true,
ExecFileSystemPath::Path { .. } | ExecFileSystemPath::Special { .. } => false,
ExecFileSystemPath::Path { .. }
| ExecFileSystemPath::GeneratedDefaultPath { .. }
| ExecFileSystemPath::Special { .. }
| ExecFileSystemPath::GeneratedDefaultSpecial { .. } => false,
}),
ExecPermissionProfile::Managed {
file_system: ExecManagedFileSystemPermissions::Unrestricted,
+4 -1
View File
@@ -111,7 +111,10 @@ impl FileSystemPermissions {
) -> impl Iterator<Item = (&AbsolutePathBuf, FileSystemAccessMode)> {
self.entries.iter().filter_map(|entry| match &entry.path {
FileSystemPath::Path { path } => Some((path, entry.access)),
FileSystemPath::GlobPattern { .. } | FileSystemPath::Special { .. } => None,
FileSystemPath::GeneratedDefaultPath { .. }
| FileSystemPath::GlobPattern { .. }
| FileSystemPath::Special { .. }
| FileSystemPath::GeneratedDefaultSpecial { .. } => None,
})
}
+124 -21
View File
@@ -343,6 +343,11 @@ pub enum FileSystemPath {
// TODO(anp): Use PathUri once permission paths no longer require native-path rollout serialization.
path: AbsolutePathBuf,
},
/// A concrete path synthesized by a built-in permissions profile rather
/// than explicitly configured by the user.
GeneratedDefaultPath {
path: AbsolutePathBuf,
},
/// A git-style glob pattern. Pattern entries currently support
/// FileSystemAccessMode::Deny only.
GlobPattern {
@@ -351,6 +356,20 @@ pub enum FileSystemPath {
Special {
value: FileSystemSpecialPath,
},
/// A symbolic path synthesized by a built-in permissions profile rather
/// than explicitly configured by the user.
GeneratedDefaultSpecial {
value: FileSystemSpecialPath,
},
}
impl FileSystemPath {
pub fn is_generated_default(&self) -> bool {
matches!(
self,
Self::GeneratedDefaultPath { .. } | Self::GeneratedDefaultSpecial { .. }
)
}
}
const PROJECT_ROOTS_GLOB_PATTERN_PREFIX: &str = "codex-project-roots://";
@@ -403,6 +422,25 @@ impl FileSystemSandboxPolicy {
}
}
/// Removes entries synthesized by built-in profiles while preserving
/// explicitly configured entries targeting the same paths.
pub fn remove_generated_defaults(&mut self) {
self.entries
.retain(|entry| !entry.path.is_generated_default());
}
pub fn has_explicit_non_write_entry_for_path_with_cwd(&self, path: &Path, cwd: &Path) -> bool {
let Some(path) = resolve_candidate_path(path, cwd) else {
return false;
};
let cwd = AbsolutePathBuf::from_absolute_path(cwd).ok();
self.entries.iter().any(|entry| {
!entry.path.is_generated_default()
&& !entry.access.can_write()
&& resolve_entry_path(&entry.path, cwd.as_ref()).as_ref() == Some(&path)
})
}
fn has_root_access(&self, predicate: impl Fn(FileSystemAccessMode) -> bool) -> bool {
matches!(self.kind, FileSystemSandboxKind::Restricted)
&& self.entries.iter().any(|entry| {
@@ -496,9 +534,12 @@ impl FileSystemSandboxPolicy {
}
match &entry.path {
FileSystemPath::Path { .. } => !self.has_same_target_write_override(entry),
FileSystemPath::Path { .. } | FileSystemPath::GeneratedDefaultPath { .. } => {
!self.has_same_target_write_override(entry)
}
FileSystemPath::GlobPattern { .. } => true,
FileSystemPath::Special { value } => match value {
FileSystemPath::Special { value }
| FileSystemPath::GeneratedDefaultSpecial { value } => match value {
FileSystemSpecialPath::Root => entry.access == FileSystemAccessMode::Deny,
FileSystemSpecialPath::Minimal | FileSystemSpecialPath::Unknown { .. } => {
false
@@ -717,6 +758,13 @@ impl FileSystemSandboxPolicy {
let cwd = AbsolutePathBuf::from_absolute_path(cwd).ok();
for entry in &mut self.entries {
match &entry.path {
FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::ProjectRoots { .. },
} => {
if let Some(path) = resolve_file_system_path(&entry.path, cwd.as_ref()) {
entry.path = FileSystemPath::GeneratedDefaultPath { path };
}
}
FileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots { .. },
} => {
@@ -734,7 +782,9 @@ impl FileSystemSandboxPolicy {
}
}
FileSystemPath::Special { value: _ } => {}
FileSystemPath::GeneratedDefaultSpecial { value: _ } => {}
FileSystemPath::Path { .. } => {}
FileSystemPath::GeneratedDefaultPath { .. } => {}
}
}
self
@@ -749,6 +799,22 @@ impl FileSystemSandboxPolicy {
let mut entries = Vec::with_capacity(self.entries.len());
for entry in self.entries {
match entry.path {
FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::ProjectRoots { subpath },
} => {
entries.extend(workspace_roots.iter().map(|root| FileSystemSandboxEntry {
path: FileSystemPath::GeneratedDefaultPath {
path: match subpath.as_ref() {
Some(subpath) => AbsolutePathBuf::resolve_path_against_base(
subpath,
root.as_path(),
),
None => root.clone(),
},
},
access: entry.access,
}));
}
FileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots { subpath },
} => {
@@ -786,12 +852,24 @@ impl FileSystemSandboxPolicy {
access: entry.access,
});
}
FileSystemPath::GeneratedDefaultPath { path } => {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::GeneratedDefaultPath { path },
access: entry.access,
});
}
FileSystemPath::Special { value } => {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::Special { value },
access: entry.access,
});
}
FileSystemPath::GeneratedDefaultSpecial { value } => {
entries.push(FileSystemSandboxEntry {
path: FileSystemPath::GeneratedDefaultSpecial { value },
access: entry.access,
});
}
}
}
self.entries = entries;
@@ -1095,7 +1173,10 @@ impl FileSystemSandboxPolicy {
FileSystemPath::GlobPattern { pattern } => {
Some(AbsolutePathBuf::resolve_path_against_base(pattern, cwd))
}
FileSystemPath::Path { .. } | FileSystemPath::Special { .. } => None,
FileSystemPath::Path { .. }
| FileSystemPath::GeneratedDefaultPath { .. }
| FileSystemPath::Special { .. }
| FileSystemPath::GeneratedDefaultSpecial { .. } => None,
})
.map(|pattern| pattern.to_string_lossy().into_owned())
.collect::<Vec<_>>();
@@ -1138,7 +1219,8 @@ impl FileSystemSandboxPolicy {
for entry in &self.entries {
match &entry.path {
FileSystemPath::GlobPattern { .. } => {}
FileSystemPath::Path { path } => {
FileSystemPath::Path { path }
| FileSystemPath::GeneratedDefaultPath { path } => {
if entry.access.can_write() {
if cwd_absolute.as_ref().is_some_and(|cwd| cwd == path) {
workspace_root_writable = true;
@@ -1147,7 +1229,8 @@ impl FileSystemSandboxPolicy {
}
}
}
FileSystemPath::Special { value } => match value {
FileSystemPath::Special { value }
| FileSystemPath::GeneratedDefaultSpecial { value } => match value {
FileSystemSpecialPath::Root => match entry.access {
FileSystemAccessMode::Deny => {}
FileSystemAccessMode::Read => {}
@@ -1289,9 +1372,13 @@ fn resolve_file_system_path(
cwd: Option<&AbsolutePathBuf>,
) -> Option<AbsolutePathBuf> {
match path {
FileSystemPath::Path { path } => Some(path.clone()),
FileSystemPath::Path { path } | FileSystemPath::GeneratedDefaultPath { path } => {
Some(path.clone())
}
FileSystemPath::GlobPattern { .. } => None,
FileSystemPath::Special { value } => resolve_file_system_special_path(value, cwd),
FileSystemPath::Special { value } | FileSystemPath::GeneratedDefaultSpecial { value } => {
resolve_file_system_special_path(value, cwd)
}
}
}
@@ -1335,16 +1422,32 @@ fn resolve_candidate_path(path: &Path, cwd: &Path) -> Option<AbsolutePathBuf> {
/// by `has_write_narrowing_entries`.
fn file_system_paths_share_target(left: &FileSystemPath, right: &FileSystemPath) -> bool {
match (left, right) {
(FileSystemPath::Path { path: left }, FileSystemPath::Path { path: right }) => {
left == right
}
(FileSystemPath::Special { value: left }, FileSystemPath::Special { value: right }) => {
special_paths_share_target(left, right)
}
(
FileSystemPath::Path { path: left }
| FileSystemPath::GeneratedDefaultPath { path: left },
FileSystemPath::Path { path: right }
| FileSystemPath::GeneratedDefaultPath { path: right },
) => left == right,
(
FileSystemPath::Special { value: left }
| FileSystemPath::GeneratedDefaultSpecial { value: left },
FileSystemPath::Special { value: right }
| FileSystemPath::GeneratedDefaultSpecial { value: right },
) => special_paths_share_target(left, right),
(FileSystemPath::Path { path }, FileSystemPath::Special { value })
| (FileSystemPath::Special { value }, FileSystemPath::Path { path }) => {
special_path_matches_absolute_path(value, path)
}
| (FileSystemPath::Path { path }, FileSystemPath::GeneratedDefaultSpecial { value })
| (FileSystemPath::GeneratedDefaultPath { path }, FileSystemPath::Special { value })
| (
FileSystemPath::GeneratedDefaultPath { path },
FileSystemPath::GeneratedDefaultSpecial { value },
)
| (FileSystemPath::Special { value }, FileSystemPath::Path { path })
| (FileSystemPath::Special { value }, FileSystemPath::GeneratedDefaultPath { path })
| (FileSystemPath::GeneratedDefaultSpecial { value }, FileSystemPath::Path { path })
| (
FileSystemPath::GeneratedDefaultSpecial { value },
FileSystemPath::GeneratedDefaultPath { path },
) => special_path_matches_absolute_path(value, path),
(
FileSystemPath::GlobPattern { pattern: left },
FileSystemPath::GlobPattern { pattern: right },
@@ -1670,7 +1773,7 @@ fn append_default_read_only_project_root_subpath_if_no_explicit_rule(
) {
append_default_read_only_entry_if_no_explicit_rule(
entries,
FileSystemPath::Special {
FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::project_roots(Some(subpath.into())),
},
);
@@ -1970,19 +2073,19 @@ mod tests {
access: FileSystemAccessMode::Write,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
path: FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::project_roots(Some(".git".into())),
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
path: FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::project_roots(Some(".agents".into())),
},
access: FileSystemAccessMode::Read,
},
FileSystemSandboxEntry {
path: FileSystemPath::Special {
path: FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::project_roots(Some(".codex".into())),
},
access: FileSystemAccessMode::Read,
@@ -2129,7 +2232,7 @@ mod tests {
];
expected_entries.extend(PROTECTED_METADATA_PATH_NAMES.iter().map(|name| {
FileSystemSandboxEntry {
path: FileSystemPath::Special {
path: FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::project_roots(Some((*name).into())),
},
access: FileSystemAccessMode::Read,
+57 -28
View File
@@ -41,10 +41,21 @@ pub fn normalize_additional_permissions(
.and_then(|path| AbsolutePathBuf::from_absolute_path(path).ok())
.unwrap_or(path),
},
FileSystemPath::GeneratedDefaultPath { path } => {
FileSystemPath::GeneratedDefaultPath {
path: canonicalize_preserving_symlinks(path.as_path())
.ok()
.and_then(|path| AbsolutePathBuf::from_absolute_path(path).ok())
.unwrap_or(path),
}
}
FileSystemPath::GlobPattern { pattern } => {
FileSystemPath::GlobPattern { pattern }
}
FileSystemPath::Special { value } => FileSystemPath::Special { value },
FileSystemPath::GeneratedDefaultSpecial { value } => {
FileSystemPath::GeneratedDefaultSpecial { value }
}
};
let normalized_entry = FileSystemSandboxEntry {
path,
@@ -302,7 +313,10 @@ fn deny_entry_constrains_accepted_grant(
match &deny_entry.path {
FileSystemPath::GlobPattern { pattern } => glob_static_prefix_path(pattern, cwd)
.is_some_and(|prefix| paths_overlap(prefix.as_path(), grant_path.as_path())),
FileSystemPath::Path { .. } | FileSystemPath::Special { .. } => {
FileSystemPath::Path { .. }
| FileSystemPath::GeneratedDefaultPath { .. }
| FileSystemPath::Special { .. }
| FileSystemPath::GeneratedDefaultSpecial { .. } => {
resolve_permission_path(&deny_entry.path, cwd).is_some_and(|deny_path| {
paths_overlap(deny_path.as_path(), grant_path.as_path())
})
@@ -349,6 +363,14 @@ fn materialize_cwd_dependent_entry(
cwd: &Path,
) -> FileSystemSandboxEntry {
match &entry.path {
FileSystemPath::GeneratedDefaultSpecial {
value: FileSystemSpecialPath::ProjectRoots { .. },
} => resolve_permission_path(&entry.path, cwd)
.map(|path| FileSystemSandboxEntry {
path: FileSystemPath::GeneratedDefaultPath { path },
access: entry.access,
})
.unwrap_or_else(|| entry.clone()),
FileSystemPath::Special {
value: FileSystemSpecialPath::ProjectRoots { .. },
} => resolve_permission_path(&entry.path, cwd)
@@ -365,41 +387,48 @@ fn materialize_cwd_dependent_entry(
},
access: entry.access,
},
FileSystemPath::Path { .. } | FileSystemPath::Special { .. } => entry.clone(),
FileSystemPath::Path { .. }
| FileSystemPath::GeneratedDefaultPath { .. }
| FileSystemPath::Special { .. }
| FileSystemPath::GeneratedDefaultSpecial { .. } => entry.clone(),
}
}
fn resolve_permission_path(path: &FileSystemPath, cwd: &Path) -> Option<AbsolutePathBuf> {
match path {
FileSystemPath::Path { path } => Some(path.clone()),
FileSystemPath::Path { path } | FileSystemPath::GeneratedDefaultPath { path } => {
Some(path.clone())
}
FileSystemPath::GlobPattern { .. } => None,
FileSystemPath::Special { value } => match value {
FileSystemSpecialPath::Root => {
let root = cwd.ancestors().last()?;
AbsolutePathBuf::from_absolute_path(root).ok()
}
FileSystemSpecialPath::ProjectRoots { subpath } => {
let cwd = AbsolutePathBuf::from_absolute_path(cwd).ok()?;
Some(match subpath {
Some(subpath) => {
AbsolutePathBuf::resolve_path_against_base(subpath, cwd.as_path())
}
None => cwd,
})
}
FileSystemSpecialPath::Tmpdir => {
let tmpdir = std::env::var_os("TMPDIR")?;
if tmpdir.is_empty() {
None
} else {
AbsolutePathBuf::from_absolute_path(PathBuf::from(tmpdir)).ok()
FileSystemPath::Special { value } | FileSystemPath::GeneratedDefaultSpecial { value } => {
match value {
FileSystemSpecialPath::Root => {
let root = cwd.ancestors().last()?;
AbsolutePathBuf::from_absolute_path(root).ok()
}
FileSystemSpecialPath::ProjectRoots { subpath } => {
let cwd = AbsolutePathBuf::from_absolute_path(cwd).ok()?;
Some(match subpath {
Some(subpath) => {
AbsolutePathBuf::resolve_path_against_base(subpath, cwd.as_path())
}
None => cwd,
})
}
FileSystemSpecialPath::Tmpdir => {
let tmpdir = std::env::var_os("TMPDIR")?;
if tmpdir.is_empty() {
None
} else {
AbsolutePathBuf::from_absolute_path(PathBuf::from(tmpdir)).ok()
}
}
FileSystemSpecialPath::SlashTmp => AbsolutePathBuf::from_absolute_path("/tmp")
.ok()
.filter(|path| path.as_path().is_dir()),
FileSystemSpecialPath::Minimal | FileSystemSpecialPath::Unknown { .. } => None,
}
FileSystemSpecialPath::SlashTmp => AbsolutePathBuf::from_absolute_path("/tmp")
.ok()
.filter(|path| path.as_path().is_dir()),
FileSystemSpecialPath::Minimal | FileSystemSpecialPath::Unknown { .. } => None,
},
}
}
}
+27 -4
View File
@@ -81,7 +81,7 @@ pub fn resolve_windows_restricted_token_filesystem_overrides(
return Ok(None);
}
let (file_system_sandbox_policy, network_sandbox_policy) =
let (mut file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let needs_direct_runtime_enforcement = file_system_sandbox_policy
@@ -101,6 +101,11 @@ pub fn resolve_windows_restricted_token_filesystem_overrides(
));
}
// Windows protects built-in metadata defaults through the legacy writable
// root projection when those paths exist. Do not turn missing generated
// defaults into explicit deny-write sentinels.
file_system_sandbox_policy.remove_generated_defaults();
// The restricted-token backend can still enforce split write restrictions,
// but its WRITE_RESTRICTED token does not make capability SID deny-read ACEs
// participate in read access checks. Read restrictions therefore require the
@@ -215,7 +220,7 @@ pub fn resolve_windows_elevated_filesystem_overrides(
return Ok(None);
}
let (file_system_sandbox_policy, network_sandbox_policy) =
let (mut file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
if !permission_profile_supports_windows_restricted_token_sandbox(permission_profile) {
@@ -226,6 +231,11 @@ pub fn resolve_windows_elevated_filesystem_overrides(
));
}
// Windows protects built-in metadata defaults through the legacy writable
// root projection when those paths exist. Do not turn missing generated
// defaults into explicit deny-write sentinels.
file_system_sandbox_policy.remove_generated_defaults();
let additional_deny_read_paths = codex_windows_sandbox::resolve_windows_deny_read_paths(
&file_system_sandbox_policy,
sandbox_policy_cwd,
@@ -242,6 +252,12 @@ pub fn resolve_windows_elevated_filesystem_overrides(
let needs_direct_runtime_enforcement = file_system_sandbox_policy
.needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd);
let has_explicit_write_carveouts = split_writable_roots.iter().any(|writable_root| {
writable_root.read_only_subpaths.iter().any(|path| {
file_system_sandbox_policy
.has_explicit_non_write_entry_for_path_with_cwd(path.as_path(), sandbox_policy_cwd)
})
});
let normalize_path = |path: PathBuf| dunce::canonicalize(&path).unwrap_or(path);
let legacy_projection = compatibility_sandbox_policy_for_permission_profile(
permission_profile,
@@ -282,7 +298,9 @@ pub fn resolve_windows_elevated_filesystem_overrides(
Some(split_root_paths)
};
let additional_deny_write_paths = if needs_direct_runtime_enforcement {
let additional_deny_write_paths = if needs_direct_runtime_enforcement
|| has_explicit_write_carveouts
{
let mut deny_paths = BTreeSet::new();
for writable_root in &split_writable_roots {
let writable_root_path = normalize_path(writable_root.root.to_path_buf());
@@ -303,7 +321,12 @@ pub fn resolve_windows_elevated_filesystem_overrides(
== read_only_subpath_suffix
})
});
if !already_denied_by_legacy {
let explicitly_configured = file_system_sandbox_policy
.has_explicit_non_write_entry_for_path_with_cwd(
read_only_subpath.as_path(),
sandbox_policy_cwd,
);
if explicitly_configured || !already_denied_by_legacy {
deny_paths.insert(normalize_path(read_only_subpath.to_path_buf()));
}
}