Skip to main content

heimdall_core/
error.rs

1use std::path::PathBuf;
2
3use thiserror::Error as ThisError;
4
5/// Exit code used when the sandbox cannot be configured safely.
6pub const SANDBOX_MISCONFIGURATION_EXIT_CODE: i32 = 2;
7
8/// Errors returned by sandbox runtime operations.
9#[derive(Debug, ThisError)]
10pub enum Error {
11    /// Command argv was empty.
12    #[error("missing command")]
13    MissingCommand,
14    /// Requested child working directory is not an existing directory.
15    #[error("invalid cwd: {0}")]
16    InvalidCwd(PathBuf),
17    /// Process hardening failed.
18    #[error("sandbox hardening failed: {0}")]
19    Hardening(#[source] std::io::Error),
20    /// Child process spawning failed.
21    #[error("failed to spawn child command: {0}")]
22    Spawn(#[source] std::io::Error),
23    /// Waiting for the child process failed.
24    #[error("failed to wait for child command: {0}")]
25    Wait(#[source] std::io::Error),
26    /// Shared sandbox policy is invalid.
27    #[error(transparent)]
28    SandboxPolicy(#[from] heimdall_sandbox_policy::Error),
29    /// Linux sandbox planning failed.
30    #[cfg(target_os = "linux")]
31    #[error(transparent)]
32    LinuxSandbox(#[from] heimdall_linux_sandbox::Error),
33    /// macOS sandbox planning failed.
34    #[cfg(target_os = "macos")]
35    #[error(transparent)]
36    MacosSandbox(#[from] heimdall_macos_sandbox::Error),
37    /// Sandbox policy or platform setup is invalid.
38    #[error("sandbox misconfiguration: {0}")]
39    SandboxMisconfiguration(String),
40}
41
42impl Error {
43    /// Construct a missing command error.
44    #[must_use]
45    pub const fn missing_command() -> Self {
46        Self::MissingCommand
47    }
48
49    /// Construct an invalid cwd error from the rejected path.
50    #[must_use]
51    pub fn invalid_cwd(cwd: PathBuf) -> Self {
52        Self::InvalidCwd(cwd)
53    }
54
55    /// Wrap a process hardening I/O error.
56    #[must_use]
57    pub fn hardening(error: std::io::Error) -> Self {
58        Self::Hardening(error)
59    }
60
61    /// Wrap a child spawn I/O error.
62    #[must_use]
63    pub fn spawn(error: std::io::Error) -> Self {
64        Self::Spawn(error)
65    }
66
67    /// Construct a sandbox misconfiguration error with a descriptive message.
68    #[must_use]
69    pub fn sandbox_misconfiguration(message: impl Into<String>) -> Self {
70        Self::SandboxMisconfiguration(message.into())
71    }
72
73    /// Return the documented process exit code for this error.
74    ///
75    /// All sandbox errors currently map to [`SANDBOX_MISCONFIGURATION_EXIT_CODE`].
76    #[must_use]
77    pub const fn exit_code(&self) -> i32 {
78        match self {
79            Self::MissingCommand
80            | Self::InvalidCwd(_)
81            | Self::Hardening(_)
82            | Self::Spawn(_)
83            | Self::Wait(_)
84            | Self::SandboxPolicy(_)
85            | Self::SandboxMisconfiguration(_) => SANDBOX_MISCONFIGURATION_EXIT_CODE,
86            #[cfg(target_os = "linux")]
87            Self::LinuxSandbox(_) => SANDBOX_MISCONFIGURATION_EXIT_CODE,
88            #[cfg(target_os = "macos")]
89            Self::MacosSandbox(_) => SANDBOX_MISCONFIGURATION_EXIT_CODE,
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use std::io;
97
98    use super::*;
99
100    #[test]
101    fn runtime_errors_map_to_documented_exit_codes() {
102        assert_eq!(
103            Error::missing_command().exit_code(),
104            SANDBOX_MISCONFIGURATION_EXIT_CODE
105        );
106        assert_eq!(
107            Error::invalid_cwd(PathBuf::from("missing")).exit_code(),
108            SANDBOX_MISCONFIGURATION_EXIT_CODE
109        );
110        assert_eq!(
111            Error::hardening(io::Error::other("failed")).exit_code(),
112            SANDBOX_MISCONFIGURATION_EXIT_CODE
113        );
114        assert_eq!(
115            Error::spawn(io::Error::other("failed")).exit_code(),
116            SANDBOX_MISCONFIGURATION_EXIT_CODE
117        );
118    }
119}