Skip to content

Back to the catalog

The Bitwarden CLI Supply Chain Attack: What Happened and What to Do | Blog | Endor Labs
endorlabs.comwriteup

The Bitwarden CLI Supply Chain Attack: What Happened and What to Do | Blog | Endor Labs

Analysis of a supply-chain compromise of @bitwarden/cli version 2026.4.0, published to npm for ~1.5 hours on April 22, 2026, which added a preinstall script (bwsetup.js) that downloads a Bun runtime and executes a 10MB obfuscated payload (bw1.js) harvesting CI secrets like SSH keys and API tokens and exfiltrating them to attacker-created public GitHub repos in victims' namespaces. The compromise is linked to a prior breach of Checkmarx's GitHub Actions (checkmarx/ast-github-action), used in Bitwarden's repo, and the payload includes CI/CD platform detection across 30+ platforms and a Russian-locale self-exclusion check consistent with Eastern European threat actor tooling, framed as part of the "Shai-Hulud" campaign lineage.

TL;DR Malicious @bitwarden/cli 2026.4.0 npm release used a Bun-based obfuscated payload to steal CI secrets, traced back to a Checkmarx GitHub Actions breach.
Document

What it is

A supply-chain compromise of the npm package @bitwarden/cli, malicious version 2026.4.0, live on npm ~1.5 hours (5:57–7:30 PM ET, April 22, 2026). Bitwarden attributes it to the recent breach of Checkmarx's GitHub Actions, specifically checkmarx/ast-github-action, which is used in Bitwarden's own repo. The summary covers the campaign framing; what follows are the concrete artifacts and remediation the page adds.

How it works

The malicious version adds two files absent from 2026.3.0: bwsetup.js and bw1.js. package.json is modified to add a preinstall script (node bwsetup.js) and to remap the bw bin entry from build/bw.js to bwsetup.js — so both install-time and every later bw invocation route through the bootstrap. bwsetup.js is plain Node ESM: it checks for an existing Bun (execFileSync("bun", ["--version"])), and if absent downloads Bun 1.3.13 from the official GitHub release ZIP, extracts the binary under the package directory, and runs bw1.js with it. bw1.js is a single-line, ~10 MB (10,154,904 bytes) javascript-obfuscator bundle: array-rotation IIFE, a 43,436-entry string table (_0x1ee1), and a second scrambled-alphabet cipher (seed 0x3039) hiding the C2 domain, shell commands, and credential paths.

Anti-analysis gates run before any malicious activity:

  • mz0() — exits cleanly if locale or LC_*/LANG/LANGUAGE starts with ru.
  • tz0() — a switch over 30+ CI/CD fingerprints (GITHUB_ACTIONS, GITLAB_CI, CIRCLECI, JENKINS_URL, TRAVIS, BUILDKITE, cloud build IDs) treated as "allowed to run."
  • Single-instance guard FE — a lock file at tmpdir() + "tmp.987654321.lock", checked via process.kill(pid, 0).

Preconditions / Affected

  • @bitwarden/cli@2026.4.0 installed or resolved during the ~1.5-hour window on April 22, 2026.
  • CI/CD environments are the intended target — the payload is built to run in CI and self-excludes elsewhere.
  • Note: an existing Bun install can cause bwsetup.js to return early, though bw1.js still ships beside it.

What to do

  • Last known-clean release: @bitwarden/cli@2026.3.0. Fixed re-release: @bitwarden/cli@2026.4.1 (Apr 23, 2026, 4:45 PM GMT+2). Pin to one of these.
  • Hunt for the artifacts: files bwsetup.js / bw1.js, a preinstall running node bwsetup.js, a bin mapping bw to bwsetup.js, and the lock file tmp.987654321.lock in $TMPDIR.
  • If exposed, rotate CI secrets (SSH keys, API tokens) and check victims' GitHub namespaces for attacker-created public repos holding exfiltrated data.

Caveats

The page is explicitly a developing story. It also flags an internal inconsistency: the blog claims non-CI plus failed daemonization leads to exit, but tz0() reads as an allowlist of CI environments — the exact run/no-run logic outside CI is unresolved. The credential-harvesting section ("Seven independent collectors") is cut off in the fetched text.

Organisations named here

  • checkmarx/ast-github-action
  • Shai-Hulud

Technologies named here

  • npm
  • GitHub Actions
  • Bun
  • Node.js
  • Bitwarden
  • Bitwarden CLI
  • Checkmarx
  • javascript-obfuscator