Supply chain guidance assumes the attacker tricks you into installing something you did not ask for. These two incidents broke that assumption: the packages were real, the publisher identity was real, and the provenance was valid. On 11 May 2026, 84 malicious versions of 42 genuine @tanstack/* packages went to npm inside a six-minute window. One developer who installed one was an Nx contributor, and the credential taken from that laptop published a malicious Nx Console extension seven days later. Both now sit in the CISA Known Exploited Vulnerabilities catalog, flagged for known ransomware campaign use.
Classification first, because "compromise" is doing too much work
Five categories separate the events reported under this kind of headline: A, a guardrail bypass where no system is touched; B, an attacker using a vendor's product against third parties; C, a product vulnerability disclosed with a CVE; D, a breach of the vendor's own systems or release channel by an outsider; and E, model-initiated action during evaluation, where the vendor's own model acted against a real third party in an authorised test with no attacker involved.
Nothing here is A, B or E: every action below was taken by a human attacker. Each incident is a D at the project and a C downstream, and a process that sees only the C patches a version number without asking what still holds stolen credentials.
Not a typosquat: why pinning to the official package name did not help
TanStack's advisory, published 11 May 2026 at 22:40 UTC, states that between approximately 19:20 and 19:26 UTC, 84 malicious versions across 42 @tanstack/* packages went to npm, authenticated via the legitimate GitHub Actions OIDC trusted-publisher binding for TanStack/router, and that "the publish workflow itself was not modified" (GHSA-g7cv-rxg3-hmpx).
Every consumer-side defence in common use then falls away: name and scope allowlisting were correct, lockfile pinning helps only if the lockfile already pointed at an older version, and provenance attestation helps not at all because the artefact went through the genuine binding. Nrwl's postmortem states the consequence: those versions were indistinguishable from legitimate releases, and "the only consumer-side defense is delay-based" (Nx postmortem).
A minimum release age means you install nothing published in the last N hours or days. It was set in the repository where the install happened, to seven days, and it did nothing: the pinned package-manager version predated support for the key, so the setting was silently ignored. Nrwl's verdict: "The malicious version was 77 minutes old at install time, which any non-zero release-age policy would have blocked." A configuration key is only a control if the runtime implements it.
Three weaknesses chained, and no workflow file was touched
The advisory names three known classes chained: a pull_request_target "Pwn Request" misconfiguration, GitHub Actions cache poisoning across the fork to base trust boundary, and runtime memory extraction of the OIDC token from the runner process. TanStack's postmortem adds that none alone is sufficient, and that each bridges the trust boundary the next assumed was intact (TanStack postmortem).
At the level a defender needs: pull_request_target gives a workflow the base repository's permissions and secrets rather than the fork's, unsafe once it executes pull request code. Caches are scoped per repository and shared across trigger types, so a low-trust job can write entries a production workflow later restores. And an OIDC request mints a publish-capable credential inside the runner process, reachable by any code executing there.
No workflow file in the release path was modified, so a diff review or protected-branch rule would have passed: the attacker changed what the release workflow found on disk, not what it did. The tradecraft was borrowed too: "The attacker did not invent novel tradecraft; they recombined published research."
Worm behaviour: how one compromise enumerated its way into the next project
The advisory also describes the payload enumerating the packages the victim maintains through the registry's maintainer search and republishing them with the same injection. That is worm behaviour: the blast radius of one install is every package that developer can publish, and every consumer of those. It makes "how many people installed the bad version" the wrong first question; the right one is "which of them can publish."
The Nx incident is that mechanism running through a human credential, and Nrwl states the link outright: "One of our developers was compromised by a recent supply-chain compromise on Tanstack, which leaked their GitHub credentials through the GitHub CLI (gh). This allowed the attacker to run workflows on our GitHub repository as a contributor" (GHSA-c9j4-9m59-847w).
Their timeline puts the install at 20:43:01 UTC on 11 May, the stolen token used against the GitHub API within 74 seconds, the first attacker commit at 20:43:51, bulk deletion of workflow runs within minutes, and the token reappearing on 15 May from different client tooling. Their summary: "the attacker was active in our GitHub repos for seven days without detection." That is a detection finding, not a supply chain finding.
Eighteen minutes, 28 installs, thousands of users: why counts misled everyone
Exposure and counts. The Nx advisory states 18.95.0 was published at 12:30 UTC and removed at 12:48, roughly 18 minutes on the Visual Studio Marketplace, and was on Open VSX from 12:33 to 13:09, roughly 36 minutes. The postmortem says around 11 minutes "until we patched it": both are the vendor, measuring public exposure and time from detection. Microsoft and Open VSX recorded 28 and 41, yet maintainers believe impact "two orders of magnitude higher, with thousands of affected users," and the postmortem reports roughly 6,000 activations. An install count is a reporting proxy, not an incident metric.
The date. Two primary records disagree: Nrwl's postmortem and GitHub's advisory metadata place the publish on 2026-05-18, while NVD's record for CVE-2026-48027 opens with "On 19 May 2026" (NVD). The vendor's advisory cannot predate the event it describes, so 18 May is better supported; triage should anchor on the version string anyway.
Record | Advisory secondary score | NVD primary score | KEV added | KEV due | Ransomware |
|---|---|---|---|---|---|
CVE-2026-45321 (TanStack) | CVSS 3.1 9.6 Critical (GitHub) | None assigned | 2026-05-27 | 2026-06-10 | Known |
CVE-2026-48027 (Nx Console) | CVSS 4.0 9.3 Critical (GitHub) | CVSS 3.1 9.8 | 2026-05-27 | 2026-06-10 | Known |
CVE-2026-33634 (Trivy) | CVSS 4.0 9.4 Critical (GitHub) | CVSS 3.1 8.8 | 2026-03-26 | 2026-04-09 | Unknown |
NVD carries no primary score for the TanStack record; the 9.6 is GitHub's secondary metric (NVD), so a threshold reading only NVD primary scores drops it silently. At catalogVersion 2026.08.07 KEV holds eight entries named "Embedded Malicious Code," five added since December 2025, including Nx Console and the earlier Trivy entry, another C record over a build-chain compromise (CISA KEV). TanStack is not among them: its entry is "TanStack Unspecified Vulnerability" with no CWE in KEV, and NVD carries CWE-506 only as a secondary entry from GitHub, so automation matching that name or CWE misses the entry that caused the other.
What the payloads went after
Both payloads targeted the credentials a build host or workstation accumulates.
Target class | TanStack advisory | Nx records |
|---|---|---|
Cloud, cluster and secret managers | AWS instance metadata and Secrets Manager, GCP metadata, Kubernetes service-account tokens, Vault tokens | The same, plus Parameter Store, Web Identity tokens and any active 1Password CLI session |
Registry and source control | npm tokens from npm configuration; GitHub tokens from environment variables, | The same, plus OIDC token exchange and process memory |
Keys and configuration | SSH private keys | The same, plus environment files, GCP default credentials, Docker config |
Persistence | Not described | Artefacts on macOS, Linux and Windows, plus an attempted sudo change on Linux |
The Nx payload established persistence, so removing the malicious version does not end it. Both went after the credential with the widest reach on a developer machine: Nrwl notes the gh CLI writes a long-lived token any process running as the user can read, and now bans direct use of it.
Exfiltration that does not look like exfiltration
TanStack states the harvested data left over the Session and Oxen messenger file-upload network, end-to-end encrypted with no attacker-controlled command and control, so "blocking by IP or domain is the only network mitigation." The Nx records add HTTPS, the GitHub API and DNS-based covert channels.
None of that trips a typical egress policy: a messenger network looks like consumer chat, GitHub API traffic from a build runner is what that host exists to generate, DNS is the least inspected channel, and the self-propagation used npm itself. Restrict runner egress to an allowlist, alert on volume and pattern rather than destination alone, log DNS from build networks, and treat a publish as egress.
Detection: publish events, credential origin, and the audit trail you probably do not keep
Neither project caught this with tooling it had built. TanStack heard from an external researcher 26 minutes after the first publish: "No internal alerting. We learned about the compromise from a third party." Nrwl's detection was a publisher-notification email reaching a maintainer not expecting a release: "We did not detect this through any system we built."
Publish and release events. Every publish to a public registry or marketplace should alert a human who reconciles it against a release decision.
Source control audit log. Nrwl names workflow-run deletions specifically, because the attacker swept them in bulk within minutes of taking the token. Those, workflows appearing outside the review path, and commits whose author does not match the actor are cheap to alert on.
Credential use from a new origin. The token reappearing on 15 May from different client tooling was the missed signal; a change of user agent, source network or geography separates days of dwell time from same-day containment.
Runner behaviour. Because no workflow file changed, the anomalies live at runtime: processes inspecting other processes, destinations outside the build allowlist, cache entries that do not match the dependency state, and publishes off the approved path.
Endpoint and package indicators. The Nx records publish an indicator set covering persistence artefacts on macOS, Linux and Windows, a daemonisation environment variable and unexpected privileged sudo entries; TanStack's malicious manifests carry an undeclared payload file.
Remediation and rotation order after a pipeline compromise
Rotation without sequencing hands a fresh credential back to a live implant.
Stop execution first. Update and terminate components running the malicious version, which Nrwl stresses keep exfiltrating while alive.
Remove persistence before rotating, unloading the macOS launch agent rather than only deleting the file; a credential rotated while an implant is live is one you just disclosed.
Rotate by blast radius. Source control and registry publishing credentials first, because those turn your incident into your customers', then cloud credentials, cluster and secret-manager tokens, SSH keys and environment files, including anything those CLIs minted during the window.
Rebuild dependency state, do not patch it. Remove installed dependencies and the lockfile, reinstall clean, and pin to versions published before 11 May 2026 19:00 UTC, TanStack's stated cut-off.
Treat affected runners and endpoints as compromised hosts, rotate every secret they could reach, and consider rebuilding. Nrwl advises others in the same upstream chain to review their audit logs for the pattern.
Then fix the pipeline, reading the two change lists separately, because they are not the same list. Nrwl's postmortem records three: an approval now required to publish Nx Console, enforced through GitHub Actions environments with required reviewers where the reviewer cannot be whoever triggered the workflow; monitoring of its GitHub audit log for events such as workflow-run deletions; and Action SHAs pinned instead of floating refs. It notes those practices already existed on its main repositories and were simply not applied everywhere. TanStack's list is different: caches removed from the release pipeline and from Actions, every action in the organisation pinned to a commit SHA, non-SMS 2FA enforced across npm and GitHub, every use of pull_request_target removed, and every repository on pnpm 11 to inherit install-cooldown behaviour (TanStack hardening).
The overlap is narrow, and the gap is instructive: both pinned actions to commit SHAs and both now lean on a release-age delay, but neither control that would have shortened the Nx timeline is on TanStack's completed list. Its postmortem files both as open, stating the "OIDC trusted-publisher binding has no per-publish review" and that it needs "monitoring on our own publishes." Publish approval and publish alerting are the costly ones, and they remain outstanding at the project hit first.
What a red team should now cover
Two asset classes usually written out of scope were the initial access.
Asset | What to test for | Why scope misses it |
|---|---|---|
Workflow definitions and actions | Trigger contexts exposing base repository permissions to fork code; floating action tags rather than immutable references | Reviewed as code quality, not attack surface |
Build cache and artefact stores | Whether entries written by low-trust jobs can be restored by high-trust jobs | A performance feature, not a trust boundary |
Release and publish paths | Whether one identity can publish without a second approval; whether publish tokens are reachable from job code | Owned by engineering, rarely scoped |
Developer endpoints | Long-lived source control, cloud and registry credentials readable by any process running as the user | In the endpoint programme, not pentest scope |
Detection coverage | Whether a publish, workflow-run deletion or token use from a new origin alerts anyone | Validating a detection is rarely commissioned |
If you take one scoping decision from this article, make it the last row: neither project was watching the record it already had.
Stingrai has run offensive testing from Toronto and London since 2021, is a CREST-accredited penetration testing service provider at firm level with testers holding CREST CRT, and has 18 published CVEs. Pipeline and endpoint work of this kind is human-led; Snipe, our autonomous agent, is scoped to web application testing and priced on our pricing page, and our guide to CI/CD pipeline penetration testing scope covers adjacent decisions.
Frequently Asked Questions
What happened in the TanStack npm supply chain attack?
On 11 May 2026 between roughly 19:20 and 19:26 UTC, an attacker published 84 malicious versions across 42 legitimate @tanstack/* npm packages using the project's own GitHub Actions OIDC trusted-publisher binding, and advisory GHSA-g7cv-rxg3-hmpx states the publish workflow itself was not modified. The payload harvested cloud, Kubernetes, Vault, npm, GitHub and SSH credentials from any machine that ran an install, then republished other packages the victim maintained. It is CVE-2026-45321, in CISA KEV since 27 May 2026.
Was the Nx Console extension compromised?
Yes. Nx Console 18.95.0 for Visual Studio Code was published maliciously and was available roughly 18 minutes on the Visual Studio Marketplace and 36 minutes on Open VSX; it is CVE-2026-48027, patched in 18.100.0. Nrwl states the attacker got in because one of its developers was compromised by the TanStack compromise, which leaked their GitHub credentials through the GitHub CLI. The Nx CLI, official plugins and Nx Cloud were not affected.
Does pinning package versions protect against supply chain attacks?
No. Pinning protects against resolving to an unexpected version, but the attacker published malicious versions of the correct packages under the real publisher identity with valid provenance, so a fresh resolve inside the window pinned to malware. It helps only if the pinned version predates the compromise, which is why TanStack's workaround was to pin to versions published before 11 May 2026, 19:00 UTC.
How do attackers steal GitHub Actions secrets?
In this case they did not steal a stored secret at all. TanStack's advisory describes three known weaknesses chained: a workflow trigger that ran fork-supplied code with the base repository's permissions, cache poisoning letting a low-trust job write entries a high-trust release job later restored, and extraction of the short-lived OIDC token from the runner process. A credential need not be stored to be stolen.
What is a pull_request_target Pwn Request?
The pull_request_target trigger in GitHub Actions runs a workflow with the base repository's permissions and secrets rather than the restricted context a fork pull request normally receives, which is appropriate for trusted operations such as labelling or commenting. It becomes the Pwn Request pattern when the workflow then checks out and executes code from the pull request, so contributor-controlled code runs in a trusted context. It is a documented configuration hazard, not a product bug.
How do you detect a compromised CI/CD pipeline?
Look at runtime and identity behaviour rather than file diffs, because no workflow file was modified here and a diff review would have passed. The detectable signals were bulk deletion of workflow runs, commits whose author did not match the authenticated actor, a stolen token used from different client tooling, and a publish nobody had decided to make.
Which credentials should you rotate after a build system compromise?
Stop execution and remove persistence first, because rotating while an implant is live simply hands over the new credential. Then rotate in blast-radius order: source control and registry publishing credentials, then cloud credentials, cluster and secret-manager tokens, SSH private keys and environment files. Include anything those CLIs could have minted during the window, not only what sat on disk.
Should CI/CD pipelines be in scope for penetration testing?
Yes, and here the pipeline and the developer endpoint were the initial access while the production application was never touched. A useful scope covers workflow trigger contexts that expose privileged permissions to untrusted code, build caches shared across trust boundaries, whether one identity can publish without a second approval, and whether long-lived credentials are readable by any process on a developer machine.
References
TanStack advisory GHSA-g7cv-rxg3-hmpx (CVE-2026-45321): https://github.com/TanStack/router/security/advisories/GHSA-g7cv-rxg3-hmpx
TanStack postmortem: https://tanstack.com/blog/npm-supply-chain-compromise-postmortem
TanStack, completed and planned hardening: https://tanstack.com/blog/incident-followup
Nx Console advisory GHSA-c9j4-9m59-847w (CVE-2026-48027): https://github.com/nrwl/nx-console/security/advisories/GHSA-c9j4-9m59-847w
Nx Console v18.95.0 postmortem: https://nx.dev/blog/nx-console-v18-95-0-postmortem
NVD, CVE-2026-45321: https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-45321
NVD, CVE-2026-48027: https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-48027
NVD, CVE-2026-33634: https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2026-33634
CISA KEV catalog, catalogVersion 2026.08.07: https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json



