use std::ffi::OsString;
use std::path::PathBuf;
pub const SANDBOX_EXEC_SUBCOMMAND: &str = "__sandbox-exec";
pub fn maybe_dispatch<I: IntoIterator<Item = OsString>>(args: I) -> Option<i32> {
let mut it = args.into_iter();
let _exe = it.next();
if it.next()? != SANDBOX_EXEC_SUBCOMMAND {
return None;
}
let mut no_network = false;
let mut confine_writes: Vec<PathBuf> = Vec::new();
let mut argv: Vec<OsString> = Vec::new();
let mut in_argv = false;
while let Some(arg) = it.next() {
if in_argv {
argv.push(arg);
} else if arg == "--" {
in_argv = true;
} else if arg == "--no-network" {
no_network = true;
} else if arg == "--confine-writes" {
match it.next() {
Some(dir) => confine_writes.push(PathBuf::from(dir)),
None => {
eprintln!(
"mermaid {SANDBOX_EXEC_SUBCOMMAND}: --confine-writes needs a directory"
);
return Some(2);
},
}
} else {
eprintln!("mermaid {SANDBOX_EXEC_SUBCOMMAND}: unexpected argument {arg:?}");
return Some(2);
}
}
if argv.is_empty() {
eprintln!("mermaid {SANDBOX_EXEC_SUBCOMMAND}: missing command after `--`");
return Some(2);
}
let policy = crate::runtime::SandboxPolicy {
deny_network: no_network,
allowed_writes: confine_writes,
};
match crate::runtime::enforce(&policy, &argv) {
Ok(crate::runtime::Enforcement::SelfApplied { fs_enforced }) => {
if !fs_enforced {
eprintln!(
"mermaid {SANDBOX_EXEC_SUBCOMMAND}: filesystem confinement not enforced (kernel without Landlock); continuing"
);
}
Some(exec_wrapped(&argv))
},
Ok(crate::runtime::Enforcement::ExecArgv(wrapped)) => Some(exec_wrapped(&wrapped)),
Ok(crate::runtime::Enforcement::Ran(code)) => Some(code),
Err(err) => {
eprintln!("mermaid {SANDBOX_EXEC_SUBCOMMAND}: sandbox unavailable: {err:#}");
Some(126)
},
}
}
fn exec_wrapped(argv: &[OsString]) -> i32 {
let program = &argv[0];
let rest = &argv[1..];
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
let err = std::process::Command::new(program).args(rest).exec();
eprintln!("mermaid {SANDBOX_EXEC_SUBCOMMAND}: failed to exec {program:?}: {err}");
127
}
#[cfg(not(unix))]
{
match std::process::Command::new(program).args(rest).status() {
Ok(status) => status.code().unwrap_or(1),
Err(err) => {
eprintln!("mermaid {SANDBOX_EXEC_SUBCOMMAND}: failed to spawn {program:?}: {err}");
127
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn os(items: &[&str]) -> Vec<OsString> {
items.iter().map(OsString::from).collect()
}
#[test]
fn non_sandbox_invocation_is_ignored() {
assert_eq!(maybe_dispatch(os(&["mermaid", "run", "hi"])), None);
assert_eq!(maybe_dispatch(os(&["mermaid"])), None);
}
#[test]
fn missing_command_after_separator_errors() {
assert_eq!(
maybe_dispatch(os(&["mermaid", "__sandbox-exec", "--no-network", "--"])),
Some(2)
);
}
#[test]
fn unknown_flag_before_separator_errors() {
assert_eq!(
maybe_dispatch(os(&["mermaid", "__sandbox-exec", "--bogus", "--", "true"])),
Some(2)
);
}
#[test]
fn confine_writes_without_a_directory_errors() {
assert_eq!(
maybe_dispatch(os(&["mermaid", "__sandbox-exec", "--confine-writes"])),
Some(2)
);
assert_eq!(
maybe_dispatch(os(&["mermaid", "__sandbox-exec", "--confine-writes", "--"])),
Some(2)
);
}
}