auditable_info/
error.rs

1#[derive(Debug)]
2pub enum Error {
3    NoAuditData,
4    InputLimitExceeded,
5    OutputLimitExceeded,
6    Io(std::io::Error),
7    BinaryParsing(auditable_extract::Error),
8    Decompression(DecompressError),
9    #[cfg(feature = "serde")]
10    Json(serde_json::Error),
11    Utf8(std::str::Utf8Error),
12}
13
14impl std::fmt::Display for Error {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match self {
17            Error::NoAuditData => write!(f, "No audit data found in the binary! Was it built with 'cargo auditable'?"),
18            Error::InputLimitExceeded => write!(f, "The input file is too large. Increase the input size limit to scan it."),
19            Error::OutputLimitExceeded => write!(f, "Audit data size is over the specified limit. Increase the output size limit to scan it."),
20            Error::Io(e) => write!(f, "Failed to read the binary: {e}"),
21            Error::BinaryParsing(e) => write!(f, "Failed to parse the binary: {e}"),
22            Error::Decompression(e) => write!(f, "Failed to decompress audit data: {e}"),
23            #[cfg(feature = "serde")]
24            Error::Json(e) => write!(f, "Failed to deserialize audit data from JSON: {e}"),
25            Error::Utf8(e) => write!(f, "Invalid UTF-8 in audit data: {e}"),
26        }
27    }
28}
29
30impl std::error::Error for Error {
31    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
32        match self {
33            Error::NoAuditData => None,
34            Error::InputLimitExceeded => None,
35            Error::OutputLimitExceeded => None,
36            Error::Io(e) => Some(e),
37            Error::BinaryParsing(e) => Some(e),
38            Error::Decompression(e) => Some(e),
39            #[cfg(feature = "serde")]
40            Error::Json(e) => Some(e),
41            Error::Utf8(e) => Some(e),
42        }
43    }
44}
45
46impl From<std::io::Error> for Error {
47    fn from(e: std::io::Error) -> Self {
48        Self::Io(e)
49    }
50}
51
52impl From<auditable_extract::Error> for Error {
53    fn from(e: auditable_extract::Error) -> Self {
54        match e {
55            auditable_extract::Error::NoAuditData => Error::NoAuditData,
56            other_err => Self::BinaryParsing(other_err),
57        }
58    }
59}
60
61impl From<DecompressError> for Error {
62    fn from(e: DecompressError) -> Self {
63        match e.status {
64            TINFLStatus::HasMoreOutput => Error::OutputLimitExceeded,
65            _ => Error::Decompression(e),
66        }
67    }
68}
69
70impl From<std::string::FromUtf8Error> for Error {
71    fn from(e: std::string::FromUtf8Error) -> Self {
72        Self::Utf8(e.utf8_error())
73    }
74}
75
76#[cfg(feature = "serde")]
77impl From<serde_json::Error> for Error {
78    fn from(e: serde_json::Error) -> Self {
79        Self::Json(e)
80    }
81}
82
83/// A copy of [miniz_oxide::inflate::DecompressError].
84///
85/// We use our copy instead of the miniz_oxide type directly
86/// so that we don't have to bump semver every time `miniz_oxide` does.
87#[derive(Debug)]
88pub struct DecompressError {
89    /// Decompressor status on failure. See [TINFLStatus] for details.
90    pub status: TINFLStatus,
91    /// The currently decompressed data if any.
92    pub output: Vec<u8>,
93}
94
95impl std::fmt::Display for DecompressError {
96    fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
97        f.write_str(match self.status {
98            TINFLStatus::FailedCannotMakeProgress => "Truncated input stream",
99            TINFLStatus::BadParam => "Invalid output buffer size",
100            TINFLStatus::Adler32Mismatch => "Adler32 checksum mismatch",
101            TINFLStatus::Failed => "Invalid input data",
102            TINFLStatus::Done => unreachable!(),
103            TINFLStatus::NeedsMoreInput => "Truncated input stream",
104            TINFLStatus::HasMoreOutput => "Output size exceeded the specified limit",
105        })
106    }
107}
108
109impl std::error::Error for DecompressError {}
110
111impl DecompressError {
112    pub(crate) fn from_miniz(err: miniz_oxide::inflate::DecompressError) -> Self {
113        Self {
114            status: TINFLStatus::from_miniz(err.status),
115            output: err.output,
116        }
117    }
118}
119
120#[repr(i8)]
121#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
122pub enum TINFLStatus {
123    FailedCannotMakeProgress,
124    BadParam,
125    Adler32Mismatch,
126    Failed,
127    Done,
128    NeedsMoreInput,
129    HasMoreOutput,
130}
131
132impl TINFLStatus {
133    pub(crate) fn from_miniz(status: miniz_oxide::inflate::TINFLStatus) -> Self {
134        use miniz_oxide::inflate;
135        match status {
136            inflate::TINFLStatus::FailedCannotMakeProgress => Self::FailedCannotMakeProgress,
137            inflate::TINFLStatus::BadParam => Self::BadParam,
138            inflate::TINFLStatus::Adler32Mismatch => Self::Adler32Mismatch,
139            inflate::TINFLStatus::Failed => Self::Failed,
140            inflate::TINFLStatus::Done => Self::Done,
141            inflate::TINFLStatus::NeedsMoreInput => Self::NeedsMoreInput,
142            inflate::TINFLStatus::HasMoreOutput => Self::HasMoreOutput,
143        }
144    }
145}