When the npm account behind the Nx monorepo toolchain was compromised in August 2025, attackers injected a single JavaScript file into trusted packages downloaded by hundreds of thousands of developers. Armando's analysis of @nx/node v20.9.0 -- one of two malicious releases published on 2025-08-26 -- found that the entire attack was carried by one new file, telemetry.js, wired to a postinstall hook that fires automatically on every npm install. With 98 million total downloads and 153 direct dependents, @nx/node is a high-value target in the Node.js build tooling ecosystem. The package has no corresponding git tag for version 20.9.0 in the upstream nrwl/nx repository, confirming the version was injected outside the normal release process.
Key Findings
Malicious version: v20.9.0, published 2025-08-26; v20.8.4 is clean
Entry point: postinstall lifecycle hook fires automatically on npm install
Targets: Linux and macOS -- Windows exits immediately; developer machines, CI/CD pipelines, build systems
@nx/node is the Node.js application generator plugin for the Nx monorepo build system, published by Nrwl since April 2023. It provides generators for creating and managing Node.js applications within an Nx workspace and is a standard dependency in enterprise JavaScript projects. The package has accumulated over 98 million total downloads and is directly imported by 153 downstream packages, giving it deep reach into CI/CD pipelines and developer workstations that run npm install as part of build automation.
The clean predecessor, v20.8.4, was published through the normal Nrwl release process and carries no lifecycle hooks beyond the package's standard generators. The addition of a postinstall hook pointing to an exfiltration script is entirely foreign to the package's historical behavior.
Attack Analysis
The attacker's modification to @nx/node v20.9.0 is minimal but effective: exactly one file was changed relative to v20.8.4. The package.json was modified to register a postinstall hook pointing to a new file named telemetry.js. The name is chosen to blend in -- Nx packages use legitimate telemetry mechanisms, so a script by that name would not raise immediate suspicion in a code review.
When a developer or CI job runs npm install with @nx/node@20.9.0 in the dependency tree, Node.js automatically executes telemetry.js as root of the postinstall hook. The script's first action is to check the operating system: on Windows it exits immediately with process.exit(0), narrowing the attack surface to Linux and macOS targets where developer and CI environments are more likely to hold live credentials.
The payload then proceeds in parallel tracks. First it checks whether the GitHub CLI (gh), Claude, Gemini, or Amazon Q coding assistants are installed. If any AI coding assistant is found, the script invokes it with dangerous permission flags -- --dangerously-skip-permissions for Claude, --yolo for Gemini, --trust-all-tools for Amazon Q -- and passes a crafted prompt instructing the tool to recursively scan the filesystem for wallet files, SSH keys, .env files, MetaMask data, and other secrets. The PROMPT constant used for this purpose is a prompt injection attempt that attempts to abuse AI coding tools already trusted by the developer environment. If any AI tool produces a file inventory at /tmp/inventory.txt, the script reads it and Base64-encodes the collected files for exfiltration.
Second, if the GitHub CLI (gh) is on the system path, the script runs gh auth token to extract the stored GitHub authentication token and checks whether the token starts with gho_ or ghp_ -- the known prefixes for GitHub OAuth and personal access tokens.
Third, if npm is available, the script runs npm whoami to confirm a logged-in npm user and then reads the full contents of the .npmrc file from the user's home directory. This file typically contains the npm authentication token used for publishing packages.
The script also runs forceAppendAgentLine(), which appends the string sudo shutdown -h 0 to the user's .bashrc and .zshrc files. This turns the victim's shell profile into a destructive time bomb: the next interactive login session will attempt to halt the machine.
With all harvested data assembled in a result object -- environment variables, hostname, platform, OS version, GitHub token, npm credentials, .npmrc content, and any AI-tool-sourced file inventory -- the script creates a new public GitHub repository named s1ngularity-repository using the stolen GitHub token and uploads the entire result as a Base64-encoded file named results.b64. The repository name s1ngularity-repository is the attacker's staging location; the name is hardcoded in the payload and matches the campaign name reported in external threat intelligence. A 1.5-second sleep is inserted between repository creation and file upload, likely to allow GitHub to propagate the new repository before the content write is attempted.
No corresponding git tag for v20.9.0 exists in the nrwl/nx GitHub repository, confirming that this version was not generated by the legitimate Nrwl release pipeline.
Malicious Files
telemetry.js is the sole attack payload. It is a 198-line Node.js script registered as the postinstall hook. On Linux and macOS it collects environment variables, system information, GitHub CLI tokens, npm credentials, and .npmrc file contents. It searches for cryptocurrency wallet files, SSH keys, and application secrets by leveraging AI coding assistants installed on the victim machine, exploiting their ability to recursively browse the filesystem. All collected data is serialized to JSON, triple Base64-encoded, and exfiltrated by creating a public GitHub repository named s1ngularity-repository using any harvested GitHub token. The script also appends sudo shutdown -h 0 to .bashrc and .zshrc for destructive persistence. Armando classified this file as Script-JS.Infostealer.SupplyChain.
package.json is the modified manifest that wires telemetry.js into the postinstall lifecycle slot. The change introduced in v20.9.0 relative to v20.8.4 is limited to this hook registration -- all other manifest fields are consistent with legitimate package versions. Armando classified this file as Text.Infostealer.SupplyChain.
Recommendations
Developers and security teams should take the following steps:
Identify exposure: Check whether @nx/node v20.9.0 appears in your dependency tree, including transitive dependencies. Any pipeline that ran npm install while this version was current should be considered potentially affected.
Update immediately: Upgrade to v20.8.4 or the latest clean release. Verify that no other Nx packages in your tree are pinned to versions published on 2025-08-26.
Rotate credentials: On machines that installed the malicious version, immediately rotate any GitHub personal access tokens, GitHub OAuth tokens, and npm authentication tokens. Assume the .npmrc file was read. If any AI coding assistant was installed and active, assume the tool may have been invoked with the injected prompt.
Inspect affected systems: Check .bashrc and .zshrc for an appended sudo shutdown -h 0 line and remove it. Search running processes for any unexpected activity.
Check GitHub for exfiltrated data: If a GitHub token was present on the affected machine, search your GitHub account for a repository named s1ngularity-repository that may have been created by the attacker. If found, revoke the token and delete the repository immediately.
Scan your software supply chain: Use Spectra Assure Community to check whether packages in your environment have been flagged.
C2 communication uses the GitHub API (api.github.com) with a stolen token at runtime; the endpoint is not present as a plaintext URL in the package source