wormwatch
v0.1.1
Published
Scan an npm project for known Shai-Hulud supply-chain worm indicators: compromised package versions, planted files, and malicious install scripts.
Maintainers
Readme
wormwatch
A small, zero-dependency CLI that scans an npm project for known Shai-Hulud supply-chain worm indicators: compromised package versions, planted payload files, and malicious install scripts.
Point it at a project and it tells you whether anything in your dependency tree matches a known indicator of compromise.
$ wormwatch .
[!!] .
2 indicator(s) found:
[HIGH] package: @ctrl/[email protected]
./node_modules/@ctrl/tinycolor
Installed version is a known-compromised Shai-Hulud release.
[MED ] file: setup_bun.js
./node_modules/@ctrl/tinycolor/setup_bun.js
Injected by the Shai-Hulud 2.0 wave via an added preinstall script.
Coverage: 214 package(s), 1 flagged file(s); lockfile parsed, node_modules scanned.Why
Shai-Hulud is a self-replicating worm that spread through the npm ecosystem
starting in September 2025, trojanizing hundreds of package versions (including
widely used ones like @ctrl/tinycolor and ngx-bootstrap), stealing secrets
during install, and republishing itself through compromised maintainer tokens.
If you installed or locked a bad version, the signal is sitting in your project
right now. wormwatch is a fast way to check, from the command line or in CI.
Install
npm install -g wormwatch # once published
npx wormwatch . # or run without installing
# from source:
git clone https://github.com/danraveh-ai/wormwatch && cd wormwatch
npm install && npm run build
node dist/src/cli.js .Usage
wormwatch # scan the current directory
wormwatch ./my-app ./other-app # scan several projects
wormwatch --json . # machine-readable output
wormwatch --iocs latest.json . # use an updated IOC set
wormwatch --quiet .; echo $? # exit code onlyExit codes: 0 no known indicators found, 1 at least one indicator found,
2 an error occurred. The non-zero code on a hit makes it easy to fail a CI job
when a dependency matches a known IOC.
What it checks
- Package versions. Parses
package-lock.json(v1, v2, and v3) and walksnode_modulesfor installedname@versionpairs that match known-compromised releases. - Planted files. Looks for the distinctively named files the worm drops
inside packages (
setup_bun.js,bun_environment.js,truffleSecrets.json) and the backdoor CI workflow.github/workflows/shai-hulud-workflow.yml. - Payload by hash.
bundle.jsis far too common a filename to flag by name, so it is matched by SHA-256 against the known payload hash instead. - Install scripts. Scans
preinstall/install/postinstallhooks in the root and installed manifests for known IOC strings, including the hardcoded exfiltration endpoint.
Findings are labeled HIGH or MED confidence so you can triage. High-confidence
hits (an exact bad version, a hash match, the backdoor workflow) are near-certain.
Medium hits (a distinctively named file, a suspicious install script) are strong
signals worth verifying by hand.
Scope and honesty
wormwatch does static matching against a curated IOC snapshot. It is deliberate about what it does and does not claim:
- The bundled IOC set (
data/shai-hulud-iocs.json) is a curated snapshot, not a complete inventory. Hundreds of versions were affected across multiple waves and vendor lists keep changing. Supply a fresher set with--iocs. - A clean result means no known IOCs were found in this project. It is not proof that the project is safe, and it says nothing about compromises outside the indicator set.
- wormwatch does not do behavioral or dynamic malware analysis, and it does not remove anything. It reports; you decide.
The IOC dataset carries its own sources and curation date. If you are responding to a live incident, cross-check against the authoritative vendor advisories linked in that file.
Development
npm install
npm test # builds, then runs the node:test suiteWritten in TypeScript with no runtime dependencies. Tests build a real project tree in a temp directory for each case, with no network access.
License
MIT. See LICENSE.
