forensicnomicon_data/
volatility.rs1pub use forensicnomicon_core::volatility::VolatilityClass;
12
13pub fn volatility_for(artifact_id: &str) -> Option<&'static crate::catalog::ArtifactDescriptor> {
18 crate::catalog::CATALOG
19 .by_id(artifact_id)
20 .filter(|d| d.volatility.is_some())
21}
22
23pub fn acquisition_order() -> Vec<&'static crate::catalog::ArtifactDescriptor> {
29 let mut entries: Vec<&crate::catalog::ArtifactDescriptor> = crate::catalog::CATALOG
30 .list()
31 .iter()
32 .filter(|d| d.volatility.is_some())
33 .collect();
34 entries.sort_by(|a, b| b.volatility.cmp(&a.volatility).then(a.id.cmp(b.id)));
35 entries
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41 use crate::catalog::CATALOG;
42
43 #[test]
44 fn volatility_for_returns_descriptor() {
45 let result: Option<&crate::catalog::ArtifactDescriptor> = volatility_for("shimcache");
46 assert!(result.is_some());
47 }
48
49 #[test]
50 fn shimcache_is_persistent() {
51 let entry = volatility_for("shimcache").expect("shimcache should be assessed");
52 assert_eq!(entry.volatility, Some(VolatilityClass::Persistent));
53 }
54
55 #[test]
56 fn shimcache_memory_is_volatile() {
57 let entry =
58 volatility_for("shimcache_memory").expect("shimcache_memory should be assessed");
59 assert_eq!(entry.volatility, Some(VolatilityClass::Volatile));
60 }
61
62 #[test]
63 fn mft_is_residual() {
64 let entry = volatility_for("mft_file").expect("mft_file should be assessed");
65 assert_eq!(entry.volatility, Some(VolatilityClass::Residual));
66 }
67
68 #[test]
69 fn evtx_security_is_rotating_buffer() {
70 let entry = volatility_for("evtx_security").expect("evtx_security should be assessed");
71 assert_eq!(entry.volatility, Some(VolatilityClass::RotatingBuffer));
72 }
73
74 #[test]
75 fn acquisition_order_volatile_first() {
76 let order = acquisition_order();
77 assert!(!order.is_empty());
78 assert_eq!(
79 order[0].volatility,
80 Some(VolatilityClass::Volatile),
81 "acquisition_order should start with Volatile class"
82 );
83 assert_eq!(
84 order.last().unwrap().volatility,
85 Some(VolatilityClass::Residual),
86 "acquisition_order should end with Residual class"
87 );
88 }
89
90 #[test]
91 fn unknown_artifact_returns_none() {
92 assert!(volatility_for("this_does_not_exist").is_none());
93 }
94
95 #[test]
96 fn table_covers_critical_triage_artifacts() {
97 let missing: Vec<&str> = CATALOG
98 .for_triage()
99 .into_iter()
100 .filter(|d| d.triage_priority == crate::catalog::TriagePriority::Critical)
101 .filter(|d| volatility_for(d.id).is_none())
102 .map(|d| d.id)
103 .collect();
104 assert!(
105 missing.is_empty(),
106 "Critical-priority artifacts missing from volatility table: {missing:?}"
107 );
108 }
109}