Skip to content

Validation

Every container decoder is checked against a real image produced by the tool an examiner would actually encounter (EnCase, qemu-img, hdiutil), and the NTFS fingerprinting is proven end-to-end against a genuine NTFS boot region from a public CTF disk. Run cargo test --all-features; the fixtures and assertions are linked below.

disk-forensic is validated against real images produced by real tools, not only synthetic fixtures — the doer/checker discipline: code you validated only with fixtures you hand-built inherits your own blind spots. Where a third-party decoder crate does the work, its output was byte-exact verified (boot signature and a marker planted deep in the disk) through that crate before it was wired into open().

All fixtures live in tests/data/ and the assertions are in tests/open_tests.rs and tests/cli_tests.rs. Run cargo test --all-features.

Container decoders

Each container fixture wraps the same known MBR disk (a 1 MiB image with a recognisable marker at sector 100), produced from a raw image with the tool a real examiner would encounter, then decoded back and checked.

Format Fixture How produced Decoder Asserted
Raw / dd apm.bin committed in-place scheme parses
E01 / EWF gpt_130_partitions.E01 real EnCase image (130-partition GPT) ewf decodes → Scheme::Gpt
VMDK df.vmdk qemu-img convert -O vmdk (monolithicSparse) vmdk decodes → Scheme::Mbr, virtual size exact
QCOW2 df.qcow2 qemu-img convert -O qcow2 (v3) qcow decodes → Scheme::Mbr, virtual size exact
VHDX df.vhdx qemu-img convert -O vhdx vhdx decodes → Scheme::Mbr, virtual size exact
VHD (dynamic) df-dynamic.vhd qemu-img convert -O vpc built-in (src/vhd.rs) decodes → Scheme::Mbr, CHS-rounded size
VHD (fixed) df-fixed.vhd qemu-img convert -O vpc -o subformat=fixed built-in (src/vhd.rs) decodes → Scheme::Mbr, CHS-rounded size
DMG df.dmg hdiutil convert -format UDZO dmg-core decodes → Scheme::Mbr
ISO 9660 df.iso hdiutil makehybrid -iso -joliet iso9660-forensic volume label + clean exit
AFF4 (physical) aff4::testutil::test_aff4 spec-faithful builder in the aff4 reader crate aff4 decodes → disk view, virtual size + bytes exact
AFF4 (logical) aff4::testutil::test_aff4_logical builder in the aff4 reader crate aff4 (via logical::open) container::openOpenError::LogicalContainer; logical::open lists + reads
AD1 ad1::testfix::build spec-faithful builder in the ad1-core reader crate (independent flate2 + RustCrypto ground truth) ad1-core (via logical::open) container::openOpenError::LogicalContainer; logical::open lists + reads bytes exact

The VHD virtual size is qemu's CHS-geometry-rounded value (1 079 296 bytes, not 1 MiB) — the test asserts the real value observed in the footer, a concrete example of why synthetic-only assumptions are unsafe.

Real NTFS, end to end

tests/data/ntfs.vmdk carries a genuine NTFS boot region — the real $Boot/BPB (OEM "NTFS ") extracted from MaxPowersCDrive.E01 of the public DEF CON DFIR CTF 2018 image — re-based onto a compact 64 MiB disk with real Windows MBR boot code and wrapped in a sparse VMDK (192 KiB). Only the BPB hidden_sectors/total_sectors were adjusted so the geometry stays self-consistent with the compact layout.

vmdk_with_real_ntfs_fingerprints_the_partition asserts the whole stack on real data: VMDK decode → MBR parse → the partition fingerprints as DetectedFs::Ntfs (filesystem identified from its real VBR, not the partition type byte alone).

This validates filesystem fingerprinting at the partition level. disk-forensic does not parse NTFS internals ($MFT, etc.) — that is a filesystem analyzer's job — so the fixture is a boot-region slice, not a fully mountable volume.

Report normalization

Every layer is normalized into the shared forensicnomicon::report model (Report { findings, provenance, timeline }), categorized with the canonical Category::from_code, so disk4n6 renders one uniform view.

Layer Findings Provenance Timeline Finding evidence offset
MBR — (no datable events) ✅ (offset)
GPT — (no datable events) ❌ — analyzer exposes no uniform offset
APM — (no datable events) ❌ — analyzer exposes no uniform offset
ISO 9660 ✅ (full volume + Rock Ridge authoring intel) ✅ (PVD create/modify + authoring window) ❌ — offsets are per-AnomalyKind

tests/iso_normalize_tests.rs checks the ISO provenance completeness and the reconstructed timeline against the real df.iso volume (system/mastering id, session count, Rock Ridge owners, and the create/authoring-window events), asserting timeline events by structure — present, attributed, and datable (when.is_some()) — never by literal timestamp value.

Known boundary: uniform per-finding byte-offset evidence is only produced for MBR, because only mbr-forensic exposes a top-level Anomaly.offset. GPT/APM/ISO bury offsets inside individual AnomalyKind variants; surfacing them uniformly requires those analyzer crates to expose an offset, not a per-variant match here.

Safety properties

  • #![forbid(unsafe_code)] (enforced via [lints.rust]).
  • Fuzz target (cargo fuzz, fuzz/fuzz_targets/analyse_disk.rs) exercises analyse_disk against crafted/corrupted input.
  • Corrupt or unsupported-variant containers fail with a typed OpenError::Decode rather than producing silently wrong output; a legacy QCOW1 and a VHD differencing disk are rejected explicitly.