Validation
forensic-vfs is a contract crate: it defines traits and one value-producing
codec (the Locator canonical URI). Correctness is established by the evidence
below, tiered honestly by what confirms it.
The Locator URI codec — property-checked + fuzzed
The URI form is our own format, so no external tool decodes it — there is no independent oracle to differential against. Correctness is therefore defined by a self-checking property, not a hand-authored answer key:
Round-trip invariant. For every
Locator,from_uri(to_uri(spec)) == spec, byte-for-byte.
This is stronger than a fixture-with-expected-output (the LZNT1 trap): the test does not assert "this input maps to that hand-written string"; it asserts the encoder and decoder are mutual inverses, so a bug in either breaks the equality. The property is checked two ways:
- Unit + integration tests exercise it across a rich chain, hostile path
bytes (
|,%,/, space,0xFF,0x00), every layer kind, everyFileIdandStreamIdvariant, empty paths, and theBoth-with-empty-path branch. - Fuzzing (
fuzz/fuzz_pathspec) drivesfrom_uriover arbitrary bytes and asserts the round-trip on everything it accepts — 15.7M executions, no panic and no round-trip violation.from_urirejects malformed input with a loud error carrying the offending string, never a panic.
This is the legitimate use of self-authored tests: a detection/serialization rule whose correctness is defined by the rule plus a self-checking property, with no external oracle possible. When the engine lands, a spec pasted from a report and re-resolved on real evidence will be the end-to-end confirmation.
Panic-free bounded readers — fuzzed
The be_*/le_* bounded integer readers are the panic-free foundation every
reader parses offsets through. fuzz/fuzz_read drives them over arbitrary
(data, offset) pairs including out-of-range and usize::MAX offsets —
20.2M executions, no panic — confirming the "0 on out-of-range, never panic"
contract.
Object-safety of every trait
The design rests on Arc<dyn ImageSource>, Arc<dyn FileSystem>,
Box<dyn VolumeSystem>, and Box<dyn EncryptionLayer> composing at runtime. A reader
double for each trait is driven through its trait object in
tests/contracts.rs; if any trait lost object-safety the crate would fail to
compile. The default forensic surface (data_streams, hardlinks, slack,
findings) and the credential/NeedCredentials path are exercised there too.
Coverage
100% production line coverage (cargo llvm-cov --all-features), enforced in CI by
a DA:n,0 gate that fails on any uncovered production line. Two lines carry
// cov:unreachable: the get_mut(..want) fallbacks in SubRange and
SourceCursor, provably unreachable because want <= buf.len() by the preceding
min(), kept as defence-in-depth.