Lightning Got Owned: When `import lightning` Steals Your Credentials
import lightning shows up at the top of millions of PyTorch training scripts. On April 30, 2026, that line was enough to ship a developer’s credentials to an attacker.
Two malicious versions of the lightning PyPI package, 2.6.2 and 2.6.3, were uploaded today. Lightning averages 311 thousand downloads a day, and any CI that ran uv sync --upgrade between the upload and the takedown pulled the bad wheel. Neither version corresponds to a GitHub release on Lightning-AI/pytorch-lightning; the latest tagged release there is still 2.6.1 from January 30. The bad code was uploaded directly to PyPI, which is the same pattern as the litellm compromise six weeks ago. Socket flagged both releases 18 minutes after upload, and PyPI has since quarantined the entire project.
The malware fired on import
The malicious wheel ships a hidden _runtime/ directory next to the rest of the package. The package’s __init__.py spawns a daemon thread that runs _runtime/start.py, which downloads the Bun JavaScript runtime from GitHub and executes an 11 MB obfuscated payload called router_runtime.js. (The file sizes and counts are Socket’s; the rest follows from the layout they describe.)
This is a different execution surface from the litellm attack. Litellm shipped a .pth file, which Python runs at every interpreter startup whether or not anyone imports the package. Lightning waits to be imported. For a framework that is the centerpiece of most training scripts, that distinction collapses: any code path that touches the framework triggers the payload. Spawning the work in a daemon thread keeps the import responsive while the payload runs in the background, so the user sees a normal import lightning followed by a normal Trainer.fit().
Socket’s analysis of the obfuscated JavaScript reports that the payload targets repository tokens, environment variables, cloud credentials, and the contents of nearby Git repositories. It can commit encoded data back to repositories using stolen tokens, and it can backdoor npm tarballs the developer is preparing to publish. That last detail is the part that should worry every developer who works in both Python and JavaScript: a Python package was the delivery vehicle, but the worm reaches across ecosystems. The obfuscation style and credential-collection patterns resemble the Shai-Hulud worm seen earlier this year.
The minimal closure of security warnings on Lightning-AI’s GitHub during the active window is consistent with a maintainer account still under attacker control, though that interpretation is Socket’s, not a confirmed statement from the project.
The same playbook keeps shipping
This is the third high-profile PyPI compromise in six weeks targeting AI and ML developers. Litellm was hit on March 24. Xinference was hit on April 22, with a credential-stealing payload added directly to __init__.py after a maintainer-account takeover. The earlier Ultralytics compromise in December 2024 used GitHub Actions script injection instead, but the rest of the pattern is the same shape: a high-traffic ML package, a fast upload, a compromised account on the publisher side, and credentials draining out of CI before anyone can react.
The defense apparatus has improved. PyPI’s quarantine workflow lets admins lock a project before maintainers can push more bad releases, which is what stopped the lightning incident from getting worse. Socket’s automated detection turned around in 18 minutes. Neither of those is fast enough on its own. A CI pipeline that ran uv sync inside that 18-minute window installed the compromised wheel like any other run, and a quarantine that lands an hour later cannot stop a payload that already executed on a developer machine.
Layer the defenses you already have
No single change stops every attack of this shape. The defenses that work, work together. The handbook covers each one in its own page; this section is the summary plus the lightning-specific reasoning.
Pin to a lockfile
A uv lockfile records the exact version and hash of every dependency. A project pinned to lightning==2.6.1 did not pull 2.6.2 or 2.6.3 today, regardless of how fast resolution ran or how recently CI fired. Pinning is the single biggest defense most projects can adopt, and it is also the easiest. Anyone whose project resolved against lightning>=2.6 without a lockfile, or whose CI ran uv sync --upgrade between the upload and the quarantine, is in scope.
Add a dependency cooldown
uv’s exclude-newer refuses any package version published inside a rolling window. With a 7-day cooldown, today’s malicious versions would not have been considered for resolution at all, regardless of how fast Socket flagged them. The cooldown is the right defense for the moment when a new version actually enters the dependency tree, which is the gap a lockfile alone leaves open.
Verify dependencies with hashes
Hash pinning catches tampered wheels in transit. It does not catch a malicious version uploaded to PyPI through legitimate channels, because the recorded hash comes from the malicious artifact itself. Pin hashes anyway, but understand that they protect the retrieval path, not the upload path that today’s attack used.
Audit dependencies in CI
uv audit and pip-audit check installed versions against the OSV database. Once the lightning compromise is recorded there, an audit step in CI fails the build for any project that pulled the bad versions before the quarantine. This is the catch-it-after-the-fact layer; pair it with the layers that block ingestion.
Switch to trusted publishing
Trusted publishing replaces long-lived PyPI tokens with short-lived OIDC credentials tied to a specific CI provider. If a project ships releases through trusted publishing, an attacker holding a stolen API token cannot upload a release at all. This is the defense that would have prevented today’s incident at the source.
The handbook has a full explainer on why a Python install can run code, including the import surface that lightning exploited and the file-format mechanics that make _runtime/ directories possible. Each layered defense matches a specific surface in that explainer.
What would have helped today
Hash pinning alone would not have helped. The malicious 2.6.2 and 2.6.3 wheels were legitimate PyPI uploads with valid hashes; verification against those hashes succeeds because the artifact under verification is the malicious one.
A lockfile pinned to 2.6.1 is the cleanest defense on the consumer side. A 7-day exclude-newer cooldown closes the gap on new resolutions, where 18 minutes of detection time is not enough and seven days is. Most other recent supply-chain attacks were caught within that window, and the cost is a one-week delay on adopting new dependency versions, which most projects can absorb. Trusted publishing on Lightning-AI’s side is the upstream fix, since it would remove the upload path the attacker used.
If a project consumed lightning==2.6.2 or 2.6.3 today, treat anything the import statement could reach as exposed: environment variables, cloud credentials, and any GitHub or npm tokens on the affected machine. Because the payload commits back to Git using stolen tokens, also read recent commits on any repository the developer could push to.
See Socket’s writeup has the IoCs and the payload analysis.