Add mask option to hide paths/files from sandbox

This commit is contained in:
2026-04-01 23:19:08 +02:00
parent 0119834d5a
commit c7c4c673cb
5 changed files with 176 additions and 1 deletions

View File

@@ -37,6 +37,7 @@ pub fn build(args: Args, file_config: Option<FileConfig>) -> Result<SandboxConfi
chdir: resolve_chdir(args.chdir, profile.chdir, globals.chdir)?,
extra_rw: merge_paths(args.extra_rw, &profile.rw, &globals.rw)?,
extra_ro: merge_paths(args.extra_ro, &profile.ro, &globals.ro)?,
mask: merge_mask(args.mask, &profile.mask, &globals.mask),
command,
command_args,
})
@@ -98,6 +99,10 @@ fn merge_paths(
Ok(config_paths.chain(cli_paths).collect())
}
fn merge_mask(cli: Vec<PathBuf>, profile: &[PathBuf], globals: &[PathBuf]) -> Vec<PathBuf> {
globals.iter().chain(profile).cloned().chain(cli).collect()
}
fn resolve_command(
mut positional: Vec<OsString>,
profile_cmd: Option<CommandValue>,
@@ -180,6 +185,8 @@ pub struct Options {
pub rw: Vec<PathBuf>,
#[serde(default)]
pub ro: Vec<PathBuf>,
#[serde(default)]
pub mask: Vec<PathBuf>,
}
impl Options {
@@ -193,10 +200,19 @@ impl Options {
if let Some(ref chdir) = self.chdir {
self.chdir = Some(expand_and_canonicalize(chdir)?);
}
for p in &mut self.mask {
*p = expand_and_require_absolute(p)?;
}
Ok(())
}
}
fn expand_and_require_absolute(path: &Path) -> Result<PathBuf, SandboxError> {
let expanded = expand_tilde(path)?;
require_absolute(&expanded)?;
Ok(expanded)
}
#[derive(Deserialize, Clone)]
#[serde(untagged)]
pub enum CommandValue {
@@ -531,6 +547,72 @@ mod tests {
));
}
#[test]
fn build_tilde_expansion_in_config_paths() {
let home = std::env::var("HOME").unwrap();
let file_config = FileConfig {
options: Options {
rw: vec![PathBuf::from("~/")],
..Options::default()
},
..FileConfig::default()
};
let config = build(args_with_command(), Some(file_config)).unwrap();
assert_eq!(config.extra_rw, vec![PathBuf::from(&home)]);
}
#[test]
fn build_relative_config_path_rejected() {
let file_config = FileConfig {
options: Options {
rw: vec![PathBuf::from("relative/path")],
..Options::default()
},
..FileConfig::default()
};
assert!(matches!(
build(args_with_command(), Some(file_config)),
Err(SandboxError::ConfigPathNotAbsolute(_))
));
}
#[test]
fn build_chdir_from_config() {
let file_config = FileConfig {
options: Options {
chdir: Some(PathBuf::from("/tmp")),
..Options::default()
},
..FileConfig::default()
};
let config = build(args_with_command(), Some(file_config)).unwrap();
assert_eq!(config.chdir, std::fs::canonicalize("/tmp").unwrap());
}
#[test]
fn build_mask_accumulates() {
let file_config = FileConfig {
options: Options {
mask: vec![PathBuf::from("/tmp/a")],
..Options::default()
},
profile: HashMap::from([(
"extra".into(),
Options {
mask: vec![PathBuf::from("/tmp/b")],
..Options::default()
},
)]),
};
let args = Args {
profile: Some("extra".into()),
mask: vec![PathBuf::from("/tmp/c")],
..args_with_command()
};
let config = build(args, Some(file_config)).unwrap();
assert_eq!(config.mask.len(), 3);
}
fn assert_paths(actual: &[PathBuf], expected: &[&str]) {
let expected: Vec<PathBuf> = expected.iter().map(PathBuf::from).collect();
assert_eq!(actual, &expected);