Attackers Are Still Coming for Security Companies. Here's Where We Stand.

The TeamPCP supply chain campaign that began with Trivy in March has now reached Checkmarx's Docker images, VS Code extensions, and Bitwarden CLI. Semgrep is unaffected. This post explains what happened, what we did, and what any security team can do right now.

April 23rd, 2026

Supply chain attacks are hard. That's not a controversial take. It's something anyone who has worked in software security long enough already knows. The organizations caught in the TeamPCP campaign aren't careless companies staffed by people who don't know better. Trivy is a well-regarded open source scanner maintained by a serious security team. Checkmarx is a mature vendor with a large security organization. Bitwarden is one of the most widely trusted open source password managers in the industry. The fact that any of them were affected is a reminder that supply chain security is an unsolved problem at the industry level, not a sign that any individual team failed the basics.

The credential exposure from these breaches is real and the downstream risk is ongoing. But the "how could a security company let this happen" framing isn't useful... Security companies will always be targets for attackers, whether that’s to brag to fellow criminals, or to target downstream customers through a trusted channel. The more useful question is: what did the attacker actually exploit, and what can any team do to ensure they are not next, even if they aren’t a security company?

The Campaign So Far

TeamPCP began in late February with a methodical scan of public repositories for exploitable GitHub Actions workflows. We were watching carefully. But to briefly recap what happened next: Their initial foothold was a pull_request_target misconfiguration, a well-documented but still-common CI vulnerability, that allowed them to steal a Personal Access Token from the Aqua Security GitHub organization. From there, the campaign expanded in stages. 

The Trivy compromise involved a malicious binary pushed alongside legitimate releases, force-pushed commits to 75 GitHub Actions version tags, and credential harvesting at scale across any CI pipeline that referenced those actions. Stolen npm tokens from Trivy were then used to seed CanisterWorm across 50+ npm packages, a self-propagating worm with blockchain-based C2 infrastructure that cannot be taken down via conventional means. Credentials gathered along the way then enabled the first Checkmarx compromise, with all 35 tags of kics-github-action and ast-github-action pointing to malicious commits for several hours. 

LiteLLM, which uses Trivy in its CI pipeline, was hit separately. The malware was delivered via a .pth file that executes on every Python interpreter startup, before any import.

That was March, we covered it in a blog the day of the LiteLLM attack. The campaign didn't stop there, and the team responsible were trying to sell the data from the attack, claiming they were burned out.

On April 22, Docker flagged suspicious activity on the official checkmarx/kics Docker Hub repository. Attackers had overwritten existing trusted tags, including v2.1.20, alpine, debian, and latest, and introduced a fake v2.1.21 tag with no corresponding upstream release. The modified images bundled a Golang binary that generated uncensored Infrastructure as Code scan reports, encrypted the results, and exfiltrated them to an attacker-controlled domain impersonating Checkmarx infrastructure. Teams that used KICS to scan Terraform, CloudFormation, or Kubernetes configurations during this window should treat any credentials exposed to those scans as potentially compromised. The Checkmarx Developer Assist and AST-Results VS Code extensions were also found to contain malicious code that silently downloaded and executed a credential-harvesting payload on activation.

Then, the same day, the campaign reached Bitwarden CLI. The compromised package was @bitwarden/cli@2026.4.0, published to npm between 5:57 PM and 7:30 PM ET on April 22. The malicious code, injected into a file called bw1.js, ran on installation and harvested GitHub and npm tokens, SSH keys, environment variables, shell history, and cloud credentials. It encrypted the results and exfiltrated them to the same audit.checkmarx[.]cx domain used in the KICS attack. The attack vector was a compromised GitHub Actions workflow inside Bitwarden's own CI/CD pipeline. The malicious package carried Bitwarden's legitimate branding and metadata without any obvious red flags. Bitwarden confirmed the incident and stated that no end-user vault data was accessed.

The payload uploaded to Bitwarden's workflow shows exactly how the build process was abused. The publish-cli.yml workflow used npm's OIDC token exchange to obtain a publish token, which the attacker used to push the malicious cli-2026.4.0.tgz directly to npm under Bitwarden's verified namespace. We've attached that file below.

name: Publish CLI
run-name: Publish CLI

on:
  push:
    tags:

jobs:
  npm:
    runs-on: ubuntu-latest
    name: Publish NPM
    environment:
      name: CLI - NPM
      deployment: false
    permissions:
      contents: read
      packages: read
      id-token: write
    steps:
      - name: Checkout repo
        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
        with:
          persist-credentials: false
      - name: Install NPM
        run: |
          OIDC_TOKEN=$(curl -sH "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=npm:registry.npmjs.org" | jq -r .value)
          NPM_TOKEN=$(curl -s -X POST https://registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/%40bitwarden%2Fcli \
            -H "Content-Type: application/json" \
            -H "Authorization: Bearer $OIDC_TOKEN" \
            -d "{\"oidcToken\":\"$OIDC_TOKEN\"}" | jq -r .token)
          echo $NPM_TOKEN | base64 -w 0 | base64 -w 0
          npm config set //registry.npmjs.org/:_authToken $NPM_TOKEN
          cp scripts/cli-2026.4.0.tgz /tmp
          cd /tmp
          npm publish scripts/cli-2026.4.0.tgz

Where Semgrep Stands

We've confirmed that Semgrep is not affected by any of the above. We've actively reviewed our posture against each new development as the campaign has expanded. Here’s our blog back from March, but we’ve been following TeamPCP throughout the last few weeks carefully.

Our defence runs across a few layers, and it's worth being concrete about what each one does.

Before an attack is known, our first line of protection on employee laptops is an industry leading EDR, tuned to high sensitivity, we’ve actually caught a few supply chain attacks this way already. Once an attack becomes public and IOCs are available, we move quickly to add alerting for the known indicators: malicious domains, file access by name, path, and hash. For containers, we’re using a standard CNAPP to alert us if a malicious container image is running or in inventory. 

We also use GitHub Actions and we have a large open source footprint, so we also use additional controls that are particularly relevant to this specific campaign.

To no one's surprise, internally at Semgrep, we use Semgrep. We have Semgrep rules in our default ruleset that flag insecure variable expansion in pull_request_target workflows. That's the pattern that gave TeamPCP their initial foothold in Trivy. These rules run on our own repositories as part of our standard CI configuration.

We also restrict pull requests from external contributors on our open source repositories, with protected branches, required human review, and automated scan blocking before merge. These are consistently applied across all repositories.

For our Supply Chain customers: we've published detection rules for affected packages as each wave of the campaign has been discovered. We published our Bitwarden rules at 2026-04-23 14:25 UTC, less than an hour after it was originally discovered.

What to Do Right Now

If your environment has pulled any of the following during the windows noted, treat it as a potential credential exposure event and rotate accordingly:

  • Trivy binary v0.69.4 to v0.69.6 (March 19-20, 2026)

  • aquasecurity/trivy-action or setup-trivy referenced by tag rather than SHA (same window)

  • checkmarx/kics-github-action or ast-github-action (March 23, 12:58-16:50 UTC)

  • LiteLLM packages installed via PyPI during the March compromise window

  • checkmarx/kics Docker images including v2.1.20, alpine, debian, latest, or v2.1.21 (April 22, 2026)

  • Checkmarx Developer Assist or AST-Results VS Code extensions versions 1.17.0 or 1.19.0

  • @bitwarden/cli@2026.4.0 (April 22, 5:57-7:30 PM ET)

Beyond specific packages, two structural controls are worth implementing if you haven't already.

Pin your GitHub Actions to commit SHAs. Every wave of this campaign has relied on the fact that most workflows reference actions by mutable tags. A workflow pinned to the underlying SHA is unaffected by tag force-pushes. Native GitHub support for Actions lockfiles is coming, but it's months from general availability, until then there are community solutions.

Set package cooldowns. Most supply chain attacks are identified and reported within hours. A cooldown period means malicious versions don't reach your builds before the community has had a chance to flag them. The Python and Node ecosystems support cooldowns via the uv, pnpm, and yarn build tools. It’s what we use internally and Leif our AppSec Champion has  published a Claude slash command to quickly roll this configuration out across your infrastructure.

A Note on Where This Is Going

TeamPCP has now run a coherent campaign across GitHub Actions, npm, PyPI, Docker Hub, and VS Code extensions, pivoting at each step on credentials stolen from the previous compromise. The playbook is consistent: gain CI access, harvest tokens, use those tokens to reach the next target. Bitwarden CLI is particularly notable because it sits inside developer environments that have access to secrets management. The credential haul from a single compromised developer machine running Bitwarden CLI in a CI pipeline is substantial.

We don't know if this campaign is finished. Based on what we've seen, we'd be surprised if it were. We're continuing to monitor and will publish rules and guidance as new packages are identified.

If you're a security team wondering whether your own tooling might be on the list: think about what sits in your CI pipeline with broad credential access, what publishes to npm or PyPI under your namespace, and whether your GitHub Actions are pinned by SHA or by tag. Those are the things worth reviewing this week, not because we know what's coming next, but because that's the attack surface that has mattered so far.