Security Supply Chain March 2026

The Trivy Supply Chain Attack: A Lesson in Mutable Tags

Between March 19 and 23, 2026, a threat actor known as TeamPCP compromised Aqua Security’s CI/CD pipeline. For four days, anyone who pulled a Trivy container image received malware. Here’s what happened — and what it teaches us about how we reference dependencies.

18,000+
Pipelines compromised
4
Days of exposure
76/77
trivy-action tags overwritten

It was a Wednesday morning when I first saw the advisory come through.

Aqua Security’s CI/CD pipeline had been compromised. Between March 19 and March 23, 2026, a threat actor known as TeamPCP had been pushing backdoored versions of Trivy — a widely used container vulnerability scanner — to Docker Hub. The irony was hard to ignore: the tool people run to find vulnerabilities in their containers had itself become the vulnerability.

If you pulled aquasec/trivy:latest or any of the version tags 0.69.4, 0.69.5, or 0.69.6 during that window, you got malware. An infostealer that quietly harvested CI/CD secrets, cloud credentials, SSH keys, and Docker configurations.

It didn’t stop at Docker Hub either. The attackers force-pushed 76 of 77 version tags in the aquasecurity/trivy-action GitHub Action and all 7 tags in aquasecurity/setup-trivy. Every GitHub Actions workflow referencing those tags — and there are millions of them — silently started running attacker-controlled code.

Microsoft, CrowdStrike, and Wiz all published advisories. The last known clean release was 0.69.3.

Why this one lands differently

Here’s what makes this attack particularly interesting. It targeted the people who care most about security.

If you’re running Trivy in your CI/CD pipeline, you’re doing the right thing. You’re scanning your images for vulnerabilities before deploying them. You’ve thought about supply chain risk. And then the supply chain you trusted handed you malware.

Most teams reference Trivy one of three ways:

  • aquasec/trivy:latest in a Docker pull step
  • aquasecurity/trivy-action@v0.69.4 in a GitHub Actions workflow
  • A scheduled cron job pulling the latest image nightly

All three were compromised. If your pipeline ran between March 19 and March 23, you should treat your CI/CD secrets as exposed.

The case for pinning to digests

The correct technical response to this class of attack is to pin dependencies to immutable references — content-addressable digests rather than tags.

Tags are mutable. Anyone with push access to a registry can overwrite them. What you cached as v0.69.4 last Tuesday isn’t guaranteed to be the same bytes as v0.69.4 this Tuesday. This incident made that concrete in a way that a lot of teams hadn’t fully internalized.

A Docker image digest looks like this:

  • aquasec/trivy@sha256:4b8c9f2a…

That hash is a cryptographic fingerprint of the exact image content. It cannot be silently replaced. If an attacker overwrites the image at a given tag, the digest changes — your pinned reference breaks visibly rather than invisibly running compromised code.

Pinning to digests is genuinely good practice, and this incident is a compelling argument for adopting it.

But pinning creates maintenance overhead you have to account for

Here’s the part that’s easy to gloss over.

When you pin to a digest, you opt out of automatic updates. That shift in responsibility is real. Security patches, bug fixes, upstream vulnerability remediation — none of it reaches you automatically. If Aqua Security releases Trivy 0.70.0 with critical CVE fixes, your pinned reference keeps running the older version until someone manually updates it, verifies the new digest, and deploys the change.

For teams without a disciplined process for tracking upstream releases, pinning can quietly create a different problem: you’ve eliminated one attack vector (mutable tag hijacking) while inadvertently introducing another (running stale images with known vulnerabilities). It’s a trade-off that only pays off when you have a real update process alongside it.

Pinning is not a set-and-forget solution. It’s a practice that demands ongoing attention to stay effective.

Where you run the scanner matters too

Most teams run Trivy inside their GitHub Actions workflow — which is exactly the attack surface TeamPCP exploited. Running security tooling on infrastructure you control directly, rather than in pipelines exposed to upstream CI/CD supply chains, is a meaningfully different threat model worth thinking through.

This applies not just to Trivy but to any tool that runs with access to secrets or production credentials.

What to review in your own pipeline

If your pipeline was running during the March 19–23 window with an unverified Trivy image, treat any secrets that passed through it as potentially exposed and rotate them.

Going forward, a few things worth reviewing:

  • Are you referencing mutable tags anywhere in your security tooling? Trivy, your linters, your base images — all of these are potential supply chain exposure points.
  • If you pin to digests, do you have a process for tracking and applying upstream updates? Pinning without a maintenance plan trades one risk for another.
  • Where does your scanning tooling run? Infrastructure you control is a different threat surface than a shared CI/CD pipeline.

The thing about trust

Supply chain attacks work because they exploit the trust you’ve built up in the tools and services you depend on. The Trivy compromise isn’t an indictment of Aqua Security — they make excellent software and they responded transparently. It’s a reminder that the attack surface is larger than most teams actively think about, and that the tools designed to protect you are part of it too.

We don’t have this problem entirely solved ourselves — there’s always more to tighten up. But incidents like this are useful prompts to review your assumptions about the things you implicitly trust.

Pinning is the floor, not the ceiling

There’s one more nuance worth naming explicitly.

For Docker image digests, pinning works well. A registry digest is a content-addressed hash of the image manifest itself. You can’t “fork” a Docker image the way you can fork a Git repository. If the hash matches, the content matches.

But for GitHub Actions, pinning a commit SHA has a subtle weakness. GitHub resolves commit SHAs globally across forks. An attacker can create a commit in a forked repository, and if a workflow references that SHA, GitHub Actions will happily check out the attacker’s code as if it belongs to the original project. The version comment next to the SHA — # v6.0.2 — is free text. Nothing validates it. Reviewers read the comment instead of comparing 40-character hex strings, and the attacker knows this.

RoseSecurity wrote an excellent breakdown of this in SHA Pinning Is Not Enough. Their advice: pin your SHAs, then verify what they point to. Confirm the commit exists on the upstream repo’s actual release branch, not an orphaned fork. Automate that check. And layer additional controls — signed commits, action allowlists, internal mirroring, and artifact attestations.

Digest pinning for container images remains solid. For GitHub Actions, verify commit provenance too. Security is never a solved problem — every defense has edges, and the interesting attacks live there.


We run Trivy as part of our managed scanning infrastructure and have been working through some of these same trade-offs. If you’re thinking through how your own supply chain practices hold up, we’re happy to compare notes.