pub mod guard;
mod immutable;
mod seatbelt;
use std::ffi::OsString;
use std::os::unix::process::CommandExt;
use std::process::{Command, ExitCode};
use anyhow::{anyhow, Result};
use super::{Availability, Backend, Plan};
pub fn availability() -> Vec<(Backend, Availability)> {
let linux_only = |feature: &str| Availability::Unavailable {
reason: format!("{feature} is a Linux kernel feature with no macOS equivalent"),
};
let seatbelt = if seatbelt::available() {
Availability::Available {
detail: "Seatbelt, applied to this process and inherited by everything it starts"
.to_string(),
}
} else {
Availability::Unavailable {
reason: "this build has no sandbox_init to call".to_string(),
}
};
vec![
(Backend::Mount, linux_only("mount namespaces")),
(Backend::Landlock, linux_only("Landlock")),
(Backend::Seatbelt, seatbelt),
(
Backend::Immutable,
Availability::Available {
detail: "chflags uchg, refused to every process until it is cleared — \
a narrowing an agent can undo with `chflags nouchg`, not a sandbox"
.to_string(),
},
),
]
}
pub fn enforce_and_exec(plan: &Plan, command: &[OsString]) -> Result<ExitCode> {
match plan.backend {
Backend::Seatbelt => match &plan.profile {
Some(profile) => seatbelt::apply(profile)?,
None => {
return Err(anyhow!(
"internal error: the seatbelt profile was not built"
))
}
},
other => return Err(anyhow!("internal error: {other} cannot enforce on macOS")),
}
Err(exec(command))
}
fn exec(command: &[OsString]) -> anyhow::Error {
let Some((program, arguments)) = command.split_first() else {
return anyhow!("no command given");
};
let error = Command::new(program).args(arguments).exec();
anyhow::Error::new(error).context(format!("failed to run `{}`", program.to_string_lossy()))
}