mirror of
https://github.com/openai/codex.git
synced 2026-05-17 17:53:06 +00:00
Plan Windows metadata targets from filesystem policy
This commit is contained in:
@@ -110,11 +110,11 @@ pub(crate) struct WindowsSandboxFilesystemOverrides {
|
||||
pub(crate) read_roots_include_platform_defaults: bool,
|
||||
pub(crate) write_roots_override: Option<Vec<PathBuf>>,
|
||||
pub(crate) additional_deny_write_paths: Vec<AbsolutePathBuf>,
|
||||
pub(crate) protected_metadata_targets: Vec<WindowsProtectedMetadataTarget>,
|
||||
}
|
||||
|
||||
/// Layer: Windows adapter layer. This is the Windows projection of
|
||||
/// `WritableRoot::protected_metadata_names` from `FileSystemSandboxPolicy`.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) struct WindowsProtectedMetadataTarget {
|
||||
pub(crate) path: AbsolutePathBuf,
|
||||
@@ -124,7 +124,6 @@ pub(crate) struct WindowsProtectedMetadataTarget {
|
||||
/// Layer: Windows adapter layer. The enforcement layer needs to know why a
|
||||
/// protected metadata path is absent instead of treating every missing path as
|
||||
/// an existing filesystem object.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum WindowsProtectedMetadataMode {
|
||||
ExistingDeny,
|
||||
@@ -1151,7 +1150,9 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
|
||||
}
|
||||
}
|
||||
|
||||
if additional_deny_write_paths.is_empty() {
|
||||
let protected_metadata_targets = windows_protected_metadata_targets(&split_writable_roots)?;
|
||||
|
||||
if additional_deny_write_paths.is_empty() && protected_metadata_targets.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@@ -1163,6 +1164,7 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
|
||||
.into_iter()
|
||||
.map(|path| AbsolutePathBuf::from_absolute_path(path).map_err(|err| err.to_string()))
|
||||
.collect::<std::result::Result<_, _>>()?,
|
||||
protected_metadata_targets,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1283,9 +1285,12 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides(
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let protected_metadata_targets = windows_protected_metadata_targets(&split_writable_roots)?;
|
||||
|
||||
if read_roots_override.is_none()
|
||||
&& write_roots_override.is_none()
|
||||
&& additional_deny_write_paths.is_empty()
|
||||
&& protected_metadata_targets.is_empty()
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -1296,9 +1301,36 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides(
|
||||
read_roots_override,
|
||||
write_roots_override,
|
||||
additional_deny_write_paths,
|
||||
protected_metadata_targets,
|
||||
}))
|
||||
}
|
||||
|
||||
fn windows_protected_metadata_targets(
|
||||
writable_roots: &[codex_protocol::protocol::WritableRoot],
|
||||
) -> std::result::Result<Vec<WindowsProtectedMetadataTarget>, String> {
|
||||
let mut targets = BTreeSet::new();
|
||||
for writable_root in writable_roots {
|
||||
for metadata_name in &writable_root.protected_metadata_names {
|
||||
let path =
|
||||
normalize_windows_override_path(writable_root.root.join(metadata_name).as_path())?;
|
||||
let path = AbsolutePathBuf::from_absolute_path(path).map_err(|err| err.to_string())?;
|
||||
targets.insert(WindowsProtectedMetadataTarget {
|
||||
mode: windows_protected_metadata_mode(&path),
|
||||
path,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(targets.into_iter().collect())
|
||||
}
|
||||
|
||||
fn windows_protected_metadata_mode(path: &AbsolutePathBuf) -> WindowsProtectedMetadataMode {
|
||||
if std::fs::symlink_metadata(path.as_path()).is_ok() {
|
||||
return WindowsProtectedMetadataMode::ExistingDeny;
|
||||
}
|
||||
|
||||
WindowsProtectedMetadataMode::MissingCreationMonitor
|
||||
}
|
||||
|
||||
fn has_reopened_writable_descendant(
|
||||
writable_roots: &[codex_protocol::protocol::WritableRoot],
|
||||
) -> bool {
|
||||
|
||||
@@ -663,6 +663,20 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
|
||||
read_roots_include_platform_defaults: false,
|
||||
write_roots_override: None,
|
||||
additional_deny_write_paths: expected_deny_write_paths,
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
],
|
||||
}))
|
||||
);
|
||||
}
|
||||
@@ -700,6 +714,7 @@ fn windows_elevated_supports_split_restricted_read_roots() {
|
||||
read_roots_include_platform_defaults: false,
|
||||
write_roots_override: None,
|
||||
additional_deny_write_paths: vec![],
|
||||
protected_metadata_targets: vec![],
|
||||
}))
|
||||
);
|
||||
}
|
||||
@@ -707,6 +722,9 @@ fn windows_elevated_supports_split_restricted_read_roots() {
|
||||
#[test]
|
||||
fn windows_elevated_supports_split_write_read_carveouts() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let expected_root = dunce::canonicalize(temp_dir.path())
|
||||
.expect("canonical temp dir")
|
||||
.abs();
|
||||
let docs = temp_dir.path().join("docs");
|
||||
std::fs::create_dir_all(&docs).expect("create docs");
|
||||
let expected_docs = dunce::canonicalize(&docs).expect("canonical docs");
|
||||
@@ -757,6 +775,146 @@ fn windows_elevated_supports_split_write_read_carveouts() {
|
||||
codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(expected_docs)
|
||||
.expect("absolute docs"),
|
||||
],
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: expected_root.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: expected_root.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: expected_root.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
],
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_metadata_plan_marks_existing_metadata_for_deny() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let cwd = dunce::canonicalize(temp_dir.path())
|
||||
.expect("canonical temp dir")
|
||||
.abs();
|
||||
std::fs::create_dir_all(cwd.join(".git").as_path()).expect("create .git");
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: true,
|
||||
exclude_slash_tmp: true,
|
||||
};
|
||||
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
|
||||
codex_protocol::permissions::FileSystemSandboxEntry {
|
||||
path: codex_protocol::permissions::FileSystemPath::Special {
|
||||
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: codex_protocol::permissions::FileSystemAccessMode::Read,
|
||||
},
|
||||
codex_protocol::permissions::FileSystemSandboxEntry {
|
||||
path: codex_protocol::permissions::FileSystemPath::Special {
|
||||
value: codex_protocol::permissions::FileSystemSpecialPath::project_roots(
|
||||
/*subpath*/ None,
|
||||
),
|
||||
},
|
||||
access: codex_protocol::permissions::FileSystemAccessMode::Write,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
resolve_windows_elevated_filesystem_overrides(
|
||||
SandboxType::WindowsRestrictedToken,
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
&cwd,
|
||||
/*use_windows_elevated_backend*/ true,
|
||||
),
|
||||
Ok(Some(WindowsSandboxFilesystemOverrides {
|
||||
read_roots_override: None,
|
||||
read_roots_include_platform_defaults: false,
|
||||
write_roots_override: None,
|
||||
additional_deny_write_paths: vec![],
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::ExistingDeny,
|
||||
},
|
||||
],
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_metadata_plan_does_not_materialize_nested_missing_git() {
|
||||
let temp_dir = tempfile::TempDir::new().expect("tempdir");
|
||||
let repo = dunce::canonicalize(temp_dir.path())
|
||||
.expect("canonical temp dir")
|
||||
.abs();
|
||||
std::fs::create_dir_all(repo.join(".git").as_path()).expect("create parent .git");
|
||||
let cwd = repo.join("child");
|
||||
std::fs::create_dir_all(cwd.as_path()).expect("create child workspace");
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: true,
|
||||
exclude_slash_tmp: true,
|
||||
};
|
||||
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
|
||||
codex_protocol::permissions::FileSystemSandboxEntry {
|
||||
path: codex_protocol::permissions::FileSystemPath::Special {
|
||||
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: codex_protocol::permissions::FileSystemAccessMode::Read,
|
||||
},
|
||||
codex_protocol::permissions::FileSystemSandboxEntry {
|
||||
path: codex_protocol::permissions::FileSystemPath::Special {
|
||||
value: codex_protocol::permissions::FileSystemSpecialPath::project_roots(
|
||||
/*subpath*/ None,
|
||||
),
|
||||
},
|
||||
access: codex_protocol::permissions::FileSystemAccessMode::Write,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
resolve_windows_elevated_filesystem_overrides(
|
||||
SandboxType::WindowsRestrictedToken,
|
||||
&policy,
|
||||
&file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
&cwd,
|
||||
/*use_windows_elevated_backend*/ true,
|
||||
),
|
||||
Ok(Some(WindowsSandboxFilesystemOverrides {
|
||||
read_roots_override: None,
|
||||
read_roots_include_platform_defaults: false,
|
||||
write_roots_override: None,
|
||||
additional_deny_write_paths: vec![],
|
||||
protected_metadata_targets: vec![
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".agents"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".codex"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
WindowsProtectedMetadataTarget {
|
||||
path: cwd.join(".git"),
|
||||
mode: WindowsProtectedMetadataMode::MissingCreationMonitor,
|
||||
},
|
||||
],
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user