From 9ccf9797de00bd8ab7983e0432d1c3db0e6c72e3 Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Thu, 26 Mar 2026 10:13:30 -0400 Subject: [PATCH] Create secrets file with more restricted file permissions (#8118) Signed-off-by: Sam Doran Co-authored-by: Douwe Osinga --- crates/goose/src/config/base.rs | 44 ++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/crates/goose/src/config/base.rs b/crates/goose/src/config/base.rs index b59b5866..c42eb663 100644 --- a/crates/goose/src/config/base.rs +++ b/crates/goose/src/config/base.rs @@ -14,6 +14,26 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use thiserror::Error; +fn write_secrets_file(path: &Path, content: &str) -> std::io::Result<()> { + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .mode(0o600) + .open(path)?; + + file.write_all(content.as_bytes()) + } + + #[cfg(not(unix))] + { + std::fs::write(path, content) + } +} + const KEYRING_SERVICE: &str = "goose"; const KEYRING_USERNAME: &str = "secrets"; pub const CONFIG_YAML_NAME: &str = "config.yaml"; @@ -856,7 +876,7 @@ impl Config { } SecretStorage::File { path } => { let yaml_value = serde_yaml::to_string(&values)?; - std::fs::write(path, yaml_value)?; + write_secrets_file(path, &yaml_value)?; } }; @@ -897,7 +917,7 @@ impl Config { } SecretStorage::File { path } => { let yaml_value = serde_yaml::to_string(&values)?; - std::fs::write(path, yaml_value)?; + write_secrets_file(path, &yaml_value)?; } }; @@ -937,7 +957,7 @@ impl Config { std::fs::create_dir_all(Paths::config_dir())?; let path = Self::secrets_file_path(); let yaml_value = serde_yaml::to_string(values)?; - std::fs::write(path, yaml_value)?; + write_secrets_file(&path, &yaml_value)?; Ok(()) } @@ -1942,4 +1962,22 @@ mod tests { Ok(()) } + + #[test] + #[cfg(unix)] + fn test_secrets_file_created_with_restricted_permissions() -> Result<(), ConfigError> { + use std::os::unix::fs::PermissionsExt; + + let dir = TempDir::new().unwrap(); + let config_file = NamedTempFile::new().unwrap(); + let secrets_path = dir.path().join("secrets.yaml"); + + let config = Config::new_with_file_secrets(config_file.path(), &secrets_path)?; + config.set_secret("key", &"value")?; + + let mode = std::fs::metadata(&secrets_path)?.permissions().mode() & 0o777; + assert_eq!(mode, 0o600); + + Ok(()) + } }