Trivy, the scanner that was stealing secrets
There is a perfect irony in the most deployed tool for scanning your images’ vulnerabilities turning, for the duration of a few-hour window, into an infostealer running inside your pipelines with full access to their secrets. On March 19, 2026, the group calling itself TeamPCP compromised Aqua Security’s Trivy ecosystem and the story deserves telling not for the thrill, but because it illustrates to perfection a design mistake half the CI/CD planet still commits today.
What happened
Armed with access obtained a few weeks earlier and a credential rotation that had not been carried out atomically, the attacker did two things in parallel. On one side, they published a poisoned Trivy v0.69.4 binary on every official distribution channel: GitHub releases, Docker Hub images, GHCR, ECR. On the other and this is where it gets interesting, they force-pushed 76 of the 77 version tags of the aquasecurity/trivy-action repository, plus the 7 tags of setup-trivy, to point them at a malicious entrypoint. The latter ran silently before the real scan, harvested the secrets accessible to the runner, exfiltrated them and even went as far, when the main channel failed, as creating a tpcp-docs repository inside your own GitHub organization to store the loot. The incident is tracked as CVE-2026-33634.
The refinement that hurts: the malicious commits cloned the author and commit dates of the legitimate versions, via GIT_AUTHOR_DATE and GIT_COMMITTER_DATE, so the git history looked flawless. One single tag survived, v0.35.0, because it already pointed at master’s HEAD and the attacker’s automation skipped it.
The lesson, which you already knew
Here is what to retain and you probably knew it already, which makes not having applied it all the more embarrassing. A tag is a mutable reference. When your workflow writes uses: aquasecurity/trivy-action@v0.28.0 , it is not asking for a specific content; it is asking for “whatever this tag will designate at execution time” and anyone with write access can move that tag to any commit without your workflow file changing by a single comma. You reread your YAML, it is identical to yesterday’s, yet it now executes foreign code.
The only immutable reference, for an action, is the full commit SHA and for an image, the sha256 digest. A SHA cannot be force-pushed, moved or renamed; it designates one content, forever. Pinning to it turns a moving promise into a verifiable fact.
What we do now
Concretely, in decreasing order of urgency. Pin all third-party actions to the full commit SHA, never a tag, even if that means adopting a tool to manage those pins. Pin images to their digest. Enable GitHub’s immutable releases where possible. Reduce the scope of secrets exposed to jobs, ideally by switching to OIDC to eliminate long-lived credentials. And monitor runner egress, because a scanner suddenly talking to an unknown IP is exactly the kind of signal a Harden-Runner knows to raise.
For the record, the sane versions at the end of the incident were trivy v0.69.3, trivy-action v0.35.0 and setup-trivy v0.2.6. But retain this above all, valid well beyond Trivy: your vulnerability scanner had a vulnerability and it was you, trusting a mutable pointer.