mirror of
https://github.com/openai/codex.git
synced 2026-05-04 03:16:31 +00:00
Reserve missing preserved sandbox paths
This commit is contained in:
@@ -1253,13 +1253,13 @@ impl SandboxPolicy {
|
||||
roots
|
||||
.into_iter()
|
||||
.map(|writable_root| {
|
||||
let protect_missing_dot_codex = cwd_root
|
||||
let protect_missing_preserved_paths = cwd_root
|
||||
.as_ref()
|
||||
.is_some_and(|cwd_root| cwd_root == &writable_root);
|
||||
WritableRoot {
|
||||
read_only_subpaths: default_read_only_subpaths_for_writable_root(
|
||||
&writable_root,
|
||||
protect_missing_dot_codex,
|
||||
protect_missing_preserved_paths,
|
||||
),
|
||||
root: writable_root,
|
||||
}
|
||||
@@ -1272,7 +1272,7 @@ impl SandboxPolicy {
|
||||
|
||||
fn default_read_only_subpaths_for_writable_root(
|
||||
writable_root: &AbsolutePathBuf,
|
||||
protect_missing_dot_codex: bool,
|
||||
protect_missing_preserved_paths: bool,
|
||||
) -> Vec<AbsolutePathBuf> {
|
||||
let mut subpaths: Vec<AbsolutePathBuf> = Vec::new();
|
||||
let top_level_git = writable_root.join(".git");
|
||||
@@ -1281,7 +1281,7 @@ fn default_read_only_subpaths_for_writable_root(
|
||||
// writable root itself.
|
||||
let top_level_git_is_file = top_level_git.as_path().is_file();
|
||||
let top_level_git_is_dir = top_level_git.as_path().is_dir();
|
||||
if top_level_git_is_dir || top_level_git_is_file {
|
||||
if top_level_git_is_dir || top_level_git_is_file || protect_missing_preserved_paths {
|
||||
if top_level_git_is_file
|
||||
&& is_git_pointer_file(&top_level_git)
|
||||
&& let Some(gitdir) = resolve_gitdir_from_file(&top_level_git)
|
||||
@@ -1292,16 +1292,16 @@ fn default_read_only_subpaths_for_writable_root(
|
||||
}
|
||||
|
||||
let top_level_agents = writable_root.join(".agents");
|
||||
if top_level_agents.as_path().is_dir() {
|
||||
if protect_missing_preserved_paths || top_level_agents.as_path().is_dir() {
|
||||
subpaths.push(top_level_agents);
|
||||
}
|
||||
|
||||
// Keep top-level project metadata under .codex read-only to the agent by
|
||||
// Keep top-level preserved paths under .codex read-only to the agent by
|
||||
// default. For the workspace root itself, protect it even before the
|
||||
// directory exists so first-time creation still goes through the
|
||||
// protected-path approval flow.
|
||||
// preserved path approval flow.
|
||||
let top_level_codex = writable_root.join(".codex");
|
||||
if protect_missing_dot_codex || top_level_codex.as_path().is_dir() {
|
||||
if protect_missing_preserved_paths || top_level_codex.as_path().is_dir() {
|
||||
subpaths.push(top_level_codex);
|
||||
}
|
||||
|
||||
@@ -4375,7 +4375,6 @@ mod tests {
|
||||
#[test]
|
||||
fn restricted_file_system_policy_derives_effective_paths() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
std::fs::create_dir_all(cwd.path().join(".agents")).expect("create .agents");
|
||||
std::fs::create_dir_all(cwd.path().join(".codex")).expect("create .codex");
|
||||
let canonical_cwd = codex_utils_absolute_path::canonicalize_preserving_symlinks(cwd.path())
|
||||
.expect("canonicalize cwd");
|
||||
@@ -4456,6 +4455,11 @@ mod tests {
|
||||
.expect("canonical docs/public");
|
||||
let expected_dot_codex = AbsolutePathBuf::from_absolute_path(canonical_cwd.join(".codex"))
|
||||
.expect("canonical .codex");
|
||||
let expected_dot_git = AbsolutePathBuf::from_absolute_path(canonical_cwd.join(".git"))
|
||||
.expect("canonical .git");
|
||||
let expected_dot_agents =
|
||||
AbsolutePathBuf::from_absolute_path(canonical_cwd.join(".agents"))
|
||||
.expect("canonical .agents");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
@@ -4480,7 +4484,9 @@ mod tests {
|
||||
(
|
||||
canonical_cwd,
|
||||
vec![
|
||||
expected_dot_agents.to_path_buf(),
|
||||
expected_dot_codex.to_path_buf(),
|
||||
expected_dot_git.to_path_buf(),
|
||||
expected_docs.to_path_buf()
|
||||
],
|
||||
),
|
||||
@@ -4489,6 +4495,46 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_workspace_write_nested_readable_root_stays_writable() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
let docs = AbsolutePathBuf::resolve_path_against_base("docs", cwd.path());
|
||||
let canonical_cwd = codex_utils_absolute_path::canonicalize_preserving_symlinks(cwd.path())
|
||||
.expect("canonicalize cwd");
|
||||
let expected_dot_codex = AbsolutePathBuf::from_absolute_path(canonical_cwd.join(".codex"))
|
||||
.expect("canonical .codex");
|
||||
let expected_dot_git = AbsolutePathBuf::from_absolute_path(canonical_cwd.join(".git"))
|
||||
.expect("canonical .git");
|
||||
let expected_dot_agents =
|
||||
AbsolutePathBuf::from_absolute_path(canonical_cwd.join(".agents"))
|
||||
.expect("canonical .agents");
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
read_only_access: ReadOnlyAccess::Restricted {
|
||||
include_platform_defaults: true,
|
||||
readable_roots: vec![docs],
|
||||
},
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: true,
|
||||
exclude_slash_tmp: true,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
sorted_writable_roots(
|
||||
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&policy, cwd.path())
|
||||
.get_writable_roots_with_cwd(cwd.path())
|
||||
),
|
||||
vec![(
|
||||
canonical_cwd,
|
||||
vec![
|
||||
expected_dot_agents.to_path_buf(),
|
||||
expected_dot_codex.to_path_buf(),
|
||||
expected_dot_git.to_path_buf()
|
||||
]
|
||||
)]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_system_policy_rejects_legacy_bridge_for_non_workspace_writes() {
|
||||
let cwd = if cfg!(windows) {
|
||||
|
||||
Reference in New Issue
Block a user