#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::collections::BTreeSet;
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Recon {
pub confirmed: Vec<char>,
pub false_positive: Vec<char>,
pub oracle_only: Vec<char>,
}
pub fn reconcile(ours: &[Option<char>], oracle: &[char]) -> Recon {
let ours: BTreeSet<char> = ours.iter().filter_map(|d| *d).collect();
let oracle: BTreeSet<char> = oracle.iter().copied().collect();
Recon {
confirmed: ours.intersection(&oracle).copied().collect(),
false_positive: ours.difference(&oracle).copied().collect(),
oracle_only: oracle.difference(&ours).copied().collect(),
}
}
#[test]
fn reconcile_flags_a_drive_present_in_both_as_confirmed() {
let r = reconcile(&[Some('Z')], &['Z']);
assert_eq!(r.confirmed, vec!['Z']);
assert!(r.false_positive.is_empty());
assert!(r.oracle_only.is_empty());
}
#[test]
fn reconcile_flags_our_drive_absent_from_oracle_as_a_false_positive() {
let r = reconcile(&[Some('E')], &['Z']);
assert_eq!(r.false_positive, vec!['E']);
assert!(r.confirmed.is_empty());
assert_eq!(r.oracle_only, vec!['Z']);
}
#[test]
fn reconcile_reports_oracle_only_drives_as_a_completeness_gap() {
let r = reconcile(&[Some('Z')], &['Z', 'E']);
assert_eq!(r.confirmed, vec!['Z']);
assert!(r.false_positive.is_empty());
assert_eq!(r.oracle_only, vec!['E']);
}
#[test]
fn reconcile_ignores_none_entries_and_deduplicates() {
let r = reconcile(&[None, Some('Z'), Some('Z'), None], &['Z', 'Z']);
assert_eq!(r.confirmed, vec!['Z']);
assert!(r.false_positive.is_empty());
assert!(r.oracle_only.is_empty());
}
#[test]
fn reconcile_of_two_empty_sets_is_clean() {
assert_eq!(reconcile(&[], &[]), Recon::default());
}
#[test]
fn bagmru_walk_matches_the_regipy_pyfwsi_oracle() {
let Ok(hive_path) = std::env::var("SHELLBAG_TEST_HIVE") else {
eprintln!("SKIP: set SHELLBAG_TEST_HIVE to a real NTUSER.DAT / UsrClass.dat");
return;
};
let Some(oracle_drives) = run_oracle(&hive_path) else {
eprintln!(
"SKIP: python3 with regipy + pyfwsi unavailable (pip install regipy[full]); \
cannot run the independent oracle"
);
return;
};
let hive = winreg_core::hive::Hive::from_path(std::path::Path::new(&hive_path))
.expect("valid REGF hive");
let ours = peripheral_core::shellbag::parse_shellbags(&hive, "shellbag-oracle-diff");
let our_drives: Vec<Option<char>> = ours.iter().map(|e| e.drive_letter).collect();
let r = reconcile(&our_drives, &oracle_drives);
assert!(
r.false_positive.is_empty(),
"our BagMRU walk reported drive letters the oracle does not decode: {:?}\n \
ours={our_drives:?}\n oracle={oracle_drives:?}",
r.false_positive
);
for d in &r.confirmed {
assert!(
ours.iter().any(|e| {
e.drive_letter == Some(*d) && e.path.to_ascii_uppercase().contains(&format!("{d}:"))
}),
"confirmed drive {d}: has no matching `{d}:` token in our reconstructed path"
);
}
if !r.oracle_only.is_empty() {
eprintln!(
"KNOWN GAP (docs/validation.md): oracle decoded drive letters our walk missed: {:?}",
r.oracle_only
);
}
eprintln!(
"differential OK — confirmed drives {:?}; oracle total drive refs {oracle_drives:?}",
r.confirmed
);
}
fn run_oracle(hive_path: &str) -> Option<Vec<char>> {
let script = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/oracle/shellbags_oracle.py"
);
let output = std::process::Command::new("python3")
.arg(script)
.arg(hive_path)
.output()
.ok()?;
if !output.status.success() {
eprintln!(
"oracle failed: {}",
String::from_utf8_lossy(&output.stderr).trim()
);
return None;
}
let stdout = String::from_utf8_lossy(&output.stdout);
let drives: BTreeSet<char> = stdout
.lines()
.filter_map(|line| line.split('\t').next())
.filter_map(|d| d.chars().next())
.filter(char::is_ascii_alphabetic)
.map(|c| c.to_ascii_uppercase())
.collect();
Some(drives.into_iter().collect())
}