
It’s been a rough week for the open source security ecosystem, the kind of week that makes you step back and really question some fundamental assumptions about the trust model that underpins modern software development. And if you’re running any kind of CI/CD pipeline, AI agent framework, or Kubernetes environment, it might have made your week rather tough too!
You might have seen the recent Veritasium video, delving in to the XZ hack that, if it wasn’t for some geeky levels of attention, could have been one of the worst security incidents in history! I think did a great job of showing to the masses what was otherwise perhaps only known in the security community – but these kind of attacks seem to be getting more frequent, and in the era of GenAI, the ability of human reviewers to defend against them getting harder and harder.
Security turning against you
On March 19th, a threat actor group calling themselves TeamPCP compromised Aqua Security’s Trivy – one of the most widely used open source vulnerability scanners in the industry; a tool that thousands of organisations use to scan for vulnerabilities and trust implicitly in their pipelines.
The attack was sophisticated but followed a familiar playbook. TeamPCP had actually gained an initial foothold weeks earlier, back in late February, exploiting a misconfiguration in Trivy’s GitHub Actions environment to steal a privileged access token. Aqua discovered the first compromise around March 1st and rotated credentials – but the rotation wasn’t comprehensive enough, and the attackers retained residual access and waited a bit.
On March 19th they struck and force-pushed all but the latest version tags in the trivy-action GitHub Action repository to point at malicious commits, published a backdoored Trivy binary as v0.69.4, and compromised the setup-trivy action as well. Effectively, if you were referencing Trivy by tag, as most people do, there was a good change your CI/CD pipeline was silently running a credential stealer. The malware harvested SSH keys, cloud provider credentials, Kubernetes configs, GitHub tokens – basically anything it could find on the CI runner – encrypted it all with AES-256 and RSA-4096, and exfiltrated it to a typosquatted domain that looked enough like the real thing to avoid a casual glance at network logs.
But here’s the most concerning part, and I suspect we are still at the tip of the iceberg here – they managed to steal GitHub access tokens to who knows how many other repositories, and now we are seeing the next wave(s) of this attack.
The cascade hits Checkmarx
By March 23rd, TeamPCP had moved on. This time the target was Checkmarx’s KICS – another infrastructure-as-code security scanner. In the space of about four hours, they hijacked 35 version tags and pushed credential-stealing payloads using a new typosquatted domain, checkmarx.zone. Same playbook, different target, different exfiltration domain – specifically designed so that anyone who had updated their monitoring after the Trivy incident and was looking for the Trivy indicators of compromise would miss this one entirely.
The evidence suggests that credentials harvested from the Trivy attack provided the access needed to compromise the Checkmarx actions. One poisoned action harvests credentials that enable the poisoning of further actions. It’s a self-sustaining supply chain worm, and the attack surface expands with every victim.
They also went after Checkmarx’s VS Code extensions on the OpenVSX marketplace, and defaced all internal repositories in Aqua Security’s aquasec-com GitHub organisation with “TeamPCP Owns Aqua Security” messages – seemingly just to show off how much access they had accumulated.
There have even been targeted attacks on specific geographies: a payload was discovered that checks if the victim system is in an Iranian timezone, and if it is, deploys a wiper that destroys Kubernetes clusters and runs rm -rf / on non-containerised hosts. Non-Iranian systems get the standard backdoor instead.
And then LiteLLM
Which brings us to today, March 24th, and the one that hits closest to home for anyone working in the AI platform space.
LiteLLM – a great tool that serves as an API gateway to over 100 LLM providers, with millions of monthly downloads – has been compromised in exactly the same fashion. Versions 1.82.7 and 1.82.8 on PyPI contain credential-stealing payloads. The 1.82.8 variant uses a .pth file, which is particularly nasty because .pth files execute automatically on every Python process startup, not just when you import the library. Simply having the package installed is enough.
The entire package has now been pulled from PyPI – not just the compromised versions, but everything.
The target selection here is strategic in a way that should really make people sit up. LiteLLM is, by definition, the package that has access to every LLM API key in your organisation. If you’re using it as your gateway to OpenAI, Anthropic, Azure, Bedrock, and everything else, then a compromise of LiteLLM means every single one of those keys is potentially in the attacker’s hands. And because LiteLLM is a transitive dependency for a growing number of AI agent frameworks and MCP servers, plenty of people who never installed it directly were still pulling it in through something else, potentially not even realising. Thankfully it is now quarantined, and the attack window was open only for a short time.
What does this all mean?
I’ve been thinking about this a lot today, and a few things stand out.
First, the pattern here is clear: TeamPCP is deliberately targeting the security and infrastructure tools that organisations trust implicitly: vulnerability scanners. IaC security tools. API gateways that by their nature have access to yet more credentials.
Second, we have a fundamental problem with how GitHub Actions tags work. Mutable version tags can be force-pushed to point at arbitrary commits, and most of the ecosystem references actions by tag rather than by SHA hash. This has been a known risk since the tj-actions/changed-files compromise back in March 2025, a full year ago, and yet the industry still hasn’t widely adopted SHA pinning. It’s the sort of thing that’s easy to say “we should do this” but in practice rarely gets prioritised until something like this happens.
Ultimately though, supply chain security is really hard. Pinning to specific versions helps, but only if you pinned before the compromise. Lockfiles help, but only if you don’t blindly update them. A single compromised maintainer account can cascade through thousands of downstream projects in minutes, and the window between compromise and detection, while shrinking, is still wide enough for lots of damage to occur, as we are seeing. We build our platforms on top of stacks of open source dependencies, and the trust model assumes that each one of those is maintained and published securely. As we’ve seen this week, that assumption can be shattered in moments.
Thankfully in my case, I avoided issues both by SHA pinning, but also by keeping away from the latest and greatest release! Although I firmly believe in regular LCM, I always stay a few versions behind. However that only goes to your direct control, and nested dependencies can mean you have exposure you haven’t even realised!
For those of us building platforms in this space (and I count myself very much in this group), this is a moment to pause and audit. Check your CI/CD pipelines. Check what versions of Trivy, KICS, and LiteLLM are installed in your environments. Pin your GitHub Actions to known, trusted, commit SHAs. And if any system has had contact with the compromised versions, rotate everything – SSH keys, cloud credentials, API keys, the lot. But also, audit your dependencies – is that trusted package you have pulling something that itself could be compromised?
It’s been a bad week for open source security, and as I opened with, I don’t think it’s going to get any easier in our brave new world! But incidents like this will hopefully force the industry to take a deeper look at how we can improve supply chain integrity, and perhaps something good comes out of it. Of course we said the same thing last time, and the time before that…


