Hardening npm Supply Chains: Lessons from Mini Shai-Hulud
Mini Shai-Hulud compromised 42 TanStack packages via GitHub Actions. Three hardening layers against npm supply chain attacks in practice.

In the early hours of May 12, 2026, the threat actor collective TeamPCP published 84 tampered versions across 42 TanStack packages in the npm registry within a few hours. The attack also hit Mistral AI and Guardrails AI on PyPI, along with additional npm packages from the OpenSearch and Squawk ecosystems. The campaign goes by the name Mini Shai-Hulud and continues a series that has been hitting DevSecOps toolchains on a weekly cadence since late April 2026. Four days earlier, the same actor compromised the official Checkmarx Jenkins AST plugin.
What sets this incident apart from classic maintainer account hijacking is the attack path. The attackers did not convince a single maintainer to publish a malicious package. They took over the build bot. That shift moves defense away from trusting individual humans and toward hardening the pipelines that publish on their behalf. This article unpacks the technical details and outlines three hardening layers that would make TanStack-style incidents detectable or impossible in the future.
Anatomy of the Attack
The vector is a chained workflow attack via pull_request_target in GitHub Actions, combined with cache poisoning. The pull_request_target trigger is one of the most dangerous configurations in GitHub Actions because it runs the workflow in the context of the target branch with access to all repository secrets, while the code comes from the pull request. GitHub explicitly documents this constellation as a risk in its security hardening guide for GitHub Actions.
TeamPCP exploited this setup in two stages. First, a crafted pull request was filed against the TanStack repository, and its code poisoned the action cache during the build. Then a trusted workflow executed in the context of the main branch, read the poisoned cache back, and ran the injected code with the repository secrets in scope. The result was theft of the OIDC token directly out of the runner process. With that token, the attacker was able to trigger signed npm publishes that were indistinguishable from genuine releases for consumers.
The payload is named router_init.js and is obfuscated. On each execution, it queries the AWS Instance Metadata Service, the GitHub token store on the runner, SSH keys in the standard path, and credentials from common CI systems, then exfiltrates them to the domain filev2.getsession.org. The code then uses the GitHub GraphQL API with the stolen tokens to establish additional trust relationships in the victim repositories. External researchers from Aikido Security, Endor Labs, SafeDep, and Socket detected the wave within roughly 20 minutes. The official pipeline owners needed several hours to unpublish the compromised versions.
The associated vulnerability is tracked as CVE-2026-45321 with a CVSS score of 9.6.
Immediate Measures for Affected Teams
If you have any of the affected packages in your lockfile, three steps should be triggered in parallel today. The first is the identification of all build artifacts produced with the compromised versions. Container images, Lambda packages, and static bundles produced between May 11 and May 12, 2026 must be treated as potentially infected, even if router_init.js did not visibly execute during the build.
The second step is to rotate every secret the runner process knew about. The order is not arbitrary. It follows the trust chain: npm tokens first, because they grant immediate publish rights, then GitHub personal access tokens and OIDC trusts, then AWS access keys and session tokens, then Vault tokens and Kubernetes service account tokens. Inverting the order risks an already exfiltrated identity also lifting the freshly rotated one.
The third step is a forensic review of CI logs from the past seven days. Outbound connections to filev2.getsession.org, unusual GitHub GraphQL calls, and new entries in OIDC trust policies are the most important indicators. If you do not have centralized audit logs for GitHub Actions, take this incident as a trigger to mirror that telemetry into a SIEM.
Structural Hardening Against Pipeline Takeover
Hardening starts with the trigger that made the attack possible in the first place. pull_request_target should be removed from every public repository where it is not strictly required. In the rare cases where the trigger is needed, for example to automatically label external contributions, the workflow should be split into two: the first runs against the PR code without access to any repository secrets, the second runs only after manual approval and with the secrets in scope but without checking out the PR code. This separation has been described in GitHub's guidance on pull_request_target since 2020 but is rarely implemented in practice.
Cache poisoning can be limited through deliberate cache key strategies. Building a cache key over the hash of package-lock.json from the PR code hands the attacker control over the cache contents. The safer approach is to bind cache keys exclusively to trusted inputs, such as the commit hash of the main branch or static version tags. Workflows that build foreign code should disable the cache entirely or maintain a separate cache namespace with no read or write relationship to the production build.
For OIDC trust configuration, the most common weakness is an overly permissive subject condition. Trust policies like repo:org/repo:* allow any workflow in the repository to obtain AWS or GCP tokens, including a malicious workflow from a fork. The trust policy should instead be restricted to specific refs, such as repo:org/repo:ref:refs/heads/main, or to specific reusable workflows. Cloudflare documented this approach in a public blog post after an internal audit surfaced the same weakness.
Defense in Depth Across the Supply Chain
Hardening your own workflows is necessary but not sufficient. Mini Shai-Hulud demonstrates that attackers can manipulate the code an organization runs long before it reaches npm install. The following layering raises the cost of the attack and gives defenders time to react.
The first layer is avoiding lifecycle scripts in CI. npm ci --ignore-scripts prevents postinstall hooks from firing during the build. This breaks a small number of packages but specifically defeats the class of attacks where the payload activates at installation time. The second layer is a strict pinning strategy: exact versions instead of ^ or ~ ranges, regular verification of lockfile integrity, and automated diffs against the last approved version.
The third layer is pre-publish scanning through external services like Socket, Aikido, Endor Labs, or Snyk. These services analyze new package versions in near real time for suspicious patterns such as hidden network calls, obfuscation, or unusual file access, and publish alerts before the packages reach end users. These are exactly the tools that detected the TanStack hit in under 20 minutes. They replace neither pinning nor audits, but they are the fastest available early warning layer today.
The fourth layer is a private registry with a quarantine delay. Verdaccio, JFrog Artifactory, Sonatype Nexus, or GitHub Packages can be configured so that freshly published versions only enter the internal mirror after 24 to 48 hours. This delay is usually enough for the early warning services to flag a wave. In addition, SLSA Level 3 can be required so that only artifacts with reproducible build provenance reach the internal mirror. npm provenance and Sigstore signatures are the open standards that express this provenance.
What Does Not Work
Mini Shai-Hulud refutes three widespread assumptions. First, trusting maintainers is no longer a defense model. Nobody at TanStack deliberately published a malicious version. The question is no longer whether the maintainer has integrity, but whether their pipeline does.
Second, npm audit is not an early warning system. The tool reports known vulnerabilities from advisory databases that are typically populated with a delay of hours to days. The critical phase of a supply chain incident is in the first few minutes after publication.
Third, automated patch merges via Dependabot or Renovate without delay are dangerous. Automerging on patch updates without a quarantine window or at least an external scan pulls a compromised version in during the exact window when detection is happening. The NIST Secure Software Development Framework and CISA guidance on software supply chain security now address this requirement explicitly.
Conclusion
Mini Shai-Hulud is not an isolated incident. It is a series that has established the toolchain class as an attack surface. Defenders who rely only on known maintainers and on npm audit are watching a path that TeamPCP has just left behind. Teams that defuse pull_request_target triggers, narrow OIDC trusts, run a private registry with quarantine latency, and embed pre-publish scanning consistently in their builds reduce the attack surface to a defensible level. We assess these four points as a standard part of our DevSecOps penetration tests and find at least two of them open in most engagements. That is where the leverage sits in 2026.