mirror of
https://github.com/openai/codex.git
synced 2026-09-07 15:40:00 +00:00
support symlinks
This commit is contained in:
1
codex-rs/Cargo.lock
generated
1
codex-rs/Cargo.lock
generated
@@ -2621,7 +2621,6 @@ dependencies = [
|
||||
"codex-utils-pty",
|
||||
"ctor 0.6.3",
|
||||
"futures",
|
||||
"libc",
|
||||
"pretty_assertions",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
||||
@@ -43,9 +43,6 @@ tokio-tungstenite = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
codex-test-binary-support = { workspace = true }
|
||||
|
||||
@@ -3,7 +3,6 @@ use base64::engine::general_purpose::STANDARD;
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::Path;
|
||||
use tokio::io;
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
@@ -203,9 +202,10 @@ pub(crate) async fn run_direct_request(
|
||||
}))
|
||||
}
|
||||
FsHelperRequest::ReadFileInfo(params) => {
|
||||
let metadata = tokio::fs::symlink_metadata(params.path.as_path())
|
||||
let file = open_read_file(params.path.as_path())
|
||||
.await
|
||||
.map_err(map_fs_error)?;
|
||||
let metadata = file.metadata().await.map_err(map_fs_error)?;
|
||||
validate_read_file_metadata(params.path.as_path(), &metadata).map_err(map_fs_error)?;
|
||||
Ok(FsHelperPayload::ReadFileInfo(FsReadFileInfoResponse {
|
||||
file_size_bytes: metadata.len(),
|
||||
@@ -307,9 +307,9 @@ pub(crate) async fn run_direct_stream_request(
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
match request {
|
||||
FsHelperRequest::ReadFileStream(params) => {
|
||||
let metadata = tokio::fs::symlink_metadata(params.path.as_path()).await?;
|
||||
let mut file = open_read_file(params.path.as_path()).await?;
|
||||
let metadata = file.metadata().await?;
|
||||
validate_read_file_metadata(params.path.as_path(), &metadata)?;
|
||||
let mut file = open_read_file_no_follow(params.path.as_path()).await?;
|
||||
tokio::io::copy(&mut file, stdout).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -339,31 +339,8 @@ fn validate_read_file_metadata(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn open_read_file_no_follow(path: &Path) -> io::Result<tokio::fs::File> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
tokio::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(libc::O_NOFOLLOW)
|
||||
.open(path)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000;
|
||||
|
||||
tokio::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT)
|
||||
.open(path)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(not(any(unix, windows)))]
|
||||
{
|
||||
tokio::fs::File::open(path).await
|
||||
}
|
||||
async fn open_read_file(path: &std::path::Path) -> io::Result<tokio::fs::File> {
|
||||
tokio::fs::File::open(path).await
|
||||
}
|
||||
|
||||
fn map_fs_error(err: io::Error) -> JSONRPCErrorError {
|
||||
|
||||
@@ -722,6 +722,42 @@ async fn file_system_sandboxed_read_body_rejects_symlink_to_denied_file(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(false ; "local")]
|
||||
#[test_case(true ; "remote")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn file_system_sandboxed_read_body_allows_symlink_to_readable_file(
|
||||
use_remote: bool,
|
||||
) -> Result<()> {
|
||||
let context = create_file_system_context(use_remote).await?;
|
||||
let file_system = context.file_system;
|
||||
|
||||
let tmp = TempDir::new()?;
|
||||
let allowed_dir = tmp.path().join("allowed");
|
||||
let target_path = allowed_dir.join("target.csv");
|
||||
let symlink_path = allowed_dir.join("report.csv");
|
||||
std::fs::create_dir_all(&allowed_dir)?;
|
||||
std::fs::write(&target_path, "readable")?;
|
||||
symlink(&target_path, &symlink_path)?;
|
||||
|
||||
let sandbox = read_only_sandbox(allowed_dir);
|
||||
let body = file_system
|
||||
.read_file_body(&absolute_path(symlink_path), Some(&sandbox))
|
||||
.await
|
||||
.with_context(|| format!("mode={use_remote}"))?;
|
||||
|
||||
assert_eq!(body.file_name, "report.csv");
|
||||
assert_eq!(body.file_size_bytes, "readable".len() as u64);
|
||||
let chunks = body
|
||||
.stream
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
.into_iter()
|
||||
.collect::<std::io::Result<Vec<_>>>()?;
|
||||
assert_eq!(chunks.concat(), b"readable");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(false ; "local")]
|
||||
#[test_case(true ; "remote")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
|
||||
Reference in New Issue
Block a user