March 26th, 2026 · 4 minute read

Pins, Hashes, and Stabilization Windows - Lessons from the LiteLLM Compromise

The LiteLLM supply chain attack is a good case study for why pinning, hash verification, and stabilization windows should be standard practice.

Aidan Matzko

Aidan Matzko

Founding Engineer, aisy

TL;DR

Now:Pin litellm to <1.82.7 in your pyproject.toml or requirements.txt.

Long term:Implement dependency pinning, hash verification, and dependency management across your repositories.

What Happened

On March 24th, TeamPCP used a PyPI API token exposed during the Trivy/Aqua Security breach to hijack a LiteLLM maintainer's PyPI credentials and publish two malicious versions (v1.82.7 and v1.82.8). The payload exfiltrated cloud credentials, SSH keys, CI/CD secrets, and shell history. v1.82.8 also installed a .pth file that ran on any Python interpreter startup, even without importing LiteLLM. PyPI quarantined both versions within about three hours. Full details in the Wiz writeup and GitHub issue.

Locking Down Your Dependency Pipeline

The LiteLLM compromise was a textbook supply chain attack: hijack a maintainer's credentials, publish a malicious version, and wait for unpinned installs to pull it in. The three-hour window before PyPI quarantined the packages was enough. Any team running pip install litellm without a pinned version during that window got the payload. Here is how to make sure your pipeline is not exposed to this class of attack.

Pin exact versions

Pinning every dependency to an exact version in your pyproject.toml or requirements.txt prevents a new malicious version from being pulled in automatically. This is the first line of defense against the kind of attack that hit LiteLLM. If your install targets litellm==1.82.6, versions 1.82.7 and 1.82.8 are never fetched.

Verify with hashes

Hashes protect against a different vector: a malicious package published under an existing version number. If an attacker replaces the artifact for a version you already pin, a hash mismatch will fail the install. If you are using uv, uv lock includes hashes in the lockfile by default. For pip, you can get the same protection with pip-tools:

pip-compile --generate-hashes requirements.in -o requirements.txt

At install time, enforce them with:

pip install --require-hashes -r requirements.txt

Automate updates with a stabilization window

Pinned versions do not update themselves, so you stop getting bug fixes and security patches. Use Renovate or Dependabot to open PRs for new versions automatically, and set a stabilization window so updates are only proposed after a delay. Most compromised packages are detected and yanked within hours. A three-day window is enough to filter out the vast majority of supply chain attacks while still keeping your dependencies reasonably current.

Renovate config with a stabilization window (renovate.json):

{
"extends": ["config:recommended"],
"packageRules": [
{
"matchManagers": ["pip_requirements", "pep621"],
"minimumReleaseAge": "3 days",
"rangeStrategy": "pin"
}
]
}

Dependabot equivalent (.github/dependabot.yml):

version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
versioning-strategy: "increase-if-necessary"

Each layer covers a different gap. Pinned versions block new malicious releases. Hashes catch tampering with existing versions. A stabilization window gives the community time to flag compromised packages before they reach your systems. Together, every version change is explicit, every artifact is verified, and nothing reaches your pipeline until it has been vetted.

See what's actually exploitable.

Sign up to receive your complimentary Boundary Map – analysis that groups your external assets, routes, and endpoints into logical infrastructure segments, providing a firsthand look at the deep intelligence aisy uses to prioritize risk.