It Happened Again
On April 22, 2026, the official Bitwarden CLI npm package (@bitwarden/cli) was compromised. For roughly 90 minutes, between 5:57 PM and 7:30 PM ET, anyone who ran npm install @bitwarden/cli received a malicious package. Around 334 developers did exactly that.
The attackers did not break into Bitwarden’s npm account directly. Instead, they hijacked a GitHub Actions workflow in Bitwarden’s CI/CD pipeline and weaponised npm’s Trusted Publishing mechanism to push a poisoned release. Trusted Publishing is OIDC-based and requires no stored credentials: it was introduced as a hardening measure after credential-based attacks. It became the entry point.
The malicious preinstall scripts (bw1.js and bwsetup.js) ran before anyone typed a single CLI command. They harvested SSH keys, GitHub tokens, npm tokens, cloud provider credentials, .env files, and more: anything sitting in the environment. They also specifically targeted credentials for AI coding tools: Claude, OpenAI Codex, Gemini CLI, Cursor, and others. The exfiltrated data was encrypted and sent to audit.checkmarx[.]cx, a typosquatted domain, and written to Dune-themed public repositories created in the victims’ own GitHub accounts. If a valid GitHub token was found, the malware attempted to inject malicious workflows into other repositories, making the blast radius potentially worm-like within an organisation.
The attack is attributed to a group called “TeamPCP” and is part of a broader campaign named “Shai-Hulud” (a Dune reference), which has been running for months.
Bitwarden detected and yanked the package. A clean version followed. The incident window was 90 minutes.
But I am not here to write another incident report. What I want to do is step back, look at the pattern, and talk about what you can actually do about it.
This Is Not New: The Pattern of the Last Few Months
The Bitwarden attack is the latest link in a chain. The months leading up to it were dense with similar incidents.
Notepad++ was targeted by the Lotus Blossom group (also known as Lotus Panda) between mid-2025 and early 2026. The attackers did not exploit a bug in the editor itself: they compromised the shared hosting infrastructure and hijacked the update channel. A small number of targets, mainly government, telecom, and critical infrastructure organisations in Southeast Asia and Central America, received trojanised installers carrying Cobalt Strike beacons and custom backdoors. The general public was not affected, but the technique was precise and persistent: the attackers maintained access for six months using stolen internal credentials.
Trivy, Aqua Security’s widely used container vulnerability scanner, was compromised in March 2026. The GitHub Actions pipeline was hijacked via spoofed release tags, and poisoned Docker images were distributed. Because Trivy is used inside build pipelines at thousands of organisations, the attack had an outsized potential blast radius: a compromised scanner sitting inside your CI/CD is not just a threat to the build; it is a threat to everything the build touches. Aqua and the community caught it. The campaign is attributed to the same TeamPCP group.
Axios, the JavaScript HTTP library with well over 100 million weekly downloads, was hit in the same March 2026 wave. A maintainer account on npm was compromised, and a malicious release was pushed with a postinstall credential stealer targeting AWS and GCP secrets, Kubernetes credentials, and any cloud service token present in the environment. Given Axios’s download numbers, even a brief window of exposure has enormous reach. LiteLLM and Telnyx fell in the same wave, with attackers cascading from one compromised project to the next using credentials stolen along the way.
The pattern across all of these runs the same way: trusted update channels, legitimate-looking releases, interpreted payloads (JavaScript or Python), and credential theft that targets developer environments and CI/CD pipelines. The goal is rarely to destroy anything immediately; it is to get persistent access to the secrets that open the next door.
EDR Is Not Enough at the Package Layer, But It Is Not Irrelevant Either
Most organisations run endpoint detection and response platforms: CrowdStrike, SentinelOne, Microsoft Defender, and similar tools. They are well-funded, well-marketed, and genuinely effective at what they were built to do.
The problem is that they were not built for the package installation layer.
I came across an interview on Laterstack with Paul McCarty, a supply chain security researcher who was part of the group that caught the Trivy compromise before the major vendors published anything. His explanation of why EDR misses interpreted malware is worth reading in full, but the short version is this:
EDR platforms are built around two detection primitives. The first is file hashes of known bad binaries: if they have seen a bad .exe or .dll before, they will catch it again. The second is anomalous behaviour signatures like mass file encryption. Both are irrelevant when the attacker ships a malicious .js or .py postinstall script. There is no binary to hash: a threat actor can push twenty new versions of a malicious JavaScript payload in a single day, and each one will have a different hash. The behaviour of an infostealer does not look like ransomware: it reads a few files in well-known browser or credential directories, serialises them into a JSON blob, and POSTs them over HTTPS. That is exactly what legitimate apps do constantly.
McCarty ran thousands of interpreted payloads through VirusTotal and the major sandboxes. The EDR vendors did not detect them. The only exception he found was Kaspersky, and even that was rudimentary.
If you are running npm install or pip install in a developer environment or a CI/CD pipeline, your EDR vendor is not meaningfully protecting that operation. The assumption that it is protected is the assumption that gets organisations into headlines.
The Axios compromise shows where EDR can still contribute. The attack did not stop at a JavaScript package: it chained into a cross-platform binary RAT (WAVESHAPER.V2), deployed via a renamed PowerShell executable on Windows. That binary stage is exactly what behavioural EDR was built for. SentinelOne’s Lunar engine caught the renamed binary execution technique regardless of payload hash, and their Wayfinder team ran proactive hunts across all MDR regions using specific IOCs within hours of the attack being confirmed.
In short, EDR provides no meaningful protection at the interpreted-package stage, but it can intercept the binary payload stage that follows, if the attack chain includes one and if the EDR is configured to act autonomously at machine speed rather than waiting for a human analyst. The gap is still real. It is just not the entire picture.
Ground Rules for When Your Luck Runs Out
There is no configuration that makes you immune. The Bitwarden attack abused a legitimate publishing mechanism. The Axios attack used a compromised maintainer account. The Trivy attack spoofed a release tag. Each of these vectors looks legitimate at the moment it fires.
You can reduce the blast radius and speed up detection. Here is what I’d actually put in place.
1. Pin GitHub Actions to a Commit SHA
Tags in GitHub Actions are mutable. When you write uses: actions/checkout@v4, you are trusting whoever controls that tag to have not moved it to a different commit. The Bitwarden attack exploited this exact primitive: a hijacked workflow step, referenced by tag, became the entry point.
The correct form is:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
The comment preserves readability; the hash enforces integrity. In the Bitwarden case, this is the specific control that would have raised a red flag. Tools like Dependabot and StepSecurity Harden-Runner can automate the pinning and keep the hashes current.
2. Enforce Least-Privilege Token Permissions on Every Workflow
The Bitwarden attacker used the GITHUB_TOKEN in the hijacked workflow to publish to npm. Default GitHub Actions token permissions are broader than most workflows need. Set explicit permissions at the workflow level and override at the job level only where necessary:
permissions: {}
jobs:
publish:
permissions:
contents: read
id-token: write # only if using OIDC trusted publishing
Workflow permissions that are not explicitly granted cannot be abused.
3. Use MFA on Every Registry Account
npm, PyPI, Docker Hub, and every other package registry account should have multi-factor authentication enabled and enforced. The Axios attack started with a compromised maintainer account; MFA would not have made that impossible, but it raises the cost significantly. npm has supported hardware key and TOTP MFA for years, and for accounts with publish access it can be made mandatory at the organisation level.
It still gets skipped.
4. Treat Postinstall Scripts as Hostile Until Proven Otherwise
The Bitwarden malware ran before a single CLI command was issued, through npm’s preinstall lifecycle hook. Every package that ships install-time scripts deserves a question: does this package actually need to run code during installation?
For dependencies you own or control, remove postinstall scripts unless they are strictly necessary. For third-party dependencies, npm install --ignore-scripts prevents lifecycle hooks from executing. This is not always practical for complex toolchains, but in CI/CD environments where you are installing a known, locked dependency set, it is a reasonable default to consider.
The LiteLLM attack, documented in SentinelOne’s analysis of the three March 2026 supply chain attacks, introduced a sharper edge to this problem. In one confirmed incident, an AI coding agent running with unrestricted permissions (claude --dangerously-skip-permissions) auto-updated to the infected version without any human review. The human friction that might have caught something off (a maintainer pausing, noticing an unexpected version bump) disappeared entirely. Supply chain attacks already exploit the minutes between a malicious publish and community detection; autonomous agents with install permissions remove even that friction. If you are running agentic workflows, restricting install permissions and treating auto-update as a default-off behaviour is not optional.
5. Lock and Verify Your Dependency Tree
package-lock.json, yarn.lock, poetry.lock, and their equivalents exist for a reason. A locked dependency tree means that an attacker who publishes a malicious patch version will not automatically land in your pipeline. Commit your lockfiles, configure your CI to fail if the lockfile is out of sync with the manifest, and review lockfile diffs in pull requests: they are often where supply chain injections hide.
For higher-stakes environments, go further. Tools like Socket and Endor Labs analyse packages at install time and flag newly introduced install scripts, unusual network activity patterns, and known malicious behaviour. They are not EDR; they are purpose-built for the interpreted-package threat model.
6. Know Your Secrets Before You Need to Rotate Them
When the Bitwarden window closed, Bitwarden published a list of what should be considered compromised: SSH keys, GitHub tokens, npm tokens, cloud credentials, anything in the environment during install. The advice was to rotate everything immediately.
That is correct advice, but it assumes you know what you have and where it lives. If you cannot enumerate all the secrets in a developer environment in under an hour, rotation under pressure becomes a guessing game. The right time to build that inventory and those rotation runbooks is before the incident.
Use a secrets manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault) with short-lived credentials wherever possible. A token that expires in an hour is far less valuable to an attacker than one with no expiry. Enable audit logging on every secrets backend so you can tell whether a stolen credential was actually used.
7. Signal-Share and Monitor the Community
Paul McCarty’s group caught the Trivy compromise before CrowdStrike, Wiz, and Socket had published anything. How? Not through better tools: through an intel-sharing network of researchers who communicate directly and quickly. The big vendor platforms caught up later.
Follow the communities doing this work: OpenSourceMalware.com, the Open Source Security Foundation, Endor Labs’ blog, and the security advisories for every critical dependency in your tree. Subscribe to the GitHub Security Advisories feed for your language ecosystem. Set up Dependabot alerts and actually triage them. The signal exists; the question is whether you have a pipeline to receive it.
8. Run AI Coding Agents in an Isolated Sandbox
All the controls above operate at the policy layer: pin the hash, restrict permissions, check the lockfile. When an AI agent with install permissions is itself the compromised party (as LiteLLM demonstrated), policy controls only hold if the agent is not the vector. The infrastructure layer also has an answer: run the agent in an environment where the blast radius is contained by design, not by convention.
Docker Sandboxes (sbx) does exactly that. Each agent runs inside its own microVM with a network proxy that enforces an explicit allow-list. A malicious preinstall script attempting to POST harvested credentials to an attacker-controlled domain (like the audit.checkmarx[.]cx used in the Bitwarden attack) receives a 403. The exfiltration channel does not exist. The credential handling adds another layer: AI service credentials are never stored inside the VM; the proxy injects them at request time, so an infostealer reads a placeholder, not a real key. Add the --branch flag and no agent code reaches production without a human review.
I tested and wrote about this setup in detail in a dedicated post.
A Note on Trust Models
There is one more thread from McCarty’s interview worth pulling on. Git was not built with security in mind: you can push a commit attributed to Linus Torvalds with nothing more than a local config change. The forge (GitHub, GitLab, wherever you host) is the implicit trust anchor, and the Bitwarden attack succeeded in part by subverting exactly that anchor through its Trusted Publishing mechanism. gittuf, currently in beta under the OpenSSF Supply Chain Integrity Working Group, is a structural answer to this: it moves security policy into the repository itself, signed and verifiable independently of the forge. Worth watching.
The goal across all of these controls is not zero risk; none of them makes you immune. What they do is raise the cost and complexity of a successful attack enough that you are no longer the easiest target in the room. The software supply chain has been a primary attack surface for years. The incidents of the last few months just make the pattern impossible to keep ignoring.
Build your defences accordingly.
References
- Endor Labs: Shai-Hulud, the Bitwarden CLI Supply Chain Attack
- OX Security: Bitwarden CLI, Inside the Shai-Hulud Attack
- StepSecurity: Bitwarden CLI Hijacked on npm
- Laterstack: EDR, Open Source Malware, and the People Gap (Paul McCarty interview)
- Cloud Security Newsletter: Supply Chain Attack on Trivy, LiteLLM and Axios
- SentinelOne: How SentinelOne’s AI EDR Stops the Axios Attack Autonomously
- SentinelOne: Hypersonic Supply Chain Attacks — One Solution That Didn’t Need to Know the Payload
- The Hacker News: Bitwarden CLI Compromised in Ongoing Supply Chain Attack
- gittuf: Platform-Agnostic Git Security (OpenSSF)
- SecurityCon 2026: gittuf, Removing the Forge as a Single Point of Trust
- msbiro.net: Docker Sandboxes — Running AI Agents in YOLO Mode, Safely