filefacts
filefacts is a Rust library (and CLI) that reads a file and tells you what is in it — a dense, honest description of a file's contents, built for feeding feature extraction pipelines. Give it bytes; it identifies the format, parses it once, and returns structured facts you can fold straight into a feature vector. It was extracted from cleave so the same extraction layer can sit underneath your own tooling.
Why ML-security researchers use it
A classifier is only as good as the signals it sees, and most file-feature code
either shells out to file / objdump / openssl / tar — slow, brittle,
non-deterministic — or reimplements a parser per format and rots. filefacts is
in-process and single-pass: one walk per file, pure-Rust crypto for
Authenticode, no subprocesses on the hot path. Same bytes in, same facts out,
every time — which is what you need when a feature vector has to be reproducible
across a training run and an incident six months later.
The features it surfaces are the ones that discriminate: byte and per-section entropy, string statistics, section layout, import and symbol tables, signature validity, and extension/content mismatches — the signals that separate benign files from the things they imitate.
The shape of the output
filefacts::open(&bytes) returns a ParsedFile whose views are computed lazily
and cached, so reading one view never pays for the others:
| View | What it carries |
|---|---|
fileid |
Format identification — type, container, compression. Free; no view computed. |
values |
Residual structural fields, navigable as a JSON tree. |
text |
Byte-scan strings (ASCII + UTF-16LE) with offsets. |
literals |
Parser-extracted language string literals. |
comments |
Comments recovered from source and documents. |
metrics |
Derived numeric features — entropy, sizes, counts. |
sections |
Binary section / segment listings. |
symbols |
Unified named entities — imports, exports, functions, calls, members, identifiers — tagged by kind. |
archive_members |
Entries inside archives, recursively. |
source_ast |
tree-sitter AST facts for source files. |
errors |
Recoverable extractor diagnostics, never a panic. |
The CLI writes the same structure as JSON, one object per file:
filefacts suspect.bin # JSON facts for one file
filefacts /tmp/samples # recurse a directory
The output schema is versioned (SCHEMA_VERSION): field additions are
non-breaking, renames or semantic changes bump it — so a feature pipeline can
pin a schema and trust it.
What it parses
- Executables — PE, ELF (with DWARF), Mach-O, Java class, Python bytecode.
- Archives — zip, tar (+ gz/bz2/xz/zst), 7z, rar, deb, rpm, pkg, cab, CHM, CRX, XPI, WHL, JAR, VSIX.
- Documents — PDF, RTF, OOXML, OLE2 (legacy Office, MSI, MSG), LNK, plist, AppleScript. Authenticode is verified in-process.
- Images — JPEG, PNG, with per-channel entropy, edge density, and histogram flatness for stego detection.
- Structured — JSON, YAML, TOML, XML, pickle, Dockerfile, Makefile, systemd units, .desktop, GitHub Actions, package manifests.
- Source — tree-sitter ASTs for ~17 languages including JavaScript, TypeScript, Python, Go, Rust, Java, C, C#, Bash, PowerShell, and PHP.
Caching
Views are cached on disk as zstd-compressed bincode keyed by SHA-256, so a second pass over the same corpus is nearly free — handy when you re-extract features after tweaking a downstream model.
Add filefacts = "1.1" to Cargo.toml to use it as a library, or see
Getting started for the CLI. Source and issues
live on Codeberg.