From d71d853c81d328b444f279c2ac777d51a051b34e Mon Sep 17 00:00:00 2001 From: Fabro Date: Mon, 16 Mar 2026 12:01:50 +0000 Subject: [PATCH] fabro(01KKV85BRXX5Z0KCPACE3BFE7P): solve (success) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fabro-Run: 01KKV85BRXX5Z0KCPACE3BFE7P Fabro-Completed: 3 Fabro-Checkpoint: 4fcf020ca06bb667ce55afa14d2ca03f5fc33396 ⚒️ Generated with [Fabro](https://fabro.sh) --- lib/crates/fabro-agent/src/local_sandbox.rs | 82 ++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/lib/crates/fabro-agent/src/local_sandbox.rs b/lib/crates/fabro-agent/src/local_sandbox.rs index 9798ea302..e93b17053 100644 --- a/lib/crates/fabro-agent/src/local_sandbox.rs +++ b/lib/crates/fabro-agent/src/local_sandbox.rs @@ -71,6 +71,20 @@ impl LocalSandbox { } } +#[cfg(unix)] +async fn set_default_file_permissions(path: &Path) -> Result<(), String> { + use std::os::unix::fs::PermissionsExt; + + tokio::fs::set_permissions(path, std::fs::Permissions::from_mode(0o644)) + .await + .map_err(|e| format!("Failed to set permissions on {}: {e}", path.display())) +} + +#[cfg(not(unix))] +async fn set_default_file_permissions(_path: &Path) -> Result<(), String> { + Ok(()) +} + #[async_trait] impl Sandbox for LocalSandbox { async fn read_file( @@ -96,7 +110,8 @@ impl Sandbox for LocalSandbox { } tokio::fs::write(&full_path, content) .await - .map_err(|e| format!("Failed to write {}: {e}", full_path.display())) + .map_err(|e| format!("Failed to write {}: {e}", full_path.display()))?; + set_default_file_permissions(&full_path).await } async fn delete_file(&self, path: &str) -> Result<(), String> { @@ -379,6 +394,7 @@ impl Sandbox for LocalSandbox { local_path.display() ) })?; + set_default_file_permissions(local_path).await?; Ok(()) } @@ -400,6 +416,7 @@ impl Sandbox for LocalSandbox { full_path.display() ) })?; + set_default_file_permissions(&full_path).await?; Ok(()) } @@ -508,6 +525,13 @@ mod tests { use super::*; use std::path::PathBuf; + #[cfg(unix)] + fn file_mode(path: &Path) -> u32 { + use std::os::unix::fs::PermissionsExt; + + std::fs::metadata(path).unwrap().permissions().mode() & 0o777 + } + fn temp_dir() -> PathBuf { let dir = std::env::temp_dir().join(format!("local_env_test_{}", uuid::Uuid::new_v4())); std::fs::create_dir_all(&dir).unwrap(); @@ -560,6 +584,62 @@ mod tests { std::fs::remove_dir_all(&dir).unwrap(); } + #[cfg(unix)] + #[tokio::test] + async fn write_file_sets_default_permissions() { + use std::os::unix::fs::PermissionsExt; + + let dir = temp_dir(); + let path = dir.join("private.txt"); + std::fs::write(&path, "old").unwrap(); + std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600)).unwrap(); + + let env = LocalSandbox::new(dir.clone()); + env.write_file("private.txt", "new").await.unwrap(); + + assert_eq!(file_mode(&path), 0o644); + std::fs::remove_dir_all(&dir).unwrap(); + } + + #[cfg(unix)] + #[tokio::test] + async fn upload_file_from_local_sets_default_permissions() { + use std::os::unix::fs::PermissionsExt; + + let dir = temp_dir(); + let scratch = temp_dir(); + let src = scratch.join("upload.txt"); + std::fs::write(&src, "content").unwrap(); + std::fs::set_permissions(&src, std::fs::Permissions::from_mode(0o600)).unwrap(); + + let env = LocalSandbox::new(dir.clone()); + env.upload_file_from_local(&src, "uploaded.txt").await.unwrap(); + + assert_eq!(file_mode(&dir.join("uploaded.txt")), 0o644); + std::fs::remove_dir_all(&dir).unwrap(); + std::fs::remove_dir_all(&scratch).unwrap(); + } + + #[cfg(unix)] + #[tokio::test] + async fn download_file_to_local_sets_default_permissions() { + use std::os::unix::fs::PermissionsExt; + + let dir = temp_dir(); + let scratch = temp_dir(); + let remote = dir.join("remote.txt"); + let local = scratch.join("downloaded.txt"); + std::fs::write(&remote, "content").unwrap(); + std::fs::set_permissions(&remote, std::fs::Permissions::from_mode(0o600)).unwrap(); + + let env = LocalSandbox::new(dir.clone()); + env.download_file_to_local("remote.txt", &local).await.unwrap(); + + assert_eq!(file_mode(&local), 0o644); + std::fs::remove_dir_all(&dir).unwrap(); + std::fs::remove_dir_all(&scratch).unwrap(); + } + #[tokio::test] async fn file_exists_true() { let dir = temp_dir();