On May 11, 2026, a sophisticated supply chain worm tore through the npm ecosystem, compromising over 47 packages spanning TanStack, UiPath, and several other organizations. The attack wasn't loud. It was surgical. And it had been fully mapped by Depi weeks before it was ever executed.
All Depi clients were automatically alerted at the moment the vulnerability was detected. This is what the full chain looked like.
The Entry Point: PR #7378
The attack began with a seemingly ordinary pull request, PR #7378, submitted to the TanStack/router repository. The attacker's goal was not to merge code into the main branch directly. Instead, they were exploiting a structural flaw in the repository's GitHub Actions configuration: the combination of a pull_request_target workflow and an unsafe checkout of fork code.
bundle-size.yml, a workflow responsible for benchmarking bundle sizes on pull requests, was triggered by the pull_request_target event. This is one of the most dangerous events in GitHub Actions. Unlike pull_request, which runs in a sandboxed environment with restricted permissions, pull_request_target runs in the context of the base branch and has full access to repository secrets and GitHub Actions cache credentials.
The benchmark-pr job checked out the PR's merge commit directly:
- uses: actions/[email protected]
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
This handed the attacker's code execution rights in a fully privileged environment: no reviewer approval required, no interaction from maintainers needed. Attack complexity: zero.
The Pivot: Post-Checkout Script Clobbering
Once code execution was achieved, the attacker pivoted using a technique similar to the previously documented Cacheract by Adnan Khan: clobbering the post-checkout hook of actions/checkout to detonate the payload after the main job steps complete.
The fork code, loaded during the build via packages/history/vite_setup.mjs, overwrote the post script of actions/[email protected]. The post step runs at the end of the job, after all workflow steps have completed, and by that point the runtime had already injected the following environment variables into the context:
ACTIONS_CACHE_URL: the endpoint to read and write from the GitHub Actions cacheACTIONS_RUNTIME_TOKEN: the bearer token authorizing those cache operations
The clobbered post-checkout script used these credentials to overwrite the shared pnpm store cache under the default branch scope, using the exact same cache key format used by every other workflow in the repository:
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
Because pull_request_target runs in the base branch context, the poisoned cache entry was written to the main branch scope, not just for this PR, but for every subsequent workflow run in the repository.
The Full Attack Chain
Depi's graph engine traced the exploit end-to-end across two workflows.
The poisoner: bundle-size.yml, triggered by pull_request_target:
WorkflowNode('TanStack/router:main:.github/workflows/bundle-size.yml')
└── JobNode('benchmark-pr' #L28) [trigger: pull_request_target]
└── StepNode('Checkout_0' #L35)
ref: refs/pull/${{ github.event.pull_request.number }}/merge ← fork code
└── StepNode('Setup Tools' #L42)
└── ActionNode('TanStack/config/.github/setup@main')
└── cache credentials exposed to fork code
└── StepNode('Measure Bundle Size' #L45)
└── SINK: pnpm nx run @benchmarks/bundle-size:build ← fork code executes
The victim: release.yml, triggered on every push to main:
WorkflowNode('TanStack/router:main:.github/workflows/release.yml')
└── JobNode('release' #L21) [trigger: push]
└── StepNode('Checkout_0' #L26)
└── ActionNode('actions/[email protected]')
└── StepNode('Check for changesets' #L30)
└── StepNode('Start Nx Agents' #L39)
└── StepNode('Setup Tools' #L42)
└── ActionNode('TanStack/config/.github/setup@main')
└── StepNode('Setup pnpm' #L6)
└── StepNode('Setup Node' #L8)
└── StepNode('Get pnpm store directory' #L13)
└── StepNode('Setup pnpm cache' #L17) ← restores poisoned cache
└── StepNode('Install dependencies' #L24)
└── SINK: pnpm install --frozen-lockfile ← poisoned
release.yml carries contents: write and id-token: write permissions and runs pnpm run changeset:publish, the npm publish step. A poisoned pnpm store restored here means backdoored packages ship directly to the registry on the next merge to main.
The permissions escalation path: contents: read → write, pull-requests: none → write, id-token: none → write.

The Worm: Self-Spreading Through the Ecosystem
Once the first batch of malicious packages was published, the attack became self-propagating.
Each compromised package shipped a 2.3 MB obfuscated binary credential stealer. On installation in any CI/CD environment, it harvested:
- GitHub tokens
- npm authentication credentials
- Any secret injected into the pipeline environment
Those stolen tokens were immediately used to publish new malicious versions of every package the victim token had write access to. Ten malicious @tanstack versions were published within a six-minute window beginning at approximately 19:20 UTC on May 11.
The worm then jumped to adjacent organizations whose pipelines had installed TanStack packages.
Blast Radius: 47+ Packages Across the Ecosystem
The following packages received malicious versions. All were published in pairs (e.g., 1.169.5 / 1.169.8, 1.166.12 / 1.166.15) to appear as routine patch releases:
@tanstack (35 packages)
@tanstack/router-core, @tanstack/react-router, @tanstack/vue-router, @tanstack/solid-router, @tanstack/router-devtools, @tanstack/history, and 29 additional packages across the TanStack Router and adjacent tooling ecosystem.
@uipath (7 packages)
@uipath/docsai-tool, @uipath/packager-tool-apiworkflow, @uipath/agent.sdk, and four additional packages.
Additional targets
@draftauth/client, @draftlab/auth, @taskflow-corp/cli, @tolka/cli, and others.
A critical detail: the malicious packages carried valid SLSA provenance attestations. Standard supply-chain integrity checks would not have flagged them.
Depi Detected the Full Chain on April 16
At Thu, 16 Apr 2026 21:34:36 CEST, Depi autonomously traced and flagged the complete exploit chain described above, 25 days before the attacker pulled the trigger.
The vulnerability was not a known CVE. There was no public disclosure. No one had reported anything. Depi identified it by analyzing the structural relationship between bundle-size.yml and the release workflows: a pull_request_target workflow that executed fork code in a privileged context, sharing a cache namespace with a release-capable workflow. The combination was unambiguous.
All Depi clients using any @tanstack package referencing the vulnerable repository as the provenance were alerted immediately, with full exploit chain details, blast radius mapping, and pinning recommendations, before a single malicious byte hit npm.

Timeline
- Thu, Apr 16, 2026 at 21:34:36 CEST: Depi detects and maps the full exploit chain in
TanStack/router - Apr 16-24, 2026: All Depi clients alerted; pinning strategies issued
- May 11, 2026 at ~19:14 UTC: Attacker submits PR #7378;
bundle-size.ymlexecutes fork code - May 11, 2026 at ~19:20 UTC: Poisoned cache written to main branch scope
- May 11, 2026 at ~19:20-19:26 UTC: 10 malicious
@tanstackversions published in 6 minutes - May 11, 2026: Worm propagates to UiPath and additional targets; 47+ packages total compromised
What This Means
This attack is notable for three reasons.
First, it required zero interaction from maintainers. No social engineering, no account takeover, no phishing. A single pull request was sufficient.
Second, the self-spreading mechanism turned a single repository compromise into an ecosystem-wide worm within minutes. Any organization whose CI/CD pipeline installed a TanStack package during that window was a potential next target.
Third, the presence of valid SLSA attestations on the malicious packages is a signal that provenance alone is not a sufficient defense. Attestations verify that a package was built by a specific pipeline, but if that pipeline is compromised, attestations become a trust amplifier, not a safeguard.
The only reliable detection is upstream behavioral analysis: modeling what a repository's workflows are capable of before an attacker acts on that capability.
That is what Depi is built for.

