mirror of
https://github.com/googleworkspace/cli.git
synced 2026-09-14 16:47:13 +08:00
🔒 fix: secure atomic file creation with restrictive permissions
Use `0o600` permissions (read/write only by the owner) when creating temporary files in `atomic_write` and `atomic_write_async`. This prevents a race condition on Unix systems where a sensitive file (like credentials or tokens) could be created with default permissions (e.g., world-readable) before its permissions are corrected. The fix uses `OpenOptions::mode(0o600)` on Unix and also adds `sync_all()` for better data durability. A new test `test_atomic_write_permissions` verifies the fix on Unix. A changeset file is also included. Co-authored-by: jpoehnelt <3392975+jpoehnelt@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@googleworkspace/cli": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
🔒 Fix: Use restrictive permissions (0600) for temporary files created during atomic writes in fs_util.
|
||||||
+60
-2
@@ -40,7 +40,21 @@ pub fn atomic_write(path: &Path, data: &[u8]) -> io::Result<()> {
|
|||||||
.map(|p| p.join(&tmp_name))
|
.map(|p| p.join(&tmp_name))
|
||||||
.unwrap_or_else(|| std::path::PathBuf::from(&tmp_name));
|
.unwrap_or_else(|| std::path::PathBuf::from(&tmp_name));
|
||||||
|
|
||||||
std::fs::write(&tmp_path, data)?;
|
{
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Write;
|
||||||
|
let mut opts = OpenOptions::new();
|
||||||
|
opts.write(true).create(true).truncate(true);
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
opts.mode(0o600);
|
||||||
|
}
|
||||||
|
let mut file = opts.open(&tmp_path)?;
|
||||||
|
file.write_all(data)?;
|
||||||
|
file.sync_all()?;
|
||||||
|
}
|
||||||
|
|
||||||
std::fs::rename(&tmp_path, path)?;
|
std::fs::rename(&tmp_path, path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -56,7 +70,21 @@ pub async fn atomic_write_async(path: &Path, data: &[u8]) -> io::Result<()> {
|
|||||||
.map(|p| p.join(&tmp_name))
|
.map(|p| p.join(&tmp_name))
|
||||||
.unwrap_or_else(|| std::path::PathBuf::from(&tmp_name));
|
.unwrap_or_else(|| std::path::PathBuf::from(&tmp_name));
|
||||||
|
|
||||||
tokio::fs::write(&tmp_path, data).await?;
|
{
|
||||||
|
use tokio::fs::OpenOptions;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
let mut opts = OpenOptions::new();
|
||||||
|
opts.write(true).create(true).truncate(true);
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use tokio::os::unix::fs::OpenOptionsExt;
|
||||||
|
opts.mode(0o600);
|
||||||
|
}
|
||||||
|
let mut file = opts.open(&tmp_path).await?;
|
||||||
|
file.write_all(data).await?;
|
||||||
|
file.sync_all().await?;
|
||||||
|
}
|
||||||
|
|
||||||
tokio::fs::rename(&tmp_path, path).await?;
|
tokio::fs::rename(&tmp_path, path).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -99,4 +127,34 @@ mod tests {
|
|||||||
atomic_write_async(&path, b"async hello").await.unwrap();
|
atomic_write_async(&path, b"async hello").await.unwrap();
|
||||||
assert_eq!(fs::read(&path).unwrap(), b"async hello");
|
assert_eq!(fs::read(&path).unwrap(), b"async hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_atomic_write_permissions() {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
|
||||||
|
// Sync
|
||||||
|
let path_sync = dir.path().join("sync.txt");
|
||||||
|
atomic_write(&path_sync, b"sync").unwrap();
|
||||||
|
let meta_sync = fs::metadata(&path_sync).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
meta_sync.permissions().mode() & 0o777,
|
||||||
|
0o600,
|
||||||
|
"Sync atomic_write should create file with 0600 permissions"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Async
|
||||||
|
let path_async = dir.path().join("async.txt");
|
||||||
|
atomic_write_async(&path_async, b"async").await.unwrap();
|
||||||
|
let meta_async = fs::metadata(&path_async).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
meta_async.permissions().mode() & 0o777,
|
||||||
|
0o600,
|
||||||
|
"Async atomic_write_async should create file with 0600 permissions"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user