Skip to content
Supply ChainCI/CDnpmcredential-theftvulnerability

Bitwarden CLI Compromise: Tagged Docker Images, Dependabot, and the Real Supply Chain Lesson

7 min read
Share

bitwarden CLI got compromised because dependabot pulled a tagged docker image. the lesson is bigger than bitwarden.

on april 22 between 5:57pm and 7:30pm eastern, the @bitwarden/[email protected] npm package was distributed with malicious code. socket and JFrog co-published the disclosure. the payload sat in a file called bw1.js and ran inside the npm preinstall hook, which means a developer running npm install executed the malware before any of bitwarden's source actually landed on disk. it scraped credentials and ran for 93 minutes before the malicious release was deprecated.

bitwarden's investigation is clear: no end-user vault data was accessed, no production system was compromised, and remediation revoked the access path within hours of detection. that part of the story is well-handled.

but the structural part is more important than the incident handling, and it's worth working through carefully because most CI/CD threat models still don't enumerate this exact pivot route.

how the malware got into bitwarden's pipeline

the original compromise was a checkmarx KICS docker hub repo. teamPCP, the supply-chain operator that's been publicly tracked since the trivy compromise earlier this year, pushed a malicious checkmarx/kics:latest image to docker hub on april 22. they had valid publisher credentials, so the push wasn't blocked.

bitwarden's CI/CD includes dependabot configured to update upstream container images automatically. dependabot pulled the new checkmarx/kics:latest image into bitwarden's pipeline. when CI ran, the malicious image executed, and the payload had access to the runner's environment, including the credentials that bitwarden uses to publish to npm.

the npm publish that produced @bitwarden/[email protected] happened from a runner that was, at that moment, running an attacker-controlled docker image.

this is a transitive supply-chain pivot. the visible artifact (bitwarden's own published cli) was clean from bitwarden's perspective. the malicious code came from an upstream container image that bitwarden had every reason to trust, because checkmarx is a security vendor and KICS is a security tool.

what the payload actually did

the bw1.js file harvested:

  • github tokens via runner.worker memory scraping
  • AWS credentials from ~/.aws/
  • azure tokens via the azd cli
  • google cloud credentials via gcloud
  • npm configuration files
  • SSH private keys
  • environment variables
  • claude configuration files
  • MCP (model context protocol) configuration files

where the malware found github tokens, it weaponized them: it injected a malicious actions workflow into the affected repository, which let the attacker extract additional CI/CD secrets at next pipeline run, and persisted across credential rotation if the rotation didn't invalidate the existing token cache.

the inclusion of claude config and MCP config files in the harvest scope is the new detail this cycle. AI tooling secrets are now part of the standard credential-theft kit. if your threat model still treats ~/.claude/config.json and your MCP server tokens as a separate, lower-tier category from production AWS keys, this is the moment to revisit.

the campaign behind it

the SANS internet storm center daily diary on april 27 frames the broader context. teamPCP took a 26-day pause through most of april after their march checkmarx GitHub Actions compromise. on april 22, the pause ended and they ran three concurrent compromises:

  • the checkmarx KICS docker hub compromise (the one that pivoted into bitwarden)
  • a poisoning of the xinference pypi package (parallel hit on the AI infrastructure stack, this is the LLM serving side)
  • the first artifacts of a self-propagating npm worm tracked as CanisterSprawl, with first observed activity on april 21

the cadence is now "burst, pause, burst again." a fortnight of operational silence is not a sign of attrition. operators retain full capability. the diary's read is that they've shifted back from credential-monetization mode (selling stolen tokens) into active-compromise mode (planting new payloads).

why the pivot route works

dependabot exists to keep dependencies up to date automatically. that's a security best practice for CVE-driven updates. but the abstraction that dependabot operates over (upstream identifier plus version range or tag plus pull) treats container images and npm packages and pypi packages as if they're all the same kind of artifact. they're not.

a tagged docker image is a mutable pointer. checkmarx/kics:latest in october 2025 was a different thing than checkmarx/kics:latest in april 2026. when dependabot pulls "the latest version", it's not pulling a specific artifact, it's pulling whatever is currently associated with that tag. if an attacker gains push access to that tag for 90 minutes, every dependabot-driven CI run during that window pulls the attacker's payload.

this is the pivot route. it has three components:

  1. the upstream tag is mutable. solved by pinning by digest (sha256:abc123...) rather than tag.
  2. the consumer's automation pulls eagerly. solved by manual review for security-tooling images, or by configuring dependabot to require approval before pulling new versions of high-trust upstream artifacts.
  3. the consumer's CI/CD has access to production credentials. solved by least-privilege CI/CD where the runner that pulls images doesn't have publishing tokens for the consumer's own packages.

most teams haven't done all three. some haven't done any. the bitwarden compromise is what happens when the consumer is a security-conscious org that did most of these things mostly right, and still got pivoted.

what to do this week

practical steps:

  1. audit your dependabot configuration. enumerate every upstream container image you pull. for any image you can't justify pinning by tag, pin by digest. for any security-tooling image (trivy, KICS, snyk, etc.), require approval before update.
  2. rotate any credentials that touched a CI/CD runner that pulled a checkmarx/kics image since april 22. include npm publish tokens, AWS deployment keys, github tokens, container registry tokens.
  3. check your claude and MCP configurations. if your devs run claude code or any MCP-enabled tooling on the same machine they run npm, assume those configs were exposed. rotate all MCP server tokens, regenerate any anthropic API keys with sufficient scope to matter.
  4. add a CI/CD audit pass for the april 21 to april 26 window specifically. look for new github actions workflows committed to your repos by automated accounts, especially ones that exfiltrate to unusual destinations.
  5. subscribe your security team to the SANS ISC daily diary RSS. the april 27 update on this campaign was the cleanest contemporaneous picture available.

the underlying point

the supply chain is not a single chain. it's a graph. every node in the graph is a potential pivot point, and the edges that look administratively boring (dependabot updates, CI/CD container image pulls, transitive dependency resolution) are the edges that operators target now, because the well-known nodes (signed packages, code review on the visible artifact, primary publisher MFA) are mostly hardened.

bitwarden's CLI is a visible, well-monitored, well-scoped artifact. the supply-chain compromise didn't go through any of those controls. it went through an upstream container image pulled by tag, by automation, on a runner that had the wrong credential scope. the durable lesson is: pin by digest, audit dependabot scope, treat security tooling as a high-value target, and treat AI tooling secrets as production credentials.

teamPCP will be back. the next campaign will have a different shape. but the pattern of "find a transitive route through someone else's automation, pivot through the credential the automation has access to, harvest broadly" is the durable shape of this category. plan accordingly.

Gigia Tsiklauri is a Security Architect and founder of Infosec.ge. Get in touch if your CI/CD supply-chain story needs a second pair of eyes.

Related reading

Checkmarx KICS, Round Two: Eighty Minutes to Trojanized DockerPortableText [components.type] is missing "span"

Marimo on KEV and the AI Supply Chain Has ArrivedPortableText [components.type] is missing "span"

Vercel x Context.ai, Week Two: Trend Micro Names the OAuth GapPortableText [components.type] is missing "span"

Sources

Socket: Bitwarden CLI compromise primary disclosurePortableText [components.type] is missing "span"

JFrog Security Research: TeamPCP campaign spreads to npm via hijacked Bitwarden CLIPortableText [components.type] is missing "span"

The Hacker News: Bitwarden CLI compromised in ongoing Checkmarx supply chain campaignPortableText [components.type] is missing "span"

BleepingComputer: Bitwarden CLI npm package compromised to steal developer credentialsPortableText [components.type] is missing "span"

Bitwarden community statement on Checkmarx supply chain incidentPortableText [components.type] is missing "span"

SANS ISC Update 008 (Kenneth Hartman, April 27 2026): TeamPCP 26-day pause ends with three concurrent compromisesPortableText [components.type] is missing "span"

Wiz Cloud Threat Retrospective 2026 (TeamPCP multi-stage cred harvest analysis)PortableText [components.type] is missing "span"