Significant cyber incident

TanStack npm worm (Mini Shai-Hulud)

The TanStack npm worm, also called Mini Shai-Hulud, was a supply chain attack.

In short

The TanStack npm worm, also called Mini Shai-Hulud, was a supply chain attack. On 11 May 2026 an attacker published 84 malicious versions across 42 TanStack npm packages in about six minutes. The malware stole developer and cloud credentials at install time and tried to spread to other packages the victim maintains. It also reached Mistral AI, and CERT-EU counts over 160 npm and PyPI packages.

What happened

On 11 May 2026, between 19:20 and 19:26 UTC, an attacker published 84 malicious versions across 42 packages of the TanStack Router and Start family on npm. TanStack says the publishes used its own trusted publishing identity, no npm tokens were stolen, and its publish workflow was not changed. An external researcher opened a public issue about 20 minutes later. The versions were deprecated within about 1 hour 43 minutes of the first publish and removed by npm by about 23:55 UTC.

The malware ran when a developer or build system installed an affected version. It looked for cloud, Kubernetes, Vault, GitHub, npm and SSH secrets, sent them out, and tried to republish other packages the victim maintained. The worm then reached Mistral AI. Mistral says its npm packages were uploaded on 11 May and its PyPI package on 12 May, with an affected developer device involved and no impact on its global infrastructure.

Two counts appear in the sources and they cover different scopes. TanStack and GitHub count 42 packages and 84 versions in the TanStack family. CERT-EU counts over 160 npm and PyPI packages across the whole campaign, which it ties to TanStack, Mistral AI, UiPath and OpenSearch. CVE-2026-45321 covers the TanStack packages. Nx says its own Console compromise began with this attack, see the Nx Console and GitHub repositories breach. CERT-EU also names TeamPCP for the earlier Trivy and LiteLLM compromises.

Timeline

  1. 2026-05-10 17:16 UTC

    The attacker creates a fork of the TanStack router repository and renames it to avoid fork searches.

  2. 2026-05-11 about 10:49 UTC

    The attacker opens pull request 7378 against the main repository. Workflows that run on pull_request_target start automatically.

  3. 2026-05-11 11:29 UTC

    A poisoned 1.1 GB build cache entry is saved for the main branch. The attacker then resets the pull request to look empty and closes it.

  4. 2026-05-11 19:15 to 19:16 UTC

    Release workflow runs on the main branch restore the poisoned cache.

  5. 2026-05-11 19:20 UTC

    The first batch of malicious versions reaches the npm registry, using a token taken from the runner's memory.

  6. 2026-05-11 19:26 UTC

    The second batch arrives. In total there are 84 versions across 42 packages.

  7. 2026-05-11 19:46 UTC

    An external researcher opens public issue 7383 on the TanStack repository.

  8. 2026-05-11 20:19 UTC

    TanStack begins deprecating the affected versions. The final batch at 21:03 UTC covers all 84.

  9. 2026-05-11

    Malicious Mistral AI npm packages are uploaded. Mistral says they were removed on 12 May and were not functional.

  10. 2026-05-11 22:13 to 23:55 UTC

    The npm registry removes the affected TanStack tarballs.

  11. 2026-05-12

    The GitHub advisory GHSA-g7cv-rxg3-hmpx is published, NVD lists CVE-2026-45321, and Mistral publishes advisory MAI-2026-002. The malicious mistralai 2.4.6 PyPI release is uploaded and later removed.

  12. 2026-05-14

    Mistral closes its investigation.

  13. 2026-05-15

    TanStack issues an official all-clear after a three-day security sweep.

  14. 2026-06-02

    CERT-EU Cyber Brief 26-06 reports over 160 npm and PyPI packages compromised and names TeamPCP.

How the attack worked

The sources do not give MITRE ATT&CK ids for this incident, so none are listed here.

Step 1: get code into the build cache

The attacker forked the TanStack router repository and opened a pull request. A workflow named bundle-size used the pull_request_target trigger, which runs without first-time-contributor approval. It checked out and built the fork’s code. That code wrote a poisoned entry into the GitHub Actions cache under the same key the release workflow would later use. Limiting the workflow’s permissions did not stop this, because cache writes use a runner-internal token. The attacker then reset the pull request so it looked empty and closed it. The cache stayed poisoned.

Step 2: take the publishing token from memory

When the release workflow next ran on the main branch, it restored the poisoned cache. The attacker’s files then ran during the build. They read the memory of the runner process and extracted the short-lived OIDC token that the workflow was allowed to mint for npm trusted publishing. TanStack notes the same memory technique was used in an earlier public incident, and that the attacker recombined published research rather than inventing new methods.

Step 3: publish directly to npm

The stolen token was used to send publish requests straight to the npm registry. The workflow’s own publish step had been skipped because tests failed. Each of the 42 packages received two malicious versions a few minutes apart. The publishes were accepted because they came from the legitimate trusted publishing identity.

Step 4: the payload

Each malicious package listed an optional dependency called @tanstack/setup. It is not a real registry package. It pointed to an orphan commit in the fork network, which GitHub serves for git-URL dependencies. That commit ran a script that then launched a roughly 2.3 MB obfuscated file, router_init.js, shipped inside the tarball but left out of the package’s files list. The optional install was made to fail so npm silently discarded it and left no trace in node_modules.

Step 5: theft and spread

The payload collected cloud metadata and secrets, Kubernetes tokens, Vault tokens, npm and GitHub tokens, and SSH keys. It sent them out over the Session messenger file-upload network, which is end-to-end encrypted and has no attacker-run command server, so TanStack and the advisory say blocking by IP or domain is the only network control. It also looked up other packages the victim maintains and republished them with the same injection. That is how the compromise reached Mistral AI, where an affected developer device was involved.

The Mistral packages

Mistral says the compromised npm packages were not functional because a setup file pointed to a file that did not exist. The compromised PyPI release, mistralai 2.4.6, ran a script when imported on Linux. It downloaded a second file to /tmp and ran it in the background. The GitHub advisory says the contents of that second file were not analyzed. CERT-EU describes the PyPI package as a Linux credential stealer with a geo-fenced destructive payload targeting Israel and Iran. Mistral’s pages in the sources do not confirm that detail, so sources differ on what the second stage did.

Impact

TanStack: 84 malicious versions across 42 @tanstack packages, two per package, all in the Router and Start family. TanStack says other families such as Query, Table, Form and Virtual were unaffected, and that every currently available published version is safe to install (all-clear of 15 May 2026). TanStack says it has no evidence of npm credentials being stolen.

Hosts that installed an affected version on 11 May 2026 are to be treated as potentially compromised. The advisory says credentials reachable from them should be rotated. The number of downloads during the publish window was an open question in the postmortem, so no count is available from the sources.

Severity: NVD lists CVE-2026-45321 with a CVSS 3.1 base score of 9.6, critical, supplied by GitHub. A CISA Coordinator SSVC entry dated 27 May 2026 lists exploitation as active.

Mistral AI: three npm packages (versions 2.2.2 to 2.2.4, plus azure and gcp variants at 1.7.1 to 1.7.3) and one PyPI release (mistralai 2.4.6). The npm packages are not functional. The PyPI release runs a malicious script on import on Linux. Mistral found an affected developer device was involved and no impact on its global infrastructure.

Wider campaign: CERT-EU counts over 160 npm and PyPI packages compromised across the campaign, touching TanStack, Mistral AI, UiPath and OpenSearch. This is a different scope from the 42 packages and 84 versions in the TanStack family.

Government and regulator response

  • 11 May 2026: An external researcher raised a public issue at 19:46 UTC and told npm security. TanStack began incident response, removed other maintainers’ push permissions, and deprecated the first two versions at 20:19 UTC. The full 84 versions were deprecated by 21:03 UTC.
  • 11 May 2026: TanStack purged all cache entries for its GitHub repositories and merged a hardening change that restructured the workflow, added repository owner guards and pinned third-party actions to fixed commits. The npm registry removed the tarballs between 22:13 and 23:55 UTC.
  • 12 May 2026: The GitHub advisory and NVD entry for CVE-2026-45321 were published. Mistral published advisory MAI-2026-002 and the mistralai PyPI project was quarantined.
  • 14 May 2026: Mistral closed its investigation. Its remediation steps are to stop using the affected versions, clean affected systems, rotate secrets, check cloud audit logs and watch for the listed indicators.
  • 15 May 2026: TanStack issued an official all-clear after a three-day security sweep and hardening pass.
  • 2 June 2026: CERT-EU described the campaign in Cyber Brief 26-06, attributing it to TeamPCP.

Lessons

  1. Trusted publishing is only as safe as the build

    The publishes came from TanStack's real trusted publishing identity. Any code that runs in the release job can mint a publish token, and TanStack notes there is no per-publish review.

  2. Pull request triggers need an audit

    The attack began with a pull_request_target workflow that built fork code. TanStack says these workflows had not been audited despite being a known dangerous pattern.

  3. Limited permissions do not protect the cache

    Cache writes use a runner-internal token, so read-only workflow permissions did not stop the poisoned cache from reaching the release job.

  4. Pin third-party actions

    TanStack names floating references on third-party actions as standing supply-chain risk and pinned them to fixed commits after the incident.

  5. Watch your own publishes

    TanStack had no internal alerting and learned of the compromise from a third party about 20 minutes after it began. Registry removal took hours because unpublish was blocked for nearly all packages.

  6. One compromised package can become many

    The malware republished other packages the victim maintained, and CERT-EU counts over 160 packages in the campaign across several vendors and two registries.

What to check in your own organization

  1. Find affected versions

    Search lockfiles, build caches, container images and package mirrors for the TanStack versions in GHSA-g7cv-rxg3-hmpx and the Mistral versions in MAI-2026-002. Check the manifest for the @tanstack/setup optional dependency without running install scripts.

  2. Treat exposed hosts as compromised

    If a developer or CI host installed an affected version on 11 May 2026, rotate the AWS, GCP, Kubernetes, Vault, GitHub, npm and SSH credentials reachable from it, and review cloud audit logs.

  3. Audit pull_request_target workflows

    List every workflow that uses pull_request_target and check whether it checks out or builds code from a fork.

  4. Review cache and token exposure

    Check which release jobs restore shared caches and which have id-token write permission. Consider separating release caches from pull request caches.

  5. Pin actions and review lifecycle scripts

    Pin third-party actions to commit SHAs. Consider turning off npm lifecycle scripts on install in CI where you can, as the advisory suggests as a workaround.

  6. Monitor your own publishes

    Alert on any package publish from your organization that does not come from the expected workflow step, and on your packages being deprecated or removed.

  7. Early warning

    Threat intelligence monitoring can help here: watching for compromised packages named in advisories, for new vulnerability alerts, and for exposed credentials tied to your developers and CI. In this case the public issue appeared about 20 minutes after the first publish, so an alert on the named packages could have told you to check your lockfiles that evening. No tool can promise this.

Questions

What happened in the TanStack npm compromise?

On 11 May 2026 an attacker published 84 malicious versions across 42 @tanstack npm packages. The malware stole credentials at install time and tried to spread to other packages.

How can 42 packages and "over 160" packages both be right?

They count different things. TanStack and GitHub count the 42 packages and 84 versions in the TanStack family. CERT-EU counts over 160 npm and PyPI packages for the whole TeamPCP campaign, including TanStack, Mistral AI, UiPath and OpenSearch.

Was Mistral AI affected?

Yes. Mistral says its npm and PyPI SDK packages were compromised by the worm. The npm packages were not functional. The PyPI release mistralai 2.4.6 ran a malicious script on import on Linux. Mistral found no impact on its global infrastructure.

Did the attacker steal TanStack's npm tokens?

TanStack says no npm tokens were stolen. The attacker took a short-lived trusted publishing token from the memory of the build runner.

Who was behind it?

CERT-EU attributes the attack to TeamPCP in its Cyber Brief of 2 June 2026. TanStack, GitHub and NVD give no attribution.

Which CVE covers it?

CVE-2026-45321, rated 9.6 critical by CVSS 3.1 as supplied by GitHub. It covers the TanStack packages.

Sources

  1. Postmortem: TanStack npm supply-chain compromise TanStack
  2. Malware in @tanstack/* packages exfiltrates cloud credentials, GitHub tokens, and SSH keys (GHSA-g7cv-rxg3-hmpx) GitHub Advisory Database
  3. CVE-2026-45321 NIST National Vulnerability Database
  4. TanStack supply chain attack affecting Mistral SDK packages (MAI-2026-002) Mistral AI
  5. Malicious dropper in mistralai 2.4.6 PyPI package (GHSA-wx9m-wx4f-4cmg) GitHub Advisory Database
  6. Broken dropper in @mistralai/mistralai, @mistralai/mistralai-azure, @mistralai/mistralai-gcp (GHSA-jgg6-4rpr-wfh7) GitHub Advisory Database
  7. Cyber Brief 26-06 - May 2026 CERT-EU