Limit what a compromised npm package can do.
Scanners flag known-bad packages. Kratex enforces what every package can do at install and at runtime, including the ones nobody has flagged yet.
Every npm install runs the maintainer's code on your machine, and so does every dependency you import. Kratex enforces rules on both.
Stop trusting lifecycle scripts.
Postinstall scripts run with the same access your shell has. kratex install puts them under your rules instead.
$ kratex install
resolving 412 packages
ok react@19.0.0
ok react-dom@19.0.0
block chalk@5.3.0
reason: postinstall.sh requested network
policy: acme / web / v17
ok vite@7.0.0
install incomplete (1 blocked){
"extends": "kratex/default",
"filesystem": "audit",
"network": [
"registry.npmjs.org",
"deps.acme.internal"
],
"subprocess": "allow"
}Apply the same rules at runtime.
Every dependency you import is also a dependency you execute. kratex run enforces the same rules every time it does.
Every decision goes to the dashboard.
Each decision records the device, the project, what was attempted, and the rule that decided. Triage without filing a ticket.
- 14:42:08block install · chalk@5.3.0 postinstall · kira-mbp
- 14:41:55allow install · react@19.0.0 · kira-mbp
- 14:39:12block process · child_process.spawn left-pad · ci-runner-7
- 14:33:01audit fs · write /etc/hosts · owen-linux
- 14:29:44allow net · connect registry.npmjs.org · ci-runner-3
Assign a policy to each project. Triage the events your machines send back.
Machine events
- blockedchalk@5.3.0postinstall · net2m
- blockedleft-pad@1.3.0child_process.spawn12m
- audit/etc/hostsfs.write18m
- allowedreact@19.0.0install26m
- allowedregistry.npmjs.orgnet.connect31m
Policies
- acme / webv17enforcetoday
- acme / paymentsv04audittoday
- acme / infrav22enforce3d ago
- acme / data-ingestv11enforce1w ago
- acme / docsv02audit2w ago
Each of these ran on machines that never imported it directly.
- node-ipc (again)
Maintainer's email domain expired. An attacker bought the lapsed domain, used a forgotten-password flow to take over the npm account, and published versions that exfiltrated AWS tokens, SSH keys, Kubernetes configs, and CI secrets to an attacker-controlled server. node-ipc carried millions of weekly downloads at the time.
- @tanstack/*
A fork pull request reached a GitHub Actions runner. The worm's successor, Mini Shai-Hulud, poisoned the CI cache, extracted the publishing OIDC token from runner memory, and shipped 84 malicious versions across 42 @tanstack/* packages in six minutes. By end of day the same campaign had reached 170+ packages across npm and PyPI.
- @bitwarden/cli
TeamPCP, pivoting from the Trivy compromise, pushed a malicious release to the Bitwarden npm namespace. Version 2026.4.0 ran a preinstall that downloaded the Bun runtime, executed an obfuscated loader, and exfiltrated npm tokens, GitHub tokens, SSH keys, and AWS, Azure, and GCP credentials. The stolen data was AES-encrypted and pushed as public repositories under each victim's own GitHub account.
- axios
A North Korean operator phished the lead maintainer and planted a RAT on the maintainer's workstation. Versions 1.14.1 and 0.30.4 added a malicious dependency, plain-crypto-js, that dropped a cross-platform RAT on macOS, Windows, and Linux. Roughly six hundred thousand installs occurred during the three-hour window before the versions were pulled.
- @aquasecurity/trivy-action, 47+ npm packages
An autonomous bot exploited a pull_request_target misconfiguration in Trivy's GitHub Actions workflows. The operator stole a personal access token, force-pushed seventy-six of seventy-seven Trivy action tags to malicious commits, and shipped a poisoned Trivy binary through official channels. Stolen credentials cascaded into the CanisterWorm campaign, which infected 47+ npm packages with a postinstall that stole tokens and republished onward.
- @crowdstrike/*
The same Shai-Hulud worm reached the crowdstrike-publisher npm account. Multiple CrowdStrike-namespace packages, including @crowdstrike/commitlint and @crowdstrike/falcon-shoelace, were republished with a loader that ran TruffleHog against the host and uploaded any matching secrets. CrowdStrike rotated keys and confirmed the Falcon sensor was unaffected.
You can't audit every npm package. Don't have to.