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.
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.
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:
-
April 22, Checkmarx KICS on Docker Hub, plus
cx-dev-assistandast-resultson Open VSX. Same C2 (audit.checkmarx[.]cx), same dropper delivery mechanism, claimed by TeamPCP via@pcpcatsafter discovery. - 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?
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
-
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 localnode_modulestrees), thennpm 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. -
Rotate every credential class the payload targets.
GitHub PATs and OAuth tokens,
npm tokens,
AWS credentials (any in
~/.aws/or environment), Azure tokens obtainable viaazd, GCP credentials obtainable viagcloud 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. -
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 markerLongLiveTheResistanceAgainstMachines. -
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
preinstallentry inpackage.jsonthat wasn't there before is high-priority. Yank or deprecate anything that looks wrong. -
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 likeformat-results.txt(carryover from Checkmarx-incident hunt items, same toolkit). -
Persistence cleanup, host-level. Remove the
lock file at
/tmp/tmp.987654321.lock. Inspect~/.bashrcand~/.zshrcfor 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 (includingpackage-updated.tgz) and remove them. Then rebuild the host from a known-clean image rather than relying on piecewise cleanup. -
Push the IOCs into your detection. DNS sinkhole
and EDR block on
audit.checkmarx[.]cxand94[.]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 togh auth token/gcloud/az/azdfrom non-interactive contexts, and creation of public GitHub repos matching the Dune pattern. -
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. - 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.
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.
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.