· 6 min read ·

When the Scanner Becomes the Threat: Trivy's Second Supply Chain Compromise

Source: lobsters

There is a particular kind of irony in a vulnerability scanner becoming a vulnerability. Trivy, the open-source scanner from Aqua Security that tens of thousands of teams rely on to find CVEs in their containers, filesystems, and infrastructure configs, had its v0.69.4 release replaced with a malicious version. StepSecurity reported the compromise, noting in the headline detail that this was the second time.

The second time. That framing matters more than any technical detail about what the malicious release did.

What Trivy Is and Why It Makes an Attractive Target

Trivy has become one of the dominant open-source security scanners over the past several years. It handles vulnerability scanning for container images, filesystems, git repositories, Kubernetes clusters, cloud configurations, and SBOMs. It supports dozens of package ecosystems, integrates with virtually every CI/CD platform, and ships as a standalone binary, a Docker image, and a GitHub Action that’s referenced in workflows across a huge proportion of public repositories.

That last point is the crux of the target value. When you add aquasecurity/trivy-action to a GitHub Actions workflow, you’re granting it access to your runner environment, your repository checkout, your CI secrets as environment variables, and potentially your Docker daemon if you’re scanning images. The tool needs broad permissions to do its job. Scanning for hardcoded credentials means reading every file in the repository. Scanning containers means talking to container registries. Fetching updated vulnerability databases means reaching out to external endpoints.

An attacker who compromises the Trivy binary or action doesn’t have to do any social engineering or lateral movement once it’s running in your pipeline. The access is already there, baked into what the tool legitimately needs.

How Release Pipeline Compromises Work

The mechanics of this class of attack follow a pattern that’s become well-documented since the SolarWinds and XZ Utils incidents brought supply chain security into mainstream awareness.

The attacker gains access to a maintainer’s credentials. This can happen through phishing, token theft from a compromised developer machine, or leaked secrets in logs. With that access, they can create a new release, upload malicious binaries that replace the legitimate ones, and push a new tag. From the outside, v0.69.4 looks like any other patch release. The version number is plausible, the release notes may look normal, and package managers and automation systems that reference that tag will silently pull the malicious version.

For a tool distributed as a GitHub Action, the attack surface is even more direct. GitHub Action references resolve tags at workflow execution time. A team pinned to aquasecurity/trivy-action@v0.69.4 will run whatever code lives at that tag, and tags in git are mutable by default. An attacker with write access to the repository can move the tag to point at a different commit.

Malicious releases in this context typically do one or more of the following: exfiltrate environment variables (which contain CI secrets), establish reverse shells, manipulate the scanner’s output to suppress specific findings, or install persistence mechanisms in build artifacts.

The First Compromise and What Should Have Changed

The “second time” framing in the StepSecurity report is the part that demands reflection. The first compromise of Trivy-related tooling established that the project was a target. Security-conscious teams took note, published recommendations, and the maintainers presumably reviewed their release security posture.

And then it happened again.

This isn’t an indictment of Aqua Security specifically. Maintaining release integrity at scale is genuinely hard, particularly for a project with the surface area Trivy has: binary releases for a dozen platform-architecture combinations, Docker images pushed to multiple registries, a GitHub Action, Homebrew formulas, package manager entries. Each of those is a point that needs to be secured, monitored, and kept consistent.

But recurring compromise of the same project forces a more uncomfortable question: are the available tools for release integrity actually being used, and if they are, why weren’t they sufficient?

The Release Integrity Toolchain That Exists and Isn’t Being Used Enough

The open-source ecosystem has developed reasonably solid tooling for release signing and provenance in the past few years. The gap is adoption.

Sigstore and its cosign tool allow maintainers to sign release artifacts with short-lived certificates tied to their GitHub identity, without managing long-lived signing keys. The signature is published to a public transparency log. Verification looks like this:

cosign verify-blob \
  --certificate-identity \
    "https://github.com/aquasecurity/trivy/.github/workflows/release.yaml@refs/tags/v0.50.0" \
  --certificate-oidc-issuer \
    "https://token.actions.githubusercontent.com" \
  trivy_Linux_64bit.tar.gz

If the artifact was produced by the official release workflow, this passes. If someone uploaded a binary that wasn’t produced by that workflow, it fails. Critically, a compromised binary uploaded through a stolen maintainer token would still fail this check if the binary wasn’t built by the workflow itself, because the signing identity is tied to the Actions execution context, not just the user’s credentials.

SLSA (Supply-chain Levels for Software Artifacts) goes further, providing a provenance attestation that records exactly how and where an artifact was built. The slsa-verifier tool can check that an artifact’s provenance matches expectations before you run it.

For GitHub Actions specifically, the most practical immediate mitigation is SHA pinning. Instead of referencing a version tag:

# Vulnerable to tag mutation
- uses: aquasecurity/trivy-action@v0.69.4

Pin to a specific commit SHA:

# Immune to tag mutation; attacker must forge a git commit hash
- uses: aquasecurity/trivy-action@1f0aa582c8c8f5f7639610d6d38baddfea4fdcee

SHA references in GitHub Actions are immutable. An attacker who moves the v0.69.4 tag to a malicious commit cannot make that commit hash resolve to anything other than what it is. This single change eliminates the most common vector for GitHub Actions supply chain attacks.

Tools like Renovate and Dependabot can automate updates to SHA-pinned references, so the argument that pinning creates maintenance burden is largely solved. The workflow is: Renovate proposes a SHA bump, a human reviews the changelog for the corresponding version, and approves.

The Uncomfortable Position of Security Tools in CI/CD

Beyond the specific mitigations, the Trivy incident points to a structural issue with how security tooling is integrated into pipelines.

Most organizations apply defense-in-depth to their application code: code review, static analysis, dependency scanning, runtime monitoring. But the tools that perform that analysis often run without the same level of scrutiny. Security scanners are typically allowlisted in egress firewalls because they need to fetch vulnerability databases. They run with broad filesystem access because that’s how they scan. They’re often exempted from the monitoring that would flag unexpected network connections because “it’s the security tool, it’s supposed to reach out to external endpoints.”

This creates a blindspot. A malicious version of a security scanner is positioned to do more damage with less detection risk than almost any other category of compromised dependency.

The mitigation here goes beyond signing and pinning. It involves running security tools in isolated environments with explicit, minimal network egress rules: allow traffic to known vulnerability database endpoints, block everything else. It involves monitoring the behavior of scanner processes the same way you’d monitor any other workload. StepSecurity’s Harden-Runner does exactly this for GitHub Actions runners, which is partly why StepSecurity keeps being the one to detect these incidents: they’re watching what processes do at runtime, not just what they claim to be.

What Recurring Incidents Actually Tell Us

When a project is compromised once, it’s a security incident. When it’s compromised twice, it’s evidence of a systemic gap.

The gap could be in the project’s release security practices. It could be in the ecosystem’s tooling: signing and provenance verification exist but aren’t surfaced prominently enough, aren’t required by package distribution systems, and aren’t checked automatically by the consumers. It could be in how organizations think about third-party security tooling, treating it as trusted infrastructure rather than as third-party code that needs the same scrutiny as any other dependency.

The honest answer is probably all three.

The Trivy compromise is worth treating as a prompt to audit your own CI/CD pipelines: check whether your GitHub Action references are SHA-pinned, whether you verify checksums or signatures before running downloaded binaries, whether your security tools run with more network access than they actually need. The tooling to do this properly exists. The habit of using it consistently is what’s still being built.

A security scanner being compromised a second time is a reminder that trust in the supply chain isn’t established once and then held. It requires active maintenance, verification at the point of consumption, and the kind of behavioral monitoring that makes compromise detectable regardless of how legitimate the binary looks from the outside.

Was this interesting?