Views
No views yet
.npy files are silently skipped when scanning a directory or a HuggingFace repomodeldir/weights_obj.npy with
numpy.load(..., allow_pickle=True) unless you want the (harmless) marker to run.picklescan supports .npy (scan_bytes() dispatches to scan_numpy()), but the two
walkers that iterate over a set of files — scan_directory_path() (--path <dir>) and
scan_huggingface_model() (--huggingface <repo>) — filter candidates against
_zip_file_extensions ∪ _pickle_file_extensions ∪ _pytorch_file_extensions and
never consult _numpy_file_extensions. A .npy is dropped with continue
before it ever reaches scan_bytes().Infected files: 1 / exit 1
when passed directly, and Scanned files: 0 / Infected files: 0 / exit 0 when
the directory containing it is scanned. There is no warning, no scan_err, no non-zero
exit code — a CI job running picklescan --path model_dir/ && deploy passes green.SECURITY.md explicitly asks to be reported. It is not claimed
as remote code execution.| path | size | what |
|---|---|---|
modeldir/weights_obj.npy | 409 B | .npy with dtype=object; the embedded pickle is a REDUCE to builtins.exec that writes a sentinel text file in the current directory. Harmless, local, no network. |
ctrl_npz/weights_obj.npz | 549 B | negative control — identical payload as .npz. picklescan does catch this one in directory mode, because .npz is in _zip_file_extensions. |
gen_poc.py | — | regenerates both files from scratch |
REPRO.sh | — | runs the four measurements below |
1python3 -m venv venv && ./venv/bin/pip install picklescan==1.0.5 numpy
2./venv/bin/python gen_poc.py
3bash REPRO.sh| run | command | result |
|---|---|---|
| 1 | picklescan --path modeldir/weights_obj.npy | dangerous import 'builtins exec' FOUND · Scanned 1 / Infected 1 · exit 1 |
| 2 | picklescan --path modeldir (same file) | Scanned 0 / Infected 0 · exit 0 ← the bug |
| 3 | np.load('modeldir/weights_obj.npy', allow_pickle=True) | sentinel file appears — the payload the scanner called clean is live |
| 4 | picklescan --path ctrl_npz (same payload as .npz) | dangerous import 'builtins exec' FOUND · Infected 1 · exit 1 |
.npz appears in
_zip_file_extensions while .npy appears only in _numpy_file_extensions, which
the walkers never read.allow_pickle=True, which is not numpy's default
(allow_pickle=False since numpy 1.16.3). It is included to show the payload is real,
not to claim that any particular downstream project loads untrusted .npy that way.--huggingface <repo> path was exercised offline with picklescan.scanner._http_get
monkey-patched (no network traffic): the .npy gives scanned_files=0, infected_files=0,
and its URL is never even fetched; the same repo serving the .npz gives
scanned_files=1, infected_files=1.exec("open('PICKLESCAN_NPY_FAILOPEN_MARKER.txt','w').write('PICKLESCAN NPY FAIL-OPEN MARKER 0x11111111')")