On August 28, 2026, a new npm supply-chain compromise hit @7nohe/openapi-react-query-codegen, a code generator that produces TypeScript clients and TanStack Query hooks from OpenAPI schemas.[1][2][3]
The attacker did not need a maintainer's npm password or a stolen long-lived publish token.
The release workflow itself was the path.
The repository listened for:
issue_comment
and a pull-request comment with the exact body:
npm publish
could trigger the release path without verifying whether the commenter was a maintainer, collaborator, or completely external GitHub user.[1][2]
The workflow then:
checked out PR-controlled code
→ ran pnpm install
→ held id-token: write
→ published through npm Trusted Publishing
The result was especially important: 10 malicious versions were published with valid npm provenance.[1][3]
That gives us one of the clearest software-supply-chain lessons of 2026:
Provenance can correctly prove where a build came from while the build itself is malicious.
There is another correction that matters.
@tanstack/react-query itself was not compromised in this incident. The attacked package is an independent code generator that targets TanStack Query.[6][7]
It is not an official TanStack package.
Information status: August 31, 2026.
TL;DR
| Question | Verified answer |
|---|---|
| What was compromised? | @7nohe/openapi-react-query-codegen |
| Date | August 28, 2026 |
| Malicious versions | 10 |
| Stable malicious versions | 8 |
| Malicious prereleases | 2 |
Was @tanstack/react-query compromised? | No |
| Why is TanStack Query relevant? | The package generates hooks/options for it |
| Main attack vector | Misconfigured GitHub Actions issue_comment release workflow |
| Maintainer npm password required? | No |
| Long-lived npm publish token required? | No |
| Publish authority | id-token: write + npm Trusted Publishing |
| Did malicious releases have provenance? | Yes |
| Does provenance guarantee safe code? | No |
| Main execution paths | binding.gyp, plus preinstall in some versions |
| Main payload file | 3FWCvzduYZg.js |
Current latest | 3.0.2 |
Is 3.0.2 known clean? | Yes |
| Advisory severity | Critical, CVSS 9.6 |
| CVE | No known CVE in the advisory as of Aug 31 |
| Classification | Embedded malicious code / supply-chain compromise |
| Campaign family | Researchers link it to Mini Shai-Hulud-derived activity |
| Operator attribution certain? | No |
| Priority response | Isolate host and rotate all reachable credentials from a clean machine |
What happened on August 28?
GitHub Security Advisory GHSA-9pvf-vcx3-x239 lists 10 malicious versions published between roughly 20:00 and 20:21 UTC.[1]
Eight were stable:
0.5.4
0.5.5
1.6.3
1.6.4
2.2.1
2.2.2
3.0.3
3.0.4
Two were prereleases:
0.0.0-365d4eb738d3146583431948d3ba6e27a32556be
0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab
The malicious latest tag pointed to 3.0.4 from approximately 20:19:29 UTC until about 22:51 UTC, when it was restored to clean 3.0.2.[1]
TanStack Query itself was not compromised
Headlines such as:
TanStack Query hacked
are inaccurate here.
@7nohe/openapi-react-query-codegen is an independent project.
It generates code for:
@tanstack/react-query
from an OpenAPI schema.[6]
That is a technology relationship, not package ownership.
The official @tanstack/react-query package remains separate, and TanStack's May postmortem also explicitly listed the Query family as unaffected by that earlier incident.[7][11]
The accurate statement is:
A third-party code generator used with TanStack Query was compromised.
What does the package do?
It generates:
- typed API clients,
- query keys,
queryOptions,infiniteQueryOptions,useQuery,useMutation,- suspense helpers,
- prefetch and SSR helpers.
Typical flow:
OpenAPI schema
↓
openapi-react-query-codegen
↓
TypeScript client
↓
TanStack Query hooks/options
It is commonly a development dependency.
That does not make it low risk.
How widely used was it?
On August 31, npm showed roughly 155,000 weekly downloads for the scoped package.[6]
That number is dynamic and should be treated as a snapshot, not a permanent historical fact.
More important is where the package runs:
- developer laptops,
- CI jobs,
- release pipelines,
- repositories with credentials,
- machines with GitHub and cloud tooling.
Those environments are extremely attractive to credential-stealing malware.
The compromise began in GitHub Actions, not npm
The release workflow listened for:
on:
issue_comment:
types: [created]
issue_comment itself is not a vulnerability.
The dangerous chain was:
external comment
+
no actor authorization gate
+
checkout PR code
+
execute PR code
+
id-token: write
+
publish
The release workflow became the privilege boundary.
One comment could enter the release path
The workflow checked whether the comment:
- belonged to a pull request,
- had the exact body
npm publish.
It did not adequately verify whether the commenter was:
OWNER
MEMBER
COLLABORATOR
or otherwise trusted.
That meant an external GitHub account could prepare a fork PR and trigger the privileged path with a comment.
The bug was an authorization failure around release automation.
Checking out the fork turned input into execution
Triggering a workflow does not automatically mean attacker code runs.
The critical next step was that the pipeline checked out pull-request-controlled code and ran:
pnpm install
At that point the PR content became executable inside a privileged release job.
The security boundary is simple:
untrusted code treated as data
versus:
untrusted code executed with privileges
GitHub's security guidance repeatedly warns against executing untrusted PR content in privileged workflows.[9]
id-token: write enabled Trusted Publishing
The release job had:
permissions:
id-token: write
This is a legitimate permission when using OIDC-based npm Trusted Publishing.
npm's model replaces long-lived publish tokens with short-lived workload identity issued to an approved CI workflow.[10]
That is normally safer.
The failure was not that OIDC existed.
The failure was that an untrusted user could influence code executing inside the workflow that had the OIDC privilege.
No maintainer password theft was required
StepSecurity notes that the attacker did not require:
- a maintainer npm password,
- a long-lived npm publish token.
The older threat model is:
steal token
→ publish package
The newer model is:
control trusted workflow execution
→ workflow receives legitimate identity
→ publish package
That moves supply-chain defense from account security into CI/CD architecture.
Malicious releases had valid provenance
Because the releases came through the genuine workflow and Trusted Publishing, the malicious versions carried valid provenance attestations.[1][3]
Provenance effectively said:
this artifact came from
this repository
through this workflow
That statement could be completely true.
The missing question was:
was the code executed by that workflow authorized and benign?
Provenance is not malware detection
Provenance can prove:
- build origin,
- workflow identity,
- source association,
- chain of custody.
It does not automatically prove:
- source code is safe,
- PR was authorized,
- workflow logic was secure,
- package contains no malicious code.
Therefore:
valid provenance
≠
safe package
npm's own Trusted Publishing documentation presents provenance as origin verification, not as a universal malware verdict.[10]
First execution path: binding.gyp
Some malicious stable releases had no obvious:
"preinstall": "..."
Instead they introduced:
binding.gyp
A package that looks like a native addon can cause npm/node-gyp processing during installation.
The malicious binding.gyp abused GYP's Python expression evaluation to reach os.system() and execute the JavaScript payload.[1][5]
For defenders, this matters because scanning only lifecycle-script fields is insufficient.
Second execution path: explicit preinstall
Later stable malicious releases also included:
"preinstall": "node 3FWCvzduYZg.js"
Those were:
0.5.5
1.6.4
2.2.2
3.0.4
Earlier stable releases:
0.5.4
1.6.3
2.2.1
3.0.3
used the binding.gyp path without the explicit lifecycle script.[1]
A single detection rule would therefore miss part of the malicious set.
The two prereleases were different
The two malicious 0.0.0-* prereleases contained a bad preinstall reference, but:
"files": ["dist"]
excluded the referenced payload files from the tarballs.[1]
So all 10 releases are malicious publications, but they are not identical artifacts and do not all have the same execution path.
The eight stable releases are the most important execution set.
Main payload: 3FWCvzduYZg.js
Malicious stable tarballs contained:
3FWCvzduYZg.js
at roughly 4.4 to 6.4 MB, depending on the release.[1]
StepSecurity observed that 0.5.4 jumped from about 41 KB in 0.5.3 to more than 5.6 MB.[2]
A huge package-size increase in a code generator with no legitimate reason for multi-megabyte obfuscated JavaScript is a valuable anomaly signal.
What the payload did at runtime
StepSecurity detonated affected versions in isolated GitHub-hosted runners.[2]
For 3.0.4, observed activity included:
Node starts payload
→ curl downloads Bun
→ Bun executes under /tmp/trinnyyyy-*
→ gh auth token is called
→ Git credential manager queried
→ ssh / scp checked
→ running processes enumerated
→ temporary updater.py invoked
Researchers also observed GitHub API access and probing of the Google Cloud metadata hostname.
This is credential-oriented malware.
Which secrets should be considered exposed?
Actual exposure depends on the host.
A malicious install process can potentially access what the installing user or CI runner can access:
GitHub credentials
npm credentials
SSH keys
cloud environment credentials
CI/CD secrets
source repositories
deployment credentials
This does not mean every secret was stolen on every machine.
It means that if an affected version executed, all credentials reachable from that process should be treated as potentially exposed.
Why a dev dependency can be extremely dangerous
A developer may think:
it's only codegen
it never ships to the browser
That is irrelevant to install-time malware.
The payload only needs to run during:
npm install
pnpm install
yarn install
Development dependencies often execute on the most sensitive systems in the software lifecycle.
Package installation is a code-execution surface
Package managers support lifecycle hooks such as:
preinstall
install
postinstall
prepare
Native addon flows may also run tooling like node-gyp.
So package installation is effectively:
dependency resolution
+
download
+
potential code execution
Supply-chain controls must therefore operate before or during dependency installation, not only against final application artifacts.
Was TeamPCP definitely behind it?
JFrog, Socket and other researchers connect the payload to Mini Shai-Hulud and related descendants.[3][4]
But operator attribution is less certain.
Similar code and campaign markers may indicate:
- the same group,
- leftover access,
- public malware reuse,
- a copycat.
JFrog explicitly discusses that uncertainty.[4]
We should therefore distinguish:
malware family similarity
from:
proven actor identity
Why Mini Shai-Hulud keeps appearing
Mini Shai-Hulud-style campaigns focus on:
- credential theft,
- CI/CD systems,
- package registries,
- GitHub,
- self-propagation.
In May 2026, researchers tracked waves involving TanStack, Mistral, UiPath and other ecosystems.[12]
The recurring pattern is:
enter through build/release tooling
→ obtain publish authority
→ ship trusted-looking package
→ execute on developer/CI hosts
→ steal credentials
→ propagate
The campaign name is less important than this repeatable attack model.
The May TanStack attack was a different technical incident
On May 11, 2026, the affected TanStack repository was Router/Start.[11]
The attacker chained:
- a
pull_request_targetpwn-request pattern, - GitHub Actions cache poisoning,
- OIDC token extraction from runner memory.
The result:
42 packages
84 malicious versions
That is not the same root cause as the August 28 compromise.
TanStack Query was clean in May too
TanStack's official postmortem said only Router/Start was affected and explicitly listed Query among unaffected repositories/families.[11]
So in 2026 there were at least two incidents that can be lazily described as “TanStack-related supply-chain attacks”, yet neither justifies saying:
@tanstack/react-query was infected
May affected Router/Start.
August affected an independent code generator for TanStack Query.
The May incident demonstrated real downstream consequences
OpenAI disclosed that two employee devices were affected by the May TanStack/Mini Shai-Hulud incident.[13]
OpenAI reported:
- credential-focused activity,
- limited credential material exfiltrated from a subset of source repositories,
- no evidence of user-data access,
- no evidence of production-system or IP compromise,
- credential and signing-certificate rotation as precautionary response.
That history explains why a short malicious-package exposure window can still matter to major organizations.
August attack vs May attack
May
fork PR
→ pull_request_target
→ poisoned cache
→ trusted release restores cache
→ OIDC token extracted from memory
→ publish
August
fork PR
→ public "npm publish" issue_comment
→ privileged workflow checks out PR
→ pnpm install executes attacker code
→ job already has id-token: write
→ Trusted Publishing
The August path is simpler.
No cache poisoning was required.
How to check whether your project was affected
Search lockfiles and historical build records for the exact affected versions.[1]
Check:
package-lock.json
pnpm-lock.yaml
yarn.lock
bun.lock
SBOM
CI install logs
dependency caches
Do not only inspect the current package.json.
A floating range may have resolved to a malicious version during the incident window even though latest is clean today.
Important IOCs
Public indicators include:[1][2][5]
3FWCvzduYZg.js
binding.gyp
is_it_this_simple.js
nu.js
/tmp/trinnyyyy-*/bun
/tmp/*/updater.py
Also investigate:
- unexpected Bun downloads during install,
gh auth tokenduring package installation,- unusual Git credential-manager calls,
- outbound GitHub release traffic from package installers,
- unexpected node-gyp execution in a package that should not contain native code.
Behavioural detection is more durable than filename-only detection.
What to do after an affected installation
GitHub advisory and StepSecurity recommend treating the host as potentially compromised.[1][2]
Minimum response:
1. stop builds
2. isolate the host
3. use a separate clean machine
4. rotate all reachable credentials
5. invalidate active sessions
6. review GitHub/npm/cloud audit logs
7. purge dependency caches
8. discard artifacts built on the exposed runner
9. rebuild from clean inputs
Do not rotate only the npm token if the process could access more.
Which versions are safe?
The GitHub advisory points to 3.0.2 as known good and says releases published before August 28 are not affected by this incident.[1]
StepSecurity lists clean predecessors for each release line:
0.5.3
1.6.2
2.2.0
3.0.2
As of August 31, npm again shows:
latest = 3.0.2
What did upstream change?
According to the GitHub advisory, remediation included:[1]
- removing the
issue_commentrelease trigger, - moving releases to tag push,
- revoking the npm trusted publisher,
- rotating long-lived tokens,
- restoring
latestto3.0.2, - removing the
predist-tag, - deprecating all 10 malicious versions,
- requesting npm removal.
A public interaction should never directly become a gateway to a production publish identity.
Snyk also classifies the compromise as Critical with CVSS 9.6, reinforcing the severity of install-time malicious-code execution.[8] TanStack's follow-up hardening after the May incident is also relevant context for cache, workflow and release-pipeline defenses.[14]
How to harden GitHub Actions
Do not execute untrusted PR code in privileged jobs
GitHub recommends separating untrusted build execution from privileged actions.[9]
Require actor authorization
Comment-driven release automation needs an explicit authorization gate.
Minimize permissions
A job that does not publish does not need:
id-token: write
Separate build and publish
Prefer:
untrusted CI
↓
artifact treated as data
↓
trusted trigger/review
↓
release
Pin third-party actions by SHA
Action tags are also part of the software supply chain.
How to harden dependency installation
There is no single magic switch.
Useful controls include:
- lockfiles,
- frozen installs,
- package release cooldown/minimum age,
- monitoring new versions,
- package-size anomaly detection,
- lifecycle-script policy,
- sandboxed CI,
- outbound network restrictions,
- malicious-package scanning in addition to CVEs,
- SBOM,
- minimal credentials on runners.
npm Trusted Publishing still makes sense.[10]
But:
Trusted Publishing
must be paired with:
Trusted Workflow Design
Production checklist
Dependencies
- Lockfile is mandatory.
- CI rejects unexpected lockfile changes.
- Minimum release age/cooldown is configured.
- New dependency versions do not reach production immediately.
- Package-size changes are monitored.
- Install scripts are inspected.
- Unexpected
binding.gypis flagged. - Malicious-package detection supplements CVE scanning.
GitHub Actions
- Fork code never runs in privileged context.
-
issue_commentworkflows have authorization gates. -
pull_request_targetis used only when necessary. -
GITHUB_TOKENis least privilege. -
id-token: writeexists only where required. - Publishing requires a trusted trigger.
- Build and publish are separated.
- Actions are pinned to commit SHA.
- Comment-driven automation has a negative test for unauthorized users.
- Release workflows never check out untrusted fork code.
npm
- Trusted Publisher is limited to the minimum workflow set.
- Unnecessary long-lived publish tokens are removed.
- New publishes are monitored.
- Unexpected releases alert immediately.
- Provenance is verified but never treated as a malware verdict.
- Incident runbook covers deprecation/revocation.
Runners and developer endpoints
- CI runners are ephemeral.
- Workspaces do not contain unnecessary secrets.
- Cloud credentials are short-lived.
- SSH keys have minimal scope.
- Outbound traffic is monitored.
- Dependency installs cannot reach the whole infrastructure.
- Developer endpoints have runtime/EDR monitoring.
- Hosts can be isolated quickly.
Incident response
- Dependency incident owner is defined.
- Lockfiles can be searched historically.
- CI caches can be purged.
- Sessions can be invalidated quickly.
- Credential exposure from runners is mapped.
- Credential rotation happens from a clean machine.
- npm accounts are checked for unauthorized publishes.
- GitHub audit logs are reviewed after rotation.
POLPROG verdict
The @7nohe/openapi-react-query-codegen compromise is one of the clearest 2026 examples of how software supply-chain attacks are changing.
The attacker did not need to:
hack npm
steal a maintainer password
defeat 2FA
steal a classic publish token
It was enough for the release workflow to let an untrusted user get attacker-controlled code executed inside a job that had Trusted Publishing authority.
That is the key shift.
Account security still matters.
But the modern security boundary increasingly includes:
CI/CD graph
workflow triggers
artifacts
caches
OIDC
release authorization
dependency execution
The second lesson is provenance.
Valid provenance remains valuable.
Here, however, it correctly attested the origin of a malicious build.
That is not a cryptographic failure.
It is a workflow-trust failure.
The third lesson is precision.
TanStack Query itself was not compromised.
A third-party generator used with TanStack Query was compromised.
Security reporting should not expand incident scope simply because a technology name appears in a package name or dependency graph.
As of August 31:
malicious versions: identified
advisory: published
latest: restored to 3.0.2
release workflow: changed
The most important question for JavaScript teams is therefore not only:
Did we use this package?
It is:
Does our own release pipeline contain a similar path from untrusted input to a job with publish authority?
If the answer is not immediately clear, audit your GitHub Actions workflows now.

