Cryptographic lockfile for AI/LLM Python dependencies โ catches supply chain attacks before they hit prod
Project description
ailock ๐
Cryptographic lockfile for AI/LLM Python dependencies. Like
package-lock.json, but forlitellm,langchain,openai,transformers, and friends.
๐จ Active Threat: TeamPCP Campaign (March 2026)
The supply chain campaign is active RIGHT NOW. Three ecosystems compromised in 8 days:
| Date | Package | Versions | CVE | SHA256 (malicious wheel) |
|---|---|---|---|---|
| Mar 19 | trivy-action (GitHub) | 75 of 77 tags | CVE-2026-33634 | โ |
| Mar 24 | litellm | 1.82.7, 1.82.8 | CVE-2026-33634 | 8395c326... / d2a0d5f5... |
| Mar 27 | telnyx | 4.87.1, 4.87.2 | CVE-2026-33634 | 7321caa3... / cd081158... |
ailock audit has all four malicious package hashes pre-loaded. Run it now.
The Attack
On March 24, 2026 at 10:39 UTC, two versions of LiteLLM were published to PyPI by TeamPCP after they compromised maintainer credentials via a poisoned Trivy CI scanner.
litellm 1.82.7 โ malicious payload embedded in proxy_server.py. Triggers on import. Three-stage attack: credential harvest (SSH keys, .env files, AWS tokens, Kubernetes secrets) โ lateral movement โ persistent systemd backdoor. Exfiltrates to models.litellm.cloud as tpcp.tar.gz.
litellm 1.82.8 โ even worse: adds litellm_init.pth to site-packages. This fires on every Python startup โ including pip, IDE language servers, anything. No import required.
On March 27, the stolen credentials from LiteLLM victims were used to compromise telnyx (4.87.1, 4.87.2). WAV steganography delivers platform-specific payloads from 83.142.209.203:8080.
The version number matched. The package name matched. Standard pip install --require-hashes passed โ because the attacker used legitimate credentials to publish malicious bytes. Only a pre-attack lockfile would have shown the hash change.
ailock verify would have caught it in CI, before it hit prod.
Install
pip install ailock
Quick Start
# 1. Generate a lockfile for your AI/LLM dependencies
ailock generate
# 2. Commit it
git add .ailock && git commit -m "chore: add ailock lockfile"
# 3. Verify in CI โ fails loudly if ANY hash changed
ailock verify
# 4. Check against known-bad versions database
ailock audit
How It Works
ailock generate
Scans your requirements.txt / pyproject.toml, identifies AI/LLM ecosystem packages, fetches the SHA256 hashes from PyPI's JSON API for each pinned version, and writes a .ailock lockfile:
{
"schema_version": "1",
"generated_at": "2024-11-15T09:41:22Z",
"packages": {
"litellm": {
"version": "1.82.6",
"hashes": [
"sha256:a7c3f8e2b1d4..."
],
"source": "pypi"
},
"langchain-core": {
"version": "0.3.1",
"hashes": [
"sha256:f4e9a1b5c2d3..."
],
"source": "pypi"
}
}
}
ailock verify
Re-fetches hashes from PyPI and compares. If a package's hash changed since you ran generate, that package was modified after it was released.
โ All 12 packages verified โ integrity intact
๐จ SUPPLY CHAIN ATTACK DETECTED
1 package has been modified since your lockfile was generated.
DO NOT deploy. Investigate immediately.
โโโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Package โ Version โ Issue โ
โโโโโโโโโโโผโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ litellm โ 1.82.7 โ 2 new hashes added after lockfile was generated โ
โโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ailock audit
Cross-checks your locked packages against the community known-bad.json database. Ships with the current TeamPCP campaign entries plus historical attacks:
| Package | Version | Severity | CVE | Attack |
|---|---|---|---|---|
| litellm | 1.82.7 | CRITICAL | CVE-2026-33634 | Credential harvester + Kubernetes lateral movement |
| litellm | 1.82.8 | CRITICAL | CVE-2026-33634 | .pth persistence โ fires without any import |
| telnyx | 4.87.1 | CRITICAL | CVE-2026-33634 | WAV steganography payload delivery |
| telnyx | 4.87.2 | CRITICAL | CVE-2026-33634 | WAV steganography payload delivery |
| ultralytics | 8.3.41 | HIGH | โ | CI/CD pipeline cryptominer |
Real SHA256 hashes for all malicious wheels are included. The database is fetched fresh from GitHub on each run. Community PRs welcome โ known-bad.json.
GitHub Actions โ One-Liner
- name: Verify AI/LLM dependency integrity
run: pip install ailock && ailock verify && ailock audit
Full workflow:
name: Supply Chain Security
on: [push, pull_request]
jobs:
ailock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Install ailock
run: pip install ailock
- name: Generate lockfile (first run)
# Only needed if .ailock doesn't exist yet
run: |
if [ ! -f .ailock ]; then
ailock generate
echo "Generated .ailock โ commit this file to your repo"
exit 1
fi
- name: Verify package integrity
run: ailock verify
- name: Audit against known-bad database
run: ailock audit
What ailock protects against
โ Post-release replacement attacks (the LiteLLM scenario)
Package published clean โ attacker gains PyPI access โ replaces the wheel with malicious code under the same version number.
ailock verify catches this because the SHA256 changes.
โ Known compromised versions
ailock audit cross-checks against a curated database of confirmed malicious versions. Catches packages you may have locked before the attack was public.
โ Typosquats with pinned versions
If you accidentally pinned langchian==0.1.0 instead of langchain, the lockfile records it โ and ailock audit flags known typosquats.
โ What it doesn't protect against
- Packages you haven't locked (unpin them at your peril)
- Brand new attacks not yet in the known-bad database
- Compromised packages where the attacker maintains the same hash (very hard but possible via pre-image attacks โ SHA256 makes this computationally infeasible today)
Supported Package Ecosystem
ailock automatically recognises 100+ AI/LLM packages including:
LLM SDKs: litellm, openai, anthropic, cohere, mistralai, groq, replicate, together LangChain: langchain, langchain-core, langchain-community, langgraph, langsmith LlamaIndex: llama-index, llama-index-core, llama-index-llms-* HuggingFace: transformers, huggingface-hub, tokenizers, accelerate, peft, datasets Vector Stores: chromadb, pinecone-client, weaviate-client, qdrant-client Frameworks: haystack-ai, semantic-kernel, autogen, crewai, dspy Utilities: tiktoken, sentence-transformers, instructor, outlines, guidance
Use --all-packages to lock your entire dependency tree.
CLI Reference
Usage: ailock [OPTIONS] COMMAND [ARGS]...
ailock โ cryptographic lockfile for AI/LLM dependencies.
Commands:
generate Scan AI/LLM dependencies and write a cryptographic lockfile.
verify Verify installed packages against .ailock cryptographic hashes.
audit Cross-check packages against known-bad versions database.
ailock generate
Options:
-o, --output PATH Output lockfile path. [default: .ailock]
-a, --all-packages Include ALL packages (not just AI/LLM).
-i, --from-installed Use currently installed package versions.
-f, --file PATH Specific requirements file(s) to scan.
--no-hashes Skip PyPI hash fetching (faster but less secure).
ailock verify
Options:
-l, --lockfile PATH Path to .ailock lockfile. [default: .ailock]
--fail-on-missing Exit with error if any package not on PyPI.
--json-output Output results as JSON (for CI integration).
ailock audit
Options:
-l, --lockfile PATH Path to .ailock lockfile. [default: .ailock]
--offline Use only bundled database (no network call).
--db-url TEXT URL to known-bad JSON database.
--json-output Output results as JSON (for CI integration).
--show-db Print the full known-bad database and exit.
Contributing to the Known-Bad Database
The ailock/data/known-bad.json file is community-maintained at github.com/midnightrunai/ailock.
If you discover a compromised package version:
- Fork github.com/midnightrunai/ailock
- Add an entry to
ailock/data/known-bad.json - Include: package name, version, SHA256 hashes (if known), description, references
- Open a PR
Format:
{
"package": "some-ai-package",
"version": "1.2.3",
"hashes": ["sha256:..."],
"severity": "CRITICAL",
"cve": "CVE-2024-XXXXX",
"description": "What happened and why it's bad.",
"reported_at": "2024-11",
"references": ["https://..."]
}
Why not just use pip's built-in hash checking?
pip install --hash works but requires you to:
- Manually specify hashes in requirements.txt
- Include hashes for every package (including transitive deps)
- Re-generate everything on every update
ailock focuses specifically on the AI/LLM ecosystem, adds a curated known-bad database, and makes the workflow CI-native.
License
MIT โ see LICENSE
Built by Midnight Run โ the AI that builds while you sleep.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ailock-0.2.0.tar.gz.
File metadata
- Download URL: ailock-0.2.0.tar.gz
- Upload date:
- Size: 30.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1afe40d892ef992d11a1002fee21f9886e94d0a2383b0bdcb7280f38d0d1bb87
|
|
| MD5 |
3d7ab42ee5fa86c1d9f2a6f5b93556fd
|
|
| BLAKE2b-256 |
f2012098d467e1d443fb2a6fbcb1c204d869792c2b6a80d40bc9a3bf6d7c04ef
|
File details
Details for the file ailock-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ailock-0.2.0-py3-none-any.whl
- Upload date:
- Size: 29.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
10f4cf88d80457090c15eac0ac34daea621384f0d0a368648829bc76dae6b4d8
|
|
| MD5 |
f2d043059a765de1aba150e52eed0344
|
|
| BLAKE2b-256 |
44dba5b32eeb1cd32ac81f0d88ff80f0b10fef853861422fec0b5071117ad673
|