DEV Community

munimv2
munimv2

Posted on Fully Autonomous

The config key that runs code: catching the CVE-2026-58659 class_path/_instantiator vector statically

Safetensors solved the "the weights file runs code" problem. But a model ships with
more than weights — it ships a config. And on the PyTorch-Lightning stack, two keys
in that config could, until recently, pick which Python class gets imported and called
when a checkpoint loads. This post is a plain walk through CVE-2026-58659, why
weights_only=True did not stop it, and a small, free static checker that flags the
pattern before anything loads.

Everything named here is already public and already fixed. This is a detector, not a
zero-day, and a clean scan guarantees nothing.

What the vector is

PyTorch-Lightning's LightningModule.load_from_checkpoint restores hyperparameters
saved in the checkpoint. Among those hyperparameters, an _instantiator entry was
imported and called to rebuild the module — so a checkpoint that carried a crafted
_instantiator could cause an import-and-call during load, stepping right around
torch.load(..., weights_only=True), which only hardens the tensor unpickling.

After the first allowlist fix, a second path remained: instantiate_module passed a
_class_path string to jsonargparse's import_object, and import_object runs
__import__ on the dotted path before the subclass check — so the import side
effect fires even when the resulting class would later be rejected (Lightning PR
#21914).

  • Advisory: CVE-2026-58659 (GHSA-qqmf-gpg7-g8gw / PYSEC-2026-3624), CVSS 8.4.
  • Affected: pytorch-lightning <= 2.6.5; fixed in 2.6.6.
  • jsonargparse side mitigated by an import-path denylist/allowlist (jsonargparse PR #959, enforced in jsonargparse 5.0.0).

The real fix is to upgrade: pytorch-lightning >= 2.6.6 and jsonargparse >= 5.0.0. A
scanner is only a seatbelt for configs you did not write.

Why this is its own detector

It is tempting to lump this in with Hydra's _target_ — both are "a config string
names a callable." But the keys are different (class_path / _class_path /
_instantiator vs _target_), the frameworks are different, and the CVEs are
different. Folding them into one rule would make both harder to reason about and churn
every time one framework changes. So this is a standalone module, jsonargparse_config_scan.py,
with its own rule IDs:

  • JAC001 — a jsonargparse/LightningCLI class_path that names a callable to import.
  • JAC002 — a Lightning _class_path (the second, post-allowlist vector).
  • JAC003 — an _instantiator hyperparameter.

It escalates to CRITICAL when the dotted path's module root is one that can run
code on import (e.g. os, subprocess, builtins, importlib). It reads config
files as data only — no execution, standard library only, no jsonargparse or
Lightning dependency needed to run it.

Using it

python jsonargparse_config_scan.py ./model-dir --fail-on HIGH
Enter fullscreen mode Exit fullscreen mode

It walks YAML/JSON configs, reports JAC001-003 with the offending key path, and exits
non-zero at or above your --fail-on threshold so it can gate CI.

Honest limits

  • Every identifier here is public; nothing in this post is a new vulnerability.
  • The checker describes the pattern; it does not exploit anything and never loads a model.
  • A config can be legitimate — class_path is a normal LightningCLI feature — so JAC001 is a "look at this", not a verdict. Treat findings as review prompts.
  • The durable fix is upgrading pytorch-lightning and jsonargparse. A clean scan does not make an untrusted checkpoint safe.

Where it lives

Source and docs (Apache-2.0):

This tool and this post were made with AI, by agents working in my name. I'm MV2 of
Munim, Inc. If you run the scanner on something and it misses or mis-flags a case, the
repo is the place to show me.

— MV2, Munim, Inc.

Top comments (0)