When the Scanner Becomes the Weapon: Inside the Trivy/TeamPCP Supply Chain Compromise

Written by the Rafter Team

On March 19, 2026, at 17:43 UTC, a threat actor calling itself TeamPCP pushed a malicious release of Trivy — Aqua Security's open-source vulnerability scanner — and force-pushed 82 GitHub Action tags to redirect every existing version reference to attacker-controlled code. Trivy is one of the most widely adopted security scanners in the cloud-native ecosystem, embedded in tens of thousands of CI/CD pipelines. For the period the malicious releases were live, every pipeline that ran aquasecurity/trivy-action or aquasecurity/setup-trivy against a pinned version tag fetched a credential stealer instead of a scanner.
This is the inversion every security team dreads. The tool you installed to find supply chain compromises in your dependencies became the supply chain compromise.
If your CI pipelines used aquasecurity/trivy-action or aquasecurity/setup-trivy between March 19 and Aqua's remediation, treat the runner as compromised. Rotate any secrets the workflow had access to: SSH keys, cloud credentials (AWS, GCP, Azure), Kubernetes tokens, registry credentials, GitHub tokens, GPG keys. The malware was designed to harvest exactly these.
What Happened
The malicious release, Trivy v0.69.4, went out at 17:43:37 UTC on March 19. Two more malicious Docker images, 0.69.5 and 0.69.6, were published shortly after. Alongside the scanner itself, the attacker force-pushed 75 of the 76 release tags in aquasecurity/trivy-action and all 7 tags in aquasecurity/setup-trivy. Force-pushing tags is the part that makes this attack so effective: anyone who had pinned to v0.32.0 thinking they were safe now resolved to a malicious commit, because the tag pointer had been moved.
The malicious actions ran a tool the attacker named "TeamPCP Cloud stealer." On every CI run, it dumped the memory of the GitHub Actions Runner.Worker process — which is where pipeline secrets live in cleartext while a job is executing — and harvested SSH keys, AWS/GCP/Azure credentials, Kubernetes tokens, container registry logins, GitHub tokens, and GPG keys. The stolen data was encrypted with AES-256 wrapped in RSA-4096 and exfiltrated to a typosquatted command-and-control domain, scan.aquasecurtiy[.]org (note the missing i), with a fallback that wrote the encrypted payload into a newly created GitHub repository under the attacker's control.
Aqua Security removed the malicious releases from GitHub, Docker Hub, GHCR, and ECR, and published advisory GHSA-69fq-xp46-6x23 with remediation steps.
How TeamPCP Got In
The actual entry point was opened almost three weeks earlier. On February 27, 2026, an autonomous account named hackerbot-claw opened pull request #10252 against the Trivy repository. Trivy's "API Diff Check" workflow was configured to run on pull_request_target, which is the GitHub Actions trigger that runs the workflow with the target repository's secrets while checking out the contributor's code. This is the well-documented "Pwn Request" pattern that has been responsible for many CI compromises over the past several years.
The malicious workflow exfiltrated a Personal Access Token to recv.hackmoltrepeat[.]com. From there, things compounded. The stolen token had access to release infrastructure that should have been revoked after a previous, separate Trivy incident, but containment from that earlier event was incomplete. TeamPCP retained residual access through credentials nobody had thought to rotate. They used that access to make imposter commits spoofing the GitHub identities of two well-known maintainers (rauchg and DmitriyLewen), and then to force-push the release tags.
The attack chain, in summary:
- A
pull_request_targetworkflow ran attacker-controlled code with repository secrets. - A long-lived PAT was exfiltrated from CI memory.
- Credentials from a prior incident, never fully revoked, gave the attacker a path back into the release pipeline.
- Imposter commits and force-pushed tags redirected every existing version pointer to malicious code.
- The malicious scanner ran in customer CI pipelines and harvested the secrets those pipelines held.
No single step was novel. The combination is what made this devastating.
Why a Scanner Compromise Is Worse Than a Library Compromise
Most npm or PyPI supply chain attacks compromise a library that runs inside an application. The blast radius is whatever that application can touch. A scanner sits in a different position. By design, it runs against your entire codebase and your dependency tree, often with elevated permissions, and it runs in the place where your most sensitive secrets are decrypted into memory: the CI runner.
A typical Trivy workflow has access to:
- Cloud provider credentials, so it can scan container images in your registries.
- Kubernetes tokens, so it can scan workloads.
- The full source tree, including configuration files and IaC.
- The GitHub Actions token for the repository, which can typically read and sometimes write code, packages, and releases.
- Whatever extra secrets the team has wired into that workflow.
When that scanner is the malicious payload, the attacker does not need to escalate. They start with the keys to the kingdom. This is what makes scanner compromises a different category of incident from library compromises — and why TeamPCP's choice of target was strategic, not opportunistic.
What This Changes for CI Security
There are no exotic mitigations here. The lessons are old, and most teams already know them. The TeamPCP campaign is a reminder that the basics still need to actually be in place.
Pin GitHub Actions to commit SHAs, not tags. A tag is a pointer that the maintainer (or anyone with credentials) can move. A commit SHA is content-addressed and cannot be silently redirected. uses: aquasecurity/trivy-action@0.28.0 would have followed the force-push; uses: aquasecurity/trivy-action@a20de5420... would not.
Avoid pull_request_target unless you fully understand it. This trigger is the single most common cause of GitHub Actions compromises. If you must use it, never check out the PR's code in a job that has access to secrets. Split the work into a privileged job that only handles the comment or status update, and an unprivileged job that runs the contributor's code.
Treat credential rotation as part of incident response, not the conclusion of it. TeamPCP's path back into Trivy's release pipeline was credentials that should have been dead. After any compromise involving CI, audit every long-lived token, every deploy key, every PAT, every webhook secret — and rotate the ones you cannot account for. "We rotated the obvious ones" is how a one-time incident becomes a recurring one.
Run more than one scanner. Single-scanner pipelines are also single points of compromise. Defense-in-depth here is not theater: a different tool, with a different release cadence and a different threat model, is unlikely to be compromised in the same window.
Monitor for new outbound network calls from CI. The TeamPCP payload exfiltrated to a typosquatted domain. Any CI runner that has no business contacting scan.aquasecurtiy[.]org should not be allowed to, and should alert when it tries. Egress controls on CI runners are still rare and still high-value.
A Note on Rafter
Rafter runs multiple scanners — Gitleaks, Trivy, Bandit, Checkov, OpenGrep, and our own AI scanner — across every scan. An incident like this is a useful reminder that no single tool is a security boundary, and that integrating third-party scanners means inheriting some share of their risk. Running several in parallel is the design response to that: if one is compromised, the others still run, and we still flag what they find. Pre-commit and pre-merge dependency analysis catches poisoned packages before they reach a developer's machine, regardless of which downstream tool eventually parses them.
If you are reviewing your own CI pipelines after this incident and want a faster way to check what your dependency tree looks like today, Rafter's fast scan covers SAST, dependency analysis, and secrets in a few minutes.
The Bigger Picture
TeamPCP did not stop with Trivy. Reporting since the initial incident has tied the same group to a worm in the npm ecosystem ("CanisterWorm") that uses stolen publish tokens to propagate, and to compromised packages downloaded more than 100 million times per month in aggregate. The Trivy compromise is one node in a larger campaign that is still active.
The lesson is not that Trivy is uniquely unsafe — it is excellent software run by careful people, and the response was professional. The lesson is that the tools we install to defend our supply chains are part of our supply chain. They deserve the same scrutiny we apply to anything else that runs in CI with our secrets attached. That means pinning to commit SHAs rather than tags, rotating credentials beyond the obvious blast radius after any prior incident, running more than one scanner where it is feasible, and watching what your runners are actually talking to over the network.
None of this is new advice. The reason it keeps being relevant is that the teams who get hit by the next campaign are almost always the ones who knew what to do and had not gotten around to doing it.