agentsec · Tools · pickleprobe

Offline · stdlib-only · MIT

pickleprobe: detect code-execution risk in pickle model files in AI agents


Scans pickle-based model files for code-execution risk without loading them.

What it detects

Scans pickle-based model files for code-execution risk without loading them.

Loading a pickle (torch.load, joblib.load, pickle.load, np.load(allow_pickle=True)) can run any Python the file asks for. pickleprobe walks the pickle opcodes with the standard library's pickletools (it never unpickles) and reports which Python callables a load would import, so you can check a model downloaded from a hub or handed over by a teammate before anything touches it.

Supported inputs: raw pickles (.pkl, .pickle, .joblib, .bin, .dat, ...), PyTorch zip checkpoints (.pt, .pth, .ckpt, .bin), NumPy .npy / .npz, and Keras .keras archives. Files are recognised by their bytes, not only their extension, so a pickle renamed to model.safetensors is caught.

Rules

RuleSeverityFinding
PP01criticalDangerous import: os/posix/nt, subprocess, builtins.eval/exec/getattr/open/__import__, socket, runpy, importlib, ctypes, marshal, network clients, operator.attrgetter, functools.partial, types.CodeType, nested pickle/torch.load and more
PP02mediumImport that is not on the safe allowlist (tensor rebuilders, OrderedDict, NumPy reconstructors, plain builtins). Review it, or allow it with --allow
PP03mediumPickle stream, .npy header, zip or Keras config could not be fully parsed. Scanners that give up early are a known bypass, so this is not ignored
PP04highPickle data in a file named like a safe format (.safetensors, .json, .png, .onnx, .gguf, ...); medium for a zip with such a name
PP05mediumNumPy object array, which needs allow_pickle=True and unpickles its payload (the payload is scanned too)
PP06highArchive member with an absolute path or ..
PP07highKeras Lambda layer (serialized Python code); medium for other serialized functions
PP08infoThe file uses a pickle-based format at all. Prefer safetensors

Usage

cd tools/pickleprobe
python3 examples/make_examples.py           # writes FAKE examples/risky and examples/clean
python3 pickleprobe.py examples/risky       # 17 findings, exit 1
python3 pickleprobe.py examples/clean       # info only, exit 0
python3 pickleprobe.py ~/models --json --min-severity medium
python3 pickleprobe.py model.pt --allow 'mylib.models.*' --sarif > pickleprobe.sarif

After pip install of the kit, the same options work as agentsec pickleprobe …. Every tool also accepts --help and almost every tool accepts --json.

Example output

See the examples under tools/pickleprobe/examples/ in the repository.

SARIF and CI

agentsec pickleprobe path/to/input --sarif > pickleprobe.sarif

# GitHub Actions (composite action ships with the repo)
- uses: dunkleschitz/agentsec@v0
  with:
    tools: pickleprobe
    paths: path/to/input
    fail-on: high

SARIF 2.1.0 can be uploaded to GitHub code scanning. The composite action writes per-tool reports under agentsec-sarif/ by default.

OWASP mapping

Heuristic mapping of what this tool helps a reviewer look for. Not an official OWASP endorsement, and not complete coverage of any item. Sources: OWASP Top 10 for LLM Applications 2025 and OWASP Top 10 for Agentic Applications 2026.

Known limits

be harmful through an allowed callable used in an unexpected way, or a library class whose own __setstate__ / __reduce__ does something dangerous. Treat PP02 seriously.

obfuscated pickles that build the names some other way are reported as unresolved (PP02).

legitimate models (scikit-learn estimators, custom classes) will show PP02 until you --allow their modules.

HDF5 .h5 Keras files, TorchScript code, ONNX/GGUF custom ops, and files over 512 MB inside zip archives (skipped).

torch.load(..., weights_only=True).

Full source, tests, and fixtures: tools/pickleprobe/ in the repository. Install the kit with pip install git+https://github.com/dunkleschitz/agentsec.git.