HIGHSecurity · Supply-chain · npm · CI/CD · April 2026

Bitwarden CLI 2026.4.0 Compromised on npm, What to Do If You Installed It

By NewMaxx /April 23, 2026

Socket Research reports that @bitwarden/cli version 2026.4.0 on npm was compromised, with malicious code published in bw1.js and executed at install time via a preinstall hook. Per Bitwarden's April 23 statement, the malicious package was live on the npm delivery path for roughly 90 minutes, between 5:57 PM and 7:30 PM ET on April 22, 2026; users who did not download during that window were not affected, and Bitwarden says no evidence was found that end-user vault data or production systems were compromised. Socket attributes the breach to what appears to have been a compromised GitHub Action in Bitwarden's CI/CD pipeline, the same supply-chain vector pattern Socket identified in the Checkmarx KICS compromise one day earlier. The malicious code targets developer credentials, cloud secrets, GitHub tokens, npm tokens, and Claude/MCP configuration files, and propagates through npm token theft and injected GitHub Actions workflows. The Bitwarden Chrome extension, MCP server, and other distributions are reported unaffected.

Indicators of Compromise, per Socket
Malicious package: @bitwarden/cli == 2026.4.0 Malicious file inside the package: bw1.js Execution: runs at install time via preinstall hook Affected npm window (per Bitwarden): 2026-04-22, 5:57 PM to 7:30 PM ET (~90 minutes) Users who did not pull during that window were not affected. Recommended downgrade target: @bitwarden/cli == 2026.3.0 (Or use official signed binaries from bitwarden.com) Network indicators: Exfiltration endpoint (primary): https://audit.checkmarx[.]cx/v1/telemetry C2 IP: 94[.]154[.]172[.]43 Fallback exfil channel: Public GitHub repos created under victim's own account; encrypted blobs committed, tokens in commit messages. (Same infrastructure as the Apr 22 Checkmarx KICS compromise.) Host-side indicators: Lock file: /tmp/tmp.987654321.lock Staging path: /tmp/_tmp_/ Republish artifact: package-updated.tgz Shell profile injection: ~/.bashrc, ~/.zshrc Specific AI/MCP config targets (per reporting): ~/.claude.json ~/.kiro/settings/mcp.json Runtime mechanism: Bun v1.3.13 downloaded from GitHub releases Russian-locale kill switch: payload exits silently if Intl.DateTimeFormat().resolvedOptions().locale starts with "ru" or LC_ALL/LC_MESSAGES/LANGUAGE/LANG match. Exfil-repo branding (visible on GitHub): Repo description: "Shai-Hulud: The Third Coming" Commit-message marker: LongLiveTheResistanceAgainstMachines Debug string: "Would be executing butlerian jihad!" Repo names follow Dune-themed pattern: {word}-{word}-{3digits} Word list (per Socket): atreides, cogitor, fedaykin, fremen, futar, gesserit, ghola, harkonnen, heighliner, kanly, kralizec, lasgun, laza, melange, mentat, navigator, ornithopter, phibian, powindah, prana, prescient, sandworm, sardaukar, sayyadina, sietch, siridar, slig, stillsuit, thumper, tleilaxu
Network IOCs belong in DNS sinkhole, EDR, and SIEM rules immediately. The Dune-themed repo word list is for searching your GitHub org for unauthorized public repositories created under your accounts.
What the Bitwarden CLI payload actually does, per Socket and JFrog

The bw1.js payload is executed at install time via a preinstall hook (per JFrog), meaning it runs on any npm install that resolved to the malicious version, not only on explicit CLI invocation. It shares core infrastructure with the mcpAddon.js from the Checkmarx incident: same C2 endpoint, same gzip+base64 embedded payloads, same __decodeScrambled obfuscator with seed 0x3039. Targets include: GitHub tokens via Runner.Worker memory scraping and environment variables, AWS credentials via ~/.aws/ files and environment, Azure tokens via azd, GCP credentials via gcloud config config-helper, npm tokens via .npmrc, SSH keys, environment variables, shell history, and Claude/MCP configuration files (reporting explicitly names ~/.claude.json and ~/.kiro/settings/mcp.json).

Exfiltration is multi-channel: the primary path is the audit.checkmarx[.]cx endpoint; if that is blocked, the malware falls back to GitHub itself, committing encrypted result blobs to public repositories created under the victim's own account (Dune-themed naming pattern in the IOC block), with tokens embedded in commit messages using the LongLiveTheResistanceAgainstMachines marker. Per public reporting, stolen data is encrypted with hybrid AES-256-GCM + RSA before exfiltration. The malware also steals npm tokens, identifies writable packages, and republishes them with injected preinstall hooks, that's the self-propagation mechanism.

Status snapshot, 23 April 2026

Confirmed by Bitwarden (April 23 statement): a malicious package was briefly distributed through the npm delivery path for @bitwarden/cli@2026.4.0 between 5:57 PM and 7:30 PM ET on April 22, 2026 (a roughly 90-minute window), in connection with the broader Checkmarx supply-chain incident. Bitwarden's investigation "found no evidence that end user vault data was accessed or at risk, or that production data or production systems were compromised." Compromised access was revoked, the malicious release was deprecated, and Bitwarden says it has reviewed internal environments, release paths, and related systems with no additional impacted products or environments identified at this time. A CVE for @bitwarden/cli@2026.4.0 is being issued. Users who did not download the package from npm during that window were not affected.

Confirmed by Socket: @bitwarden/cli 2026.4.0 was published with malicious bw1.js, executed at install time via a preinstall hook (per JFrog), with the C2 infrastructure and payload structure shared with the Apr 22 Checkmarx KICS compromise. Bitwarden's Chrome extension, MCP server, and other distributions are reported unaffected.

Reported initial-access vector: Socket says the attack "appears to have leveraged" a compromised GitHub Action in Bitwarden's CI/CD pipeline, consistent with the pattern across the campaign. Bitwarden's own statement does not explicitly name the initial-access mechanism. Treat the compromised-Action framing as Socket's reported likelihood, not a Bitwarden-confirmed root cause. Security researcher Adnan Khan has separately described this as the first known compromise of a package using npm's Trusted Publishing mechanism, which is worth noting because it complicates the standard "use OIDC trusted publishing to eliminate long-lived tokens" defense.

Attribution nuance: Socket frames this as part of the same ongoing Checkmarx campaign because the C2 infrastructure, obfuscator, and embedded payload structure are identical to the Checkmarx KICS compromise. However, Socket explicitly notes the operational signatures differ in ways that complicate attribution: the Checkmarx attack was claimed by TeamPCP via the @pcpcats account after discovery, whereas this Bitwarden payload embeds explicit ideological branding directly in the malware (the Shai-Hulud / Butlerian Jihad / Dune manifesto strings). Socket suggests this could be a different operator using shared infrastructure, a splinter group, or an evolution in posture. Treat the campaign-association as Socket's call, not settled attribution.

Still unclear at time of writing: full hash IOCs for bw1.js, the identity of the compromised GitHub Action (if that is indeed the initial-access vector), and the CVE identifier that Bitwarden says is being issued.


Why this one matters more than most

The Bitwarden CLI is installed in a lot of developer environments. Bitwarden serves more than 10 million users and over 50,000 businesses, and ranks among the top password managers by enterprise adoption. The CLI specifically lives where credentials live: in CI runners that pull secrets at build time, in dev environments where engineers script around their vault, in scripts that orchestrate password rotations, in MCP servers integrated with AI agents.

None of that means your Bitwarden vault contents were directly accessed by this malware, the payload Socket describes does not target the vault itself; it harvests the developer-environment credentials sitting around the CLI's host. But the population of machines that run bw is the population of machines that hold the highest-value collateral: GitHub PATs, cloud credentials, npm tokens, signing keys, MCP configurations. A poisoned CLI in this position is a high-impact catch.


How this fits the broader campaign

Two days, two compromises with shared infrastructure and shared tooling:

  1. April 22, Checkmarx KICS on Docker Hub, plus cx-dev-assist and ast-results on Open VSX. Same C2 (audit.checkmarx[.]cx), same dropper delivery mechanism, claimed by TeamPCP via @pcpcats after discovery.
  2. April 23, Bitwarden CLI on npm. Same C2, same obfuscator, same gzip+base64 embedded payload structure, but with new persistence mechanisms (lock file, shell profile injection), a Russian-locale kill switch, and explicit ideological branding embedded in the malware itself.

The shared infrastructure says "same toolkit." The differing operational posture (TeamPCP claim-and-blend vs. ideological manifesto baked into the payload) is what Socket flags as complicating attribution. Whatever the exact relationship between operators, the practical implication for defenders is: more packages on the same pipeline-compromise pattern are likely. If you depend on npm packages whose CI/CD runs on GitHub Actions using third-party actions, you're inside the blast pattern.


Am I affected?

Check first, then act

Per Bitwarden, only users who pulled @bitwarden/cli@2026.4.0 from npm between 5:57 PM and 7:30 PM ET on April 22, 2026 are affected. If an npm install resolved to that version during that roughly 90-minute window, anywhere, developer workstation, CI runner, MCP server, container build, treat the host as a credential-exposure and CI/CD compromise event. Because the malicious code ran via a preinstall hook, simply having the package arrive on the host is enough; explicit bw execution is not required.

Quick checks

On any host you suspect:

npm ls -g @bitwarden/cli 2>/dev/null | grep 2026.4.0

npm ls @bitwarden/cli 2>/dev/null | grep 2026.4.0

Search lockfiles and CI logs across the whole environment:

grep -rE '@bitwarden/cli[@"]? *2026\.4\.0' . 2>/dev/null

For the lock file dropped by the payload:

ls -la /tmp/tmp.987654321.lock 2>/dev/null && echo "FOUND, host likely compromised"

Also check shell profiles for unexpected appended blocks:

tail -n 50 ~/.bashrc ~/.zshrc 2>/dev/null

The currently published known-good version on npm is @bitwarden/cli@2026.3.0; the signed binaries from bitwarden.com are also an option and sidestep the npm delivery path entirely.


Response, if you installed 2026.4.0

  1. Isolate the host, remove the package, clean the cache. Pull the host off the network or block outbound traffic. Run npm uninstall -g @bitwarden/cli (and from any local node_modules trees), then npm cache clean --force. Reinstall from a known-good version (npm install -g @bitwarden/cli@2026.3.0) or switch to the signed binaries from bitwarden.com to avoid the npm delivery path entirely. Rebuild any CI runner images that were built off the malicious version. Block outbound traffic to audit.checkmarx[.]cx and 94[.]154[.]172[.]43 at your perimeter.
  2. Rotate every credential class the payload targets. GitHub PATs and OAuth tokens, npm tokens, AWS credentials (any in ~/.aws/ or environment), Azure tokens obtainable via azd, GCP credentials obtainable via gcloud config config-helper, SSH private keys on the host, all environment variables containing secrets, and Claude/MCP configuration files (rotate any API keys those configs hold). For CI runners: repository secrets, deploy keys, environment-scoped secrets, OIDC federations the runner was authorized for. For npm specifically, also revoke and reissue any classic tokens; consider switching to granular access tokens scoped to specific packages.
  3. Hunt for unauthorized GitHub repositories under your accounts. The malware creates public repos with Dune-themed names matching the pattern {word}-{word}-{3digits} using the word list in the IOC block (atreides, fedaykin, fremen, harkonnen, mentat, sandworm, sietch, etc.). Search every account that had a token on an affected host for any newly-created public repo, and check repo descriptions for "Shai-Hulud," "Butlerian," or "Resistance" strings. Search commit messages across your organizations for the marker LongLiveTheResistanceAgainstMachines.
  4. Audit npm for unauthorized publishes and injected preinstall hooks. Per Socket, the malware steals npm tokens specifically to identify writable packages and republish them with injected preinstall hooks. For every npm package your organization publishes, audit recent versions: any unexpected publish in the last 24-48 hours is suspect, and any version with a new preinstall entry in package.json that wasn't there before is high-priority. Yank or deprecate anything that looks wrong.
  5. Audit GitHub Actions for injected workflows and unauthorized runs. Look for new files under .github/workflows/ on transient or unexpected branches. Review recent workflow runs for unauthorized triggers, unusual artifact downloads, and unexpected secret use. Search for artifact names like format-results.txt (carryover from Checkmarx-incident hunt items, same toolkit).
  6. Persistence cleanup, host-level. Remove the lock file at /tmp/tmp.987654321.lock. Inspect ~/.bashrc and ~/.zshrc for injected payload blocks and remove them; on multi-user hosts, check every user's profile. Inspect /tmp/_tmp_* staging directories for any leftover payload artifacts (including package-updated.tgz) and remove them. Then rebuild the host from a known-clean image rather than relying on piecewise cleanup.
  7. Push the IOCs into your detection. DNS sinkhole and EDR block on audit.checkmarx[.]cx and 94[.]154[.]172[.]43. SIEM detections for: presence of /tmp/tmp.987654321.lock, Bun runtime invocations from non-Bun-using projects, reads of .npmrc / .git-credentials / .env / cloud credential stores, unexpected calls to gh auth token / gcloud / az / azd from non-interactive contexts, and creation of public GitHub repos matching the Dune pattern.
  8. Pin third-party GitHub Actions to commit SHAs, not tags. Per Socket's own framing, the initial access vector here was a compromised GitHub Action in Bitwarden's CI/CD pipeline. The structural defense is to reference Actions by immutable commit SHA (uses: org/action@<sha>) rather than mutable tag (uses: org/action@v3), review the Action's source at the SHA you pin, and update deliberately. Same logic as Docker-image digest pinning, applied to Actions.
  9. Document what was rotated. Keep a record of every credential rotated and when. If an incident surfaces in weeks, you want to be able to answer "rotated April 23 at 18:00 UTC" without reconstructing it from memory.
If you publish npm packages

The propagation mechanism here is npm-token-driven republishing of other packages. If your org publishes anything to npm and any of your tokens lived on a host that ran the malicious Bitwarden CLI, your packages are in scope, audit recent publishes and revoke tokens before adversaries use them downstream.


The broader lesson

Two takeaways beyond the immediate response:

First, the supply-chain vector here, per Socket's reading, is third-party components inside the CI/CD pipeline itself, not a stolen publisher token or a hijacked maintainer account. Socket says the attack "appears to have leveraged" a compromised GitHub Action inside Bitwarden's release pipeline, which then injected the malicious bw1.js into the build artifact and published it through Bitwarden's legitimate release path. Worth noting: security researcher Adnan Khan has separately described this as the first known compromise of a package using npm's Trusted Publishing (OIDC-based) mechanism. That's significant because npm's Trusted Publishing was designed specifically to eliminate long-lived publishing tokens, which is the defense I (and others) have been recommending across earlier posts in this series. Trusted Publishing still helps, but this incident shows it is not a complete mitigation on its own when the pipeline stages upstream of the publish step are themselves compromisable.

The controls that reduce risk and blast radius for this class of incident stack together rather than substituting for each other: SHA-pin third-party Actions rather than referencing them by mutable tag, tighten workflow permissions to the minimum, gate publishes behind a publish-environment with branch restrictions and mandatory approval, use short-lived credentials where possible, restrict who can push to release branches, and monitor for new public repositories or workflow changes created outside normal release processes. None of those individually is a "would have prevented this" claim, the public evidence on the specific initial-access vector is not yet complete enough to say that, but together they narrow the window.

Second, the self-propagation mechanism (npm token theft to republish writable packages with preinstall hooks) means this campaign is not bounded by the attacker's initial target list. Every developer credential the malware harvests becomes a possible next-hop publishing identity. That is how a campaign that started with one compromised pipeline turns into a wave of small, distributed compromises across the npm ecosystem within days.

One-line takeaway

If any npm install in your environment pulled @bitwarden/cli@2026.4.0 between 5:57 PM and 7:30 PM ET on April 22, 2026: isolate the host, rotate every developer and cloud credential it touched (GitHub, npm, AWS, Azure, GCP, SSH, Claude/MCP), check for the lock file /tmp/tmp.987654321.lock and shell-profile injection, hunt your GitHub org for Dune-themed public repos, audit your published npm packages for unauthorized republishes, downgrade to 2026.3.0 (or use the signed binaries from bitwarden.com), and tighten third-party Action use in release pipelines going forward.

All Bulletins ↑ Primary source: Socket Research →