AI Supply Chain Attacks: The Supply Chain Is the Surface
On March 19, 2026, a threat group called TeamPCP force-pushed 76 of 77 version tags in Aqua Security’s trivy-action GitHub repository. Trivy is the most widely deployed open-source vulnerability scanner in the industry. The new tags pointed to malicious commits containing a credential stealer. Because most CI/CD pipelines reference GitHub Actions by mutable version tags instead of pinned commit SHAs, every organization running Trivy in their build pipeline began executing the malicious code immediately. The scans still appeared to succeed. The scanner still reported vulnerabilities. It also quietly harvested every secret in the runner environment.1
Supply chain attacks exploit the composition of authorized operations to produce unauthorized behavior. After the attackers compromised Trivy via GitHub Actions tag hijacking, they used stolen CI credentials to backdoor LiteLLM on PyPI, reaching 46,996 installations in 46 minutes. Each component operated within its granted permissions. The structural defense is pinning dependencies to immutable references and treating package installation as code execution.
Five days later, on March 24, TeamPCP used a PyPI publishing token stolen from one of those CI environments to publish two backdoored versions of LiteLLM, a popular AI proxy library present in 36% of cloud environments.2 Version 1.82.8 included a .pth file that executes automatically on any Python startup, before any import, before any application-level sandbox. The payload swept SSH keys, cloud credentials, database passwords, cryptocurrency wallets, and CI/CD secrets, encrypted them with a hardcoded RSA public key, and exfiltrated the archive to an attacker-controlled domain registered the day before.3
PyPI quarantined both versions after 46 minutes. In that window, 46,996 installations occurred.4 The LiteLLM maintainer deleted his GitHub account, rotated all keys, and engaged Mandiant.5
The security scanner scanned. The build pipeline built. The package manager installed. Every component did exactly what it was trusted to do.
TL;DR
- The chain: TeamPCP compromised Trivy (security scanner) March 19 via GitHub Actions tag hijacking, then compromised LiteLLM (AI proxy) March 24 using a PyPI token stolen from a Trivy-infected CI environment. 46,996 downloads in 46 minutes.4
- The technique: Version 1.82.8 abuses Python’s
.pthmechanism to run a credential stealer on everypython3invocation on the infected system, without importing LiteLLM.3 - The blast radius: 2,337 PyPI packages depend on LiteLLM. 88% had version specs that allowed the compromised versions. Confirmed downstream impact on Google ADK, Stanford DSPy, MLflow, Guardrails AI, and Cisco AI Defense SDK.4
- The campaign: TeamPCP hit five ecosystems in one week: GitHub Actions, Docker Hub, npm (CanisterWorm, 28+ packages), Open VSX (Checkmarx KICS), and PyPI (LiteLLM).6
- The structural lesson: Each link in the chain operated within its granted scope. Composing those authorized operations produced unauthorized behavior. The pattern mirrors the composition gap that enables silent egress at the agent level and Clinejection at the CI/CD level. Supply chain attacks are composition attacks.
- What you can do: Pin dependencies to immutable references. Isolate publishing credentials from CI runners. Monitor outbound network requests at the OS level. Treat
pip installas code execution, because it is.
A Security Scanner Walks Into Your Build Pipeline
The attack started with a misconfiguration in Trivy’s GitHub Actions environment in late February 2026. An attacker exploited a vulnerable pull_request_target workflow to exfiltrate Aqua Security’s aqua-bot personal access token. Aqua rotated some credentials on March 1, but missed others. TeamPCP retained valid tokens.1
On March 19, they used those tokens to force-push 76 of 77 version tags in the trivy-action repository and all 7 tags in setup-trivy, redirecting every mutable tag reference to malicious commits. They also triggered the release automation to publish a malicious Trivy binary as v0.69.4.1
The payload was a credential stealer that dumped Runner.Worker process memory, harvested SSH keys, cloud credentials, Kubernetes secrets, Docker configs, Git credentials, and .env files. It encrypted the haul with AES-256 and a 4096-bit RSA key, then exfiltrated the archive to attacker-controlled servers. The malicious code ran alongside legitimate Trivy functionality. Scans succeeded. The scanner still reported vulnerabilities. The stealer ran in the background.7
Between March 19 and March 23, the attackers also poisoned Docker Hub images under the trivy:0.69.4, trivy:0.69.5, trivy:0.69.6, and trivy:latest tags. CrowdStrike, Microsoft, Wiz, and Palo Alto all published technical analysis within days.789
The irony is structural: the most security-conscious organizations were the most exposed. If you ran Trivy in CI because you care about vulnerability scanning, your build pipeline was the one harvesting credentials.
From Scanner to Stealer to AI Proxy
LiteLLM’s CI pipeline ran Trivy. Specifically, ci_cd/security_scans.sh pulled Trivy via apt from Aqua’s repository without version pinning.10 During the compromise window, a CI run pulled the poisoned Trivy binary, which harvested the PYPI_PUBLISH_PASSWORD from the GitHub Actions environment.
On March 23, the attacker registered litellm.cloud. On March 24 at 10:39 UTC, they published litellm==1.82.7 to PyPI using the stolen credentials. Version 1.82.8 followed at 10:52 UTC.4
The two versions used different attack techniques. Version 1.82.7 embedded the payload in proxy_server.py, which fires on import litellm.proxy. Version 1.82.8 dropped a .pth file into site-packages/, which fires on any Python interpreter startup. The .pth mechanism is a legitimate Python feature for path configuration. Python’s site.py automatically executes any file ending in .pth that it finds in site-packages/ during interpreter initialization. Most Python developers do not know this feature exists.3
The payload in both versions collected environment variables, SSH keys, AWS/GCP/Azure credentials, Kubernetes configs, database passwords, cryptocurrency wallets, and CI/CD secrets. It encrypted the collection with AES-256-CBC using a random session key, wrapped the session key with a hardcoded 4096-bit RSA public key, and exfiltrated the archive via HTTPS POST. Only the attacker can decrypt the stolen data.3
A bug in the 1.82.8 malware gave it away. The .pth file spawns a child Python process, which re-triggers the .pth file, creating an exponential fork bomb. Andrej Karpathy flagged the bug on X. Without the fork bomb, the stealer would have run silently on every Python invocation on every infected system, potentially for weeks before anyone noticed.4
PyPI quarantined both versions at 11:25 UTC – 46 minutes of exposure, 46,996 downloads.4
The Blast Radius
Developers download LiteLLM approximately 3.4 million times per day. Wiz reports it runs in 36% of cloud environments.2 The 46-minute exposure window was enough.
FutureSearch analyzed PyPI’s BigQuery download logs and found that during the attack window, pip pulled version 1.82.8 at 6x the rate of any safe version. Downloads skewed heavily toward pip (which resolves to latest) versus uv (which uses lock files). Of the 46,996 downloads, 23,142 were pip installs of v1.82.8, each of which executed the payload during installation itself because the .pth file fires during pip’s own Python process.4
2,337 packages on PyPI depend on LiteLLM. 88% (2,054 packages) had version specifications that allowed the compromised versions to install. The downstream exposure includes:4
| Project | Monthly Downloads | Impact |
|---|---|---|
| CrewAI | 5.9M | At risk |
| Browser-Use | 4.2M | At risk |
| Opik (Comet) | 3.5M | Confirmed 2 CI workflows ran compromised versions |
| Mem0 | 2.7M | At risk |
| DSPy (Stanford NLP) | 1.6M | Pinned to <=1.82.6 |
| Agno | 1.6M | At risk |
| Google ADK | – | Confirmed: litellm breaks all installs |
| MLflow | – | Pinned to <=1.82.6 |
| Guardrails AI | – | Emergency advisory issued |
| Cisco AI Defense SDK | – | Emergency advisory issued |
LiteLLM Cloud and Docker proxy users were unaffected because their dependencies were pinned in requirements.txt. The distinction between “pinned” and “unpinned” was the difference between compromised and safe.5
Five Ecosystems in One Week
TeamPCP did not stop at PyPI. The campaign hit five package ecosystems in rapid succession:6
GitHub Actions (March 19): 76 trivy-action tags and all 7 setup-trivy tags hijacked. Malicious Trivy binaries v0.69.4 through v0.69.6 published.
Docker Hub (March 19-22): Malicious Trivy images pushed as aquasec/trivy:latest and version-specific tags. Propagated to mirror.gcr.io.
Open VSX (March 23): TeamPCP compromised the Checkmarx KICS GitHub Action, hijacking 35 tags between 12:58 and 16:50 UTC.2
npm (March 23-24): CanisterWorm, a self-propagating worm, deployed across 28+ npm packages.
PyPI (March 24): LiteLLM versions 1.82.7 and 1.82.8 published with credential-stealing payloads.
Each ecosystem compromise used credentials harvested from the previous one. The campaign was a directed graph of trust exploitation, where each compromised node yielded the keys to the next.
Supply Chain Attacks Are Composition Attacks
The structural pattern in the TeamPCP campaign is the same pattern I described in the context of agent security and silent egress: individually authorized components composing into unauthorized behavior.
Trivy had permission to run in CI. CI held publishing credentials by design. The package manager installed packages on command. Each .pth file configured Python paths as intended. Every link in the chain operated within its defined scope. Composing those legitimate operations produced credential exfiltration at scale.
The Clinejection attack demonstrated the same pattern at the CI/CD level: an issue title injected adversarial text into Cline’s automated pipeline, which executed an npm preinstall script, which poisoned the build cache, which contaminated cross-workflow artifacts. Each step held its own authorization.11
The MCPTox benchmark demonstrated the same pattern at the agent level: malicious instructions embedded in tool descriptions redirect agents to exfiltrate credentials using legitimate tools already on the server. The agent never executes the poisoned tool itself. It reads the poisoned description and uses its own legitimate tools to carry out the attack.12
Silent egress demonstrates it at the runtime level: adversarial instructions in web page metadata induce an agent to exfiltrate runtime context through an outbound HTTP request that the agent already has permission to make.13
The attack surface is not a single vulnerable component. The attack surface is the composition of trusted components. Securing individual links does not secure the chain. Trivy alone was not the vulnerability. LiteLLM alone was not the vulnerability. The trust relationship between them – mediated by mutable version tags and unpinned dependencies – was the vulnerability.
What Actually Helps
The defenses that would have stopped each stage of this attack are boring. They are also the ones most organizations skip.
Pin to immutable references. LiteLLM pulled Trivy via apt without version pinning. If security_scans.sh had pinned to a specific version or commit SHA, the poisoned v0.69.4 binary would never have run. GitHub Actions should reference commit SHAs, not mutable tags. The difference between uses: aquasecurity/[email protected] (compromised) and uses: aquasecurity/trivy-action@57a97c7 (safe) is the difference between losing your publishing credentials and not.1
Isolate publishing credentials from CI runners. The PYPI_PUBLISH_PASSWORD sat exposed as an environment variable in the GitHub Actions runner where Trivy ran. If the team had isolated publishing credentials in a separate workflow with no third-party action dependencies, the Trivy compromise would not have yielded PyPI access. PyPI’s Trusted Publishers (OIDC) eliminates stored tokens entirely.5
Monitor outbound network requests. The credential stealer exfiltrated data via HTTPS POST to models.litellm.cloud, a domain the attackers registered 24 hours before the attack. Egress monitoring that flags requests to newly registered domains would have caught this. My own hook system blocks outbound requests to domains not on an allowlist, the same pattern that would have intercepted this exfiltration.14
I learned the same lesson in production the hard way. A trusted Cloudflare cache purge path fired a fully authorized API call and still produced the wrong outcome because the surrounding guardrails were too loose. That failure pushed me to add destructive-action approvals and preflight hooks around high-impact operations, not just domain allowlists.
Treat pip install as code execution. A .pth file in site-packages/ runs on every Python startup. There is no opt-out. There is no sandbox. If you run pip install in a CI environment with access to credentials, you grant arbitrary code execution to every transitive dependency in the package. The mitigation: run pip install in an environment with no credential access, then copy the installed packages to the environment that needs them.
Use lock files. FutureSearch’s analysis showed that uv users (who rely on lock files) rarely pulled the compromised version, while pip users (who resolve to latest) pulled it constantly. Lock files are not a complete defense, but they prevent the most common exposure: an unpinned >= version constraint silently upgrading to a compromised release.4
The Uncomfortable Implication
The TeamPCP campaign demonstrates that supply chain security is not a secondary concern that sits behind application security, network security, and endpoint protection. For organizations running AI agents with tool access, the supply chain is the primary attack surface.
An AI agent that calls pip install in a CI environment, fetches URLs, or installs MCP servers composes trust relationships at runtime. Each operation carries its own authorization. The composition may not. The deploy-and-defend paradox accelerates this: organizations deploy agents faster than they audit the trust chains those agents compose.
The fork bomb bug in LiteLLM 1.82.8 is the only reason anyone detected this attack quickly. Without that implementation error, the credential stealer would have run silently on every Python invocation on every infected system. 46,996 installations in 46 minutes. 36% of cloud environments. 2,337 downstream packages. The attacker made one mistake. Next time, they might not.
Update — March 31, 2026: A Different Vector, Same Outcome
Six days after this post went live, npm had its next incident. On March 31, axios maintainer Jason Saayman disclosed that his personal computer had been compromised through a targeted social engineering campaign followed by remote access trojan malware. The attackers used his npm credentials to publish axios 1.14.1 and 0.30.4, both of which injected a new dependency called [email protected] that installed a cross-platform RAT on macOS, Windows, and Linux. The malicious versions stayed live for approximately three hours before community contributors triaged the incident and npm yanked them.16
The axios vector was different from TeamPCP. There was no compromised CI runner, no credential harvested from a third-party security scanner, no multi-ecosystem worm. The campaign against Saayman ran for roughly two weeks before publication, and the compromise traveled through the maintainer’s personal device straight to the npm publishing pipeline. Remediation required a complete wipe of all his devices and rotation of every credential across every platform he had ever used, personal or otherwise. The C2 infrastructure was sfrclak[.]com on 142.11.206.73:8000.16
The structural lesson is the same. The trust chain now includes maintainer attention and vigilance as links, because individually legitimate operations compose into unauthorized behavior when an adversary manipulates a human in the chain. The mitigations the axios team is adopting (OIDC publishing, immutable releases, device isolation)16 address the same composition gap that Trivy-to-LiteLLM exposed: publishing credentials stored where they can be reached by adversaries who already compromised something adjacent to them.
Three hours. 46 minutes. These are the windows modern supply chain attacks operate in now. Pinning and lockfiles remain the primary defense: axios users who had fresh installs outside the 00:21-03:15 UTC window of March 31 were unaffected.16
FAQ
How do I check if I was affected?
Search your CI logs and local environments for LiteLLM versions 1.82.7 or 1.82.8 installed between 10:39 and 11:25 UTC on March 24, 2026. If you find either version, rotate every credential that the system could reach – environment variables, SSH keys, config files. LiteLLM’s official guidance: treat any system that ran the compromised versions as fully compromised.5
Which advisory identifier should I track?
Track PYSEC-2026-2 in OSV and the PyPA advisory database. OSV also aliases the incident as MAL-2026-2144. As of March 25, 2026, the public OSV advisory page does not list a CVE alias for the malicious LiteLLM releases, so incident response teams should key off the affected versions, the install window, and PYSEC-2026-2 first.15
Were LiteLLM Cloud or Docker proxy users affected?
No. LiteLLM Cloud and the official Docker proxy image pin dependencies in requirements.txt, preventing automatic resolution to the compromised versions. Only users who installed LiteLLM via pip install litellm (unpinned) during the 46-minute window were affected.5
What is a .pth file and why is it dangerous?
A .pth file is a Python feature for configuring module search paths. Python’s site.py module reads and executes any file ending in .pth in the site-packages/ directory during interpreter initialization. Execution happens before any import statement, before any application code, and before any Python-level sandbox. The Python standard library documents the mechanism, but few application developers know it exists. It is a legitimate feature repurposed as an attack vector.3
Is this related to the Trivy supply chain attack?
Yes. The LiteLLM compromise was a direct consequence of the Trivy compromise. TeamPCP compromised Trivy’s GitHub Actions on March 19, which harvested CI/CD credentials from organizations running Trivy in their build pipelines. One of those credentials was LiteLLM’s PyPI publishing token. The attacker used that token to publish the malicious LiteLLM versions five days later.10
What is TeamPCP?
TeamPCP (also tracked as DeadCatx3, PCPcat, ShellForce, CipherForce) is a threat group that conducted a multi-ecosystem supply chain campaign in March 2026, compromising packages across GitHub Actions, Docker Hub, npm, Open VSX, and PyPI. The group used credential harvesting, tag hijacking, and self-propagating worm techniques across five package ecosystems in approximately one week.6
How does this relate to AI agent security?
AI agents that install packages, fetch URLs, or connect to MCP servers compose trust relationships at runtime. Each operation carries its own permission grant. The composition of those operations can produce unauthorized behavior, as demonstrated by silent egress (agent-level), Clinejection (CI/CD-level), and this attack (supply-chain-level). Runtime behavioral monitoring, domain allowlisting, and credential isolation address the composition gap at different layers of the stack.14
Sources
-
CrowdStrike, “From Scanner to Stealer: Inside the trivy-action Supply Chain Compromise,” March 2026. Technical analysis of tag hijacking, credential harvesting, and attack timeline. ↩↩↩↩
-
Wiz, “Three’s a Crowd: TeamPCP Trojanizes LiteLLM in Continuation of Campaign,” March 2026. LiteLLM present in 36% of cloud environments. Full campaign progression from Trivy to KICS to LiteLLM. ↩↩↩
-
Sonatype, “Compromised litellm PyPI Package Delivers Multi-Stage Credential Stealer,” March 2026. Technical analysis of the
.pthfile technique, payload encryption, and exfiltration mechanism. ↩↩↩↩↩ -
FutureSearch (Daniel Hnyk), “LiteLLM Hack: Were You One of the 47,000?” March 2026. PyPI BigQuery analysis: 46,996 downloads in 46 minutes. 2,337 dependent packages, 88% allowing compromised versions. Downstream exposure by monthly download volume. ↩↩↩↩↩↩↩↩↩
-
LiteLLM, “Security Update: Suspected Supply Chain Incident,” March 2026. Official postmortem. Confirmed Trivy as the vector. Docker/Cloud users unaffected. Mandiant engaged. ↩↩↩↩↩
-
Kaspersky, “Trojanization of Trivy, Checkmarx, and LiteLLM Solutions,” March 2026. Full campaign analysis across five ecosystems. ↩↩↩
-
Microsoft, “Guidance for Detecting, Investigating, and Defending Against the Trivy Supply Chain Compromise,” March 2026. Detection guidance, affected version matrix, compromise indicators. ↩↩
-
Aqua Security, “Trivy Supply Chain Attack: What You Need to Know,” March 2026. Official incident response. Incomplete credential rotation acknowledged. ↩
-
Palo Alto Networks, “When Security Scanners Become the Weapon,” March 2026. Technical breakdown of the attack. ↩
-
Snyk, “How a Poisoned Security Scanner Became the Key to Backdooring LiteLLM,” March 2026. Trivy-to-LiteLLM attack chain. Unpinned Trivy dependency in
ci_cd/security_scans.sh. ↩↩ -
Khan, Adnan, via Simon Willison, “Clinejection: Compromising Cline’s Production Releases,” simonwillison.net, March 2026. ↩
-
Zhiqiang Wang et al., “MCPTox: A Benchmark for Tool Poisoning Attack on Real-World MCP Servers,” arXiv:2508.14925, AAAI 2026. ↩
-
Lan, Qianlong et al., “Silent Egress: When Implicit Prompt Injection Makes LLM Agents Leak Without a Trace,” arXiv:2602.22450, February 2026. ↩
-
Blake Crosley, “What I Told NIST About AI Agent Security,” blakecrosley.com, February 2026. ↩↩
-
OSV / PyPA Advisory Database, “PYSEC-2026-2” published March 24, 2026. Public advisory for the malicious LiteLLM releases. Alias listed:
MAL-2026-2144. ↩ -
Jason Saayman, “Post Mortem: axios npm supply chain compromise,” github.com/axios/axios, March 31, 2026. Primary disclosure from the axios lead maintainer. Timeline, remediation, and vector analysis. Additional technical analysis: StepSecurity, “Axios Compromised on npm — Malicious Versions Drop Remote Access Trojan,” and Datadog Security Labs, “Axios npm Supply Chain Compromise,” March 2026. ↩↩↩↩