The threat model that scanning was built for is gone
Package security grew up around a single assumption: a dangerous package is one with a known flaw in its code. Find the flaw, match it to a CVE, block the package. That model held while attackers published artifacts that could eventually be recognized as malicious.
The attacks that defined 2025 and 2026 break that assumption. They carry no known flaw. They take over a maintainer account or a build pipeline, then publish malicious code through the trusted release path, signed and provenance-attested. Nothing about the artifact looks wrong.
For teams securing software distribution, the question has changed. It is no longer only what is in the package. It is whether the process that built and published it can be trusted.
How npm attacks evolved: September 2025 to mid-2026

For a decade, npm attacks followed a few patterns: typosquatting, dependency confusion, and the occasional account takeover. The 2018 event-stream incident set the template, where an attacker quietly took over a popular package to skim cryptocurrency.
Those early attacks were often manual and eventually visible, because a suspicious package or an isolated payload could be found and removed. Package scanning was built for that world. In 2025 the pattern broke.
The timeline
Late August 2025, Nx (s1ngularity). Attackers stole an Nx publishing token and pushed malicious versions, exfiltrating secrets from developer machines over roughly four hours. Nx draws around 4 million weekly downloads. Wiz later traced this as the upstream of what followed: GitHub token theft led to npm token theft, which led to mass package poisoning.
September 8, 2025, chalk and debug (the “qix” compromise): A phishing email from the fake domain npmjs.help pushed maintainer Josh Junon to reset his 2FA on a fake login page. The attacker took over his account and shipped malicious versions of 18 packages, including chalk, debug, and ansi-styles, which together draw roughly 2.6 billion weekly downloads. The malicious versions were live for roughly two hours before they were reverted. The payload was a browser-based cryptocurrency wallet stealer.
September 15, 2025, Shai-Hulud: Days later, the first self-propagating worm in npm history appeared. Malicious versions shipped a script that harvested secrets and sent them to attacker-created public GitHub repositories named Shai-Hulud. Whenever it found additional npm tokens, it republished malicious versions of any package it could reach. A single dormant token from one repository’s GitHub Actions secrets gave the attacker control of @ctrl/tinycolor, which then propagated through thousands of dependent packages. Shai-Hulud marked an important shift. Rather than publishing fake packages, attackers began injecting malicious code into legitimate packages through trusted maintainer identities and release workflows. That pattern would define many of the attacks that followed.
November 2025, Shai-Hulud 2.0 (“Second Coming”): A larger, faster wave hit between November 21 and 24, compromising hundreds of packages and more than 25,000 GitHub repositories in a few hours, including AsyncAPI, PostHog, Postman, and Zapier packages. Three things escalated:
- Earlier execution: The malware moved to the pre-install phase and used the Bun runtime to evade tooling focused on Node.js, allowing the payload to execute before installation completed.
- Wider reach. It stole tokens from .npmrc files and environment variables, then ran TruffleHog to scan for AWS, GCP, and Azure keys, routing stolen data through public GitHub repositories instead of a command-and-control server.
- Persistence and a failsafe. It could backdoor up to 100 of a victim’s packages, registered infected machines as self-hosted GitHub Actions runners, and tried to wipe the home directory if it could not authenticate or find anything to exfiltrate.
December 9, 2025, the token reckoning: npm enforced its cutoff for classic tokens, which had bypassed 2FA for automated publishing. Teams moved to short-lived granular tokens or to OIDC trusted publishing. This closed the stolen-token path that powered the September and November waves. The next attacks adapted to that change.
March 31, 2026, axios. The HTTP client with over 100 million weekly downloads shipped two poisoned versions, 1.14.1 and 0.30.4, each carrying a cross-platform remote access trojan delivered through an injected dependency. The lead maintainer had been socially engineered for weeks. The malicious versions were live for about three hours. Microsoft attributed the activity to the North Korean actor it tracks as Sapphire Sleet; Google tracks the same operator as UNC1069, tied to the BlueNoroff cluster within Lazarus. This marked the entry of state-linked actors into npm supply chain attacks.
Spring 2026, the Mini Shai-Hulud campaign (TeamPCP): A dedicated group ran a rapid series of compromises, hitting Aqua Security’s Trivy scanner, the Bitwarden CLI, and SAP packages, among others. Google assesses the group, which it tracks as UNC6780, as North Korea-nexus. The targeting was deliberate: security tools run with permission to read secrets and reach cloud APIs, so hijacking one buys better access than perimeter exploitation usually allows. One confirmed infection began with a trojanized Checkmarx Docker image that Dependabot pulled on its own during a routine update, then ran inside CI with access to repository secrets and no human review.
May 11, 2026, TanStack: An attacker published 84 malicious versions across 42 @tanstack/* packages in roughly six minutes. No npm tokens were stolen, and the publish workflow itself was not modified. The attacker chained three weaknesses: the pull_request_target “Pwn Request” pattern, GitHub Actions cache poisoning across the fork-to-base trust boundary, and runtime extraction of an OIDC token from the runner’s memory. TanStack’s own release pipeline then published the packages using its trusted OIDC identity.
That makes this the first documented malicious npm package carrying valid SLSA Build Level 3 provenance. The Sigstore attestations correctly proved the packages were built by the real release workflow on the main branch. What provenance does not prove is that the workflow was authorized to run, or that the triggering commit was legitimate. @tanstack/react-router alone draws over 12 million weekly downloads.
June 1, 2026, Red Hat (Miasma): A compromised Red Hat employee GitHub account pushed malicious orphan commits to RedHatInsights repositories, bypassing code review. Those commits triggered workflows that requested OIDC tokens and published trojanized versions of at least 32 @redhat-cloud-services packages, again carrying valid SLSA provenance. The attestation was accurate: the packages really were built by that pipeline. The pipeline had malware injected at the time.
The aggregate picture
The individual incidents matter. The trend matters more. Sonatype’s 2026 State of the Software Supply Chain report, published in January 2026, counted more than 454,600 new malicious open-source packages in 2025, pushing its cumulative blocked total past 1.23 million, a 75 percent jump year over year. Hundreds of npm packages were compromised across the waves from September 2025 to May 2026. Defenders are facing a new baseline.
The numbers tell only part of the story. Across these incidents, the common thread was not vulnerable code but compromised trust in the software publishing process. That shift fundamentally changes what defenders need to verify.
What the sequence tells us
The entry point moved to the human and the pipeline: Across nearly every incident, the root cause was a phished maintainer, a stolen token, or an abused workflow, never a vulnerable line of code. Defenses built to find vulnerable code look in the wrong place.
The attack changed from forgery to substitution: A traditional malicious package is a forgery: a fake artifact under a name no one trusts yet, hoping someone installs it by mistake. The new attacks are substitutions: real packages, real maintainer identities, the same release path the maintainer uses every day, with malicious contents inside. To everyone downstream it looks like a routine update.
Speed became the deciding factor: The axios payload lived for 174 minutes. The TanStack versions were caught in 20 to 26 minutes and had already started to spread. Tools that look up known advisories cannot react when no advisory exists yet. The teams that escaped had decided in advance to stop auto-executing untrusted code, through pinned lockfiles and disabled install scripts, before any alert fired.
Each security upgrade opened new ground as it closed old ground: Retiring classic tokens removed the stolen-token path. TanStack and Red Hat show attackers moving up to the OIDC trusted-publishing flow itself. Code execution inside the release workflow was enough to mint a publish token. This is the central lesson for anyone relying on provenance: SLSA attestations prove where a package was built, not that the build was authorized or clean.
The blast radius is now systemic: A few poisoned foundational packages such as axios, chalk, and the TanStack router family reach tens of thousands of dependents. Persistence has spread into developer workstations, with recent payloads writing themselves into editor and tool directories to survive uninstalls.
Why the defenses you already run miss this
The new attacks pass the checks teams rely on, because those checks were built to catch forgeries. A forgery announces itself: an unfamiliar name, an untrusted source, a known-bad signature. A substitution answers every one of those the expected way, so it sails through.
The same blind spot shows up across the rest of the standard toolkit. Each tool was built to catch a package that looks wrong, so a malicious release that arrives signed, correctly named, and through the trusted path slips past all of them:
Trusted publishing shrinks one surface and leaves another
Trusted publishing, the OIDC-based passwordless flow that lets CI/CD publish directly to npm, PyPI, RubyGems, and crates.io, was built to remove stored long-lived credentials from pipelines. It does that well. A workflow authenticates with a short-lived, workflow-scoped token instead of a static secret sitting in a CI variable, and npm attaches a provenance attestation to every publish by default. The original Shai-Hulud worm is the case for this control, since it spread by stealing the long-lived tokens that trusted publishing retires.
Trusted publishing removes the stored token. It does not protect the build pipeline that issues the short-lived one in its place, and that pipeline is still exposed. A phished maintainer, a compromised reusable workflow, or a poisoned composite action still produces a publish that is legitimate, signed, and provenance-bearing.
That exposure is wider than most teams realize, because CI/CD is interconnected. A compromised reusable workflow or composite action can propagate malicious behavior to every project that consumes it, often without additional review or approval. Many workflows also call shared actions by a moving label like v3 or main instead of a fixed version. Whoever controls that label controls the code your pipeline runs, so an attacker can change what your build does without ever touching your repository.
What a defensible build pipeline requires
Defensible does not mean impenetrable. It means that if any link in the build chain is compromised, you can detect it before it reaches production, and detection beats the attack’s spread. That calls for controls that verify the build process, not only the artifacts it produces.
When prevention fails, contain what runs
Build-integrity controls lower the odds that a poisoned artifact ships. They cannot promise it never happens. The Shai-Hulud payloads ran the moment a package was installed, during the pre-install and post-install hooks, before a human looked at anything. A defensible pipeline plans for that case and asks a blunt question: if malicious code does execute, what can it actually do?
Two container controls answer it, and CleanStart builds both into its images. A shell-less image ships no /bin/sh, no /bin/bash, no busybox. When an install script tries to spawn a shell, read a secrets file, install a tool, or open a reverse shell, the command is not there to run. The Shai-Hulud chain depended on exactly those steps: launch a process, run TruffleHog, reach a cloud API. With no shell, most of that chain has nothing to execute.
A read-only root filesystem closes the other half. The payloads wrote themselves into editor and tool directories to survive uninstalls, backdoored other packages on disk, and registered the machine as a runner. On a read-only filesystem every one of those writes fails at the OS level, and only the few paths you declare writable, such as /tmp, accept data at all. The code may run once. It cannot persist, alter the application, or stage files for exfiltration.
Conclusion: trust before distribution
Every attack in this timeline ran on borrowed trust: a signature, a maintainer name, or a provenance attestation that proved origin but never proved intent. The signed package was still malware. The lesson is not that signatures, SBOMs, or provenance are flawed. It’s that they must be built on a software supply chain that is itself worthy of trust.
That’s the approach CleanStart takes. Every image is built from verified sources in a sealed, hermetic build environment, then attested with SLSA provenance and a signed SBOM. The runtime is hardened the same way: shell-less, read-only images give malicious code nothing to spawn and nowhere to persist. Verified source, sealed build, signed proof, and contained runtime ensure that trust is established at every stage of the software supply chain, rather than assumed after the fact.
In the whitepaper - Six Layers of Unbreakable Defense, we walk you through all six layers and how they hold against the attacks.



