mirror of
https://github.com/openai/codex.git
synced 2026-10-29 19:08:43 +00:00
artifacts: honor filesystem policy carveouts in path checks
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
use codex_protocol::protocol::FileSystemSandboxPolicy;
|
||||
use std::path::Path;
|
||||
|
||||
pub(super) fn path_is_readable(
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
cwd: &Path,
|
||||
path: &Path,
|
||||
) -> bool {
|
||||
if file_system_sandbox_policy
|
||||
.get_unreadable_roots_with_cwd(cwd)
|
||||
.iter()
|
||||
.any(|root| path.starts_with(root.as_path()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if file_system_sandbox_policy.has_full_disk_read_access() {
|
||||
return true;
|
||||
}
|
||||
|
||||
file_system_sandbox_policy
|
||||
.get_readable_roots_with_cwd(cwd)
|
||||
.iter()
|
||||
.any(|root| path.starts_with(root.as_path()))
|
||||
}
|
||||
|
||||
pub(super) fn path_is_writable(
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
cwd: &Path,
|
||||
path: &Path,
|
||||
) -> bool {
|
||||
if file_system_sandbox_policy
|
||||
.get_unreadable_roots_with_cwd(cwd)
|
||||
.iter()
|
||||
.any(|root| path.starts_with(root.as_path()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if file_system_sandbox_policy.has_full_disk_write_access() {
|
||||
return true;
|
||||
}
|
||||
|
||||
file_system_sandbox_policy
|
||||
.get_writable_roots_with_cwd(cwd)
|
||||
.iter()
|
||||
.any(|root| root.is_path_writable(path))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::path_is_readable;
|
||||
use super::path_is_writable;
|
||||
use codex_protocol::protocol::FileSystemAccessMode;
|
||||
use codex_protocol::protocol::FileSystemPath;
|
||||
use codex_protocol::protocol::FileSystemSandboxEntry;
|
||||
use codex_protocol::protocol::FileSystemSandboxPolicy;
|
||||
use codex_protocol::protocol::FileSystemSpecialPath;
|
||||
use codex_protocol::protocol::FileSystemSpecialPathKind;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn unreadable_subpaths_override_root_read_access() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
let readable = cwd.path().join("readable.txt");
|
||||
let blocked = cwd.path().join("blocked.txt");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath {
|
||||
kind: FileSystemSpecialPathKind::Root,
|
||||
subpath: None,
|
||||
},
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath {
|
||||
kind: FileSystemSpecialPathKind::CurrentWorkingDirectory,
|
||||
subpath: Some(PathBuf::from("blocked.txt")),
|
||||
},
|
||||
},
|
||||
access: FileSystemAccessMode::None,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(path_is_readable(&policy, cwd.path(), &readable), true);
|
||||
assert_eq!(path_is_readable(&policy, cwd.path(), &blocked), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unreadable_subpaths_override_writable_roots() {
|
||||
let cwd = TempDir::new().expect("tempdir");
|
||||
let allowed = cwd.path().join("allowed.txt");
|
||||
let blocked = cwd.path().join("blocked.txt");
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath {
|
||||
kind: FileSystemSpecialPathKind::CurrentWorkingDirectory,
|
||||
subpath: None,
|
||||
},
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath {
|
||||
kind: FileSystemSpecialPathKind::CurrentWorkingDirectory,
|
||||
subpath: Some(PathBuf::from("blocked.txt")),
|
||||
},
|
||||
},
|
||||
access: FileSystemAccessMode::None,
|
||||
},
|
||||
]);
|
||||
|
||||
assert_eq!(path_is_writable(&policy, cwd.path(), &allowed), true);
|
||||
assert_eq!(path_is_writable(&policy, cwd.path(), &blocked), false);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub(crate) mod agent_jobs;
|
||||
pub mod apply_patch;
|
||||
mod artifact_path_access;
|
||||
mod dynamic;
|
||||
mod grep_files;
|
||||
mod js_repl;
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::path_utils::resolve_symlink_write_paths;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::artifact_path_access;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
@@ -111,8 +112,16 @@ async fn authorize_path_access(
|
||||
PathAccessKind::Write => effective_write_path(&access.path),
|
||||
};
|
||||
let allowed = match access.kind {
|
||||
PathAccessKind::Read => path_is_readable(turn, &effective_path),
|
||||
PathAccessKind::Write => path_is_writable(turn, &effective_path),
|
||||
PathAccessKind::Read => artifact_path_access::path_is_readable(
|
||||
&turn.file_system_sandbox_policy,
|
||||
&turn.cwd,
|
||||
&effective_path,
|
||||
),
|
||||
PathAccessKind::Write => artifact_path_access::path_is_writable(
|
||||
&turn.file_system_sandbox_policy,
|
||||
&turn.cwd,
|
||||
&effective_path,
|
||||
),
|
||||
};
|
||||
if allowed {
|
||||
return Ok(());
|
||||
@@ -188,28 +197,6 @@ async fn authorize_path_access(
|
||||
)))
|
||||
}
|
||||
|
||||
fn path_is_readable(turn: &TurnContext, path: &Path) -> bool {
|
||||
if turn.sandbox_policy.has_full_disk_read_access() {
|
||||
return true;
|
||||
}
|
||||
|
||||
turn.sandbox_policy
|
||||
.get_readable_roots_with_cwd(&turn.cwd)
|
||||
.iter()
|
||||
.any(|root| path.starts_with(root.as_path()))
|
||||
}
|
||||
|
||||
fn path_is_writable(turn: &TurnContext, path: &Path) -> bool {
|
||||
if turn.sandbox_policy.has_full_disk_write_access() {
|
||||
return true;
|
||||
}
|
||||
|
||||
turn.sandbox_policy
|
||||
.get_writable_roots_with_cwd(&turn.cwd)
|
||||
.iter()
|
||||
.any(|root| root.is_path_writable(path))
|
||||
}
|
||||
|
||||
fn effective_read_path(path: &Path) -> PathBuf {
|
||||
normalize_for_path_comparison(path).unwrap_or_else(|_| normalize_without_fs(path))
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ use crate::path_utils::resolve_symlink_write_paths;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::tools::context::ToolOutput;
|
||||
use crate::tools::context::ToolPayload;
|
||||
use crate::tools::handlers::artifact_path_access;
|
||||
use crate::tools::handlers::parse_arguments;
|
||||
use crate::tools::registry::ToolHandler;
|
||||
use crate::tools::registry::ToolKind;
|
||||
@@ -100,8 +101,16 @@ async fn authorize_path_access(
|
||||
PathAccessKind::Write => effective_write_path(&access.path),
|
||||
};
|
||||
let allowed = match access.kind {
|
||||
PathAccessKind::Read => path_is_readable(turn, &effective_path),
|
||||
PathAccessKind::Write => path_is_writable(turn, &effective_path),
|
||||
PathAccessKind::Read => artifact_path_access::path_is_readable(
|
||||
&turn.file_system_sandbox_policy,
|
||||
&turn.cwd,
|
||||
&effective_path,
|
||||
),
|
||||
PathAccessKind::Write => artifact_path_access::path_is_writable(
|
||||
&turn.file_system_sandbox_policy,
|
||||
&turn.cwd,
|
||||
&effective_path,
|
||||
),
|
||||
};
|
||||
if allowed {
|
||||
return Ok(());
|
||||
@@ -173,28 +182,6 @@ async fn authorize_path_access(
|
||||
)))
|
||||
}
|
||||
|
||||
fn path_is_readable(turn: &TurnContext, path: &Path) -> bool {
|
||||
if turn.sandbox_policy.has_full_disk_read_access() {
|
||||
return true;
|
||||
}
|
||||
|
||||
turn.sandbox_policy
|
||||
.get_readable_roots_with_cwd(&turn.cwd)
|
||||
.iter()
|
||||
.any(|root| path.starts_with(root.as_path()))
|
||||
}
|
||||
|
||||
fn path_is_writable(turn: &TurnContext, path: &Path) -> bool {
|
||||
if turn.sandbox_policy.has_full_disk_write_access() {
|
||||
return true;
|
||||
}
|
||||
|
||||
turn.sandbox_policy
|
||||
.get_writable_roots_with_cwd(&turn.cwd)
|
||||
.iter()
|
||||
.any(|root| root.is_path_writable(path))
|
||||
}
|
||||
|
||||
fn effective_read_path(path: &Path) -> PathBuf {
|
||||
normalize_for_path_comparison(path).unwrap_or_else(|_| normalize_without_fs(path))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user