The Nx compromise and the credential-stealing postinstall
A popular build tool's npm packages ship a payload that harvests tokens and pushes them to public repos.
Malicious versions of the nx build tool and several related packages were published to npm with a postinstall script that scanned the developer's machine for credentials and published them to a public GitHub repository created under the victim's own account.
the payload#
The script searched for:
- npm and GitHub tokens
- SSH private keys
- Cloud provider credentials in the usual locations
- Environment variables matching credential-shaped patterns
- Cryptocurrency wallet files
It then created a public repository in the victim's GitHub account named with a recognizable prefix and pushed the harvested data to it.
Some variants additionally invoked locally-installed AI coding CLI tools with a prompt asking them to enumerate sensitive files — using the developer's own agent as a discovery mechanism. That detail is new and it is going to be studied.
the entry point#
Compromised publishing credentials. The specifics of how they were obtained matter less than the pattern: a maintainer's token, or a CI workflow with publishing rights, was reachable by an attacker.
This is the dominant supply chain attack shape. Not typosquatting, not dependency confusion, not a malicious contribution. Take over the account of a legitimate maintainer of a package people already depend on.
the controls that would have stopped it#
In order of effectiveness:
1. --ignore-scripts. The payload was in postinstall. An install that does not run lifecycle scripts does not run the payload.
npm ci --ignore-scriptsSet it in .npmrc for your CI and see what breaks. For most projects the answer is: nothing, or one package that needs a native build, which you allowlist.
2. Trusted publishing. OIDC-based publishing from a verified CI workflow rather than a long-lived token. A token that does not exist cannot be stolen. npm supports this now and adoption is the bottleneck.
3. Install cooldown. The malicious versions were live for hours before removal. A policy of not adopting a version until it is 24 to 72 hours old would have avoided this entirely, and avoids most incidents of this shape, because the window between publish and detection is short.
{ "minimumReleaseAge": 4320 }4. Short-lived credentials. The payload harvested long-lived tokens. If your CI uses OIDC federation to get a fifteen-minute cloud credential, there is nothing durable to steal.
if you were affected#
The order matters:
- Rotate everything the machine had access to. npm tokens, GitHub PATs, SSH keys, cloud credentials. Assume everything readable was read.
- Check for the public repository in your GitHub account and delete it — but capture its contents first for your incident record.
- Audit for persistence. Check
~/.bashrc,~/.zshrc,~/.profile, shell history, cron, launch agents, and git hooks in your repositories. - Review recent activity on every account whose credentials were on that machine.
Rotation before cleanup. Cleanup on a compromised machine while live credentials are still valid is how you spend an afternoon on remediation and the attacker spends it on access.
the trend#
This is one of several npm incidents this year and they are getting more sophisticated: better targeting, cleaner payloads, and now the use of local AI tooling as an attack primitive.
The ecosystem's structural weakness has not changed. A single maintainer's credentials protect a package that millions of machines install and execute automatically. Every control above mitigates; none of them fix that.
The fix requires the registry to make unattended publishing hard by default, and that is a change to how a lot of people work. It is going to happen anyway, because the alternative is this every few weeks.
— Dom, August 29, 2025