TanStack Query npm package targeted through compromised OpenAPI codegen tool

Última actualización: 08/29/2026
  • Multiple compromised releases of @7nohe/openapi-react-query-codegen injected malware into projects using TanStack Query-based hooks.
  • The attacker abused a binding.gyp sandbox escape and preinstall scripts to execute an obfuscated JavaScript payload during npm install.
  • The payload harvests extensive cloud and developer credentials, exfiltrating them to attacker-controlled GitHub repositories.
  • Stolen tokens are then used to spread the malware to more npm packages and GitHub repos, poisoning the wider JavaScript supply chain.

TanStack Query npm package illustration

For teams that lean on TanStack Query as their go-to npm package for managing server state in React and other front-end frameworks, a recent supply-chain incident shows how quickly that trust can be undermined. A popular companion tool, built specifically to generate type-safe TanStack Query hooks from OpenAPI schemas, was quietly weaponised and pushed to the npm registry with a sophisticated malware payload baked in.

Security researchers describe the attack as a TeamPCP-style campaign that reuses many of the theatrics and technical patterns seen in earlier incidents. This time, however, the focus is squarely on developers who scaffold their data layer using the @7nohe/openapi-react-query-codegen package, a utility that promises to save time by auto-generating TanStack Query hooks instead of writing boilerplate by hand.

estado estable de javascript 2025
Related article:
The Stable State of Modern JavaScript

How a TanStack Query code generator became a malware delivery vehicle

The package at the centre of the incident, @7nohe/openapi-react-query-codegen, is designed to take an OpenAPI specification and emit fully typed hooks compatible with TanStack Query. With over 150,000 weekly downloads, it sits in the critical path for many teams who standardise on TanStack Query to orchestrate fetching, caching and synchronising backend data in their React-based frontends.

According to the post-mortem, the attacker managed to compromise both the npm package and its GitHub repository. The breach was achieved by exploiting a vulnerability in one of the project’s GitHub Actions workflows, meaning the malicious builds still appeared to come from the legitimate CI pipeline and shipped with valid provenance attestations. Those cryptographic attestations, normally a strong trust signal for npm users, became misleading once the workflow itself was under hostile control.

Within a narrow window of around twenty minutes, the attacker published ten poisoned versions of the package to npm, each containing code designed to execute during installation and bootstrap a multi-stage malware payload. The affected releases span several minor and patch versions, all of which should now be treated as untrusted.

  • @7nohe/openapi-react-query-codegen@0.5.4
  • @7nohe/openapi-react-query-codegen@0.5.5
  • @7nohe/openapi-react-query-codegen@1.6.3
  • @7nohe/openapi-react-query-codegen@1.6.4
  • @7nohe/openapi-react-query-codegen@2.2.1
  • @7nohe/openapi-react-query-codegen@2.2.2
  • @7nohe/openapi-react-query-codegen@3.0.3
  • @7nohe/openapi-react-query-codegen@3.0.4
  • @7nohe/openapi-react-query-codegen@0.0.0-365d4eb738d3146583431948d3ba6e27a32556be
  • @7nohe/openapi-react-query-codegen@0.0.0-ec7876d6c917dad516ba69bbfafc948b834bf0ab

All of these variants share the same basic intent: take advantage of the package’s popularity among TanStack Query users to gain code execution during npm install and then fan out through the wider JavaScript supply chain.

The “Trinitite” payload and its installation-time execution tricks

The malware identifies itself with the tongue-in-cheek label “Trinitite: Sponsored by Preview 2 Effects”, a reference to the glassy material formed by nuclear tests. Earlier internal script names like is_it_this_simple.js hint at the attacker’s casual attitude, but the underlying technique is anything but simplistic.

The primary execution hook hinges on a binding.gyp sandbox escape. In normal Node.js projects, binding.gyp is used to configure native addons compiled by node-gyp, but here it is weaponised. When npm encounters a binding.gyp file, node-gyp evaluates its conditions field using Python. The attacker inserts a carefully constructed Python expression in that field, exploiting Python’s class hierarchy to break out of the expected sandbox and run arbitrary shell commands.

In the compromised package, the conditions expression is heavily obfuscated with Unicode and hex escapes. Once decoded, it walks Python’s internal __subclasses__ tree to locate the catch_warnings class, uses that as a pivot to reach __builtins__, imports the os module and finally calls os.system() to launch the main payload with Node. The type field in binding.gyp is hex-encoded as none, so no genuine native build is attempted; the configuration exists purely to smuggle in a single shell command.

This approach means the package can execute malicious code during installation even if there is no explicit preinstall script defined in package.json. Not all of the poisoned versions rely on this exact trick, though. Two prerelease builds use more straightforward preinstall hooks: one executes a Node script named nu.js directly, while another fetches the Bun runtime via wget piped to bash and then runs the aptly named is_it_this_simple.js. Later versions combine both methods, shipping a malicious binding.gyp and a preinstall entry pointing to the same main JavaScript payload for redundancy.

Obfuscation layers and sandbox evasion in the main JavaScript payload

Once the initial hook fires, the attack pivots into a large JavaScript file named 3FWCvzduYZg.js, dropped in the root of the npm package. At roughly 5.4 MB and minified down to a single line, the script is designed to be as opaque as possible at a glance, and further analysis shows it is protected by several distinct encryption and obfuscation layers.

The outermost layer consists of a 1.6‑million‑element integer array, decrypted with a simple XOR against the constant 29. That decryption yields a secondary script whose job is to retrieve the Bun runtime quietly, saving it under a temporary filename with a trinnyyyy-XXXXX pattern in the system’s temp directory, and then use Bun to execute the real core payload. The core itself is wrapped with AES‑128‑GCM encryption and then passed through custom ciphers on top of standard JavaScript obfuscation techniques, including those seen in popular tools like obfuscator.io.

Fully unpacked, this deeply layered payload reveals a blend of capabilities: a credential harvester targeting a wide swath of cloud and developer platforms, a worm component able to spread the infection to new targets, and a destructive command channel. Before any of that activates, the script performs a series of environmental checks to avoid sandboxes and security tools, exiting quietly if certain telltale conditions are met.

Among the checks are locale, environment and filesystem indicators commonly associated with research environments. The malware bails out if the system locale is Russian, if directories belonging to endpoint security products like CrowdStrike, SentinelOne or Carbon Black are present, if the current working directory matches paths used by known supply-chain scanning tools, or if environment variables contain fake credentials used by security scanners. It also looks at the GITHUB_REPOSITORY_OWNER and the presence of hardening tools such as StepSecurity’s harden-runner, steering clear of GitHub accounts known to belong to security researchers.

What the malware steals from TanStack Query-based development environments

Once the environment checks are passed, the malware begins harvesting credentials with a wide net. While the original package is framed as a helper for TanStack Query users, the actual target surface reaches far beyond a single library, touching almost every layer of a modern JavaScript developer’s toolchain and cloud stack.

On the developer side, the payload scans for GitHub personal access tokens in multiple formats, including classic tokens starting with gho or ghp and newer fine-grained tokens beginning with github_pat_. On GitHub Actions runners, it also requests OpenID Connect tokens via the documented ACTIONS_ID_TOKEN_REQUEST_TOKEN and ACTIONS_ID_TOKEN_REQUEST_URL environment variables, giving the attacker a path to assume workload identities rather than just static secrets.

The script then turns to common language registries. It reads npm, PyPI and RubyGems tokens from configuration files such as ~/.npmrc, scans for additional project-local .npmrc files and attempts to validate discovered npm bearer tokens by calling the /-/whoami endpoint on the public registry. Similar patterns are used for PyPI and RubyGems, mining authentication data and confirming that each token can successfully talk to the respective registry before exfiltrating it.

The cloud footprint is even larger. On Amazon Web Services, the malware enumerates environment variables like AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, parses ~/.aws/credentials and ~/.aws/config, and queries the EC2 Instance Metadata Service at 169.254.169.254, preferring IMDSv2 but falling back as needed. For container workloads, it talks to the ECS metadata endpoint at 169.254.170.2. Any credentials or temporary tokens it recovers are tested against sts:GetCallerIdentity to confirm validity before being queued for exfiltration.

Comparable logic is present for other cloud providers. The script can gather Azure client credentials and managed identities, including AZURE_CLIENT_ID, AZURE_CLIENT_SECRET and AZURE_TENANT_ID, and then access Azure Key Vault, Azure Resource Manager and Microsoft Graph once authenticated. For Google Cloud, it searches for GOOGLE_APPLICATION_CREDENTIALS files, queries the GCP metadata server at metadata.google.internal and, when possible, uses the resulting tokens to call Google Secret Manager. HashiCorp Vault deployments are also on the list: the payload looks for Vault addresses and tokens via environment variables and token files, then probes Vault’s HTTP API to enumerate accessible secrets.

Beyond cloud IAM, the malware performs an extensive filesystem sweep using over 150 glob patterns. It hunts for SSH private keys, Docker configs, .env files, Git credential stores, .npmrc and .pypirc files, Terraform and cargo credentials, shell history files, cryptocurrency wallet data, and configuration directories for widely used messaging apps like Signal, Telegram Desktop, Discord, Slack and Element. It also checks for VPN configuration files from services including NordVPN, ProtonVPN, CyberGhost, PIA and others, along with tokens and domains for binary repository managers such as JFrog Artifactory.

Exfiltration through GitHub repos and the “meow meow meow” commits

After collecting all of these secrets, the malware needs a place to send them. Rather than using a fixed command-and-control domain that could be easily blocked, the attackers rely on public GitHub repositories as an exfiltration channel, blending stolen data into the normal flow of developer traffic.

The payload first searches GitHub’s public commit history for messages matching a specific pattern that embeds an encrypted token in plain sight, using a phrase that scolds users for revoking it. Once such a commit is found, the malware decrypts the embedded token and uses the associated account to create a new repository where stolen data will be stored. This indirection means that credentials taken from one victim are saved in repositories technically owned by another GitHub user, making the network of compromised repos harder to trace back to a single attacker identity.

The newly created repositories have whimsical, auto-generated names composed of two characters from the Touhou Project series and a random number, resulting in combinations like cirno-marisa-74291 or reimu-kogasa-12048. Their descriptions are set to the same “Trinitite: Sponsored by Preview 2 Effects” phrase seen in the payload, providing one of the only clear markers for defenders trying to identify these repos at scale.

Stolen credentials are committed as encrypted JSON files inside a results/ directory, using filenames that start with doubletrinnys- followed by a counter and timestamp. Each file contains an envelope field holding the encrypted data bundle and a key field containing the encryption key itself, further wrapped using the attacker’s RSA public key. From a casual glance, these look like arbitrary encrypted blobs. Only someone with the matching private key can reverse the process, so even if the repositories are found, the actual secrets remain opaque to third parties. Commit messages are typically short and flippant, with strings like “meow meow meow” used repeatedly.

Worm-like propagation into other npm packages and GitHub projects

One of the most concerning aspects of the Trinitite campaign is its ability to reuse stolen tokens to compromise additional packages and repositories. Rather than limiting itself to the original TanStack Query code generator package, the malware behaves like a worm, systematically searching for ways to spread.

With a valid npm token in hand, the payload queries the npm API to enumerate all tokens associated with the account, selecting the most privileged publish token. It then performs a search for every package owned by that maintainer, downloads the existing tarball for each, and injects the same binding.gyp sandbox escape and main payload file into the package contents. After the modifications, it publishes a new version to npm under the legitimate maintainer’s name, effectively turning trusted packages into new infection vectors. Similar behaviour is implemented for PyPI and RubyGems via their respective upload endpoints, with TLS certificate checks disabled in outbound requests to avoid client-side pinning issues.

GitHub repositories are treated in a comparable way. When the malware obtains a GitHub token with sufficient scope, it lists every accessible repository and branch, skipping a few known automation branches such as those prefixed with release/, dependabot/, renovate/ or copilot/. For the remaining branches, it commits a set of carefully chosen files that serve both as backdoors and as hooks into modern development tooling.

Among these are backdoored VS Code and Claude configuration files, including .vscode/tasks.json, .claude/settings.json and scripts like setup.mjs that are executed when a developer opens a project in certain AI-assisted editors. The malware also drops a fake .github/workflows/codeql.yml workflow that masquerades as a security scan while providing another channel for code execution inside CI. Additional files are targeted at other editors and tools, such as Gemini, Cursor and assorted code assistants, helping the attacker maintain persistence wherever developers actually work.

Each of these commits is authored under the github-actions identity, with messages that look like routine maintenance such as “chore: update dependencies” and a trailer flagging skip-checks:true to slip past required checks. To a busy team scanning commit logs at a glance, these changes can easily be mistaken for innocuous automation updates.

Indicators of compromise for teams using TanStack Query tooling

For organisations that rely on TanStack Query and have integrated @7nohe/openapi-react-query-codegen into their workflow, identifying potential exposure starts with version and file-level indicators that multiple sources have now highlighted. These clues can help pinpoint which projects may have run the compromised code during their install steps.

Any usage of the affected versions of @7nohe/openapi-react-query-codegen listed earlier should be treated as suspect, especially if builds were performed on shared CI infrastructure or developer laptops with access to sensitive credentials. Security teams are advised to audit lockfiles, package.json histories and build logs to confirm whether those releases were ever pulled into production or staging environments.

On disk, a key sign of compromise is the presence of the 3FWCvzduYZg.js file at the package root. Investigators have catalogued several SHA‑256 hashes associated with different obfuscated builds of this script, all linked to the same underlying functionality:

  • 8e5d1af68ca340ae0c6e8132cb00c686ec2d60502c1994d94ce353d1472ad5a3
  • b49afb7dba04cd99b357ce7c652c823a3707f28e130bd5c6645851a7adc030d6
  • 59370c67b54a0ccaedd265e2356f04540b2fba1e1845300ef6de4d5437d99380
  • 778d6f0058045d6a2ab9a7e1d3e3be8e7e6b4d9cc217d13949bf1dfbab759a7c
  • b24d121667f21f492cb9db34fbfd515d5922a8dd30b9c45215c7220abbb10ca8
  • e1f1162ece9a6e6ea21a20399cbf31c563a8149d433a68711f4223870c203d5a
  • b6012b2ff87f08f93ee53921c48db907ddbcf5461b03bb988083b01a36886237
  • 709af2fdeb50324229e94c44c679a0fab18bd8e17d3864405989c526cbb63ad8

In addition, many infected hosts create temporary directories with names beginning with trinnyyyy- followed by a short string of random alphanumeric characters in the system temp folder. While this pattern alone does not prove a compromise, in combination with a suspicious binding.gyp file and one of the known payload hashes, it significantly raises the likelihood that the Trinitite malware has executed on that system.

Some security vendors have already folded these indicators into their detection feeds. For example, platforms focused on developer tooling and supply-chain security now flag any repository or project that imports the affected versions as a critical malware issue, often scoring it at the maximum severity level to prompt immediate investigation. Teams are encouraged to run rescans across their organisations, especially in environments where TanStack Query is central to application architecture and code generation tools are widely used.

Specialised tools and open-source utilities are emerging that sit in front of npm, yarn, pnpm and related package managers, checking package versions against curated intelligence feeds before installation. The idea is to intercept malicious or suspicious packages, particularly those tied to incidents like this one, before they ever land in node_modules, reducing the risk that a compromised helper library can indirectly expose TanStack Query-powered applications and their associated infrastructure.

For development teams, this incident underlines how a seemingly innocuous convenience package—one that simply generates typed hooks for TanStack Query from an OpenAPI spec—can become a powerful attack vector when its CI pipeline and distribution channel are subverted. Defensive measures now need to extend beyond checking the main application dependencies to include the generators, CLIs and automation scripts that quietly shape how those dependencies are used every day.

Related posts: