groundy
security

Bitwarden CLI Compromise Extends the Checkmarx Supply-Chain Campaign to Credential Tooling

A trojanized @bitwarden/cli release spent 93 minutes on npm April 22. The Checkmarx-themed payload harvested credentials via preinstall hook, exposing vault session tokens.

11 min···10 sources ↓

A trojanized release of @bitwarden/cli spent roughly 93 minutes on npm on April 22, 2026, before Bitwarden pulled it. Socket’s advisory1, published the following day, links the attack to the Checkmarx-themed supply-chain campaign running through CI and security tooling since at least March. The shared infrastructure is specific: same C2 endpoint, same obfuscation routine, same Dune-flavored dead-drop mechanism. Pivoting to a password-manager CLI after build-time scanners is not accidental. This target class has session tokens and vault-unlocked credentials sitting in process memory.

The 93-Minute Window

Timing differs between sources: Socket’s advisory1 places the malicious publish at 5:22 PM ET; Bitwarden’s community statement2 sets the distribution window from 5:57 PM to 7:30 PM ET on April 22. The gap likely reflects the difference between when the attacker completed the publish and when npm’s CDN propagated it to install requests. Neither figure has been formally reconciled. The same Bitwarden community post estimates roughly 334 downloads2, but marks that as approximate; it is a forum comment, not a post-mortem figure.

No legitimate 2026.4.0 ever existed. Bitwarden replaced the slot with CLI 2026.4.13, described in release notes as a “Re-release of CLI v2026.3.0,” which is the unambiguous signal that the entire 2026.4.0 release was the attacker’s artifact. The Snap package was confirmed unaffected; only the npm distribution path was poisoned.

From CI Artifact to Preinstall Hook

The entry point was a compromised GitHub Action in Bitwarden’s own CI/CD pipeline, the same attack class Socket attributed to the broader Checkmarx campaign1 throughout April 2026. The attacker didn’t need to touch Bitwarden’s source code or signing keys; they needed to land a single malicious file in the published npm package. That file was bw1.js, triggered through a preinstall hook in package.json.

JFrog noted4 that the script downloads the Bun runtime and executes bw1.js through it, keeping the Node.js dependency footprint clean and the payload off disk until after the hook fires.

How the Trusted-Publishing Pipeline Was Turned

[Updated June 2026] The original advisory window left the exact CI failure mode open. Subsequent analysis filled it in, and the detail matters because it is a failure of the mechanism npm has been telling everyone to adopt. According to Phoenix Security’s reconstruction, the attacker pivoted off a compromised Checkmarx KICS Docker image that ran under a pull_request_target workflow, which grants the job repository write context and access to the pipeline’s Azure Key Vault and GitHub OIDC. From there an unsigned commit landed in .github/workflows/publish-cli.yml at roughly 21:18 UTC, and that commit base64-encoded the short-lived npm publishing token into the workflow log. Anyone with read access to the log could decode the token and npm publish as @bitwarden/cli before it expired.

That is the part worth sitting with. Trusted publishing (OIDC) exists specifically to kill long-lived publish tokens: a CI job mints a credential good for one publish, scoped to one package, and it expires in minutes. The pitch is that there is no standing secret to steal. The Bitwarden compromise is, by several vendors’ accounting, the first publicly documented case of that model being defeated, and it was defeated without breaking OIDC at all. The attacker did not forge a token. They got the legitimate pipeline to mint a real one and print it where it could be read. Short-lived only helps if the window to abuse the token is shorter than the time to notice the leak, and a token printed to a log the attacker is already watching closes that gap to zero. Groundy walked through the same structural weakness in the TanStack compromise, where OIDC trusted publishing itself became the attack vector.

What bw1.js Actually Steals

Socket’s advisory1 details the collection targets: GitHub and npm tokens, SSH keys, AWS/Azure/GCP credentials, .npmrc and .env files, shell history, and GitHub Actions secrets. The payload writes shell-RC persistence to ~/.bashrc and ~/.zshrc. It exits without executing if the system locale starts with ru.

The more notable addition is a dedicated module for AI coding-tool configurations: Claude, Cursor, Codex CLI, Aider, and Kiro. Those config files routinely contain API keys and sometimes implicit access to whatever files the developer has open. This isn’t an opportunistic add-on; it is a second-stage credential layer that maps to the current developer toolchain specifically.

The mechanism is more interesting than straight config theft. [Updated June 2026] Later analysis from Palo Alto’s Unit 426 and Phoenix Security7 describes the payload injecting heredoc blocks into shell RC files that the shell never executes but an LLM assistant reading the file as context will happily ingest. The injected text is instruction, not code: it tells a coding agent to leak secrets or backdoor the next commit. That moves the attack from harvesting static keys to poisoning the assistant’s working context, which is a different and harder problem to detect. The 2026 npm waves repeatedly treated AI coding assistants as a supply-chain target in their own right rather than as incidental victims.

Endor Labs’ Kiran Raj5 described the payload as among the most capable npm malware to date, citing multi-cloud harvesting, RSA-signed C2 commands, self-propagating worm logic, and the AI-tool config module. That framing holds on the technical merits, though “most capable ever” always has a short shelf life.

The Checkmarx-Campaign Throughline

Attribution here rests on shared infrastructure and code, not a formal government or vendor attribution report. Socket and others1 point to three specific reuses: the C2 endpoint audit.checkmarx[.]cx/v1/telemetry, the __decodeScrambled obfuscation routine with seed 0x3039, and the exfiltration dead-drop: AES-256-GCM1 encrypted data committed to public GitHub repos under victim accounts, using Dune-themed repo names with the string “Shai-Hulud: The Third Coming” as a marker.

The group has been linked to TeamPCP5, previously connected to Trivy and LiteLLM supply-chain incidents. The @pcpcats X account associated with the group was suspended around the time of the advisory.

One nuance Socket raises: the possibility that this incident involves a different operator reusing shared infrastructure rather than the same team running it. The Shai-Hulud naming appeared in earlier npm worm campaigns, and infrastructure sharing can mean resale or leakage as easily as continuity. Treat the attribution as working hypothesis.

Why the Target Pivot Changes the Blast Radius

The earlier Checkmarx-campaign victims (Trivy, LiteLLM) were build-time tools. An attacker who compromises a scanner gets scan output and whatever environment variables leak from the runner. An attacker who compromises the CLI you use to authenticate against your password manager gets something different: whatever bw had in memory when it ran.

Bitwarden’s statement2 is precise: “no evidence that end user vault data was accessed or at risk,” production systems not compromised. That is accurate as a server-side statement. It does not address what bw1.js could read from the process running on the developer’s machine: session tokens in memory, secrets the CLI had retrieved that session, files the process had access to during execution. Blast radius assessment requires local state, not just Bitwarden’s backend logs.

Remediation Checklist

If anyone in your organization ran npm install -g @bitwarden/cli between approximately 5:22 PM and 7:30 PM ET on April 22, 2026 (Socket and Bitwarden disagree on the start time; use the earlier figure as the conservative bound):

Confirm exposure:

  • Check npm install logs or shell history for @bitwarden/cli installs in that window
  • Search GitHub for new public repos under affected developer accounts containing “Shai-Hulud” in the name
  • Audit recent commits from those accounts for base64-encoded or AES-encrypted blob files

Rotate on affected hosts:

  • GitHub tokens and fine-grained personal access tokens
  • npm publish tokens
  • SSH keys, particularly those loaded into ssh-agent during the window
  • AWS, Azure, and GCP credentials present in environment variables or credential files
  • API keys in .env, .npmrc, and AI coding-tool configs (Claude, Cursor, Codex CLI, Aider, Kiro)

Session token handling: Any bw session token generated during the window should be treated as compromised. If a developer unlocked their vault between those times, credentials retrieved during that session are in scope for rotation regardless of Bitwarden’s server-side assurances.

Persistence cleanup: Audit ~/.bashrc and ~/.zshrc on affected machines for entries added after April 22 that reference unknown binaries or contain base64 strings.

What Happened Next

[Updated June 2026] The Bitwarden CLI hit was not the end of the campaign; it was an early data point in a worm that kept mutating. In May 2026 the same operator infrastructure resurfaced under the label “Mini Shai-Hulud,” a self-propagating worm that, per Tenable’s tracking, spread across roughly 170 packages spanning npm and PyPI with more than 518 million cumulative weekly downloads.8 One wave hit TanStack, publishing 84 malicious versions across 42 packages in under six minutes,8 every one of them carrying valid SLSA Build Level 3 provenance attestations. That is the same lesson the Bitwarden incident taught, scaled up: a cryptographically attested build pipeline attests that the build ran as configured, not that the configuration is trustworthy. Groundy covered the Mini Shai-Hulud variant that shipped malicious npm packages with valid SLSA provenance separately.

The CVE situation is worth correcting against the original advisory. As of late June 2026, no Bitwarden-specific CVE for @bitwarden/cli@2026.4.0 has surfaced publicly; the identifier that did get assigned, CVE-2026-45321, covers the TanStack chain (cache poisoning into OIDC token theft), not the Bitwarden package. The waves continued through June: a June 1 compromise pushed a payload named Miasma through 32 packages in the @redhat-cloud-services npm namespace, which Groundy tracked when malicious npm packages hit Red Hat’s published JavaScript clients, and a mid-June resurgence turned up over 1,600 exfiltration repositories across compromised GitHub accounts.9

The registry-level context sharpens the irony. npm had already revoked classic publish tokens and moved to session-based auth with a hard 90-day cap on granular write tokens by December 2025, steering maintainers toward OIDC trusted publishing as the safe default. The campaign’s answer was to attack the safe default directly. Killing long-lived tokens removed one large attack surface and exposed a smaller, sharper one: the CI workflow that mints the short-lived credential.

Open Questions

Two things remain unresolved.

First, the mechanism response. Researcher Adnan Khan’s early framing5 of this as the first supply-chain compromise of a package using npm’s trusted publishing has since been corroborated by multiple vendors, and the mechanics are now documented above. What is still open is what npm and GitHub do about it. The leak depended on a privileged workflow (pull_request_target with OIDC and key-vault access) printing a real short-lived token to a readable log, and there is no published commitment yet to mask minted publish tokens in logs, restrict OIDC issuance to signed workflow definitions, or otherwise close the print-to-log path that turned a one-shot credential into a reusable one.

Second, attribution consistency. The Checkmarx-branded infrastructure has appeared across enough incidents that the “same campaign” framing deserves scrutiny. If the C2 endpoint and obfuscation seed are available to multiple operators (purchased, leaked, or published), then attributing each incident to TeamPCP may overcount a single adversary’s footprint. The @pcpcats suspension removes some signal. What’s left is infrastructure overlap, which is necessary but not sufficient for identity, and the May-June waves under the “Mini Shai-Hulud” label kept reusing the same dead-drop pattern: encrypted blobs committed to public GitHub repos under victim accounts, a channel that rarely trips outbound DLP because GitHub traffic is almost never on an organization’s blocklist.

Frequently Asked Questions

Are Bitwarden browser extensions, mobile apps, or Snap users affected by this compromise?

No. Bitwarden confirmed the incident was isolated to the npm registry path, and the Snap package was explicitly verified unaffected. Browser extensions, mobile apps, and direct GitHub release binaries were never in scope. [Updated June 2026] Only the single @bitwarden/cli@2026.4.0 version distributed during the roughly 93-minute window on April 22, 2026, carries the malicious bw1.js payload. Note that despite early reports of a CVE being issued for the package, no Bitwarden-specific identifier had surfaced publicly as of late June 2026; the campaign-related CVE that was assigned, CVE-2026-45321, covers the separate TanStack compromise, not the Bitwarden CLI.

How did the broader security vendor response frame this incident, and what angles were under-covered?

Vendors including Socket, JFrog, OX Security, Endor Labs, StepSecurity, Aikido, GitGuardian, Mend, and SafeDep published analyses within 24 hours of the April 23 advisory. Most coverage focused on threat-actor IoCs and Checkmarx-campaign attribution. [Updated June 2026] The under-reported operational angles are the concrete remediation checklist for affected developers and the trusted-publishing failure mode, which later analysis confirmed: the attacker did not break OIDC but got a privileged workflow to mint a real short-lived publish token and write it to a readable log, making this the first publicly documented compromise of an npm package published via trusted publishing.

Which specific AI coding assistants were targeted, and why do their configs represent a distinct risk category?

The payload included a dedicated module for Claude, Cursor, Codex CLI, Aider, and Kiro configurations. Unlike static secrets in .env files, these configs often grant implicit access to the developer’s current workspace and open files, exposing not just API keys but also the context of whatever the developer was actively editing. This transforms the malware from a simple credential scraper into a tool for harvesting live development context, making AI-tool config rotation and workspace auditing part of incident response.

What does the move from Trivy and LiteLLM to Bitwarden CLI signal about future supply-chain targets?

The campaign has pivoted from build-time scanners in March 2026 to a credential CLI in April, targeting memory-resident session tokens rather than static CI secrets. While Socket links the pattern to TeamPCP, the shared C2 endpoint, obfuscation seed, and dead-drop infrastructure suggest the underlying toolkit may be available to multiple operators. That raises the likelihood of copycat attacks against other password managers, VPN clients, or cloud-authentication tools that distribute via npm, regardless of whether the original group remains active.

sources · 10 cited

  1. Socket's advisory: Bitwarden CLI Compromisedsocket.devprimaryaccessed 2026-04-28
  2. Bitwarden community statement on Checkmarx supply chain incidentcommunity.bitwarden.comvendoraccessed 2026-04-28
  3. Bitwarden CLI releasesgithub.comvendoraccessed 2026-04-28
  4. Bitwarden CLI npm package compromised to steal developer credentialsbleepingcomputer.comanalysisaccessed 2026-04-28