@7nohe/openapi-react-query-codegen npm compromise via exposed publishing workflow (Aug 28, 2026)
Tags
- ops
- operations
- supply-chain
- npm
- GitHub Actions
- trusted-publishing
- OIDC
- workflow-abuse
- CI/CD
- credential-theft
- Bun
- binding.gyp
- preinstall
- unauthenticated-publish
Summary
On August 28, 2026, an external GitHub user (public account p00paboot) abused the release workflow of @7nohe/openapi-react-query-codegen and published ten malicious npm versions that execute attacker-supplied code at install time. No maintainer npm password or long-lived npm token was involved: the repository's release workflow listened for issue_comment events, accepted a trigger comment whose body exactly matched npm publish on any pull request without checking the commenter's role or repository association, checked out the attacker's PR head, and published through npm Trusted Publishing using a GitHub Actions OIDC identity (id-token: write).
StepSecurity (August 28, 2026) reproduced the installation behavior on isolated GitHub-hosted runners under Harden-Runner and captured the runtime behavior: the payload downloads the Bun runtime from GitHub release infrastructure, then steals GitHub authentication (gh auth token, git credential-manager github list --no-ui), probes for SSH tooling and the Google Cloud metadata hostname, and shells out through a staged updater.py.
This is a clean example of the issue-comment-triggered release-workflow / OIDC trusted-publishing abuse pattern: an unauthenticated actor gains arbitrary package publication rights by posting one comment, because the workflow confers publish rights to any PR participant.
Affected versions
Ten malicious versions:
0.5.4,0.5.51.6.3,1.6.42.2.1,2.2.23.0.3,3.0.40.0.0-365d4eb738d3146583431948d3ba6e27a32556be0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab
The eight malicious stable versions were published between 20:00:43 and 20:20:53 UTC on August 28, 2026. 0.5.4 grew from 41,621 bytes (in 0.5.3) to 5,658,449 bytes; the other malicious stable artifacts ranged from 4.46 MB to 6.53 MB.
Clean pin targets (known-good release lines): 0.5.3, 1.6.2, 2.2.0, 3.0.2.
As of the StepSecurity post, the npm registry still mapped the
latesttag to malicious3.0.4and had not deprecated it. Verify current registry state before acting onlatest.
Attack mechanics
- Untrusted trigger reaches a privileged publisher. The release workflow fires on
issue_commentand required only that the comment belonged to a pull request and that its body exactly matchednpm publish. It did not verify the commenter's repository role or association. - The workflow grants
id-token: write. It checked out the PR head, ranpnpm install, and published via npm Trusted Publishing (GitHub Actions OIDC). This placed untrusted pull-request code inside a privileged release job. - Attacker execution.
p00pabootopened PR #215 and #216, then posted the trigger comment. The resulting workflow runs reached their publish steps; npm registry timestamps followed within seconds. npm provenance ties the published packages to GitHub Actions and the repository'srelease.yml.
Payload behavior (Harden-Runner runtime evidence)
The stable releases detonate so far (1.6.3, 2.2.1, 3.0.3, 3.0.4) all caused curl to contact GitHub and release-assets.githubusercontent.com during installation. Registry metadata confirms the same preinstall command in the newly identified 0.5.5, 1.6.4, and 2.2.2.
- Explicit preinstall hook (3.0.4):
"preinstall": "node 3FWCvzduYZg.js"— a 6,384,601-byte payload. SHA256 of3FWCvzduYZg.js: b24d121667f21f492cb9db34fbfd515d5922a8dd30b9c45215c7220abbb10ca8- Malicious
binding.gyp(3.0.4, and other stable releases): a condition that traverses Python objects to reachos.system()and decodes tonode 3FWCvzduYZg.js— a second execution path whennode-gypevaluates the file. - Direct payload execution (3.0.4, CI markers removed): Node ran
3FWCvzduYZg.js, which: - spawned
curland downloaded Bun from GitHub release infrastructure, - ran the executable from
/tmp/trinnyyyy-*/bun, - ran
gh auth tokenandgit credential-manager github list --no-ui, - checked for
sshandscp, enumerated running processes, and invoked a shell with a temporaryupdater.pypath, - contacted the GitHub API and probed the Google Cloud metadata hostname.
StepSecurity's OSS AI Package Analyst independently scored 3.0.4 critical / risk 0 / rejected, identifying XOR-decoded code passed to the JavaScript Function constructor, the malicious preinstall hook, and the obfuscated binding.gyp execution path.
Indicators of Compromise
Package versions — see the ten affected versions above.
Files / artifacts
- 3FWCvzduYZg.js (preinstall payload; SHA256 above)
- malicious binding.gyp
- is_it_this_simple.js, nu.js
- /tmp/trinnyyyy-*/bun (staged Bun runtime)
- /tmp/*.js, /tmp/*/updater.py
Registry tarball hashes (npm tarball SHA1)
| Version | SHA1 |
|---|---|
| 0.5.4 | 2d934cf137a4e62519f88e7ab669d2fabda33867 |
| 0.5.5 | 337ae261e4e73a9f365f892dcef2dc6f6932e90a |
| 1.6.3 | 0f9bc76952b67d7a28a57d1e726293f417df0119 |
| 1.6.4 | 6499c9ab4e60f9b1db6756cb0de55ebc334a72d1 |
| 2.2.1 | 5ab130e4736d4582899af2385ec7eb5a33619d05 |
| 2.2.2 | fc60551b23485829c0a6e910224b049c891a49b8 |
| 0.0.0-365d4eb738d3146583431948d3ba6e27a32556be | e7a07ca4a3cd51c262495f473abe4c4e505b7be4 |
| 3.0.3 | bafa4edaa6812fce10ae703ae450cc88ebbe1730 |
| 3.0.4 | 3fc635b988db2bd647b8578dfc1a85769913b708 |
| 0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab | 206b18c418434abc994bd40e021edcc334eee89b |
Am I affected?
- CI/CD pipelines: search workflow logs, dependency caches, lockfiles, SBOMs, and build provenance for the affected versions. Look for new Bun execution and unexpected GitHub API traffic during dependency installation.
- Developer machines: search workstations for the payload filename, the
trinnyyyytemporary directories, and unexpected Bun executables created at install time.
Recovery
- Stop builds and isolate systems that installed an affected version with lifecycle scripts enabled.
- From a separate clean machine, rotate npm, GitHub, cloud, CI/CD, SSH, signing, and deployment credentials the affected process could reach.
- Invalidate active sessions; review audit logs for unusual repository access, package publication, cloud API calls, or newly created credentials.
- Remove affected dependency caches and discard artifacts built on exposed runners.
- Pin to known-clean
0.5.3,1.6.2,2.2.0, or3.0.2and rebuild from a clean environment with a reviewed lockfile.
Why this matters
- Root cause is workflow authorization, not a stolen token. Unlike the Hades / Mini Shai-Hulud PyPI branch (stolen long-lived publisher token, see pantheon-agents), this compromise needed no credential: the workflow itself granted unauthenticated actors publish rights through
issue_comment+id-token: write. Trusted Publishing (OIDC) is the mitigation only if the workflow authorizes the actor, not just the event. - Durable pattern: release workflows that trigger on
issue_comment(or PR events) and holdid-token: writefor npm Trusted Publishing must verify the trigger author's repository role/membership before publishing. An exact-match comment body (npm publish) is not authentication. - Detection shape: unexpected Bun downloads from GitHub release infrastructure during
npm install,gh auth token/git credential-managerexecution from install hooks, andbinding.gyp→os.system()decode chains are the high-value hunt pivots.
Related pages
- binding.gyp npm CI/CD worm (Miasma / Mini Shai-Hulud / Hades)
- Mini Shai-Hulud npm/PyPI worm campaign
- Trojanized pantheon-agents (Hades / Mini Shai-Hulud, GHSA-93qj-5q5v-3c2h)
- npm install explicit trust controls (pattern)
Sources
- StepSecurity — "@7nohe/openapi-react-query-codegen Compromised Through an Exposed npm Publishing Workflow" (August 28, 2026): https://www.stepsecurity.io/blog/7nohe-openapi-react-query-codegen-compromised-npm-publishing-workflow
- GitHub issue #217 (first report by Charlie Eriksen), PRs #215 / #216 (attacker-triggered workflow runs) — referenced by the StepSecurity post.
- npm package page for version 3.0.4 (registry tarball hashes and
latest-tag state at capture).