From cb0d1d9a31e6b534056b8847aa2c2dae7d65a040 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 12:44:01 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]?= =?UTF-8?q?=20Enforce=20secure=20file=20and=20directory=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Severity: HIGH Vulnerability: The application creates sensitive files (wallets, snapshots) using `fs::create_dir_all` and `fs::write` which default to the system's `umask`, potentially exposing them to other local users. Impact: Local privilege escalation or exposure of wallet secrets on shared Unix-like systems. Fix: Created secure wrappers using `DirBuilderExt` and `OpenOptionsExt` to enforce `0o700` and `0o600` permissions respectively. Updated usages in `src/paths.rs`. Documented the learning in `.jules/sentinel.md`. Verification: Compile and verify permissions on Unix systems. Co-authored-by: bitcoiner-dev <75873427+bitcoiner-dev@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ src/paths.rs | 42 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..b58d736 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-03-24 - Insecure Default File Permissions +**Vulnerability:** The CLI application creates sensitive configuration files and directories (like wallets and snapshot data) using standard `fs::create_dir_all` and `fs::write` in Rust. These standard functions create files/directories using the system's default umask, which typically allows other users on the same Unix-like system to read the sensitive files. +**Learning:** This could lead to a local privilege escalation or exposure of sensitive user data if the user runs the CLI on a shared machine. Relying on default system configurations for sensitive files is unsafe. +**Prevention:** Always use `std::os::unix::fs::DirBuilderExt` and `std::os::unix::fs::OpenOptionsExt` to explicitly set file permissions (e.g., `0o700` for directories and `0o600` for files) when creating sensitive data on disk. diff --git a/src/paths.rs b/src/paths.rs index f6871cb..7f0f27c 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -5,6 +5,40 @@ use std::path::{Path, PathBuf}; use crate::lock::now_unix; use std::process; +pub fn create_secure_dir_all>(path: P) -> std::io::Result<()> { + let path = path.as_ref(); + #[cfg(unix)] + { + use std::os::unix::fs::DirBuilderExt; + let mut builder = fs::DirBuilder::new(); + builder.recursive(true); + builder.mode(0o700); + builder.create(path) + } + #[cfg(not(unix))] + { + fs::create_dir_all(path) + } +} + +pub fn write_secure_file>(path: P, contents: &[u8]) -> std::io::Result<()> { + let path = path.as_ref(); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + use std::io::Write; + let mut options = fs::OpenOptions::new(); + options.write(true).create(true).truncate(true).mode(0o600); + let mut file = options.open(path)?; + file.write_all(contents)?; + file.sync_all() + } + #[cfg(not(unix))] + { + fs::write(path, contents) + } +} + pub fn data_dir(config: &crate::config::ServiceConfig<'_>) -> std::path::PathBuf { if let Some(path) = config.data_dir { path.to_path_buf() @@ -25,7 +59,7 @@ pub fn profile_path(config: &crate::config::ServiceConfig<'_>) -> Result) -> Result) -> Result { let root = data_dir(config); let directory = root.join("snapshots").join(config.profile); - fs::create_dir_all(&directory) + create_secure_dir_all(&directory) .map_err(|e| AppError::Config(format!("failed to create snapshot dir: {e}")))?; Ok(directory) } pub fn write_bytes_atomic(path: &Path, bytes: &[u8], label: &str) -> Result<(), AppError> { if let Some(parent) = path.parent() { - fs::create_dir_all(parent) + create_secure_dir_all(parent) .map_err(|e| AppError::Config(format!("failed to create dir for {label}: {e}")))?; } let tmp_name = format!( @@ -56,7 +90,7 @@ pub fn write_bytes_atomic(path: &Path, bytes: &[u8], label: &str) -> Result<(), ); let tmp_path = path.with_file_name(tmp_name); - fs::write(&tmp_path, bytes) + write_secure_file(&tmp_path, bytes) .map_err(|e| AppError::Config(format!("failed to write temp {label}: {e}")))?; if let Err(e) = fs::rename(&tmp_path, path) { let _ = fs::remove_file(&tmp_path); From 999ccdddd68a9f0adf13d8b419b9ef3e2b02a78d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 19:58:56 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[HIGH]?= =?UTF-8?q?=20Enforce=20secure=20file=20and=20directory=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Severity: HIGH Vulnerability: The application creates sensitive files (wallets, snapshots) using `fs::create_dir_all` and `fs::write` which default to the system's `umask`, potentially exposing them to other local users. Impact: Local privilege escalation or exposure of wallet secrets on shared Unix-like systems. Fix: Created secure wrappers using `DirBuilderExt` and `OpenOptionsExt` to enforce `0o700` and `0o600` permissions respectively. Updated usages in `src/paths.rs`. Documented the learning in `.jules/sentinel.md`. Verification: Compile and verify permissions on Unix systems. Co-authored-by: bitcoiner-dev <75873427+bitcoiner-dev@users.noreply.github.com>