---
title: "The Nx npm compromise: what it did on a developer’s Mac, and what Kilobat would have shown"
url: "https://kilobat.app/incidents/nx-s1ngularity"
description: "How the malicious Nx versions of August 2025 used installed AI CLIs to find secrets and pushed them to GitHub, and what Kilobat would and wouldn’t have caught."
---

Incident · npm package compromise

# The Nx npm compromise: what it did on a developer’s Mac, and what Kilobat would have shown

For about five hours on 26 and 27 August 2025, eight versions of the Nx build system on npm carried a postinstall script that searched developers’ machines for secrets, enlisted the AI coding CLIs it found to help, and published what it collected to a new public repository in the victim’s own GitHub account.

When: 26–27 August 2025 · Sources checked: 1 November 2026

## In short

- [Protection](https://kilobat.app/mac-malware-blocker): Wouldn’t help

  Nothing to block. The code came from registry.npmjs.org and the data went to api.github.com, which no threat list names.
- [Agent guard](https://kilobat.app/mac-malware-blocker): Wouldn’t help

  No paste site, tunnel or raw IP address was involved, and the upload cut leaves well-known hosts such as api.github.com alone, by design.
- [Agent network log](https://kilobat.app/ai-coding-agents): Limited

  It would have recorded an install script talking to the GitHub API, and the AI CLI runs the script started, but only for installs on this Mac and not as a block.

## What happened

The attacker got Nx’s npm publishing token through the project’s own CI. According to the [maintainers’ advisory](https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c), a GitHub Actions workflow merged on 21 August used the `pull_request_target` trigger and echoed pull request titles into a shell, so a crafted title could run commands with the repository’s permissions. On 24 August that path was used to start the publishing workflow from a malicious commit and send the npm token to a webhook.

With the token, the attacker published `nx` 20.9.0–20.12.0 and 21.5.0–21.8.0, plus versions of `@nx/devkit`, `@nx/js`, `@nx/workspace`, `@nx/node`, `@nx/eslint`, `@nx/key` and `@nx/enterprise-cloud`. Each carried a `postinstall` hook that ran `telemetry.js`, which only acted on macOS and Linux.

The script looked for the `claude`, `gemini` and `q` command-line tools and, where they were installed, ran them with the flags that skip their permission prompts (`--dangerously-skip-permissions`, `--yolo`, `--trust-all-tools`) and a prompt asking them to list files that looked like wallets, keys and `.env` files into `/tmp/inventory.txt`. [Snyk](https://snyk.io/blog/weaponizing-ai-coding-agents-for-malware-in-the-nx-malicious-package/) and [StepSecurity](https://www.stepsecurity.io/blog/supply-chain-security-alert-popular-nx-build-system-package-compromised-with-data-stealing-malware) published the code. [Wiz](https://www.wiz.io/blog/s1ngularity-supply-chain-attack) saw this succeed in hundreds of cases, with the AI providers’ guardrails refusing some of the time.

It then read those files, collected the GitHub CLI’s token with `gh auth token` and the npm token from `~/.npmrc`, created a public repository named `s1ngularity-repository` in the victim’s account and uploaded everything as triple-base64 `results.b64`. It also appended `sudo shutdown -h 0` to `~/.zshrc` and `~/.bashrc`, so every new terminal asked for a password and shut the machine down.

Many installs weren’t typed by anyone. The maintainers found that the Nx Console editor extension, versions 18.6.30 to 18.65.1, checked for the latest `nx` on startup, so opening an editor during the window was enough. Wiz also found the malware running in CI pipelines. Wiz counted over a thousand valid GitHub tokens among the leaked data; in a second phase, stolen tokens were used to make more than 5,500 private repositories public.

## Timeline

1. 21 Aug 2025

   A workflow with a shell-injection flaw is merged into the Nx repository (maintainers’ advisory).
2. 24 Aug 2025

   A crafted pull request starts the publish workflow from a malicious commit, which sends the npm token to a webhook.
3. 26 Aug, 22:32 UTC

   `nx` 21.5.0 and several `@nx/*` packages are published with `telemetry.js`; 20.9.0 follows seven minutes later.
4. 27 Aug, 00:30 UTC

   A user opens a GitHub issue about the malicious versions. The last two, 21.8.0 and 20.12.0, appear at 00:37.
5. 27 Aug, 02:44 UTC

   npm removes the affected versions and the publish tokens.
6. 27 Aug, about 09:00 UTC

   GitHub disables the attacker-created repositories, roughly eight hours after the first ones appeared (Wiz).
7. 27 Aug 2025

   Nx Console 18.66.0 stops installing the latest `nx`; Nx moves publishing to npm’s Trusted Publishers with 2FA.
8. 28–29 Aug 2025

   Stolen tokens are used to rename private repositories and make them public; Wiz counts over 400 accounts and 5,500 repositories.

## What it looked like on the network

On a Mac, the attack used two hosts, both ones a developer’s machine talks to every day. The package came from `registry.npmjs.org` like any other. The theft went to `api.github.com`, as the victim, using the victim’s own GitHub CLI token: a `POST /user/repos` to create the repository, then a `PUT /repos/<owner>/s1ngularity-repository/contents/results.b64` with the encoded data. There was no command-and-control server and no other upload destination.

The process tree was short. `npm` (or the editor extension) ran a shell, the shell ran `node telemetry.js`, and node started `claude`, `gemini` or `q` as child processes. Each AI CLI talked to its own provider, as it does in normal use; the reading of files happened locally. The upload itself came from node. Its size depended entirely on what the files held.

## Indicators

As the sources document them. Domains are text, never links, and are defanged (`[.]`) where the source defangs them. Most of this infrastructure is long gone: indicators help you check old logs, files and accounts, not block today’s campaigns.

| Indicator | Type | Where it shows | Note |
|---|---|---|---|
| `api.github.com` | Host (legitimate) | Your Mac | `POST /user/repos`, then `PUT …/contents/results.b64`, made by node during an npm install. |
| `s1ngularity-repository, s1ngularity-repository-0, s1ngularity-repository-1` | GitHub repository name | GitHub account | Public when created; GitHub disabled them on 27 August. Renamed repositories in the second phase used `s1ngularity-repository-` plus five letters. |
| `results.b64` | File | GitHub repository | The collected data, base64-encoded two or three times. |
| `/tmp/inventory.txt, /tmp/inventory.txt.bak` | File | Your Mac | The AI CLIs’ list of file paths. |
| `sudo shutdown -h 0` | Line in ~/.zshrc, ~/.bashrc | Your Mac | Shuts the Mac down when a terminal opens and the password is given. |
| `nx 20.9.0–20.12.0, 21.5.0–21.8.0` | npm versions | Lockfiles, npm cache | Also `@nx/devkit`, `@nx/js`, `@nx/workspace`, `@nx/node` 20.9.0 and 21.5.0, `@nx/eslint` 21.5.0, `@nx/key` and `@nx/enterprise-cloud` 3.2.0. |

## What Kilobat would have shown or blocked

- **[Agent network log](https://kilobat.app/ai-coding-agents).** When an agent ran the install, say Claude Code running `npm install`, its session would list `node` under `npm` sending to `api.github.com` in the middle of an install: an install script talking to the GitHub API is worth a look. It would not be flagged *first time*, because api.github.com is a well-known host, and it would be flagged *large upload* only above 10 MB.
- **[Agent network log](https://kilobat.app/ai-coding-agents).** Kilobat recognises Claude Code and Gemini CLI by their executables, not by who started them, so the runs the script started would appear as their own sessions, in the project folder, at a moment you hadn’t started an agent. Amazon’s `q` isn’t one of the agents Kilobat recognises.
- **[Protection](https://kilobat.app/mac-malware-blocker).** Nothing. registry.npmjs.org and api.github.com are not threats, and Kilobat should never block them.
- **[Agent guard](https://kilobat.app/mac-malware-blocker).** Nothing either. The guard’s rules cover paste sites, file shares, tunnels, request catchers and raw IP addresses, none of which appeared, and its upload cut skips well-known hosts so that `git push` keeps working.

## Where Kilobat wouldn’t have helped

- Exfiltration to github.com with the victim’s own token looks the same as the GitHub CLI at work. Kilobat sees hosts and byte counts, never what was sent.
- The npm token was stolen in Nx’s GitHub Actions, on GitHub’s runners, and the second phase ran from the attacker’s side with stolen tokens. Neither touched your Mac.
- Installs started by an editor extension in VS Code aren’t attributed to an agent, so they wouldn’t get an agent session; in Cursor and Windsurf they count as the app’s.
- Reading files, running the AI CLIs’ file search and editing `~/.zshrc` aren’t network events.
- The agent network log keeps 30 days, so it can’t tell you now whether this ran on your Mac in August 2025.

## Check your Mac now

1. In each project, see whether an affected version of Nx is installed. Update if it is.

   ```
   npm ls nx
   ```
2. Look for the files the script left. macOS clears `/tmp` on restart, so finding nothing proves little.

   ```
   ls -la /tmp/inventory.txt /tmp/inventory.txt.bak
   ```
3. Check your shell startup files for the shutdown line, and delete it if it’s there.

   ```
   grep -n "shutdown" ~/.zshrc ~/.bashrc
   ```
4. In GitHub’s security log, look for a `repo.create` event with `s1ngularity-repository` in the name: [github.com/settings/security-log](https://github.com/settings/security-log?q=action%3Arepo.create). The repository itself may already be disabled.
5. If you were affected, revoke the GitHub CLI’s access under **Settings › Applications › Authorized OAuth Apps**, sign in again with `gh auth login`, and rotate npm tokens and every secret in your `.env` files and environment.
6. Clear the npm cache so an old copy can’t be installed again.

   ```
   npm cache clean --force
   ```
7. For the next one: with Kilobat’s agent network log recording, look at what each install an agent ran contacted. Watch for npm or node talking to api.github.com, and for agent sessions you didn’t start.

   ```
   kilobat agent-log --all
   ```

## Questions

**Did the AI tools turn on their users?**

No. The malicious script started them itself, with the flags that switch off their permission prompts, and gave them its own instructions. The same flags are a reason to be careful about running agents unattended with full permissions.

**Why doesn’t Kilobat block large uploads to GitHub?**

Because pushing code, releases and packages to GitHub is ordinary work, and cutting it off would break `git push`. The log shows the upload with its bytes and process instead, so you can see it.

## Sources

- [nrwl/nx security advisory GHSA-cxm3-wv7p-598c: Malicious versions of Nx and some supporting plugins were published](https://github.com/nrwl/nx/security/advisories/GHSA-cxm3-wv7p-598c)
- [Nx blog: S1ngularity, what happened, how we responded, what we learned](https://nx.dev/blog/s1ngularity-postmortem)
- [StepSecurity: Popular Nx build system package compromised with data-stealing malware](https://www.stepsecurity.io/blog/supply-chain-security-alert-popular-nx-build-system-package-compromised-with-data-stealing-malware)
- [Wiz: s1ngularity, supply chain attack leaks secrets on GitHub](https://www.wiz.io/blog/s1ngularity-supply-chain-attack)
- [Snyk: Weaponizing AI coding agents for malware in the Nx malicious package](https://snyk.io/blog/weaponizing-ai-coding-agents-for-malware-in-the-nx-malicious-package/)
- [Socket: Nx npm packages compromised in supply chain attack weaponizing AI CLI tools](https://socket.dev/blog/nx-packages-compromised)

Checked on 1 November 2026.

## See what your agents and apps connect to

Kilobat is a Mac menu-bar app that names the app, process and agent behind every connection, keeps them away from known-bad hosts, and logs what each agent session reached.

[Download for Mac ↓](https://kilobat.app/downloads/Kilobat-1.31.1.dmg?via=site)

```
brew install seifip/kilobat/kilobat
```

## More incidents

- [The “Shai-Hulud” npm worm](https://kilobat.app/incidents/shai-hulud-npm-worm): A worm that stole developers’ tokens, republished itself with them, and leaked secrets through GitHub and webhook.site.
- [The postmark-mcp backdoor](https://kilobat.app/incidents/postmark-mcp): An MCP server for sending email through Postmark quietly added a blind copy to every message.
- [All incidents](https://kilobat.app/incidents): Every case study, with what Kilobat would and wouldn’t have done.
