diff --git a/crates/sandbox/src/sandbox.rs b/crates/sandbox/src/sandbox.rs index 5c8033ec8f47a04fe989b2767922bd64042ac535..55ec28cc0594d8fe0b2b92ba2fe57a63e4db5a32 100644 --- a/crates/sandbox/src/sandbox.rs +++ b/crates/sandbox/src/sandbox.rs @@ -170,6 +170,30 @@ impl ResolvedSystemPaths { } impl SandboxConfig { + /// Shell configuration dotfiles that need read-only access. + /// Both macOS and Linux sandbox implementations use this list. + pub const READ_ONLY_DOTFILES: &[&str] = &[ + ".bashrc", + ".bash_login", + ".bash_profile", + ".gitconfig", + ".inputrc", + ".profile", + ".terminfo", + ".zlogin", + ".zlogout", + ".zprofile", + ".zshenv", + ".zshrc", + ]; + + /// Shell history dotfiles that need read-write access so shells can + /// persist command history without silent failures. + pub const READ_WRITE_DOTFILES: &[&str] = &[ + ".bash_history", + ".zsh_history", + ]; + /// Default environment variables to pass through to sandboxed terminals. pub fn default_allowed_env_vars() -> Vec { vec![ diff --git a/crates/sandbox/src/sandbox_linux.rs b/crates/sandbox/src/sandbox_linux.rs index 4e64c99e97d05ff25eeb8c224edf648c77cc0a96..61c31a1b9cb928ec814366d2ac3526e24940130f 100644 --- a/crates/sandbox/src/sandbox_linux.rs +++ b/crates/sandbox/src/sandbox_linux.rs @@ -104,26 +104,20 @@ pub fn apply_sandbox(config: &SandboxConfig) -> Result<()> { if let Ok(home) = std::env::var("HOME") { let home = Path::new(&home); - for dotfile in &[ - ".bashrc", - ".bash_profile", - ".bash_login", - ".profile", - ".zshrc", - ".zshenv", - ".zprofile", - ".zlogin", - ".zlogout", - ".inputrc", - ".terminfo", - ".gitconfig", - ] { + for dotfile in SandboxConfig::READ_ONLY_DOTFILES { let path = home.join(dotfile); if path.exists() { ruleset = add_path_rule(ruleset, &path, fs_read()) .map_err(|e| Error::other(format!("landlock dotfile rule: {e}")))?; } } + for dotfile in SandboxConfig::READ_WRITE_DOTFILES { + let path = home.join(dotfile); + if path.exists() { + ruleset = add_path_rule(ruleset, &path, fs_all()) + .map_err(|e| Error::other(format!("landlock dotfile rule: {e}")))?; + } + } let config_dir = home.join(".config"); if config_dir.exists() { ruleset = add_path_rule(ruleset, &config_dir, fs_read()) diff --git a/crates/sandbox/src/sandbox_macos.rs b/crates/sandbox/src/sandbox_macos.rs index de9f253cdbb2dd4c8ecfeaff49e285487947589a..eb74da6f0be0d7db20e6f32dbebda44b80717736 100644 --- a/crates/sandbox/src/sandbox_macos.rs +++ b/crates/sandbox/src/sandbox_macos.rs @@ -420,23 +420,10 @@ pub(crate) fn generate_sbpl_profile( write_subpath_rule(&mut p, path, "file-read* file-write*"); } - // User shell config files: read-only access to $HOME dotfiles + // User shell config files if let Ok(home) = std::env::var("HOME") { let home = Path::new(&home); - for dotfile in &[ - ".zshrc", - ".zshenv", - ".zprofile", - ".zlogin", - ".zlogout", - ".bashrc", - ".bash_profile", - ".bash_login", - ".profile", - ".inputrc", - ".terminfo", - ".gitconfig", - ] { + for dotfile in SandboxConfig::READ_ONLY_DOTFILES { let path = home.join(dotfile); if path.exists() { write!( @@ -447,6 +434,17 @@ pub(crate) fn generate_sbpl_profile( .unwrap(); } } + for dotfile in SandboxConfig::READ_WRITE_DOTFILES { + let path = home.join(dotfile); + if path.exists() { + write!( + p, + "(allow file-read* file-write* (literal \"{}\"))\n", + sbpl_escape(&path) + ) + .unwrap(); + } + } // XDG config directory let config_dir = home.join(".config"); if config_dir.exists() {