A read-only scanner for indicators associated with the August 2026 Keyv and Cacheable npm supply-chain compromise.
The campaign began with keyv@6.0.0 and spread as a worm to hundreds of packages. Affected releases use an npm preinstall hook to execute setup.mjs, download a standalone Bun runtime, and launch an obfuscated second-stage payload. The malware targets developer and CI credentials and can install a gh-token-monitor dead-man switch that reacts when a stolen GitHub token is revoked.
This scanner does not install packages, execute package lifecycle scripts, or remove files.
- Linux or macOS
- Bash
- Node.js 18 or later
- Network access to Socket's campaign CSV for the latest package list
The scanner has no npm dependencies.
Run a broad scan using the default locations:
./scan.shThe default scan covers the current directory, the user's home directory, npm and pnpm caches, /tmp, /opt, /usr/local, and /etc. Nested roots are deduplicated.
Scan one or more specific locations:
./scan.sh --root /path/to/project
./scan.sh --root /path/to/project --root /path/to/another/projectUse only the built-in baseline of independently confirmed Keyv and Cacheable releases:
./scan.sh --offlineUse a locally saved Socket-format campaign CSV:
./scan.sh --packages-csv packages.csvSkip the running-process check:
./scan.sh --no-process-scanDisplay all options:
./scan.sh --helpThe scanner checks exact package name/version pairs in:
- Installed
package.jsonfiles - Direct dependencies pinned to exact versions
package-lock.jsonnpm-shrinkwrap.jsonpnpm-lock.yamlyarn.lockbun.lockandbun.lockb
By default, it downloads Socket's current campaign CSV at scan time. If the download fails, it continues with a built-in baseline of 11 independently confirmed releases and exits with status 2 rather than reporting a complete clean result.
The scanner detects:
- The campaign's
preinstall: node setup.mjslifecycle hook - Known malicious SHA-256 hashes for both
setup.mjsloader variants - The known SHA-256 hash for
Math_Symbol.jsandmath_init.js - Loader strings referencing Bun 1.3.13 and the second-stage payload
- Stage-two files matching the observed 727,680-byte payload size
bun-dl-*temporary runtime directories
The scanner checks for the additional execution paths planted in compromised repositories:
- Claude Code
SessionStarthooks invoking.vscode/setup.mjs - VS Code
folderOpentasks invoking.claude/setup.mjs - Git hooks containing campaign-specific indicators
- GitHub Actions workflows that export all repository secrets with
toJSON(secrets)
The scanner checks for:
gh-token-monitor.shgh-token-monitor.servicecom.user.gh-token-monitor.plist~/.config/gh-token-monitor/- Running processes containing known payload or persistence names
| Status | Meaning |
|---|---|
0 |
No known indicators were found, and the requested package intelligence loaded successfully. |
1 |
One or more suspicious or malicious indicators were found. |
2 |
The scan could not load the current package list or could not start reliably. |
A clean result means the scanner did not find the indicators it knows about. It does not prove that the system was never compromised.
Do not rotate GitHub credentials before checking for and disabling gh-token-monitor persistence. Token revocation is the monitor's trigger and can execute an attacker-supplied handler.
Recommended response order:
- Isolate the affected workstation or CI runner from normal network access.
- Preserve relevant files, process information, npm logs, CI output, and filesystem timestamps.
- Find and disable
gh-token-monitorservices, LaunchAgents, scripts, and state files. - Rebuild the affected environment when practical.
- Rotate credentials from a known-clean system, including GitHub, npm, cloud, CI, Vault, Kubernetes, database, and private-key material.
- Audit npm publications, GitHub repositories and workflows, cloud activity, and source-control access.
- Purge compromised releases from private registries, dependency caches, lockfiles, and installed dependency trees.
Coordinate destructive cleanup with an incident-response team when forensic evidence may be required.
Run the dependency-free contract tests:
./test_scan.shThe tests cover clean scans, compromised package and loader detection, Yarn scoped-package detection, repository hooks, and incomplete package-intelligence handling.
For shell and JavaScript linting when the tools are installed:
shellcheck scan.sh test_scan.sh
oxlint scan.mjs- This is an IOC scanner, not a complete forensic or endpoint-detection tool.
- The campaign is active, so package intelligence can change after a scan.
- Exact package matches show exposure but do not prove the malicious lifecycle script executed.
- IDE configuration files may require workspace trust or user approval before executing.
- Permission errors can prevent some directories from being inspected; the scan summary reports their count.
- npm cache archives are not unpacked or executed during scanning.
- Network indicators alone are not treated as proof because the loader abuses legitimate GitHub-hosted Bun releases and the payload can resolve changing destinations.