Audit a ZIP for tampering, and read every common codec — in pure Rust, with zero C-FFI dependencies.
// Surface where an archive's central directory disagrees with its local headers —
// the classic post-hoc-edit signal a happy-path reader silently trusts.
for anomaly in zip_forensic::audit_path("evidence.zip".as_ref())? {
println!("[{:?}] {}: {}", anomaly.severity, anomaly.code, anomaly.note);
}
// [High] ZIP-CD-LFH-MISMATCH: entry 3 (report.docx): central-directory crc32
// (0x1a2b3c4d) disagrees with the local file header (0x00000000) — consistent
// with a post-hoc edit of one copyThat's it — point it at a zip and read graded findings. Each is an observation
("consistent with"), never a verdict, and converts to a
forensicnomicon Finding.
zip-forensic-core parses the container and decodes every common method with only
pure-Rust crates — the three C libraries the popular zip crate pulls
(bzip2-sys, zstd-sys, lzma-sys) are gone:
use std::io::Read;
let mut archive = zip_core::ZipArchive::new(std::fs::File::open("eg.zip")?)?;
let mut entry = archive.by_name("data.bin")?; // Stored/Deflate/Deflate64/Bzip2/Zstd/LZMA/XZ
let mut bytes = Vec::new();
entry.read_to_end(&mut bytes)?; // CRC-32 verified on EOF; fails loud on mismatch$ cargo tree -p zip-forensic-core -e normal | grep -- -sys
$ # empty — no C-FFI in the runtime treeEncrypted entries decrypt with a password — traditional ZipCrypto and WinZip AES (128/192/256), the latter on audited RustCrypto with HMAC verification:
let mut entry = archive.by_name_decrypt("secret.bin", b"password")?;
// plain by_name() refuses an encrypted entry — secure by defaultA forensic image stored in a ZIP at ~0% compression is, at the deflate level, a
run of byte-aligned stored blocks. zip-forensic-core indexes them so any offset is
addressable directly, with no full inflation and no temp spill:
let entry = zip_core::open_entry("case.zip".as_ref(), "image.E01")?;
let mut buf = vec![0u8; 4096];
entry.read_at(&mut buf, 1_000_000_003)?; // positioned read, lock-free, no decompression from start[dependencies]
zip-forensic-core = "0.2" # the reader
zip-forensic = "0.2" # the auditor#![forbid(unsafe_code)], panic-free on untrusted input (bounds-checked reads,
entry-count and decompression-bomb caps), enclosed_name() refuses
path-traversal names, and three cargo-fuzz targets assert the parser, decoder,
and audit pipeline never panic. Correctness is established against independent
oracles — see the validation notes.
Privacy Policy · Terms of Service · © 2026 Security Ronin Ltd