#![allow(clippy::unwrap_used, clippy::expect_used)]
use ewf_forensic::{EwfIntegrityPath, Severity};
use std::path::{Path, PathBuf};
use std::process::Command;
#[derive(Debug)]
struct DiffResult {
ewfverify_exit: i32,
ewfverify_output: String,
ewf_anomalies: Vec<String>,
ewf_errors: Vec<String>,
ewf_criticals: Vec<String>,
}
impl DiffResult {
fn ewfverify_clean(&self) -> bool {
self.ewfverify_exit == 0
}
fn ewf_clean(&self) -> bool {
self.ewf_errors.is_empty() && self.ewf_criticals.is_empty()
}
fn has_anomaly_containing(&self, needle: &str) -> bool {
self.ewf_anomalies.iter().any(|a| a.contains(needle))
}
}
fn run_differential(e01_path: &Path) -> Option<DiffResult> {
let ev = match Command::new("ewfverify").arg("-q").arg(e01_path).output() {
Ok(o) => o,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return None,
Err(e) => panic!("ewfverify failed to launch: {e}"),
};
let exit = ev.status.code().unwrap_or(-1);
let output = format!(
"{}{}",
String::from_utf8_lossy(&ev.stdout),
String::from_utf8_lossy(&ev.stderr)
);
let findings = EwfIntegrityPath::from_path(e01_path)
.analyse()
.expect("ewf-forensic I/O must not fail");
let ewf_errors: Vec<String> = findings
.iter()
.filter(|a| a.severity() == Severity::High)
.map(|a| format!("{a}"))
.collect();
let ewf_criticals: Vec<String> = findings
.iter()
.filter(|a| a.severity() == Severity::Critical)
.map(|a| format!("{a}"))
.collect();
let ewf_anomalies: Vec<String> = findings.iter().map(|a| format!("{a}")).collect();
Some(DiffResult {
ewfverify_exit: exit,
ewfverify_output: output,
ewf_anomalies,
ewf_errors,
ewf_criticals,
})
}
fn fixture(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/data")
.join(name)
}
#[test]
fn bogus_e01_both_report_invalid() {
let Some(r) = run_differential(&fixture("bogus.E01")) else {
return;
};
assert!(
!r.ewfverify_clean(),
"ewfverify must reject bogus.E01 (0-byte file); exit={}; output={}",
r.ewfverify_exit,
r.ewfverify_output
);
assert!(
!r.ewf_clean(),
"ewf-forensic must not report clean for bogus.E01 (0-byte file); \
anomalies={:?}",
r.ewf_anomalies
);
let has_chain_error = r.has_anomaly_containing("section chain")
|| r.has_anomaly_containing("chain broken")
|| !r.ewf_criticals.is_empty();
assert!(
has_chain_error,
"ewf-forensic must report a section-chain CRITICAL for bogus.E01; \
anomalies={:?}",
r.ewf_anomalies
);
}
#[test]
fn bogus_e02_both_report_invalid() {
let Some(r) = run_differential(&fixture("bogus.E02")) else {
return;
};
assert!(
!r.ewfverify_clean(),
"ewfverify must reject bogus.E02 (0-byte file); exit={}; output={}",
r.ewfverify_exit,
r.ewfverify_output
);
assert!(
!r.ewf_clean(),
"ewf-forensic must not report clean for bogus.E02 (0-byte file); \
anomalies={:?}",
r.ewf_anomalies
);
let has_chain_error = r.has_anomaly_containing("section chain")
|| r.has_anomaly_containing("chain broken")
|| !r.ewf_criticals.is_empty();
assert!(
has_chain_error,
"ewf-forensic must report a section-chain CRITICAL for bogus.E02; \
anomalies={:?}",
r.ewf_anomalies
);
}
#[test]
fn gpt_130_partitions_both_clean() {
let Some(r) = run_differential(&fixture("gpt_130_partitions.E01")) else {
return;
};
assert!(
r.ewfverify_clean(),
"ewfverify must report SUCCESS for gpt_130_partitions; \
exit={}; output={}",
r.ewfverify_exit,
r.ewfverify_output
);
assert!(
r.ewf_clean(),
"ewf-forensic must report no errors for gpt_130_partitions; \
errors={:?}; criticals={:?}",
r.ewf_errors,
r.ewf_criticals
);
assert!(
r.ewf_anomalies.is_empty(),
"ewf-forensic must report zero anomalies (all severities) for \
gpt_130_partitions; got={:?}",
r.ewf_anomalies
);
}