openapi-react-query-codegen compromised: are you exposed?
Ten malicious @7nohe/openapi-react-query-codegen versions shipped a credential-stealing npm worm on Aug 28, 2026, with valid provenance. Which versions, how to check your lockfiles and machines, and how to remediate.
Short answer: On August 28, 2026, ten malicious versions of @7nohe/openapi-react-query-codegen (about 150,000 weekly downloads) were published to npm with valid provenance. They run a credential-stealing, self-propagating worm at install time. npm has since pulled all ten and latest again resolves to the clean 3.0.2, but any machine or CI runner that installed one of them between roughly 20:00 and 23:00 UTC on August 28 should be treated as compromised. Safe versions per release line: 0.5.3, 1.6.2, 2.2.0, 3.0.2.
This post covers what happened, exactly which versions are affected, how to check your lockfiles and machines, what to do if you are exposed, and how to close the install-time gap this kind of attack lives in. Run the free InstallSafe scan if you want your lockfile checked against advisory data in one step.
What happened to @7nohe/openapi-react-query-codegen?
@7nohe/openapi-react-query-codegen generates type-safe TanStack Query hooks from an OpenAPI schema. It is a dev dependency in a lot of React, Next.js and TanStack Router projects, which is exactly why it is a good target: it gets installed on developer laptops and CI runners, not on end-user devices.
According to Socket's investigation and Aikido's writeup, an attacker abused the project's comment-triggered GitHub Actions release workflow to publish code from a pull-request fork under the repository's trusted-publishing identity. The npm registry's own timestamps confirm the timeline:
- 2026-08-28 20:00–20:02 UTC (wave 1):
0.5.4,1.6.3,2.2.1,3.0.3and one0.0.0-*prerelease published. - 2026-08-28 20:19–20:21 UTC (wave 2):
3.0.4,1.6.4,2.2.2,0.5.5and a second0.0.0-*prerelease published.latestpointed at3.0.4. - 2026-08-28 ~23:11 UTC: all ten versions removed from the registry;
latestback on3.0.2.
That is a roughly three-hour window in which a plain npm install @7nohe/openapi-react-query-codegen, or any CI job with a floating range like ^3.0.0, pulled a trojanized build.
Which versions are affected?
| Release line | Malicious versions (do not install) | Last known-good |
|---|---|---|
| 0.x | 0.5.4, 0.5.5 | 0.5.3 |
| 1.x | 1.6.3, 1.6.4 | 1.6.2 |
| 2.x | 2.2.1, 2.2.2 | 2.2.0 |
| 3.x | 3.0.3, 3.0.4 | 3.0.2 |
| prereleases | 0.0.0-365d4eb738d3146583431948d3ba6e27a32556be, 0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab | n/a |
Only this one package is confirmed so far. Because the payload is a worm that republishes itself under any npm token it steals, the list of affected packages may grow. Check the Socket and Aikido posts above for updates before you assume the blast radius is closed.
How did the attacker publish with valid provenance?
This is the part worth understanding even if you never used the package. The project's release.yml workflow triggered on issue_comment and gated only on the comment text being npm publish. It then checked out the pull request head from the fork and ran pnpm publish --no-git-checks with id-token: write. Nobody checked who wrote the comment.
So any GitHub account could open a PR from a fork, comment npm publish, and have the official workflow build and sign the fork's contents. Because GITHUB_REF for comment events is the default branch, the resulting provenance attestation pointed at refs/heads/main at the last legitimate commit. npm audit signatures passes on every one of the ten malicious versions. Provenance proves which workflow built an artifact; it does not prove that the workflow only built trusted source.
What does the payload do?
Each malicious tarball ships a 5.6 MB obfuscated loader, 3FWCvzduYZg.js, plus a binding.gyp that abuses a Python expression in its conditions field to run node 3FWCvzduYZg.js during node-gyp processing. Wave-2 versions also add an explicit "preinstall": "node 3FWCvzduYZg.js". Either way it fires on npm install, on laptops and CI runners alike.
The loader silently downloads Bun 1.4.0 into a temp directory named trinnyyyy-XXXXXX, decrypts an AES-128-GCM second stage, and runs it detached from the install process. Based on the two static analyses, the second stage:
- Harvests GitHub, npm, PyPI, RubyGems and JFrog tokens from files, environment variables and process memory; AWS, Azure and GCP credentials including cloud metadata endpoints; Kubernetes service-account tokens; SSH keys;
.envfiles; Docker and git credentials; shell history; crypto wallets; and AI-agent config such as~/.claude.jsonand~/.claude/*. - Validates what it finds (for example
sts:GetCallerIdentity,registry.npmjs.org/-/whoami) before exfiltrating. - Exfiltrates encrypted bundles to attacker-created public GitHub repositories named after two Touhou characters plus a number (e.g.
cirno-marisa-74291), with the descriptionTrinitite: Sponsored by Preview 2 Effectsand commit messages likemeow meow meow. - Self-propagates: with a stolen npm token it enumerates every package that maintainer owns, injects the same
binding.gyp+ loader, and publishes new versions. It does the same for PyPI and RubyGems. - Poisons every reachable GitHub repository and branch with backdoor files:
.vscode/tasks.json,.claude/settings.json(aSessionStarthook running.claude/setup.mjs), a fake.github/workflows/codeql.yml, plus.gemini,.cursor,.codexand.opencodeconfig. Commits are authored asgithub-actionswith the messagechore: update dependencies. - Installs persistence on macOS (
~/Library/LaunchAgents/com.user.sysvinit-detect-fash.plist) and Linux (~/.config/systemd/user/sysvinit-detect-fash.service) that watches a stolen GitHub token for 72 hours and runs a stored handler if the token is revoked.
It also bails out silently if it detects a Russian locale, EDR directories (CrowdStrike, SentinelOne, Carbon Black), known security-research GitHub owners, or decoy credentials used by scanners.
How do you check if you're exposed?
You are only at risk if the package was installed from the registry during the window, or from a cache that captured a bad tarball. Work through these in order:
- Check your lockfiles. Search every
package-lock.json,pnpm-lock.yamlandyarn.lockin your repos for the ten version strings above.grep -rn "openapi-react-query-codegen" --include=*.json --include=*.yaml --include=*.lock .then inspect the resolved version. - Check what is actually installed.
npm ls @7nohe/openapi-react-query-codegen(orpnpm why/yarn why) in each project, including transitive installs. - Look for the loader on disk.
find / -name 3FWCvzduYZg.js 2>/dev/null. Also checknode_modules/@7nohe/openapi-react-query-codegen/binding.gyp; the clean package has no native build manifest at all. - Look for the runtime artifacts. Temp directories matching
trinnyyyy-*; thesysvinit-detect-fashLaunchAgent, systemd unit, and files under~/.local/binand~/.config/sysvinit-detect-fash/. - Check CI logs. Any job that ran
npm install,pnpm installoryarnbetween 20:00 and 23:15 UTC on August 28 in a project that depends on this package, especially with a floating range. - Check your GitHub account. Look for unexpected commits authored as
github-actionswith the messagechore: update dependencies, new.claude/,.vscode/tasks.jsonor.github/workflows/codeql.ymlfiles you did not add, and any new public repository with the descriptionTrinitite: Sponsored by Preview 2 Effects. - Check your npm account. If you maintain packages, review recent publishes for versions you did not release.
If you would rather not grep by hand, the free InstallSafe scan checks your lockfile against OSV.dev advisory data; once the OSV malware entry for these versions lands, it will flag them.
What should you do if you installed a malicious version?
- Isolate first. Take the affected laptop or runner off the network. Treat it as compromised, not merely dirty.
- Rebuild before you rotate. Prefer reimaging from a known-clean image. If you must clean in place, remove the persistence (LaunchAgent / systemd unit /
~/.local/bin/sysvinit-detect-fash.sh) before revoking tokens, because the token monitor runs a stored handler when it sees a revoked GitHub token. - Rotate everything the machine could reach: GitHub PATs and Actions secrets, npm, PyPI and RubyGems tokens, AWS/Azure/GCP keys, Kubernetes service accounts, SSH keys, Docker and git credentials, Vault tokens, AI-provider API keys.
- Audit your repos and packages for the worm's commits and republishes described above, and revert them.
- Pin and reinstall. Set
@7nohe/openapi-react-query-codegento0.5.3,1.6.2,2.2.0or3.0.2with an exact version, deletenode_modules, clear the package-manager cache (npm cache clean --force,pnpm store prune), and reinstall from a clean lockfile.
If you maintain a package: audit your publish workflow today
The root cause was a workflow that publishes on issue_comment and checks nothing but the comment body. If you have anything similar:
- Gate on
github.event.comment.author_associationbeingOWNERorMEMBER, or better, do not publish from comment events at all. Publish from tags or releases on the default branch. - Never check out
pull/<n>/headin a job that holdsid-token: writeor publish credentials. - Require 2FA and, where your registry supports it, restrict publishing to trusted publishing from a specific workflow and branch.
How does a registry firewall help against an attack like this?
Be clear about what it does and does not do. A registry firewall such as InstallSafe sits between your package manager and npm. Every npm install, on a laptop, in CI, or run by an AI coding agent, resolves through it, and any version flagged in OSV.dev advisory data is refused before the tarball is written to disk. Clean versions pass through byte-for-byte.
InstallSafe does not do behavioral analysis and would not have blocked these versions in the first minutes before anyone had flagged them. Tools like Socket and Aikido, which detected this one, catch that pre-advisory window. What the firewall form factor gives you is the other half of the problem:
- Nothing depends on a human reading the news. Once the advisory is in OSV, the block applies to every developer and every pipeline at once, including the runner that re-installs from a stale cache next week.
- It covers installs you do not see. AI agents running
npm installin a sandbox, a contractor's laptop, a forgotten nightly job. See how to block AI agents from installing malicious npm packages. - One config line.
npm config set registry https://r.installsafe.io, or the same in.npmrccommitted to the repo.
Pair it with exact-version pinning and ignore-scripts=true in CI where you can afford it, and the install-time execution path this worm relies on gets a lot narrower. If you want to see where you stand today, scan your lockfile for free. For a similar incident and how the response played out, read our breakdown of the keyv worm that hit 400+ npm packages.
FAQ
Is @7nohe/openapi-react-query-codegen safe to install now?
As of August 29, 2026, the ten malicious versions have been removed from npm and latest resolves to 3.0.2, which predates the compromise. Pin an exact known-good version (0.5.3, 1.6.2, 2.2.0 or 3.0.2) rather than a range until the maintainer publishes a post-incident release from a fixed workflow.
Does npm audit signatures detect the malicious versions?
No. All ten carry valid provenance attestations because they were built by the project's real GitHub Actions workflow. Provenance verifies the build workflow, not the source it built.
I only had it as a devDependency. Am I still at risk?
Yes. The payload runs at install time on whatever machine performs the install, which for dev dependencies is your laptop and your CI runner. That is where the credentials it wants live.
Does ignore-scripts stop this?
It blocks the preinstall hook in wave-2 versions, but not the binding.gyp path, which node-gyp evaluates during the native-build step. Treat ignore-scripts as one layer, not the fix.
How do I know if the worm touched my other packages?
Review your npm account's recent publishes for versions you did not release, and inspect each tarball for a binding.gyp plus 3FWCvzduYZg.js. Check GitHub repos for commits authored as github-actions with the message chore: update dependencies.
Will InstallSafe block these versions?
InstallSafe blocks versions flagged in OSV.dev. At the time of writing there is not yet a GHSA/OSV entry for this incident; once it is published the ten versions are refused at install. Until then the practical protection is pinning to a known-good version and clearing caches.