threatflux_binary_analysis/
error.rs1use thiserror::Error;
4
5pub type Result<T> = std::result::Result<T, BinaryError>;
7
8#[derive(Error, Debug)]
10pub enum BinaryError {
11 #[error("Failed to parse binary format: {0}")]
13 ParseError(String),
14
15 #[error("Unsupported binary format: {0}")]
17 UnsupportedFormat(String),
18
19 #[error("Unsupported architecture: {0}")]
21 UnsupportedArchitecture(String),
22
23 #[error("Invalid binary data: {0}")]
25 InvalidData(String),
26
27 #[error("Disassembly failed: {0}")]
29 DisassemblyError(String),
30
31 #[error("Control flow analysis failed: {0}")]
33 ControlFlowError(String),
34
35 #[error("Symbol resolution failed: {0}")]
37 SymbolError(String),
38
39 #[error("Entropy analysis failed: {0}")]
41 EntropyError(String),
42
43 #[error("I/O error: {0}")]
45 IoError(#[from] std::io::Error),
46
47 #[error("Memory mapping error: {0}")]
49 MemoryMapError(String),
50
51 #[error("Configuration error: {0}")]
53 ConfigError(String),
54
55 #[error("Feature not available: {0} (try enabling the corresponding feature flag)")]
57 FeatureNotAvailable(String),
58
59 #[error("Internal error: {0}")]
61 Internal(String),
62}
63
64impl From<goblin::error::Error> for BinaryError {
65 fn from(err: goblin::error::Error) -> Self {
66 BinaryError::ParseError(err.to_string())
67 }
68}
69
70#[cfg(feature = "disasm-capstone")]
71impl From<capstone::Error> for BinaryError {
72 fn from(err: capstone::Error) -> Self {
73 BinaryError::DisassemblyError(err.to_string())
74 }
75}
76
77#[cfg(feature = "wasmparser")]
78impl From<wasmparser::BinaryReaderError> for BinaryError {
79 fn from(err: wasmparser::BinaryReaderError) -> Self {
80 BinaryError::ParseError(format!("WASM parse error: {}", err))
81 }
82}
83
84impl BinaryError {
85 pub fn parse<S: Into<String>>(msg: S) -> Self {
87 Self::ParseError(msg.into())
88 }
89
90 pub fn unsupported_format<S: Into<String>>(format: S) -> Self {
92 Self::UnsupportedFormat(format.into())
93 }
94
95 pub fn unsupported_arch<S: Into<String>>(arch: S) -> Self {
97 Self::UnsupportedArchitecture(arch.into())
98 }
99
100 pub fn invalid_data<S: Into<String>>(msg: S) -> Self {
102 Self::InvalidData(msg.into())
103 }
104
105 pub fn disassembly<S: Into<String>>(msg: S) -> Self {
107 Self::DisassemblyError(msg.into())
108 }
109
110 pub fn control_flow<S: Into<String>>(msg: S) -> Self {
112 Self::ControlFlowError(msg.into())
113 }
114
115 pub fn symbol<S: Into<String>>(msg: S) -> Self {
117 Self::SymbolError(msg.into())
118 }
119
120 pub fn entropy<S: Into<String>>(msg: S) -> Self {
122 Self::EntropyError(msg.into())
123 }
124
125 pub fn memory_map<S: Into<String>>(msg: S) -> Self {
127 Self::MemoryMapError(msg.into())
128 }
129
130 pub fn config<S: Into<String>>(msg: S) -> Self {
132 Self::ConfigError(msg.into())
133 }
134
135 pub fn feature_not_available<S: Into<String>>(feature: S) -> Self {
137 Self::FeatureNotAvailable(feature.into())
138 }
139
140 pub fn internal<S: Into<String>>(msg: S) -> Self {
142 Self::Internal(msg.into())
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use std::error::Error;
150 use std::io;
151
152 #[test]
153 fn test_binary_error_parse_creation() {
154 let error = BinaryError::parse("test message");
155 assert!(matches!(error, BinaryError::ParseError(_)));
156 assert_eq!(
157 error.to_string(),
158 "Failed to parse binary format: test message"
159 );
160 }
161
162 #[test]
163 fn test_binary_error_parse_creation_string() {
164 let msg = String::from("dynamic string");
165 let error = BinaryError::parse(msg);
166 assert!(matches!(error, BinaryError::ParseError(_)));
167 assert_eq!(
168 error.to_string(),
169 "Failed to parse binary format: dynamic string"
170 );
171 }
172
173 #[test]
174 fn test_binary_error_unsupported_format_creation() {
175 let error = BinaryError::unsupported_format("CUSTOM");
176 assert!(matches!(error, BinaryError::UnsupportedFormat(_)));
177 assert_eq!(error.to_string(), "Unsupported binary format: CUSTOM");
178 }
179
180 #[test]
181 fn test_binary_error_unsupported_arch_creation() {
182 let error = BinaryError::unsupported_arch("ARM32");
183 assert!(matches!(error, BinaryError::UnsupportedArchitecture(_)));
184 assert_eq!(error.to_string(), "Unsupported architecture: ARM32");
185 }
186
187 #[test]
188 fn test_binary_error_invalid_data_creation() {
189 let error = BinaryError::invalid_data("corrupt header");
190 assert!(matches!(error, BinaryError::InvalidData(_)));
191 assert_eq!(error.to_string(), "Invalid binary data: corrupt header");
192 }
193
194 #[test]
195 fn test_binary_error_disassembly_creation() {
196 let error = BinaryError::disassembly("unable to decode instruction");
197 assert!(matches!(error, BinaryError::DisassemblyError(_)));
198 assert_eq!(
199 error.to_string(),
200 "Disassembly failed: unable to decode instruction"
201 );
202 }
203
204 #[test]
205 fn test_binary_error_control_flow_creation() {
206 let error = BinaryError::control_flow("circular dependency detected");
207 assert!(matches!(error, BinaryError::ControlFlowError(_)));
208 assert_eq!(
209 error.to_string(),
210 "Control flow analysis failed: circular dependency detected"
211 );
212 }
213
214 #[test]
215 fn test_binary_error_symbol_creation() {
216 let error = BinaryError::symbol("undefined reference");
217 assert!(matches!(error, BinaryError::SymbolError(_)));
218 assert_eq!(
219 error.to_string(),
220 "Symbol resolution failed: undefined reference"
221 );
222 }
223
224 #[test]
225 fn test_binary_error_entropy_creation() {
226 let error = BinaryError::entropy("insufficient data");
227 assert!(matches!(error, BinaryError::EntropyError(_)));
228 assert_eq!(
229 error.to_string(),
230 "Entropy analysis failed: insufficient data"
231 );
232 }
233
234 #[test]
235 fn test_binary_error_memory_map_creation() {
236 let error = BinaryError::memory_map("permission denied");
237 assert!(matches!(error, BinaryError::MemoryMapError(_)));
238 assert_eq!(error.to_string(), "Memory mapping error: permission denied");
239 }
240
241 #[test]
242 fn test_binary_error_config_creation() {
243 let error = BinaryError::config("invalid configuration value");
244 assert!(matches!(error, BinaryError::ConfigError(_)));
245 assert_eq!(
246 error.to_string(),
247 "Configuration error: invalid configuration value"
248 );
249 }
250
251 #[test]
252 fn test_binary_error_feature_not_available_creation() {
253 let error = BinaryError::feature_not_available("disasm-capstone");
254 assert!(matches!(error, BinaryError::FeatureNotAvailable(_)));
255 assert_eq!(
256 error.to_string(),
257 "Feature not available: disasm-capstone (try enabling the corresponding feature flag)"
258 );
259 }
260
261 #[test]
262 fn test_binary_error_internal_creation() {
263 let error = BinaryError::internal("unexpected state");
264 assert!(matches!(error, BinaryError::Internal(_)));
265 assert_eq!(error.to_string(), "Internal error: unexpected state");
266 }
267
268 #[test]
269 fn test_binary_error_display_all_variants() {
270 let test_cases = vec![
271 (
272 BinaryError::ParseError("parse issue".to_string()),
273 "Failed to parse binary format: parse issue",
274 ),
275 (
276 BinaryError::UnsupportedFormat("UNKNOWN".to_string()),
277 "Unsupported binary format: UNKNOWN",
278 ),
279 (
280 BinaryError::UnsupportedArchitecture("SPARC".to_string()),
281 "Unsupported architecture: SPARC",
282 ),
283 (
284 BinaryError::InvalidData("malformed".to_string()),
285 "Invalid binary data: malformed",
286 ),
287 (
288 BinaryError::DisassemblyError("decode failed".to_string()),
289 "Disassembly failed: decode failed",
290 ),
291 (
292 BinaryError::ControlFlowError("analysis failed".to_string()),
293 "Control flow analysis failed: analysis failed",
294 ),
295 (
296 BinaryError::SymbolError("not found".to_string()),
297 "Symbol resolution failed: not found",
298 ),
299 (
300 BinaryError::EntropyError("low entropy".to_string()),
301 "Entropy analysis failed: low entropy",
302 ),
303 (
304 BinaryError::MemoryMapError("map failed".to_string()),
305 "Memory mapping error: map failed",
306 ),
307 (
308 BinaryError::ConfigError("bad config".to_string()),
309 "Configuration error: bad config",
310 ),
311 (
312 BinaryError::FeatureNotAvailable("test-feature".to_string()),
313 "Feature not available: test-feature (try enabling the corresponding feature flag)",
314 ),
315 (
316 BinaryError::Internal("bug detected".to_string()),
317 "Internal error: bug detected",
318 ),
319 ];
320
321 for (error, expected) in test_cases {
322 assert_eq!(error.to_string(), expected);
323 }
324 }
325
326 #[test]
327 fn test_binary_error_debug_formatting() {
328 let error = BinaryError::ParseError("test".to_string());
329 let debug_str = format!("{:?}", error);
330 assert!(debug_str.contains("ParseError"));
331 assert!(debug_str.contains("test"));
332 }
333
334 #[test]
335 fn test_from_io_error() {
336 let io_err = io::Error::new(io::ErrorKind::NotFound, "file not found");
337 let binary_err: BinaryError = io_err.into();
338
339 assert!(matches!(binary_err, BinaryError::IoError(_)));
340 assert!(binary_err.to_string().contains("I/O error"));
341 assert!(binary_err.to_string().contains("file not found"));
342 }
343
344 #[test]
345 fn test_from_goblin_error() {
346 let goblin_err = goblin::error::Error::Malformed("invalid header".to_string());
347 let binary_err: BinaryError = goblin_err.into();
348
349 assert!(matches!(binary_err, BinaryError::ParseError(_)));
350 assert!(binary_err
351 .to_string()
352 .contains("Failed to parse binary format"));
353 assert!(binary_err.to_string().contains("invalid header"));
354 }
355
356 #[test]
357 fn test_error_source_chain_io() {
358 let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "access denied");
359 let binary_err: BinaryError = io_err.into();
360
361 assert!(binary_err.source().is_some());
363 let source = binary_err.source().unwrap();
364 assert!(source.to_string().contains("access denied"));
365 }
366
367 #[test]
368 fn test_error_source_chain_root() {
369 let parse_err = BinaryError::ParseError("test".to_string());
370 assert!(parse_err.source().is_none());
371 }
372
373 #[test]
374 fn test_result_type_alias() {
375 fn test_function() -> Result<i32> {
376 Ok(42)
377 }
378
379 let result = test_function();
380 assert!(result.is_ok());
381 assert_eq!(result.unwrap(), 42);
382 }
383
384 #[test]
385 fn test_result_type_alias_error() {
386 fn test_function() -> Result<i32> {
387 Err(BinaryError::parse("test error"))
388 }
389
390 let result = test_function();
391 assert!(result.is_err());
392 let error = result.unwrap_err();
393 assert!(matches!(error, BinaryError::ParseError(_)));
394 }
395
396 #[cfg(feature = "disasm-capstone")]
398 #[test]
399 fn test_from_capstone_error() {
400 use capstone::Error as CapstoneError;
401
402 let capstone_err = CapstoneError::InvalidHandle;
403 let binary_err: BinaryError = capstone_err.into();
404
405 assert!(matches!(binary_err, BinaryError::DisassemblyError(_)));
406 assert!(binary_err.to_string().contains("Disassembly failed"));
407 }
408
409 #[cfg(feature = "wasmparser")]
410 #[test]
411 fn test_from_wasmparser_error() {
412 use wasmparser::BinaryReader;
413
414 let invalid_wasm = vec![0x00, 0x61, 0x73]; let mut reader = BinaryReader::new(&invalid_wasm, 0);
417
418 let wasm_result = reader.read_u32();
420 assert!(wasm_result.is_err());
421
422 let wasm_err = wasm_result.unwrap_err();
423 let binary_err: BinaryError = wasm_err.into();
424
425 assert!(matches!(binary_err, BinaryError::ParseError(_)));
426 assert!(binary_err
427 .to_string()
428 .contains("Failed to parse binary format"));
429 assert!(binary_err.to_string().contains("WASM parse error"));
430 }
431
432 #[test]
434 fn test_error_propagation_chain() {
435 fn inner_function() -> std::io::Result<()> {
436 Err(io::Error::new(
437 io::ErrorKind::UnexpectedEof,
438 "truncated file",
439 ))
440 }
441
442 fn outer_function() -> Result<()> {
443 inner_function()?;
444 Ok(())
445 }
446
447 let result = outer_function();
448 assert!(result.is_err());
449 let error = result.unwrap_err();
450 assert!(matches!(error, BinaryError::IoError(_)));
451 assert!(error.source().is_some());
452 }
453
454 #[test]
455 fn test_error_equality_and_comparison() {
456 let err1 = BinaryError::ParseError("test".to_string());
457 let err2 = BinaryError::ParseError("test".to_string());
458 let err3 = BinaryError::ParseError("different".to_string());
459 let err4 = BinaryError::InvalidData("test".to_string());
460
461 assert_eq!(err1.to_string(), err2.to_string());
463 assert_ne!(err1.to_string(), err3.to_string());
464 assert_ne!(err1.to_string(), err4.to_string());
465 }
466
467 #[test]
468 fn test_error_send_sync() {
469 fn assert_send<T: Send>() {}
470 fn assert_sync<T: Sync>() {}
471
472 assert_send::<BinaryError>();
473 assert_sync::<BinaryError>();
474 }
475
476 #[test]
477 fn test_comprehensive_constructor_parameters() {
478 let _e1 = BinaryError::parse("str literal");
480 let _e2 = BinaryError::parse(String::from("owned string"));
481 let _e3 = BinaryError::parse("str reference".to_string());
482
483 let _e4 = BinaryError::internal("");
485 assert_eq!(_e4.to_string(), "Internal error: ");
486
487 let _e5 = BinaryError::config("配置错误");
489 assert!(_e5.to_string().contains("配置错误"));
490 }
491
492 #[test]
493 fn test_error_in_threaded_context() {
494 use std::thread;
495
496 let handle = thread::spawn(|| {
497 let error = BinaryError::parse("thread error");
498 error.to_string()
499 });
500
501 let result = handle.join().unwrap();
502 assert_eq!(result, "Failed to parse binary format: thread error");
503 }
504}