Add option to pass through arguments to brwap, use shlex for dry-run

This commit is contained in:
2026-04-04 08:41:40 +02:00
parent 8958f79ece
commit 8ecba5d6dc
8 changed files with 75 additions and 3 deletions

View File

@@ -58,6 +58,10 @@ pub struct Args {
#[arg(long = "mask", value_name = "PATH", action = clap::ArgAction::Append)]
pub mask: Vec<PathBuf>,
/// Pass an arbitrary argument directly to bwrap (repeatable)
#[arg(long = "bwrap-arg", value_name = "ARG", action = clap::ArgAction::Append)]
pub bwrap_args: Vec<String>,
/// Command and arguments to run inside the sandbox
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
pub command_and_args: Vec<OsString>,

View File

@@ -37,7 +37,12 @@ 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),
mask: merge_vecs(args.mask, &profile.mask, &globals.mask),
bwrap_args: split_bwrap_args(merge_vecs(
args.bwrap_args,
&profile.bwrap_args,
&globals.bwrap_args,
))?,
command,
command_args,
})
@@ -99,7 +104,17 @@ fn merge_paths(
Ok(config_paths.chain(cli_paths).collect())
}
fn merge_mask(cli: Vec<PathBuf>, profile: &[PathBuf], globals: &[PathBuf]) -> Vec<PathBuf> {
fn split_bwrap_args(raw: Vec<String>) -> Result<Vec<String>, SandboxError> {
let mut out = Vec::new();
for value in &raw {
let words =
shlex::split(value).ok_or_else(|| SandboxError::InvalidBwrapArg(value.clone()))?;
out.extend(words);
}
Ok(out)
}
fn merge_vecs<T: Clone>(cli: Vec<T>, profile: &[T], globals: &[T]) -> Vec<T> {
globals.iter().chain(profile).cloned().chain(cli).collect()
}
@@ -204,6 +219,8 @@ pub struct Options {
pub ro: Vec<PathBuf>,
#[serde(default)]
pub mask: Vec<PathBuf>,
#[serde(default)]
pub bwrap_args: Vec<String>,
}
impl Options {

View File

@@ -24,6 +24,7 @@ pub enum SandboxError {
ConflictingMode,
UnknownConfigKey(String),
ConfigPathNotAbsolute(PathBuf),
InvalidBwrapArg(String),
}
impl std::fmt::Display for SandboxError {
@@ -65,6 +66,9 @@ impl std::fmt::Display for SandboxError {
Self::ConfigPathNotAbsolute(p) => {
write!(f, "config path is not absolute: {}", p.display())
}
Self::InvalidBwrapArg(s) => {
write!(f, "invalid quoting in --bwrap-arg: {s}")
}
}
}
}

View File

@@ -26,6 +26,7 @@ pub struct SandboxConfig {
pub extra_rw: Vec<PathBuf>,
pub extra_ro: Vec<PathBuf>,
pub mask: Vec<PathBuf>,
pub bwrap_args: Vec<String>,
pub command: PathBuf,
pub command_args: Vec<OsString>,
pub chdir: PathBuf,
@@ -63,9 +64,16 @@ pub fn run(config: SandboxConfig) -> Result<(), SandboxError> {
let mut cmd = sandbox::build_command(&config)?;
if config.dry_run {
println!("{:?}", cmd);
println!("{}", shell_quote_command(&cmd));
return Ok(());
}
Err(SandboxError::Io(cmd.exec()))
}
fn shell_quote_command(cmd: &std::process::Command) -> String {
let prog = cmd.get_program().to_string_lossy();
let args = cmd.get_args().map(|a| a.to_string_lossy());
let all: Vec<_> = std::iter::once(prog).chain(args).collect();
shlex::try_join(all.iter().map(|s| s.as_ref())).unwrap()
}

View File

@@ -41,6 +41,8 @@ pub fn build_command(config: &SandboxConfig) -> Result<Command, SandboxError> {
apply_masks(&mut cmd, &config.mask);
cmd.args(&config.bwrap_args);
cmd.arg("--")
.arg(&config.command)
.args(&config.command_args);