Model Files Cross a Code Boundary
Pickle-based checkpoints can execute constructors while loading, and malformed archives and tensor metadata can abuse paths, offsets, or allocation limits.
tensorwarden identifies the container first, disassembles pickle opcodes without loading them, validates SafeTensors headers and byte ranges, and checks archives for traversal and embedded executable payloads.
The Demo
The scanner examines three actual files. The valid SafeTensors artifact is accepted; the dangerous pickle and the traversal ZIP are quarantined with three critical findings.
Static inspection reduces loader risk. It doesn't replace sandboxing, signatures, provenance, dependency review, or runtime resource limits.
Trying to Sneak Past My Own Scanner
A security scanner deserves a real attempt to defeat it. The archive traversal check parsed member names with a POSIX-only path type, which only treats forward slashes as separators. Real zip-slip payloads routinely use backslashes, because that's how Windows extractors read them, and .pt files get loaded on Windows too.
A member named ..\..\outside.bin scanned as accepted with zero findings, and so did a drive-letter path like C:\Windows\System32\evil.dll. Both are exactly the archive-escape this scanner exists to stop, and both parsed as one harmless-looking string. Fixed by normalizing separators before parsing and checking explicitly for a drive-letter prefix.
It Was Also Quarantining Every Real Checkpoint
Auditing this page, I scanned the file a user is most likely to own: an ordinary torch.save(model.state_dict(), ...). Four critical findings and a quarantine verdict. So would every PyTorch checkpoint ever written, because GLOBAL torch._utils _rebuild_tensor_v2, REDUCE, and BINPERSID are simply how a tensor gets rebuilt on load. A scanner that rejects 100% of legitimate artifacts isn't strict. It's the kind of tool people turn off on day one, which leaves them with no scanner at all.
The fix is a narrow, fail-closed allowlist of the specific rebuild symbols a plain checkpoint needs, with everything else staying critical, including another symbol from the same module: torch._utils _rebuild_tensor_v2 is accepted and a made-up torch._utils _not_a_real_helper is not. Opcodes like REDUCE and BINPERSID are reported at the severity of the globals beside them, since alone they only call whatever a GLOBAL already pushed.
Real torch artifacts are committed as fixtures now, and the verdicts say something useful. A state dict is accepted. A full pickled model is quarantined, because saving the module object means loading it instantiates arbitrary classes, which is exactly why state_dict or SafeTensors is the thing to publish. Smuggle os.system into that same accepted checkpoint and it's quarantined again. 12 tests pass across Python 3.10 through 3.13.
Implementation Basis
SafeTensors was designed as a simple, safe alternative to pickle for tensor exchange. Read the SafeTensors documentation.
Try It
No dependencies, and nothing is ever unpickled:
git clone https://github.com/ahmeddoghri/tensorwarden && cd tensorwarden
pip install -e .
tensorwarden demo
tensorwarden scan tests/fixtures/state_dict.pt # accept
tensorwarden scan tests/fixtures/full_model.pt # quarantine
tensorwarden scan ~/Downloads/some_model.bin
tensorwarden serve # workbench on :8090