Build1 distinct publisher3 min readPublished
Socket says all ten bad tarballs of @7nohe/openapi-react-query-codegen carry genuine GitHub Actions attestations, which is what you get when the attestation covers where a build ran rather than who wrote the commit it built.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Trusted publishing swaps a long-lived npm token for a short-lived OIDC credential the workflow receives at run time. The attestation that comes out the far end can say that a run in that repository produced this tarball. It cannot say anything about the diff that run compiled. Socket reports the publish job could be invoked by a comment from an untrusted GitHub account, checking out code from a fork pull request and publishing it under the repository's trusted publishing identity [5]. Point that job at attacker code and the signature it produces is perfectly truthful about a build nobody asked for.
The execution paths are worth reading in order. Wave-1 versions run only through `binding.gyp`, which is identical across them and uses an obfuscated Python expression to reach `os.system` and launch `node 3FWCvzduYZg.js`; because node-gyp processes that file during installation, it fires on `npm install` in developer environments and CI runners [8]. So a reviewer diffing `package.json` between 0.5.3 and 0.5.4 sees no lifecycle script and nothing to object to. Wave-2 manifests keep the same `binding.gyp` and add `"preinstall": "node 3FWCvzduYZg.js"` on top, a second way in [9].
The loader itself is about 5.7 MB, obfuscated with a single-byte XOR, and decrypts an embedded AES-128-GCM payload that it writes to a random filename in the temp directory, executes via `child_process.execSync`, and unlinks in a `finally` block [6]. A 5.7 MB blob in a code generator's tarball is not subtle, but nobody reads tarballs. The `finally` matters more than the encryption: the artifact is removed whether or not the child process succeeded, so a responder who arrives late finds an empty temp directory. Socket decrypted the second stage and reports it targets cloud credentials, package registry credentials, GitHub Actions secrets and AI agent configuration, and carries self-propagation [7].
Cleanup is not one pin. Of the ten published versions, eight are two consecutive releases on each of four maintained lines, plus two `0.0.0-<commit hash>` prereleases [13]. The last known-good release on each line is exactly one patch below the first malicious one there, so 0.5.3, 1.6.2, 2.2.0 and 3.0.2 are four separate downgrades depending on which line a given service is on [14]. Meanwhile the `latest` tag still resolved to 3.0.4 at the time of writing [3].
Provenance is good engineering for the thing it was built for, which is binding an artifact to a build log you can go read. What would have to be true for a valid attestation to also function as a trust gate is that the set of people who can trigger the attested build equals the set of people you trust to write the code. Socket says that is not the case here [5]. The number I cannot get from this report is exposure: 150,000 weekly downloads across all versions [2] tells you nothing about how many resolved to the ten bad ones in the window before detection.
Ranked by verification strength, evidence, and original report placement.
Socket's Threat Research Team reports that ten malicious versions of the npm package @7nohe/openapi-react-query-codegen were published on August 28, 2026, in two waves roughly twenty minutes apart, spanning every maintained release line.
The package @7nohe/openapi-react-query-codegen receives roughly 150,000 weekly downloads across all versions.
At the time of writing all ten malicious versions remained installable and the latest tag still resolved to the malicious version 3.0.4, so a plain npm install of the package retrieved a compromised build.
All ten malicious versions carry valid npm provenance attestations issued through GitHub Actions trusted publishing.
Socket's investigation found that a comment-triggered publishing workflow can be invoked by an untrusted GitHub account to publish code from a pull request fork under the repository's trusted publishing identity.
The malicious releases execute a bundled loader, 3FWCvzduYZg.js, at install time; it is a roughly 5.7 MB single-byte-XOR-obfuscated file that decrypts an embedded AES-128-GCM payload, writes it to a random filename in the temp directory, executes it via child_process.execSync, and unlinks it in a finally block.
Distinct publishers with included, body-backed reporting in this cluster.
1 article · August 28, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
The npm audit that works because it never installs the package1 distinct publisher
security
Shai-Hulud's fourth wave shipped with valid provenance, and that is the finding1 distinct publisher
build
A file-copy Allure adapter for Katalon, and the history IDs that make retries useful1 distinct publisher
security
A misconfigured GitHub Actions workflow handed TeamPCP the token that poisoned five ecosystems1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Falsifiable in detail, unverified by anyone else
Two different grades of evidence sit in the same post. The version strings, the still-live latest tag and the attestations pointing at commit d42d1733 are checkable by any reader with npm and five minutes — Socket has put itself somewhere it can be caught. The second stage is different: the credential targets and the Mini Shai-Hulud family match rest entirely on decryption Socket did privately and describes as unfinished. No independent researcher, registry or maintainer has confirmed any part of it.
Wide install surface, uncounted hits
Exposure is real but measured in the wrong unit. The 150,000 weekly downloads describe the package, not the ten bad tarballs, and nobody has published how many of those installs landed on a compromised version during the window. What raises the floor is that the malicious build was the default: the latest tag still resolved to 3.0.4, and binding.gyp fires under node-gyp during ordinary installation, so CI runners and laptops needed no unusual configuration to execute the loader.
Slightly ahead of the counting
The prose is restrained — Socket's sharpest sentence is a definitional one about what provenance actually attests — and the forensic detail is delivered without adjectives. The stretch is arithmetic and naming: an all-versions download total does duty as the blast radius, and a campaign label is attached to behaviour Socket is still decrypting. Small overreach, and the parts most likely to be quoted are the parts most easily checked.
The finder sells the fix
Socket's threat research arm found this, and Socket's product is npm dependency scanning. The conclusion the post drives toward — that attestation tells you where a build ran and nothing about what went into it — is also the argument for buying a scanning layer, and the package references in the write-up point back at Socket's own pages. That alignment does not touch the version list, which is trivially checkable, but it does shape what gets a subhead: the failure of the free control, rather than npm's response or the maintainer's.
One house, mid-investigation
Confidence is held down by structure rather than by anything suspect in the reporting: a single publisher, self-declared as still analysing, describing an incident whose remediation status was unresolved at press time. It is held up by the reporting's testability — the pins, the tag and the attestation chain will be confirmed or contradicted within days by anyone who looks.