npm-scan-plus
v1.1.1
Published
Security scanner for npm packages - pre and post-install scanning for malicious code, supply chain attacks, and obfuscated code
Maintainers
Readme
npm-scan-plus 🔒
Security scanner for npm packages - Pre and post-install scanning for malicious code, supply chain attacks, and obfuscated code.
Why We Built This
npm package supply chain attacks are increasing at an alarming rate. Recent examples include:
- TanStack (May 2026): Malicious package published to npm registry containing cryptocurrency stealing code distributed to thousands of applications. (InfoQ)
- event-stream (2018): Maintainer deliberately added malicious code to steal cryptocurrency wallet keys from Copay users
- ua-parser-js (2021): Compromised package with cryptomining malware affecting millions of downloads
- Colors.js / Faker.js (2022): Maintainer intentionally sabotaged popular packages
These attacks succeed because:
- Developers trust npm packages without verification
- No automated scanning before install
- Obfuscated code hides malicious intent
- Typosquatting confuses developers
npm-scan-plus was built to automatically detect these threats before they reach your project.
Features
Pre-Install Scanning
- ✅ Blocklist Check - Known malicious packages (event-stream, flatmap-stream, etc.)
- ✅ Typosquatting Detection - Similar names to popular packages (lodash vs lodsh)
- ✅ Dependency Confusion Detection - Unscoped packages mimicking well-known scopes (
@types,@babel, etc.) - ✅ Vulnerability Database Check
- OSV (Google's Open Source Vulnerabilities)
- GitHub Advisory Database
- npm Audit
- ✅ License Risk Analysis - Warns about GPL, proprietary, or missing licenses
- ✅ Maintainer Trust Scoring - Identifies known trusted maintainers
- ✅ Repository Validation - Verifies repo URL matches package
- ✅ Package Integrity - Hash verification from npm registry
- ✅ Size Anomaly Detection - Flags packages > 50MB
- ✅ Deprecated Dependencies - Warns about request, moment, underscore
- ✅ Native Code Detection - Flags packages with
.node/.dll/.sofiles
Post-Install Scanning
- ✅ Obfuscation Detection - base64, eval(), hex encoding
- ✅ Malicious Pattern Detection - env exfil, shell exec, crypto mining
- ✅ Suspicious Scripts - postinstall, preinstall analysis
- ✅ Sensitive Files - .env, .ssh, credentials detection
- ✅ Bound by fetch timeouts - No hangs on unresponsive registries (30s default)
Programmatic API
- ✅ Scanner class - Embed pre/post-install scanning in your own tooling
- ✅ TypeScript types included - Full type definitions for all results
- ✅ Strict-mode compatible - Zero
any, zero implicit types
Installation
Option 1: npx (run without installing)
# Scan a package before installing
npx npm-scan-plus pre install <package>
# Full automatic wrapper (pre-scan + install + post-scan)
npx npm-scan-plus-wrap install <package>
npx npm-scan-plus-wrap install # install from package.jsonOption 2: Install globally (recommended for frequent use)
npm install -g npm-scan-plus
# Then use directly
npm-scan-plus pre install <package>
npm-scan-plus-wrap install lodash
npm-scan-plus-wrap install # install from package.jsonOption 3: Programmatic use
npm install npm-scan-plusimport { createScanner, type ScanResult } from 'npm-scan-plus';
const scanner = createScanner({ checkVulnerabilities: true });
const result: ScanResult = await scanner.preInstallScan('lodash', '4.18.1');
if (result.status === 'blocked' || result.status === 'danger') {
console.error(`Refusing to install: ${result.threats.length} threats found`);
process.exit(1);
}Quick Start: Automatic Wrapper
The recommended way to use npm-scan-plus is with the automatic wrapper:
# Install a package with automatic pre + post scan
npm-scan-plus-wrap install lodash
# Install multiple packages
npm-scan-plus-wrap install lodash axios express
# Install all dependencies from package.json
npm-scan-plus-wrap installThe wrapper automatically:
- 🔍 Pre-install scans each package
- 📥 Runs npm install
- 🔍 Post-install scans node_modules
How the wrapper finds your dependencies
When you run npm-scan-plus-wrap install with no package arguments, it scans everything in your package.json. Specifically:
- Walks up the directory tree to find the nearest
package.json— so running frompackages/frontend/will find../../package.jsonautomatically - Includes all four dependency kinds:
dependencies,devDependencies,peerDependencies, andoptionalDependencies - Shows the exact path it scanned, so you can verify it's reading the right file
- Reports clearly if it can't find a
package.jsonor if the one it found has no deps
🔒 npm-scan: Running automatic pre-install scan...
📦 Scanning 5 dependencies from C:\Users\you\my-app\package.json ...If nothing is found, the message tells you exactly what was checked:
⚠️ Found C:\Users\you\my-app\package.json but it has no dependencies,
devDependencies, peerDependencies, or optionalDependencies.
Nothing to scan.Manual Usage
If you prefer manual control:
Pre-install scan
npm-scan-plus pre install <package>
npm-scan-plus pre install axios --version 1.6.0
npm-scan-plus pre install lodash -V # verbose outputPost-install scan
npm-scan-plus post
npm-scan-plus post --folder ./node_modulesBlocklist management
npm-scan-plus blocklist list
npm-scan-plus blocklist add <package>
npm-scan-plus blocklist remove <package>Exit codes
0- scan completed, package is safe (or help was shown)1- usage error, scan failed, or package is blocked/dangerous
Detection Patterns
Obfuscation
eval()with atob/fromCharCode- Base64 encoded strings
- Hex/unicode encoded characters
Malicious Behavior
- Environment variable access (KEYS, SECRETS, TOKENS)
- Network requests to IP addresses or external code hosting
- Child process execution
- Crypto mining pool connections
- Keylogging code
Suspicious Scripts
- postinstall/preinstall with complex shell commands
- curl/wget downloads
- Packages scanning directories outside scope
Environment Variables
GITHUB_TOKEN- For higher GitHub Advisory API rate limits
Development
# Build
npm run build
# Test (107 tests across 8 suites)
npm test
# Lint (TypeScript strict mode + zero `any`)
npm run lintCode quality gates:
- TypeScript
strict: truein bothtsconfig.jsonandtests/tsconfig.json @typescript-eslint/no-explicit-any: error- explicitanyis a build failure- All HTTP requests are bounded by a 30s timeout (
fetchWithTimeoutinsrc/lib/http.ts) - 0 known runtime vulnerabilities (
npm audit --omit=devclean) - 0 lint warnings
Source layout:
src/
├── index.ts # Public API entry (Scanner, createScanner, types)
├── types.ts # Shared type definitions
├── cli/index.ts # CLI entry — returns exit code (no process.exit in async)
└── lib/
├── scanner.ts # Main scanner orchestrator (13-step pre-install scan)
├── registry.ts # npm registry HTTP client + cache
├── blocklist.ts # Known-bad packages + typosquatting
├── patterns.ts # File/code pattern matching
├── extended.ts # License/repo/maintainer analysis
├── vuln.ts # OSV + GitHub Advisory + npm Audit
├── integrity.ts # Tarball hash/size/native-code analysis
└── http.ts # fetchWithTimeout helper (AbortController-based)Security Threats Detected
| Threat Type | Example | | -------------------- | -------------------------------- | | Blocklisted | event-stream, flatmap-stream | | Typosquatting | lodsh (looks like lodash) | | Dependency Confusion | types-foo (mimics @types/foo) | | Vulnerabilities | CVE-2021-23337, GHSA-xxxx | | Obfuscation | eval(atob(...)) | | Malicious Code | process.env.API_KEY exfil | | Suspicious Scripts | postinstall: curl ... | | Native Code | .node / .dll / .so in tarball | | Size Anomaly | > 50MB unpacked | | Integrity | Hash mismatch with registry | | Dependency Issues | Deprecated packages, large trees |
License
MIT
Developed by Chris Bunting <[email protected]>
