mirror of
https://github.com/openai/codex.git
synced 2026-11-05 16:29:31 +00:00
Skip missing paths in filesystem sandbox entries (#34598)
## Why Default read-only protections for project metadata should apply when paths such as `.git`, `.agents`, and `.codex` exist, without causing sandbox setup to materialize missing paths as ACL targets. ## What changed - Add an optional `missing_path_behavior` to filesystem sandbox entries and mark default project-metadata protections with `skip`. - Preserve the behavior through permission transforms and exec/MCP protocol serialization while keeping existing path wire variants stable. - Ignore skip-missing entries when projecting configuration or Windows sandbox overrides, while retaining explicit metadata carveouts. ## Testing - Cover protocol round trips for path and special-path entries. - Verify default metadata protections and Windows explicit carveout handling. GitOrigin-RevId: 6df13dadacdd131c44aab9f15a967c81051355c1
This commit is contained in:
1 parent
ee71c4a90f
commit
87f71e35b8
63 files changed
+825
-132
No files matched your search
@@ -140,12 +140,14 @@ fn workspace_write_with_read_only_root(read_only_root: AbsolutePathBuf) -> Permi
|
||||
path: read_only_root,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
]);
|
||||
PermissionProfile::from_runtime_permissions(
|
||||
@@ -162,12 +164,14 @@ fn workspace_write_with_unreadable_path(unreadable_path: AbsolutePathBuf) -> Per
|
||||
path: unreadable_path,
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
]);
|
||||
PermissionProfile::from_runtime_permissions(
|
||||
|
||||
@@ -3588,6 +3588,7 @@ allow_local_binding = true
|
||||
pattern: format!("{}/**/*.env", test.config.cwd.as_path().display()),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
});
|
||||
assert!(
|
||||
file_system_sandbox_policy.has_denied_read_restrictions(),
|
||||
|
||||
@@ -115,6 +115,7 @@ async fn extension_tool_receives_turn_environment_sandbox() -> Result<()> {
|
||||
path: denied_path.clone(),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
});
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
&file_system_sandbox_policy,
|
||||
|
||||
@@ -1129,6 +1129,7 @@ fn read_only_sandbox(readable_root: PathBuf) -> FileSystemSandboxContext {
|
||||
path: readable_root,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
missing_path_behavior: None,
|
||||
}]),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
))
|
||||
@@ -1142,6 +1143,7 @@ fn workspace_write_sandbox(writable_root: PathBuf) -> FileSystemSandboxContext {
|
||||
path: writable_root,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
missing_path_behavior: None,
|
||||
}]),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
))
|
||||
@@ -1361,12 +1363,14 @@ async fn remote_exec_materializes_target_roots_before_sandbox_selection() -> Res
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
]),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
|
||||
@@ -453,6 +453,7 @@ async fn shell_command_enforces_glob_deny_read_policy() -> Result<()> {
|
||||
pattern: format!("{}/**/*.env", config.cwd.as_path().display()),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
});
|
||||
config
|
||||
.permissions
|
||||
|
||||
@@ -3095,6 +3095,7 @@ async fn unified_exec_enforces_glob_deny_read_policy() -> Result<()> {
|
||||
pattern: format!("{}/**/*.env", config.cwd.as_path().display()),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
});
|
||||
config
|
||||
.permissions
|
||||
|
||||
@@ -337,6 +337,7 @@ fn permission_profile_from_toml(profile: &str) -> Result<PermissionProfile> {
|
||||
Ok(FileSystemSandboxEntry {
|
||||
path,
|
||||
access: *access,
|
||||
missing_path_behavior: None,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
@@ -532,6 +532,7 @@ async fn view_image_tool_applies_local_sandbox_read_denies() -> anyhow::Result<(
|
||||
path: denied_path.clone(),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
});
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
&file_system_sandbox_policy,
|
||||
|
||||
@@ -134,24 +134,28 @@ async fn windows_restricted_token_rejects_exact_and_glob_deny_read_policy() -> a
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::GlobPattern {
|
||||
pattern: "**/*.env".to_string(),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path {
|
||||
path: future_secret,
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
]);
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
@@ -197,6 +201,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<()> {
|
||||
@@ -219,22 +276,26 @@ async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyh
|
||||
value: FileSystemSpecialPath::Root,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::GlobPattern {
|
||||
pattern: "**/*.env".to_string(),
|
||||
},
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: exact_secret },
|
||||
access: FileSystemAccessMode::Deny,
|
||||
missing_path_behavior: None,
|
||||
},
|
||||
]);
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
|
||||
Reference in new issue
Block a user