mirror of
https://github.com/openai/codex.git
synced 2026-04-30 11:21:34 +03:00
fix: allow restricted filesystem profiles to read helper executables (#15114)
## Summary This PR fixes restricted filesystem permission profiles so Codex's runtime-managed helper executables remain readable without requiring explicit user configuration. - add implicit readable roots for the configured `zsh` helper path and the main execve wrapper - allowlist the shared `$CODEX_HOME/tmp/arg0` root when the execve wrapper lives there, so session-specific helper paths keep working - dedupe injected paths and avoid adding duplicate read entries to the sandbox policy - add regression coverage for restricted read mode with helper executable overrides ## Testing before this change: got this error when executing a shell command via zsh fork: ``` "sandbox error: sandbox denied exec error, exit code: 127, stdout: , stderr: /etc/zprofile:11: operation not permitted: /usr/libexec/path_helper\nzsh:1: operation not permitted: .codex/skills/proxy-a/scripts/fetch_example.sh\n" ``` saw this change went away after this change, meaning the readable roots and injected correctly.
This commit is contained in:
@@ -347,6 +347,48 @@ impl FileSystemSandboxPolicy {
|
||||
self.resolve_access_with_cwd(path, cwd).can_write()
|
||||
}
|
||||
|
||||
pub fn with_additional_readable_roots(
|
||||
mut self,
|
||||
cwd: &Path,
|
||||
additional_readable_roots: &[AbsolutePathBuf],
|
||||
) -> Self {
|
||||
if self.has_full_disk_read_access() {
|
||||
return self;
|
||||
}
|
||||
|
||||
for path in additional_readable_roots {
|
||||
if self.can_read_path_with_cwd(path.as_path(), cwd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.entries.push(FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: path.clone() },
|
||||
access: FileSystemAccessMode::Read,
|
||||
});
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_additional_writable_roots(
|
||||
mut self,
|
||||
cwd: &Path,
|
||||
additional_writable_roots: &[AbsolutePathBuf],
|
||||
) -> Self {
|
||||
for path in additional_writable_roots {
|
||||
if self.can_write_path_with_cwd(path.as_path(), cwd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.entries.push(FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: path.clone() },
|
||||
access: FileSystemAccessMode::Write,
|
||||
});
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn needs_direct_runtime_enforcement(
|
||||
&self,
|
||||
network_policy: NetworkSandboxPolicy,
|
||||
@@ -1782,6 +1824,74 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_additional_readable_roots_skips_existing_effective_access() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
let cwd_root = AbsolutePathBuf::from_absolute_path(cwd.path()).expect("absolute cwd");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
}]);
|
||||
|
||||
let actual = policy
|
||||
.clone()
|
||||
.with_additional_readable_roots(cwd.path(), std::slice::from_ref(&cwd_root));
|
||||
|
||||
assert_eq!(actual, policy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_additional_writable_roots_skips_existing_effective_access() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
let cwd_root = AbsolutePathBuf::from_absolute_path(cwd.path()).expect("absolute cwd");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}]);
|
||||
|
||||
let actual = policy
|
||||
.clone()
|
||||
.with_additional_writable_roots(cwd.path(), std::slice::from_ref(&cwd_root));
|
||||
|
||||
assert_eq!(actual, policy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_additional_writable_roots_adds_new_root() {
|
||||
let temp_dir = TempDir::new().expect("tempdir");
|
||||
let cwd = temp_dir.path().join("workspace");
|
||||
let extra = AbsolutePathBuf::from_absolute_path(temp_dir.path().join("extra"))
|
||||
.expect("resolve extra root");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
}]);
|
||||
|
||||
let actual = policy.with_additional_writable_roots(&cwd, std::slice::from_ref(&extra));
|
||||
|
||||
assert_eq!(
|
||||
actual,
|
||||
FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path { path: extra },
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_system_access_mode_orders_by_conflict_precedence() {
|
||||
assert!(FileSystemAccessMode::Write > FileSystemAccessMode::Read);
|
||||
|
||||
Reference in New Issue
Block a user