use std::path::PathBuf; #[derive(Debug)] pub enum SandboxError { HomeNotSet, RunUserNotFound, BwrapNotFound, CommandNotFound(PathBuf), CommandNotExecutable(PathBuf), PathMissing(PathBuf), ChdirMissing(PathBuf), CurrentDirUnavailable(std::io::Error), GlobPattern(glob::PatternError), Io(std::io::Error), ConfigRead { path: PathBuf, source: std::io::Error, }, ConfigParse { path: PathBuf, source: toml::de::Error, }, ProfileNotFound(String), ConflictingMode, ConflictingEnvKey(String), InvalidEnvEntry(String), UnknownConfigKey(String), NestedExtraConfig(PathBuf), ConfigPathNotAbsolute(PathBuf), InvalidBwrapArg(String), InvalidBindSpec(String), NoCommand, Seccomp(String), SeccompUnsupportedArch(String), } impl std::fmt::Display for SandboxError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::HomeNotSet => write!( f, "$HOME is not set; cannot determine which paths to protect" ), Self::RunUserNotFound => write!( f, "cannot determine XDG_RUNTIME_DIR; tried $XDG_RUNTIME_DIR and /proc/self/status" ), Self::BwrapNotFound => write!( f, "bwrap not found; install bubblewrap (e.g. `apt install bubblewrap` or `pacman -S bubblewrap`)" ), Self::CommandNotFound(p) => write!(f, "command not found: {}", p.display()), Self::CommandNotExecutable(p) => { write!(f, "command is not executable: {}", p.display()) } Self::PathMissing(p) => write!(f, "path does not exist: {}", p.display()), Self::ChdirMissing(p) => write!(f, "--chdir path does not exist: {}", p.display()), Self::CurrentDirUnavailable(e) => write!(f, "cannot determine current directory: {e}"), Self::GlobPattern(e) => write!(f, "invalid glob pattern: {e}"), Self::Io(e) => write!(f, "I/O error: {e}"), Self::ConfigRead { path, source } => { write!(f, "cannot read config file '{}': {source}", path.display()) } Self::ConfigParse { path, source } => { write!(f, "cannot parse config file '{}': {source}", path.display()) } Self::ProfileNotFound(name) => write!(f, "profile not found in config: {name}"), Self::ConflictingMode => write!( f, "config section sets both blacklist and whitelist to true" ), Self::ConflictingEnvKey(key) => { write!(f, "conflicting environment variable options for key: {key}") } Self::InvalidEnvEntry(raw) => { write!(f, "invalid env entry (expected KEY or KEY=VALUE): {raw:?}") } Self::UnknownConfigKey(key) => write!(f, "unknown config key: {key}"), Self::NestedExtraConfig(p) => write!( f, "extra-config file '{}' sets its own extra-config (nesting not supported)", p.display() ), Self::ConfigPathNotAbsolute(p) => { write!(f, "config path is not absolute: {}", p.display()) } Self::InvalidBwrapArg(s) => { write!(f, "invalid quoting in --bwrap-arg: {s}") } Self::InvalidBindSpec(s) => { write!(f, "invalid bind spec (expected SRC or SRC:DST): {s:?}") } Self::NoCommand => write!( f, "no command to run; specify a command via config, entrypoint, or pass one after --" ), Self::Seccomp(msg) => write!(f, "failed to build seccomp filter: {msg}"), Self::SeccompUnsupportedArch(arch) => write!( f, "seccomp filtering is not supported on this architecture: {arch} (use --no-seccomp to disable)" ), } } } impl std::error::Error for SandboxError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::CurrentDirUnavailable(e) => Some(e), Self::GlobPattern(e) => Some(e), Self::Io(e) => Some(e), Self::ConfigRead { source, .. } => Some(source), Self::ConfigParse { source, .. } => Some(source), _ => None, } } } impl From for SandboxError { fn from(e: std::io::Error) -> Self { Self::Io(e) } } impl From for SandboxError { fn from(e: glob::PatternError) -> Self { Self::GlobPattern(e) } }