@lateos/npm-scan 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,864 +1,741 @@
1
- # @lateos/npm-scan
2
-
3
- [![npm version](https://img.shields.io/npm/v/@lateos/npm-scan?style=flat-square)](https://www.npmjs.com/package/@lateos/npm-scan)
4
- [![License](https://img.shields.io/badge/license-Apache%202.0%20%2B%20Commons%20Clause-blue?style=flat-square)](LICENSING.md)
5
- [![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](package.json)
6
- [![Tests](https://img.shields.io/badge/tests-536%20passing-brightgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
7
- [![Coverage](https://img.shields.io/badge/coverage-90%25-brightgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
8
- [![Docker](https://img.shields.io/badge/docker-lateos%2Fnpm--scan-2496ED?style=flat-square&logo=docker)](https://hub.docker.com/r/lateos/npm-scan)
9
- [![Sigstore](https://img.shields.io/static/v1?label=Sigstore&message=Provenance&color=green&style=flat-square&logo=sigstore)](https://github.com/lateos-ai/npm-scan/actions/workflows/publish.yml)
10
-
11
- [![中文](https://img.shields.io/badge/lang-zh--CN-red?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.zh.md)
12
- [![日本語](https://img.shields.io/badge/lang-ja-purple?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.ja.md)
13
- [![Français](https://img.shields.io/badge/lang-fr-orange?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.fr.md)
14
- [![Deutsch](https://img.shields.io/badge/lang-de-green?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.de.md)
15
-
16
- **Modern supply chain security for the npm ecosystem.**
17
- Static + behavioral analysis that catches what npm audit, Snyk, and Socket miss — obfuscated payloads, credential stealers, conditional triggers, sandbox evasion, and worm-like propagation.
18
-
19
- ---
20
-
21
- ## 📌 The Problem
22
-
23
- The 2025–2026 wave of npm supply chain attacks proved that traditional tooling is no longer enough.
24
-
25
- Attackers have moved past simple typosquatting. They now ship **obfuscated preinstall hooks**, **credential harvesters hidden behind environment detection**, **dormant backdoors with time-based activation**, and **worm-style transitive propagation** that spreads through peer dependencies.
26
-
27
- A growing attack vector is **HuggingFace org impersonation** — packages that masquerade as legitimate HF model repositories (e.g., `0penai/gpt2` instead of `openai/gpt2`) to trick users into downloading malicious model artifacts during CI/CD pipelines, often bundled with suspicious binaries (`.exe`, `.dll`) in model repos that deep-learned tools trust by default.
28
-
29
- The **Megalodon campaign** (2026) alone compromised 5,500+ repositories via fake GitHub PRs, malicious workflow injection, and cloud credential exfiltration — all coordinated through a single actor automating the entire kill chain. **@lateos/npm-scan** now detects artifacts of this campaign out of the box.
30
-
31
- The **Mini Shai-Hulud worm campaign** (May 2026) hit the npm ecosystem in three waves — TanStack CI/CD hijack (84 artifacts in 6 minutes), AntV/atool maintainer compromise (600+ malicious versions across 300+ packages), and Nx Console VS Code extension poisoning (CVE-2026-48027) — all using ctf-scramble-v2 obfuscation, daemonized persistence with CI environment checks, geographic killswitches targeting sanctioned regions, and GitHub C2 dead-drop channels for token recovery. **@lateos/npm-scan** now detects all 10 Mini Shai-Hulud signals across two detector suites.
32
-
33
- The **TrapDoor campaign** (May 2026) spans npm, PyPI, and Crates.io — 34 malicious packages, 384+ versions attributed to a single publisher, targeting crypto, DeFi, Solana, and AI developers with Fernet + ECDH encrypted payloads, AI context poisoning via zero-width Unicode injection in `.cursorrules`/`CLAUDE.md`, and credential live-validation against AWS STS and GitHub API before exfiltration. **@lateos/npm-scan** now detects all 9 TrapDoor signals.
34
-
35
- The **node-ipc compromise** (May 14, 2026) weaponized an expired maintainer email domain to hijack one of npm's most depended-upon packages (822K weekly downloads). Three malicious versions (9.1.6, 9.2.3, 12.0.1) delivered an 80KB credential stealer via DNS TXT tunneling — no HTTP, no postinstall hook, invisible to HTTP-layer firewalls. **@lateos/npm-scan** now detects all 11 node-ipc compromise signals.
36
-
37
- The **Mass Typosquatting campaign (vpmdhaj)** (May 2026) weaponized the `vpmdhaj` npm maintainer account to publish 14 typosquatted packages in a 4-hour window — targeting AWS/CI/CD environments with preinstall stagers (`setup.mjs`, `stager.js`), Bun runtime abuse, and cloud credential exfiltration (AWS IMDSv2, ECS task roles, Vault, GitHub tokens). **@lateos/npm-scan** now detects all 3 typosquatting campaign signals.
38
-
39
- The **Axios Registry Poisoning campaign** (May 2026) compromised the npm registry's axios package metadata to publish `axios@1.14.1` and `axios@0.30.4` with injected dependencies (`plain-crypto-js`) containing cross-platform RAT payloads with C2 callbacks, process injection, and system persistence. **@lateos/npm-scan** now detects all 3 axios poisoning signals.
40
-
41
- Critical infrastructure vulnerabilities in the Python ecosystem are also in scope. The **BadHost (CVE-2026-48710)** vulnerability in Starlette < 1.0.1 enables authentication bypass via unvalidated HTTP Host header injection, affecting FastAPI, vLLM, LiteLLM, MCP servers, and any project using Starlette transitively — now detected across Python manifests, transitive dependency chains, and source code patterns in a single scan.
42
-
43
- **npm audit** checks known CVEs. **Snyk** scans for vulnerabilities. **Socket** looks at package behavior. None of them were designed for the generation of attacks that emerged in 2025 — attacks that look benign until they reach production.
44
-
45
- **@lateos/npm-scan** was built for this moment.
46
-
47
- ---
48
-
49
- ## 🔬 Why @lateos/npm-scan?
50
-
51
- | Capability | npm audit | Snyk | Socket | **@lateos/npm-scan** |
52
- |---|---|---|---|---|
53
- | Known CVE matching | ✅ | ✅ | ❌ | ✅ |
54
- | Static analysis | ❌ | ✅ | ✅ | ✅ |
55
- | Obfuscated payload detection | ❌ | ❌ | ❌ | ✅ |
56
- | AST-level heuristic analysis | ❌ | ❌ | ❌ | ✅ |
57
- | Runtime behavioral sandbox | ❌ | ❌ | ✅ | ✅ |
58
- | Conditional trigger detection (ATK-009) | ❌ | ❌ | ❌ | ✅ |
59
- | Sandbox evasion detection (ATK-010) | ❌ | ❌ | ❌ | ✅ |
60
- | Transitive worm propagation (ATK-011) | ❌ | ❌ | ❌ | ✅ |
61
- | Campaign detection (Megalodon CI/CD) | ❌ | ❌ | ❌ | ✅ |
62
- | Worm campaign detection (Mini Shai-Hulud Wave 1–3) | ❌ | ❌ | ❌ | ✅ |
63
- | HF model repo impersonation + README clone | ❌ | ❌ | ❌ | ✅ |
64
- | VS Code extension supply chain scan (--vsix) | ❌ | ❌ | ❌ | ✅ |
65
- | Python vulnerability detection (CVE-2026-48710 BadHost) | ❌ | ❌ | ❌ | ✅ |
66
- | Cross-ecosystem attack detection (TrapDoor) | ❌ | ❌ | ❌ | ✅ |
67
- | Expired-domain hijack detection (node-ipc) | ❌ | ❌ | ❌ | ✅ |
68
- | Malware obfuscation detection (ctf-scramble-v2) | ❌ | ❌ | ❌ | ✅ |
69
- | Mass typosquatting campaign (vpmdhaj maintainer) | ❌ | ❌ | ❌ | ✅ |
70
- | Registry poisoning detection (axios fake versions) | ❌ | ❌ | ❌ | ✅ |
71
- | Attack taxonomy (ATK series) | ❌ | ❌ | ❌ | ✅ |
72
- | SBOM output (CycloneDX + SPDX) | ❌ | ✅ | ❌ | ✅ |
73
- | SARIF v2.1 (GitHub Code Scanning) | ❌ | ❌ | ❌ | ✅ |
74
- | NIST 800-161 compliance reporting | ❌ | ❌ | ❌ | ✅ |
75
- | EU CRA compliance reporting | ❌ | ❌ | ❌ | ✅ |
76
- | SIEM export (CEF / ECS / Sentinel / QRadar) | ❌ | ❌ | ❌ | ✅ |
77
- | Runs entirely locally — no telemetry | ✅ | ❌ | ❌ | ✅ |
78
- | Policy-as-code (YAML allowlists) | ❌ | ❌ | ❌ | ✅ |
79
-
80
- > **Privacy first.** All scanning happens on your machine. No code leaves your environment. No telemetry. No cloud dependency.
81
-
82
- ---
83
-
84
- ## ✨ Key Features
85
-
86
- | Icon | Feature | Description |
87
- |------|---------|-------------|
88
- | 🕵️ | **Heuristic static analysis** | AST-level inspection catches obfuscation, eval chains, env probing, and suspicious lifecycle scripts that regex-based tools miss |
89
- | 🧠 | **Behavioral detection** | Identifies conditional triggers (time-based, CI-aware), sandbox evasion, and dormant activation patterns |
90
- | 🧬 | **ATK attack taxonomy** | 11 classified attack types with NIST 800-161 mappings — versioned, documented, and PR-able |
91
- | 🪱 | **Worm campaign detection** | Mini Shai-Hulud — 6 sub-checks detecting burst publish, sibling compromise, SLSA attestation mismatch, publisher drift, IOC match, and token exfil across 3 waves (TanStack, AntV/atool, Nx Console) |
92
- | 🧩 | **VSIX extension scanning** | `npm-scan scan --vsix nrwl.angular-console` — detects VS Code Marketplace supply chain attacks: burst publish, publisher anomaly, activation event risk, orphan commit fetch, known IOC, and exfil patterns (Nx Console 18.95.0 CVE-2026-48027) |
93
- | 🐍 | **Python vulnerability detection** | CVE-2026-48710 (BadHost) — Starlette Host header injection across 6 Python manifest formats, 15 transitive downstream packages (fastapi, vllm, litellm, MCP), and static `request.url.path` code pattern analysis with `scope["path"]` suppression |
94
- | 🪤 | **Cross-ecosystem attack detection** | TrapDoor — 9 sub-checks: campaign marker P-2024-001, trap-core.js payload fingerprint, publisher blocklist asdxzxc, Gist-based credential exfil, AI config zero-width Unicode poisoning, crypto/DeFi lure name heuristic, Fernet+ECDH encryption, XOR key cargo-build-helper-2026, STS/GitHub API credential validation |
95
- | 📡 | **Expired-domain hijack detection** | node-ipc compromise — version blocklist (9.1.6/9.2.3/12.0.1), tarball SHA-256 verification, CJS vs ESM size anomaly with IIFE injection, DNS-over-non-standard-port C2, bootstrap resolver sh.azurestaticprovider.net, DNS TXT exfil zone bt.node.js, setImmediate() runtime trigger, ~/nt-*/ staging artifacts, unauthorized publisher atiertant, lockfile blast-radius with safe pin recommendations |
96
- | ☣️ | **Malware obfuscation detection** | ctf-scramble-v2 detection — scans package dist/lib for known malware obfuscation patterns (ctf-scramble-v2, ctf-scramble-v3), halts analysis immediately with max severity CRITICAL stop condition |
97
- | 🎭 | **Mass typosquatting campaign detection** | vpmdhaj maintainer blocklist with stop condition, levenshtein-based typosquat detection (opensearch-setup, env-config-manager), preinstall stager identification (setup.mjs, stager.js, Bun runner), AWS ECS/Vault/GitHub credential exfiltration patterns |
98
- | ☠️ | **Registry poisoning detection** | Axios version blocklist (1.14.1/0.30.4) with stop condition, decoy dependency discovery (plain-crypto-js), cross-platform RAT payload detection (DLL injection, launchd/systemd/cron persistence, PowerShell IEX, C2 callbacks) |
99
- | 🔏 | **Provenance audit trail** | Aureus-Elicitor v1.7 framework — HMAC-SHA256 signed detection manifests, content-hash verified audit trails, rule provenance URLs, campaign source attribution. Every finding includes cryptographically verifiable provenance metadata |
100
- | 📦 | **SBOM generation** | CycloneDX 1.5 and SPDX 2.3 with findings embedded as vulnerabilities |
101
- | 🔍 | **SARIF output** | GitHub Advanced Security / CodeQL compatible SARIF v2.1 — shows findings directly in Security tab |
102
- | 🧾 | **Compliance reporting** | NIST SP 800-161 traceability matrix + EU Cyber Resilience Act mapping (free tier) |
103
- | 🔌 | **SIEM export** | Splunk CEF, Elastic ECS, Microsoft Sentinel, IBM QRadar formats (premium) |
104
- | 📜 | **Policy-as-code** | YAML/JSON policy engine with allowlists, severity overrides, suppressions, and fail-on thresholds |
105
- | 🐳 | **Docker + GitHub Action** | Multi-arch images, one-command Compose pipeline, PR scan action |
106
- | 🛡️ | **Zero telemetry** | No data leaves your machine. No cloud. No callbacks. |
107
- | 💾 | **Local scan history** | SQLite-backed persistence, zero external dependencies |
108
- | 🪝 | **Pre-commit hook** | Block threats before commit — one-liner install, scans `package-lock.json` changes |
109
- | 🤖 | **HF impersonation detection** | Detects typosquatted HuggingFace orgs (Jaro-Winkler), README clones (SimHash), artifact mismatches (`.exe` in model repos), and new-org amplifier — with lazy two-stage evaluation, zero network in Stage 1 |
110
- | 📎 | **Yarn + pnpm support** | `scan-lockfile` parses `yarn.lock` and `pnpm-lock.yaml` alongside `package-lock.json` |
111
-
112
- ---
113
-
114
- ## ⚡ Quick Start
115
-
116
- ```bash
117
- # Install globally
118
- npm install -g @lateos/npm-scan
119
-
120
- # Scan a single package
121
- npm-scan scan lodash
122
-
123
- # Scan your lockfile
124
- npm-scan scan-lockfile
125
-
126
- # View latest scans
127
- npm-scan report
128
- ```
129
-
130
- **No install? No problem:**
131
-
132
- ```bash
133
- npx @lateos/npm-scan scan commander
134
- ```
135
-
136
- ---
137
-
138
- ## Validation & Accuracy
139
-
140
- ### Real-World Campaign Detection
141
-
142
- `@lateos/npm-scan` was validated against 3 active May 2026 supply chain attack campaigns:
143
-
144
- | Campaign | Packages | Detection Rate | Key Detector |
145
- |---|---|---|---|
146
- | **Dependency Confusion** (176-pkg high-version hijack) | 3 | **100%** | D6 (Version Anomaly) |
147
- | **Mini Shai-Hulud** (Obfuscation + C2) | 2 | **100%** | D7 (Obfuscation Heuristics) |
148
- | **Bitwarden Impersonation** (Typosquat + lifecycle hooks) | 2 | **100%** | D1 (Typosquat) + D3 (Lifecycle Hook) |
149
-
150
- ### False Positive Calibration
151
-
152
- Detector thresholds calibrated against **top 1,000 npm packages by download count**:
153
-
154
- - **Packages Scanned**: 990 legitimate packages
155
- - **False Positive Rate**: **0.0%** (0 FPs at production thresholds)
156
- - **Detector Performance**: See [VALIDATION.md](./VALIDATION.md) for precision/recall per detector
157
-
158
- ### Per-Detector Confidence
159
-
160
- | Detector | Avg Confidence | Threshold | Notes |
161
- |---|---|---|---|
162
- | D6 (Version Anomaly) | 92.0% | 72 | Z-score >3.0; sentinel patterns (99.99.99) always flag |
163
- | D7 (Obfuscation Heuristics) | 80.0% | 75 | Entropy + pattern matching; bundlers whitelisted |
164
- | D5 (Binary Embedding) | 81.3% | 80 | Cross-platform binary sets; rare in legitimate packages |
165
- | D4 (Lifecycle Hook) | 92.5% | 65 | postinstall/preinstall/prepare scripts analyzed |
166
- | D3 (Infostealer) | 68.7% | 72 | C2 signatures, credential exfil patterns |
167
- | D1 (Typosquat) | 87.9% | 85 | Edit-distance scoring; scoped sub-packages exempt |
168
-
169
- **Full validation report**: [VALIDATION.md](./VALIDATION.md)
170
-
171
- ---
172
-
173
- ## 🐳 Run @lateos/npm-scan anywhere with Docker — zero installation
174
-
175
- ```bash
176
- # Pull and run a single scan — no Node.js or npm required
177
- docker run --rm lateos/npm-scan:cli scan lodash
178
-
179
- # Full pipeline with persistent storage and Compose
180
- docker compose --profile pipeline up -d
181
- ```
182
-
183
- No Node.js. No `npm install`. No global packages. Works on any system with Docker — CI servers, air-gapped environments, Kubernetes clusters. Multi-arch images for `linux/amd64` and `linux/arm64`.
184
-
185
- ---
186
-
187
- ## 🛡️ Government & SOC 2 Ready
188
-
189
- | Feature | SOC 2 Controls | NIST 800-161 | STIG/FedRAMP Alignment |
190
- |---------|-------|--------------|--------------|
191
- | Audit logs (--audit-log) | CC6.8 | AU-2 | ✓ |
192
- | FIPS crypto (--fips) | CC6.1 | SC-13 | ✓ |
193
- | STIG report (--stig) | CC7.3 | RA-5 | ✓ |
194
- | Offline cache (--cache-dir) | A1.2 | SC-8 | ✓ |
195
- | Sigstore provenance | CC6.2 | SI-7 | ✓ |
196
- | SBOM (SPDX/CycloneDX) | CC7.4 | SA-10 | ✓ |
197
-
198
- ```bash
199
- # Air-gapped scan with full compliance
200
- npm-scan scan-lockfile --cache-dir /offline/cache --audit-log /var/log/npm-scan.audit --fips
201
- npm-scan report --stig
202
- ```
203
-
204
- [![SOC 2 Ready](https://img.shields.io/badge/SOC%202-Ready-green?style=flat-square&logo=aicpa)](https://www.aicpa.org/interestareas/frc/assuranceadvisoryservices/sorhome.html#soc2)
205
- [![FedRAMP Aligned](https://img.shields.io/badge/FedRAMP-Aligned-blue?style=flat-square&logo=fedramp)](https://fedramp.gov/baselines/)
206
-
207
- ---
208
-
209
- ## ☁️ BYOC — Bring Your Own Cloud
210
-
211
- Deploy npm-scan in your VPC with full data sovereignty. No data leaves your infrastructure.
212
-
213
- | Feature | Description |
214
- |---------|-------------|
215
- | **Self-hosted** | Run on EKS/GKE/AKS in your AWS/Azure/GCP account |
216
- | **SIEM Export** | CEF/ECS/Sentinel/QRadar to your existing SIEM |
217
- | **SSO/OIDC** | SAML/OIDC integration with your identity provider |
218
- | **PDF Reports** | Generate NIST-compliant PDF reports locally |
219
- | **External DB** | Connect to your existing PostgreSQL/Redis |
220
-
221
- ```bash
222
- # Deploy to your VPC with Helm
223
- git clone https://github.com/lateos-ai/npm-scan.git
224
- cd npm-scan/deploy/helm
225
- helm install npm-scan -f values.byoc.yaml .
226
-
227
- # BYOC values example (see values.byoc.yaml)
228
- premium:
229
- enabled: true
230
- edition: enterprise
231
- byoc:
232
- enabled: true
233
- cloudProvider: aws
234
- vpcId: vpc-xxx
235
- region: us-east-1
236
- ```
237
-
238
- **Pricing**: Enterprise license $10k/yr — self-supported (docs + GitHub issues).
239
-
240
- ---
241
-
242
- ## 📖 Usage Examples
243
-
244
- ### Scan a single package
245
-
246
- ```bash
247
- # Default JSON output with all findings
248
- npm-scan scan axios
249
-
250
- # Generate an SBOM alongside the scan
251
- npm-scan scan express --sbom # CycloneDX JSON
252
- npm-scan scan express --sbom xml # CycloneDX XML
253
- npm-scan scan express --sbom spdx # SPDX 2.3
254
-
255
- # Apply a YAML policy
256
- npm-scan scan some-package --policy .npm-scan.yml
257
-
258
- # Scan a local tarball (no registry fetch needed)
259
- npm-scan scan --file path/to/malicious-package.tgz
260
-
261
- # Scan a VS Code extension for Marketplace supply chain attacks
262
- npm-scan scan --vsix nrwl.angular-console
263
-
264
- # Scan a package AND a VSIX extension together (findings merge)
265
- npm-scan scan lodash --vsix nrwl.angular-console
266
- ```
267
-
268
- ### Scan a lockfile
269
-
270
- ```bash
271
- # Scan a single package
272
- npm-scan scan lodash
273
-
274
- # Scan your lockfile
275
- npm-scan scan-lockfile
276
-
277
- # Scan a VS Code extension for supply chain threats
278
- npm-scan scan --vsix nrwl.angular-console
279
-
280
- # View latest scans
281
- npm-scan report
282
- ```
283
-
284
- ### Generate reports
285
-
286
- ```bash
287
- # List all recent scans
288
- npm-scan report
289
-
290
- # View a specific scan
291
- npm-scan report -i 42
292
-
293
- # Generate an HTML report (free) with full findings + NIST table
294
- npm-scan report -i 42 --html
295
-
296
- # Print NIST 800-161 compliance table
297
- npm-scan report -i 42 --nist
298
-
299
- # Print EU CRA compliance table
300
- npm-scan report --cra
301
-
302
- # CSV export for Excel / Sheets (audit-ready)
303
- npm-scan report --csv risks.csv
304
- npm-scan scan lodash --csv # CSV to stdout
305
-
306
- # Text report (free)
307
- npm-scan report --text
308
-
309
- # PDF report (premium)
310
- npm-scan report --pdf --license-key <key>
311
-
312
- # SIEM export (premium)
313
- npm-scan report --siem cef # Splunk CEF
314
- npm-scan report --siem ecs # Elastic ECS
315
- npm-scan report --siem sentinel # Microsoft Sentinel
316
- npm-scan report --siem qradar # IBM QRadar
317
-
318
- # Combine all scans into a single report
319
- npm-scan report --html # all scans
320
- npm-scan report --pdf # all scans (premium)
321
- ```
322
-
323
- ---
324
-
325
- ## 🧬 Detection Capabilities (ATK Taxonomy)
326
-
327
- | ID | Attack Class | Detection Method | Severity | NIST 800-161 |
328
- |---|---|---|---|---|
329
- | **ATK-001** | Malicious lifecycle scripts (`preinstall`, `postinstall`, `install`) | Static | 🔴 high | SR-3.1 |
330
- | **ATK-002** | Obfuscated payload delivery (hex, base64, eval chains) | Static | 🟠 medium | SR-4.2 |
331
- | **ATK-003** | Credential harvesting (env vars, .npmrc, SSH keys) | Static + Dynamic | 🔴 high | SR-5.3 |
332
- | **ATK-004** | Persistence via editor/config dirs (.vscode, .claude, .cursor) | Static | 🔴 high | SR-6.4 |
333
- | **ATK-005** | Network exfiltration (GitHub API, DNS tunneling, HTTP C2) | Static + Dynamic | ⚫ critical | SR-7.5 |
334
- | **ATK-006** | Dependency confusion / namespace squatting | Static (lockfile) | 🟠 medium | SR-2.2 |
335
- | **ATK-007** | Typosquatting (edit-distance matching) | Static | 🟢 low | SR-2.1 |
336
- | **ATK-008** | Tarball tampering (published ≠ source) | Static | 🔴 high | SR-8.1 |
337
- | **ATK-009** | Conditional/dormant triggers (CI detection, time-based) | Behavioral | 🔴 high | SR-9.2 |
338
- | **ATK-010** | Sandbox evasion / anti-analysis | Behavioral | 🟠 medium | SR-10.3 |
339
- | **ATK-011** | Transitive propagation (worm-style lateral spread) | Behavioral | 🔴 high | SR-11.4 |
340
- | **MEGALODON** | Megalodon CI/CD campaign — workflow C2 exfil, credential harvest, publish velocity spike, publisher drift | Static + Registry | ⚫ critical | SR-3.1, SR-7.5 |
341
- | **HF_IMPERSONATION** | HuggingFace org spoof detection — Jaro-Winkler similarity against 15 known-good orgs, SimHash README clone detection, artifact mismatch (`.exe`/`.dll` in model repos), postinstall escalation, new-org amplifier | Static + Network (Stage 2) | 🔴 high / ⚫ critical | SR-2.1 |
342
- | **MINI_SHAI_HULUD** | Mini Shai-Hulud worm campaign — burst publish velocity (≥3 versions/30 min), co-temporal sibling compromise, SLSA attestation mismatch (sub-60s gap, first-ever, builder mismatch), publisher drift (<10 min account change), IOC match (scope/sha512/publisher from seed file), token exfil (NPM_TOKEN/.npmrc/atob patterns), Nx Console downstream detection | Static + Registry | 🔴 high / ⚫ critical | SR-3.1, SR-7.5 |
343
- | **VSIX_SCAN** | VS Code extension supply chain scan — burst publish (≥2 versions/30 min, hot-pull <20 min), publisher anomaly (account substitution, new-account on high-install ext, 15-min add+publish), activation event risk (onStartupFinished→HIGH, *→CRITICAL, escalation on shell keywords), orphan commit fetch (GitHub API SHA refs, npx git URL, MCP-disguised exfil, Bun install), known IOC (extensionId/publisherAccount/commit hash from seed), exfil patterns (cred paths, DNS tunneling, AES+RSA, anti-analysis, Bun APIs) | Static + Registry | 🟠 medium / 🔴 high / ⚫ critical | SR-3.1, SR-5.3 |
344
- | **CVE-2026-48710** | BadHost — Starlette authentication bypass via Host header injection (CVE-2026-48710, CVSS 7.0). Python dependency version detection (requirements.txt, pyproject.toml, poetry.lock, Pipfile, setup.py/cfg), transitive heuristic (15 known downstream packages: fastapi, vllm, litellm, MCP servers, etc.), static code pattern scan for dangerous `request.url.path` usage in auth/middleware context with `request.scope["path"]` suppression | Static + Registry | 🔴 high / 🟠 medium / ℹ️ info | SR-3.1, SR-5.3 |
345
- | **TRAPDOOR** | TrapDoor cross-ecosystem attack — campaign marker P-2024-001 in files, shared payload trap-core.js filename/48,485-byte fingerprint, publisher blocklist asdxzxc, Gist-based credential exfil (ddjidd564.github.io + credential paths), AI context poisoning via zero-width Unicode in .cursorrules/CLAUDE.md, crypto/DeFi lure name heuristic (<30 days), Fernet+ECDH crypto primitives in postinstall, XOR key cargo-build-helper-2026 in lockfiles, STS/GitHub API credential validation in postinstall | Static + Registry | 🟠 medium / 🔴 high / ⚫ critical | SR-3.1, SR-5.3, SR-7.5 |
346
- | **NODE_IPC_COMPROMISE** | node-ipc supply chain compromise (May 14, 2026) — version blocklist (9.1.6/9.2.3/12.0.1) with safe pins, tarball SHA-256 verification, CJS payload IIFE injection detection (CJS>ESM size differential), injected payload hash match, DNS-over-non-standard-port C2 (setServers + custom resolver), bootstrap resolver sh.azurestaticprovider.net + C2 IP 37.16.75.69, DNS TXT exfiltration zone bt.node.js, setImmediate() runtime trigger, ~/nt-*/ staging artifact detection, unauthorized publisher atiertant, lockfile blast-radius detection with pin recommendations | Static + Registry | ⚫ critical | SR-3.1, SR-5.3, SR-7.5 |
347
- | **MSH_SUPPLEMENT** | Mini Shai-Hulud supplement — ctf-scramble-v2 obfuscation (HALT on match), daemonization persistence (spawn detached, systemd, cron, launchd, Task Scheduler), geographic killswitch detection (ru_RU/be_BY locale checks via process.env.LANG/LC_ALL/Intl.DateTimeFormat), C2 dead-drop indicators (OhNoWhatsGoingOnWithGitHub keyword, GitHub commit scraping + token exfiltration co-occurrence) | Static + Behavioral | ⚫ critical | SR-3.1, SR-7.5, SR-9.2 |
348
- | **TYPOSQUAT_VPMDHAJ** | Mass Typosquatting campaign (vpmdhaj) — maintainer blocklist (HALT on match), vpmdhaj-* namespace prefix detection, levenshtein typosquat matching vs popular packages, preinstall stager identification (setup.mjs/stager.js/bun run with generation tracking), cloud credential exfiltration (AWS IMDSv2, ECS task role tokens, Vault credentials, GITHUB_TOKEN/GH_TOKEN with HTTP exfil) | Static + Registry | ⚫ critical | SR-2.1, SR-3.1, SR-5.3 |
349
- | **AXIOS_POISONING** | Axios Registry Poisoning — version blocklist (1.14.1/0.30.4, HALT on match), decoy dependency injection (plain-crypto-js), crypto-dependency heuristic in non-crypto packages, cross-platform RAT payload detection (PowerShell IEX, launchd, systemd, cron, DLL/LoadLibrary, CreateRemoteThread, binary drops to temp dirs, C2 callbacks) | Static + Behavioral | ⚫ critical | SR-3.1, SR-5.3, SR-7.5 |
350
-
351
- > **How evasive attacks are caught:** ATK-009 detects packages that check `process.env.CI`, probe hostnames, or use time-based activation. ATK-010 flags `debugger` statements, `os.hostname()` probes, and env fingerprinting. ATK-011 traces peer dependency graphs to detect worm-like propagation patterns.
352
- > **MEGALODON** campaign detection analyzes bundled `.github/workflows/` files for C2 co-occurrence and base64 decode chains, scans tarball files for credential + outbound network patterns, detects version publish velocity spikes via npm registry metadata, and identifies publisher account drift — all without any network calls beyond the initial package fetch.
353
- > **HF_IMPERSONATION** detection uses a lazy two-stage evaluation: Stage 1 scans `package.json` scripts and JS/TS sources for HuggingFace references (URLs, `from_pretrained()`, `hub.download()`) and runs Jaro-Winkler similarity against 15 known-good HF orgs — zero network. If spoofs are found, Stage 2 fetches the HF model API, computes SimHash of both READMEs for clone detection, validates artifact type consistency (e.g., `transformers` library with `.exe` files is flagged as critical), applies a new-org amplifier (<30 days), and escalates when the reference appears in a lifecycle script.
354
- > **MINI_SHAI_HULUD** worm campaign detection uses a lazy two-stage evaluation: Stage 1 runs burst velocity, publisher drift, IOC, and token exfil checks (in-memory, no network). If burst triggers, Stage 2 queries npm attestation endpoints for SLSA anomalies and fetches sibling package registry metadata for co-temporal burst detection. Composite finding includes wave attribution (wave1-tanstack, wave2-antv, wave3-nx-console) and critical severity when SLSA or IOC match. NX_CONSOLE_DOWNSTREAM (D7) flags npm packages with `@nx/*` dependencies and checks for `nrwl.angular-console` in `.vscode/extensions.json`.
355
- > **VSIX_SCAN** extension scanning wraps both VS Code Marketplace and Open VSX registries with rate-limited (10 req/min), cached (5 min TTL) API clients. All 6 detectors run asynchronously and aggregate into a single composite `VSIX_SCAN` finding. Zero extension code is executed — all analysis is static regex/text-pattern matching. No Bun installation required for Bun pattern detection.
356
- > **CVE-2026-48710 (BadHost)** detection uses three independent layers: Layer 1 parses 6 Python manifest formats (requirements.txt, pyproject.toml, poetry.lock, Pipfile, setup.py, setup.cfg) with PEP 440 semver-aware version comparison. Layer 2 scans for 15 known Starlette-downstream packages with Tier 1 (HIGH confidence) and Tier 2 (MEDIUM confidence) transitive heuristics, suppressed by explicit `starlette >= 1.0.1` pin. Layer 3 performs function-boundary static analysis on `.py` files for `request.url.path` usage, escalating to MEDIUM severity in auth/middleware contexts and suppressing when `request.scope["path"]` is used in the same function.
357
- > **TRAPDOOR** campaign detection runs 9 sub-detectors across all package files (README.md, package.json, .md, shell scripts, .cursorrules, CLAUDE.md) for the hardcoded marker P-2024-001, scans for trap-core.js by filename or exact 48,485-byte size, unconditionally blocks publisher asdxzxc, detects outbound references to ddjidd564.github.io or gist.github.com combined with credential-path patterns, scans .cursorrules and CLAUDE.md for zero-width Unicode characters (U+200B, U+200C, U+200D, U+FEFF), flags crypto/DeFi-themed packages <30 days old with no prior version history, detects simultaneous Fernet and ECDH/createECDH usage in postinstall JS, and identifies sts.amazonaws.com and api.github.com/user calls in postinstall hooks.
358
- > **NODE_IPC_COMPROMISE** detection intercepts the expired-domain takeover attack across 11 dimensions: version blocklist with safe-pin recommendations (9.1.5 for 9.x, 12.0.0 for 12.x), tarball SHA-256 hash verification against known malicious hashes, CJS vs ESM size comparison with IIFE suffix pattern detection for injected payload identification, DNS resolver pattern analysis (dns.promises.Resolver + setServers with non-public IP + resolveTxt), bootstrap domain and C2 IP detection, resolveTxt() with bt.node.js zone references, setImmediate() runtime trigger detection, ~/nt-*/ staging artifact path identification, publisher account verification against unauthorized account atiertant, and lockfile scanning for compromised version resolution with safe-pin suggestions.
359
- > **MSH_SUPPLEMENT** detection augments the existing Mini Shai-Hulud worm campaign with 4 additional sub-detectors: D1 scans all extracted JS files for `require('ctf-scramble-v2')` or `import 'ctf-scramble-v2'` patterns (including variant ctf-scramble-v3+) and returns a CRITICAL stop condition that halts all further scanning. D2 scans lifecycle scripts (preinstall/install/postinstall) for daemonization APIs (`daemon()`, `fork()`, `spawn({detached: true})`), persistence mechanisms (systemd unit files, crontab, launchd plists, Windows Task Scheduler), and CI environment guards (`!process.env.CI`, `process.env.CI === undefined`). D3 scans code for geographic locale checks targeting sanctioned regions (`ru_RU`, `be_BY`) via `process.env.LANG`, `process.env.LC_ALL`, and `Intl.DateTimeFormat().resolvedOptions().timeZone` with `process.exit(0)` silent termination. D4 detects C2 dead-drop indicators including the hardcoded `OhNoWhatsGoingOnWithGitHub` keyword and co-occurring GitHub token access + GitHub API commit scraping patterns. All sub-detectors produce provenance-attached findings with HMAC-SHA256 signed audit trails.
360
- > **TYPOSQUAT_VPMDHAJ** campaign detection uses 3 sub-detectors: D1 checks the npm registry metadata for the blocked maintainer `vpmdhaj` (CRITICAL stop condition), scans package names for the `vpmdhaj-*` prefix namespace, and runs levenshtein edit-distance matching against a blocklist of popular packages to detect typosquats (e.g., `env-config-manager` mimicking dotenv, `opensearch-setup` mimicking OpenSearch). D2 scans `package.json` for suspicious `preinstall` lifecycle hooks (unusual — most packages use postinstall), detects `setup.mjs`, `loader.js`, and `stager.js` entry points, and identifies Bun runtime abuse (`bun run stager.js`) as a stealthy loader technique. D3 scans all extracted files for cloud credential exfiltration patterns targeting AWS IMDSv2 (`169.254.169.254`), ECS task role tokens (`AWS_CONTAINER_AUTHORIZATION_TOKEN`, `AWS_CONTAINER_CREDENTIALS_FULL_URI`), HashiCorp Vault (`VAULT_ADDR`, `VAULT_TOKEN`), and GitHub Actions tokens (`GITHUB_TOKEN`, `GH_TOKEN`) combined with HTTP POST exfiltration or base64 obfuscation to attacker-controlled domains. All findings include provenance metadata with HMAC-SHA256 signed manifests.
361
- > **AXIOS_POISONING** campaign detection uses 3 sub-detectors: D1 checks the package name and version against a hardcoded blocklist of known-malicious axios versions (`1.14.1`, `0.30.4`) with a CRITICAL stop condition that halts all scanning on match. D2 scans axios `package.json` dependencies for injected decoy packages (`plain-crypto-js`), and applies a heuristic that flags crypto-related dependency additions in non-crypto packages as potentially malicious pre-staged decoys. D3 scans all files and lifecycle scripts for cross-platform RAT indicators: Windows (PowerShell `IEX`, `Invoke-Expression`, `cmd.exe`, binary `.exe` drops to temp dirs), macOS (launchd plist installation via `launchctl load/start`), Linux (systemd unit files, crontab persistence), process injection APIs (`CreateRemoteThread`, `VirtualAllocEx`, `LoadLibrary`/`dlopen`), and C2 network callbacks via HTTP/HTTPS/WebSocket to attacker-controlled servers. Only suspicious code patterns (curl, wget, fetch, exec, spawn, eval, etc.) in lifecycle hooks trigger D3 — legitimate build scripts are not flagged.
362
- > See [`docs/attack-taxonomy.md`](docs/attack-taxonomy.md) for full evasion surface documentation and PoC examples.
363
-
364
- ---
365
-
366
- ## 📊 Output & Reports
367
-
368
- ### Formats
369
-
370
- | Format | Availability | Description |
371
- |--------|-------------|-------------|
372
- | JSON | ✅ Free | Structured machine-readable findings |
373
- | HTML | ✅ Free | Rich HTML report with NIST compliance table, severity badges, control matrix |
374
- | Text | ✅ Free | Clean terminal-friendly text report |
375
- | CycloneDX SBOM | ✅ Free | Industry-standard SBOM with findings as vulnerabilities |
376
- | SPDX SBOM | ✅ Free | SPDX 2.3 document format |
377
- | NIST 800-161 | ✅ Free | Control traceability matrix (SR-2.1 → SR-11.4) |
378
- | EU CRA | ✅ Free | Cyber Resilience Act article mapping |
379
- | PDF | 🔐 Premium | Multi-page PDF with title page, findings table, NIST compliance matrix |
380
- | Splunk CEF | 🔐 Premium | Common Event Format for Splunk ingestion |
381
- | Elastic ECS | 🔐 Premium | Elastic Common Schema format |
382
- | Microsoft Sentinel | 🔐 Premium | Sentinel-ready formatted output |
383
- | IBM QRadar | 🔐 Premium | QRadar DSM-ready format with QID mappings |
384
-
385
- ### Sample output
386
-
387
- ```json
388
- {
389
- "scanId": 1,
390
- "findings": [
391
- {
392
- "id": "ATK-003",
393
- "severity": "high",
394
- "title": "Credential harvesting",
395
- "evidence": "process.env.NPM_TOKEN detected in postinstall.js:17"
396
- }
397
- ]
398
- }
399
- ```
400
-
401
- ---
402
-
403
- ## ⚙️ Configuration & Advanced Usage
404
-
405
- ### Policy-as-code
406
-
407
- Define allowlists, severity overrides, suppressions, and fail thresholds in a YAML file:
408
-
409
- ```yaml
410
- # .npm-scan.yml
411
- allowlist:
412
- - lodash
413
- - chalk
414
-
415
- severity_overrides:
416
- - id: ATK-001
417
- severity: medium
418
-
419
- suppress:
420
- - atk_id: ATK-009
421
- - package: some-package
422
-
423
- fail_on: high
424
- ```
425
-
426
- ```bash
427
- npm-scan scan target --policy .npm-scan.yml
428
- ```
429
-
430
- ### Environment variables
431
-
432
- | Variable | Description | Default |
433
- |----------|-------------|---------|
434
- | `NPM_SCAN_LICENSE_KEY` | Premium / enterprise license key | — |
435
- | `NPM_SCAN_DATA_DIR` | Scan history directory | `./.npm-scan` |
436
- | `NPM_SCAN_LOG_LEVEL` | Log verbosity | `info` |
437
- | `NPM_SCAN_LICENSE_SECRET` | HMAC key for license generation/validation | `npm-scan-default-dev-key` |
438
-
439
- ### IOC configuration
440
-
441
- Campaign detectors use seed IOC files for known-malicious fingerprints:
442
-
443
- | IOC File | Detector | Types |
444
- |----------|----------|-------|
445
- | `backend/detectors/mini-shai-hulud/iocs.json` | Mini Shai-Hulud (Waves 1–3) | `packageScope`, `publisherAccount`, `sha512`, `extensionId` |
446
- | `backend/detectors/trapdoor/iocs.json` | TrapDoor | `publisherAccount`, `campaignMarker`, `payloadFilename`, `payloadSize`, `xorKey`, `c2Domain`, `gistDomain` |
447
- | `backend/detectors/node-ipc-compromise/iocs.json` | node-ipc compromise | `publisherAccount`, `c2Domain`, `c2IP`, `exfilZone`, `payloadHash` |
448
- | `backend/detectors/msh-supplement/` | MSH Supplement | `obfuscationPattern`, `persistenceApi`, `targetedLocale`, `c2Keyword` |
449
- | `backend/detectors/typosquat-vpmdhaj/` | Typosquat vpmdhaj | `blockedMaintainer`, `typosquatTarget`, `loaderPattern`, `credTarget` |
450
- | `backend/detectors/axios-poisoning/` | Axios Poisoning | `blockedVersion`, `decoyDependency`, `ratPlatform`, `ratApi` |
451
- | `backend/vsix-scan/vsix-iocs.json` | VSIX extension scan | `extensionId`, `publisherAccount`, `orphanCommitHash` |
452
-
453
- IOC files follow a unified schema (`iocs: [{ type, value, ... }]`) and are loaded at module init. Update them from your threat intel feed to extend detection coverage without code changes.
454
-
455
- ### Premium licensing
456
-
457
- Contact leo@lateos.ai for a premium/enterprise license key.
458
-
459
- ```bash
460
- # Use it
461
- npm-scan scan target --license-key <key>
462
- npm-scan report --pdf --license-key <key>
463
- npm-scan report --siem cef --license-key <key>
464
- ```
465
-
466
- ---
467
-
468
- ## 🔗 Integrations
469
-
470
- ### GitHub Actions CI (for this repo)
471
-
472
- Every push and PR runs tests across Node 18, 20, and 22:
473
-
474
- ```yaml
475
- # .github/workflows/ci.yml
476
- name: CI
477
- on:
478
- push:
479
- branches: [ main ]
480
- pull_request:
481
- branches: [ main ]
482
- jobs:
483
- test:
484
- runs-on: ubuntu-latest
485
- strategy:
486
- matrix:
487
- node-version: [18, 20, 22]
488
- steps:
489
- - uses: actions/checkout@v4
490
- - uses: actions/setup-node@v4
491
- with:
492
- node-version: ${{ matrix.node-version }}
493
- cache: 'npm'
494
- - run: npm ci
495
- - run: npm test
496
- - run: npm run test:coverage
497
- - run: node --test test/detectors-corpus.test.js
498
- - run: npm run lint
499
- - run: npm run build
500
- ```
501
-
502
- ### GitHub Action (for downstream users)
503
-
504
- Scan your project's `package-lock.json` on every PR — detects typosquats, obfuscated payloads, credential harvesters, and worm propagation before they reach production. **SARIF output shows findings directly in GitHub's Security tab (Code Scanning).**
505
-
506
- ```yaml
507
- # .github/workflows/scan.yml
508
- name: npm-scan
509
- on:
510
- pull_request:
511
- paths:
512
- - 'package-lock.json'
513
- - '**/package.json'
514
- jobs:
515
- scan:
516
- runs-on: ubuntu-latest
517
- steps:
518
- - uses: actions/checkout@v4
519
- - uses: lateos/npm-scan@v1
520
- with:
521
- scan-type: lockfile
522
- sarif: results.sarif
523
- fail-on: high
524
- - name: Upload SARIF to Security tab
525
- uses: github/codeql-action/upload-sarif@v3
526
- with:
527
- sarif_file: results.sarif
528
- ```
529
-
530
- #### Action inputs
531
-
532
- | Input | Default | Description |
533
- |-------|---------|-------------|
534
- | `scan-type` | `lockfile` | `lockfile` to scan `package-lock.json` or `package` to scan a specific npm package |
535
- | `package` | — | Package name (required when `scan-type=package`) |
536
- | `fail-on` | `high` | Fail the workflow at this severity threshold: `none`, `low`, `medium`, `high`, `critical` |
537
- | `policy-file` | — | Path to a YAML/JSON policy file for allowlists, severity overrides, and suppressions |
538
- | `license-key` | — | Premium license key for SIEM export and PDF reports |
539
- | `siem-format` | — | SIEM output: `cef`, `ecs`, `sentinel`, `qradar` (premium) |
540
- | `sbom-format` | — | SBOM output: `json`, `xml`, `spdx` |
541
-
542
- #### Action outputs
543
-
544
- | Output | Description |
545
- |--------|-------------|
546
- | `findings-count` | Number of findings detected |
547
- | `scan-id` | Scan ID for later reference in reports |
548
-
549
- #### Example: scan a specific package with policy + SBOM
550
-
551
- ```yaml
552
- - uses: lateos/npm-scan@v1
553
- with:
554
- scan-type: package
555
- package: lodash
556
- policy-file: .npm-scan.yml
557
- sbom-format: spdx
558
- fail-on: critical
559
- ```
560
-
561
- #### Example: scan with SIEM export (premium)
562
-
563
- ```yaml
564
- - uses: lateos/npm-scan@v1
565
- with:
566
- scan-type: lockfile
567
- siem-format: cef
568
- license-key: ${{ secrets.NPM_SCAN_LICENSE_KEY }}
569
- ```
570
-
571
- ### CI/CD pipeline
572
-
573
- Integrate directly into your existing pipeline without the composite action:
574
-
575
- ```bash
576
- # Scan lockfile, fail build on high severity
577
- npm-scan scan-lockfile --policy .npm-scan.yml || exit 1
578
-
579
- # Scan a specific package, fail on critical only
580
- npm-scan scan lodash --policy .npm-scan.yml || exit 1
581
-
582
- # Generate SBOM as a build artifact
583
- npm-scan scan express --sbom spdx > express-sbom.spdx.json
584
-
585
- # Generate HTML compliance report in CI
586
- npm-scan report --html > report.html
587
-
588
- # Upload report as an artifact
589
- # uses: actions/upload-artifact@v4
590
- # with:
591
- # name: npm-scan-report
592
- # path: report.html
593
- ```
594
-
595
- ### Docker
596
-
597
- See the [Docker quick-start section](#-run-lateosnpm-scan-anywhere-with-docker--zero-installation) above for pull commands, Compose pipeline, and multi-arch images.
598
-
599
- Scan your project's `package-lock.json` on every PR — detects typosquats, obfuscated payloads, credential harvesters, and worm propagation before they reach production:
600
-
601
- ```yaml
602
- # .github/workflows/scan.yml
603
- name: npm-scan
604
- on:
605
- pull_request:
606
- paths:
607
- - 'package-lock.json'
608
- - '**/package.json'
609
- jobs:
610
- scan:
611
- runs-on: ubuntu-latest
612
- steps:
613
- - uses: actions/checkout@v4
614
- - uses: actions/setup-node@v4
615
- with:
616
- node-version: 20
617
- - name: Scan lockfile
618
- uses: lateos/npm-scan@v1
619
- with:
620
- scan-type: lockfile
621
- fail-on: high
622
- ```
623
-
624
- #### Action inputs
625
-
626
- | Input | Default | Description |
627
- |-------|---------|-------------|
628
- | `scan-type` | `lockfile` | `lockfile` to scan `package-lock.json` or `package` to scan a specific npm package |
629
- | `package` | | Package name (required when `scan-type=package`) |
630
- | `fail-on` | `high` | Fail the workflow at this severity threshold: `none`, `low`, `medium`, `high`, `critical` |
631
- | `policy-file` | — | Path to a YAML/JSON policy file for allowlists, severity overrides, and suppressions |
632
- | `license-key` | — | Premium license key for SIEM export and PDF reports |
633
- | `siem-format` | — | SIEM output: `cef`, `ecs`, `sentinel`, `qradar` (premium) |
634
- | `sbom-format` | — | SBOM output: `json`, `xml`, `spdx` |
635
-
636
- #### Action outputs
637
-
638
- | Output | Description |
639
- |--------|-------------|
640
- | `findings-count` | Number of findings detected |
641
- | `scan-id` | Scan ID for later reference in reports |
642
-
643
- #### Example: scan a specific package with policy + SBOM
644
-
645
- ```yaml
646
- - uses: lateos/npm-scan@v1
647
- with:
648
- scan-type: package
649
- package: lodash
650
- policy-file: .npm-scan.yml
651
- sbom-format: spdx
652
- fail-on: critical
653
- ```
654
-
655
- #### Example: scan with SIEM export (premium)
656
-
657
- ```yaml
658
- - uses: lateos/npm-scan@v1
659
- with:
660
- scan-type: lockfile
661
- siem-format: cef
662
- license-key: ${{ secrets.NPM_SCAN_LICENSE_KEY }}
663
- ```
664
-
665
- ### CI/CD pipeline
666
-
667
- Integrate directly into your existing pipeline without the composite action:
668
-
669
- ```bash
670
- # Scan lockfile, fail build on high severity
671
- npm-scan scan-lockfile --policy .npm-scan.yml || exit 1
672
-
673
- # Scan a specific package, fail on critical only
674
- npm-scan scan lodash --policy .npm-scan.yml || exit 1
675
-
676
- # Generate SBOM as a build artifact
677
- npm-scan scan express --sbom spdx > express-sbom.spdx.json
678
-
679
- # Generate HTML compliance report in CI
680
- npm-scan report --html > report.html
681
-
682
- # Upload report as an artifact
683
- # uses: actions/upload-artifact@v4
684
- # with:
685
- # name: npm-scan-report
686
- # path: report.html
687
- ```
688
-
689
- ### Pre-commit hook
690
-
691
- Block supply chain threats **before** they reach version control no CI required.
692
-
693
- ```bash
694
- # One-liner install (requires Node 18+, Git)
695
- npx husky@latest init && npm install && npx husky add .husky/pre-commit "npx lint-staged"
696
- ```
697
-
698
- **What it does:** On every `git commit`, lint-staged detects staged changes to `package.json` or `package-lock.json` and runs `npm-scan scan-lockfile --fail-on high`. Commits are blocked if threats are found.
699
-
700
- ```bash
701
- $ git commit -m "bump lodash"
702
- ✔ Preparing lint-staged configuration...
703
- ✔ Running tasks for staged package*.json files...
704
- ✔ npm-scan scan-lockfile --fail-on high
705
- 🔴 ATK-003: Credential exfiltration (DNS lookup to credentialharvest.example.com)
706
- 🔴 ATK-007: Typosquat detected (lodash@7.7.7)
707
- Exiting with code 1 — threat(s) found
708
-
709
- npm scan • @lateos/npm-scan v0.11.6
710
- error: Command failed with exit code 1.
711
- ```
712
-
713
- Add `--no-verify` to bypass for emergencies (`git commit -m "emergency fix" --no-verify`).
714
-
715
- ### Docker
716
-
717
- See the [Docker quick-start section](#-run-lateosnpm-scan-anywhere-with-docker--zero-installation) above for pull commands, Compose pipeline, and multi-arch images.
718
-
719
- ---
720
-
721
- ## 🗺️ Roadmap & Enterprise Features
722
-
723
- ### Free tier (shipped)
724
-
725
- - All 11 ATK detectors + **MEGALODON** CI/CD campaign detection (D1–D6) + **HF_IMPERSONATION** detector + **MINI_SHAI_HULUD** worm campaign (D1–D7, 3 waves, with **MSH_SUPPLEMENT** D1–D4 for obfuscation/persistence/geofence/C2) + **VSIX_SCAN** extension supply chain scan (6 detectors) + **CVE-2026-48710 (BadHost)** Python vulnerability detection (3 layers) + **TRAPDOOR** cross-ecosystem attack detection (9 rules) + **NODE_IPC_COMPROMISE** expired-domain hijack detection (11 rules) + **TYPOSQUAT_VPMDHAJ** mass typosquatting campaign (3 rules) + **AXIOS_POISONING** registry poisoning campaign (3 rules)
726
- - SBOM output (CycloneDX + SPDX)
727
- - HTML, text, and compliance reports (NIST + EU CRA)
728
- - Policy-as-code engine (YAML)
729
- - Local SQLite scan history
730
- - GitHub Action
731
- - Pre-commit hook (husky + lint-staged)
732
- - Docker images + Compose pipeline
733
- - Watch mode (--watch / --monorepo for auto-rescan)
734
- - VS Code extension scanning (--vsix flag with Marketplace + Open VSX registries)
735
-
736
- ### Premium (🔐 license key)
737
-
738
- - PDF compliance reports with NIST traceability matrix
739
- - SIEM export (Splunk CEF, Elastic ECS, Microsoft Sentinel, IBM QRadar)
740
- - Dynamic sandbox (gVisor-based ATK-008–010)
741
- - Reachability analysis (call graph filtering)
742
-
743
- ### Enterprise (🏢 custom license)
744
-
745
- - SAML 2.0 SSO (Okta, Azure AD, OneLogin, Keycloak)
746
- - REST API + webhooks (FastAPI)
747
- - Team RBAC + audit logs
748
- - Helm chart for Kubernetes deployment
749
- - PostgreSQL backend for hosted/team tier
750
- - SLA-backed priority support
751
-
752
- ---
753
-
754
- ## 🤝 Contributing
755
-
756
- We welcome contributions — especially new detectors, improved evasion resistance, and compliance templates.
757
-
758
- See [`docs/attack-taxonomy.md`](docs/attack-taxonomy.md) for the ATK governance process. Every new detector requires:
759
-
760
- 1. A proof-of-concept sample
761
- 2. A detection rule with tests
762
- 3. False-positive analysis on top-500 npm packages
763
- 4. NIST 800-161 control mapping
764
-
765
- ### Testing
766
-
767
- The project uses the **Node.js native test runner** (`node:test` + `assert/strict`).
768
-
769
- ```bash
770
- # Run all tests
771
- npm test
772
-
773
- # Run tests with coverage
774
- npm run test:coverage
775
-
776
- # Run tests with verbose spec output
777
- npm run test:verbose
778
-
779
- # Run local malicious/clean corpus (no network needed)
780
- node --test test/detectors-corpus.test.js
781
- ```
782
-
783
- **Test structure:**
784
- - `test/fixtures/mock-data.js` — shared mock scans, packages, and code snippets
785
- - `test/megalodon.test.js` — 30 Megalodon campaign detection tests (D1–D4 + aggregator + runAll integration)
786
- - `test/db.test.js` — database CRUD (save, query, persist)
787
- - `test/detectors-edge-cases.test.js` — per-detector boundary tests (no-ops, clean clears, severity)
788
- - `test/detectors-corpus.test.js` — 33 malicious + 50 clean tarball integration (offline)
789
- - `test/fetch.test.js` — tarball extraction, temp directory cleanup
790
- - `test/policy-edge-cases.test.js` — edge cases in suppress, override, load validation
791
- - `test/policy.test.js` — policy YAML/JSON load, apply, suppress, severity override tests
792
- - `test/report-snapshots.test.js` — HTML/text/CRA/PDF format assertions
793
- - `test/report.test.js` — SARIF, CSV, STIG, risk score format tests
794
- - `test/lockfile.test.js` — npm/yarn/pnpm parser, auto-detect, ATK-007/011 lockfile tests
795
- - `test/hf-impersonation.test.js` — 13 HF impersonation detection tests (no-ref, exact match, spoof, README clone, artifact mismatch, postinstall escalation, new-org tag)
796
- - `test/mini-shai-hulud.test.js` — 22 Mini Shai-Hulud worm campaign detection tests (burst, sibling, SLSA, maintainer, IOC, exfil, wave attribution)
797
- - `test/vsix-scan/burst-publish.test.js` — 4 VSIX burst publish tests (threshold, sub-threshold, hot-pull, Open VSX window)
798
- - `test/vsix-scan/publisher-anomaly.test.js` — 5 publisher anomaly tests (cross-namespace, new-account, add+publish, substitution, silent)
799
- - `test/vsix-scan/activation-event-risk.test.js` — 5 activation event risk tests (onStartupFinished, wildcard, escalation, first-time, silent)
800
- - `test/vsix-scan/orphan-commit-fetch.test.js` — 5 orphan commit tests (GitHub SHA, npx git, MCP exfil, Bun install, silent)
801
- - `test/vsix-scan/known-ioc.test.js` — 4 known IOC tests (extensionId, publisher window, outside window)
802
- - `test/vsix-scan/exfil-pattern.test.js` — 5 exfil pattern tests (creds, DNS tunnel, AES+RSA, anti-analysis, silent)
803
- - `test/vsix-scan/integration.test.js` — 4 integration tests (Nx Console CRITICAL, safe version clean, orphan commit, skipNetwork)
804
- - `test/cve-2026-48710-badhost/manifest.test.js` — 13 Python manifest parsing tests (requirements.txt, pyproject.toml, poetry.lock, version edge cases)
805
- - `test/cve-2026-48710-badhost/transitive.test.js` — 7 transitive dependency tests (Tier 1/2, fastapi version gating, pin suppression)
806
- - `test/cve-2026-48710-badhost/codePattern.test.js` — 6 static code pattern tests (auth context, INFO fallthrough, scope suppression)
807
- - `test/cve-2026-48710-badhost/integration.test.js` — 4 integration tests (end-to-end composite findings, clean project, no Python files)
808
- - `test/trapdoor.test.js` — 40 TrapDoor campaign detection tests (D1–D9: campaign marker, payload fingerprint, publisher blocklist, Gist exfil, AI poisoning, lure name, crypto primitives, XOR key, credential validation)
809
- - `test/node-ipc.test.js` — 37 node-ipc compromise detection tests (D1–D11: version blocklist, tarball hash, CJS injection, payload hash, DNS C2 pattern, bootstrap resolver, DNS TXT exfil, runtime trigger, temp artifact, unauthorized publisher, blast radius)
810
- - `test/msh-supplement.test.js` — 17 MSH supplement tests (ctf-scramble-v2 stop, daemonization, geo killswitch, C2 dead-drop, provenance, false positives)
811
- - `test/typosquat-vpmdhaj.test.js` — 16 typosquatting campaign tests (maintainer block, prefix detection, levenshtein, preinstall stagers, Bun loader, AWS/ECS/Vault/GitHub cred exfil)
812
- - `test/axios-poisoning.test.js` — 13 axios poisoning tests (version blocklist stop, decoy dependency, crypto heuristic, cross-platform RAT, C2 callback)
813
- - `test/cli.test.js` — commander integration tests (help, version, scan, report, error handling)
814
- - `test/cli-lockfile.test.js` — scan-lockfile CLI options, yarn/pnpm/monorepo/watch tests
815
-
816
- ### Need help?
817
-
818
- - 🔒 See [security policy](SECURITY.md) for vulnerability disclosure
819
- - 📖 Read the [project plan](docs/project-plan.md)
820
- - 🧬 Review the [attack taxonomy](docs/attack-taxonomy.md)
821
- - 🐛 Open an issue or PR
822
-
823
- ---
824
-
825
- ## 📄 License
826
-
827
- Apache-2.0 core + Commons Clause.
828
- See [`LICENSING.md`](LICENSING.md) for the exact boundary between free and premium features.
829
-
830
- ---
831
-
832
- ## 👤 About the Maintainer
833
-
834
- **Roongrunchai Chongolnee** — creator and maintainer of `@lateos/npm-scan`. Certified security professional (CISSP, CEH, Cisco Security, AWS Cloud Practitioner) with a decade of infrastructure and application security experience at Philips. I built this tool to give the open-source community a practical, detector-driven defense against supply-chain malware — and I'm committed to keeping it transparent, community-owned, and continuously improved.
835
-
836
- [![LinkedIn](https://img.shields.io/badge/LinkedIn-0A66C2?style=flat-square&logo=linkedin)](https://www.linkedin.com/in/roongrunchai-chong-c-ab9742108/)
837
- [![GitHub](https://img.shields.io/badge/GitHub-lateos--ai-181717?style=flat-square&logo=github)](https://github.com/lateos-ai/npm-scan)
838
-
839
- Issues, ideas, and pull requests are always welcome — security is strongest when we collaborate.
840
-
841
- ---
842
-
843
- ```
844
- @lateos/npm-scan — npm supply chain security scanner
845
- Copyright (C) 2026 Lateos
846
-
847
- Licensed under the Apache License, Version 2.0 (the "License");
848
- you may not use this file except in compliance with the License.
849
-
850
- Unless required by applicable law or agreed to in writing, software
851
- distributed under the License is distributed on an "AS IS" BASIS,
852
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
853
- ```
854
-
855
- ---
856
-
857
- **Scan your first package now:**
858
-
859
- ```bash
860
- npx @lateos/npm-scan scan lodash
861
- ```
862
- test
863
- test
864
-
1
+ # @lateos/npm-scan
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@lateos/npm-scan?style=flat-square)](https://www.npmjs.com/package/@lateos/npm-scan)
4
+ [![License](https://img.shields.io/badge/license-Apache%202.0%20%2B%20Commons%20Clause-blue?style=flat-square)](LICENSING.md)
5
+ [![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](package.json)
6
+ [![Tests](https://img.shields.io/badge/tests-696%20passing-brightgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
7
+ [![Coverage](https://img.shields.io/badge/coverage-90%25-brightgreen?style=flat-square)](https://github.com/lateos-ai/npm-scan)
8
+ [![Docker](https://img.shields.io/badge/docker-lateos%2Fnpm--scan-2496ED?style=flat-square&logo=docker)](https://hub.docker.com/r/lateos/npm-scan)
9
+ [![Sigstore](https://img.shields.io/static/v1?label=Sigstore&message=Provenance&color=green&style=flat-square&logo=sigstore)](https://github.com/lateos-ai/npm-scan/actions/workflows/publish.yml)
10
+
11
+ [![中文](https://img.shields.io/badge/lang-zh--CN-red?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.zh.md)
12
+ [![日本語](https://img.shields.io/badge/lang-ja-purple?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.ja.md)
13
+ [![Français](https://img.shields.io/badge/lang-fr-orange?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.fr.md)
14
+ [![Deutsch](https://img.shields.io/badge/lang-de-green?style=flat-square)](https://github.com/lateos-ai/npm-scan/blob/main/README.de.md)
15
+
16
+ **Modern supply chain security for the npm ecosystem.**
17
+ Static + behavioral analysis that catches what npm audit, Snyk, and Socket miss — obfuscated payloads, credential stealers, conditional triggers, sandbox evasion, and worm-like propagation.
18
+
19
+ ---
20
+
21
+ ## 📌 The Problem
22
+
23
+ The 2025–2026 wave of npm supply chain attacks proved that traditional tooling is no longer enough.
24
+
25
+ Attackers have moved past simple typosquatting. They now ship **obfuscated preinstall hooks**, **credential harvesters hidden behind environment detection**, **dormant backdoors with time-based activation**, and **worm-style transitive propagation** that spreads through peer dependencies.
26
+
27
+ A growing attack vector is **HuggingFace org impersonation** — packages that masquerade as legitimate HF model repositories (e.g., `0penai/gpt2` instead of `openai/gpt2`) to trick users into downloading malicious model artifacts during CI/CD pipelines, often bundled with suspicious binaries (`.exe`, `.dll`) in model repos that deep-learned tools trust by default.
28
+
29
+ The **Megalodon campaign** (2026) alone compromised 5,500+ repositories via fake GitHub PRs, malicious workflow injection, and cloud credential exfiltration — all coordinated through a single actor automating the entire kill chain. **@lateos/npm-scan** now detects artifacts of this campaign out of the box.
30
+
31
+ The **Mini Shai-Hulud worm campaign** (May 2026) hit the npm ecosystem in three waves — TanStack CI/CD hijack (84 artifacts in 6 minutes), AntV/atool maintainer compromise (600+ malicious versions across 300+ packages), and Nx Console VS Code extension poisoning (CVE-2026-48027) — all using ctf-scramble-v2 obfuscation, daemonized persistence with CI environment checks, geographic killswitches targeting sanctioned regions, and GitHub C2 dead-drop channels for token recovery. **@lateos/npm-scan** now detects all 10 Mini Shai-Hulud signals across two detector suites.
32
+
33
+ The **TrapDoor campaign** (May 2026) spans npm, PyPI, and Crates.io — 34 malicious packages, 384+ versions attributed to a single publisher, targeting crypto, DeFi, Solana, and AI developers with Fernet + ECDH encrypted payloads, AI context poisoning via zero-width Unicode injection in `.cursorrules`/`CLAUDE.md`, and credential live-validation against AWS STS and GitHub API before exfiltration. **@lateos/npm-scan** now detects all 9 TrapDoor signals.
34
+
35
+ The **node-ipc compromise** (May 14, 2026) weaponized an expired maintainer email domain to hijack one of npm's most depended-upon packages (822K weekly downloads). Three malicious versions (9.1.6, 9.2.3, 12.0.1) delivered an 80KB credential stealer via DNS TXT tunneling — no HTTP, no postinstall hook, invisible to HTTP-layer firewalls. **@lateos/npm-scan** now detects all 11 node-ipc compromise signals.
36
+
37
+ The **Mass Typosquatting campaign (vpmdhaj)** (May 2026) weaponized the `vpmdhaj` npm maintainer account to publish 14 typosquatted packages in a 4-hour window — targeting AWS/CI/CD environments with preinstall stagers (`setup.mjs`, `stager.js`), Bun runtime abuse, and cloud credential exfiltration (AWS IMDSv2, ECS task roles, Vault, GitHub tokens). **@lateos/npm-scan** now detects all 3 typosquatting campaign signals.
38
+
39
+ The **Axios Registry Poisoning campaign** (May 2026) compromised the npm registry's axios package metadata to publish `axios@1.14.1` and `axios@0.30.4` with injected dependencies (`plain-crypto-js`) containing cross-platform RAT payloads with C2 callbacks, process injection, and system persistence. **@lateos/npm-scan** now detects all 3 axios poisoning signals.
40
+
41
+ Critical infrastructure vulnerabilities in the Python ecosystem are also in scope. The **BadHost (CVE-2026-48710)** vulnerability in Starlette < 1.0.1 enables authentication bypass via unvalidated HTTP Host header injection, affecting FastAPI, vLLM, LiteLLM, MCP servers, and any project using Starlette transitively — now detected across Python manifests, transitive dependency chains, and source code patterns in a single scan.
42
+
43
+ **npm audit** checks known CVEs. **Snyk** scans for vulnerabilities. **Socket** looks at package behavior. None of them were designed for the generation of attacks that emerged in 2025 — attacks that look benign until they reach production.
44
+
45
+ **@lateos/npm-scan** was built for this moment.
46
+
47
+ ---
48
+
49
+ ## 🔬 Why @lateos/npm-scan?
50
+
51
+ | Capability | npm audit | Snyk | Socket | **@lateos/npm-scan** |
52
+ |---|---|---|---|---|
53
+ | Known CVE matching | ✅ | ✅ | ❌ | ✅ |
54
+ | Static analysis | ❌ | ✅ | ✅ | ✅ |
55
+ | Obfuscated payload detection | ❌ | ❌ | ❌ | ✅ |
56
+ | AST-level heuristic analysis | ❌ | ❌ | ❌ | ✅ |
57
+ | Runtime behavioral sandbox | ❌ | ❌ | ✅ | ✅ |
58
+ | Conditional trigger detection (ATK-009) | ❌ | ❌ | ❌ | ✅ |
59
+ | Sandbox evasion detection (ATK-010) | ❌ | ❌ | ❌ | ✅ |
60
+ | Transitive worm propagation (ATK-011) | ❌ | ❌ | ❌ | ✅ |
61
+ | Campaign detection (Megalodon CI/CD) | ❌ | ❌ | ❌ | ✅ |
62
+ | Worm campaign detection (Mini Shai-Hulud Wave 1–3) | ❌ | ❌ | ❌ | ✅ |
63
+ | HF model repo impersonation + README clone | ❌ | ❌ | ❌ | ✅ |
64
+ | VS Code extension supply chain scan (--vsix) | ❌ | ❌ | ❌ | ✅ |
65
+ | Python vulnerability detection (CVE-2026-48710 BadHost) | ❌ | ❌ | ❌ | ✅ |
66
+ | Cross-ecosystem attack detection (TrapDoor) | ❌ | ❌ | ❌ | ✅ |
67
+ | Expired-domain hijack detection (node-ipc) | ❌ | ❌ | ❌ | ✅ |
68
+ | Malware obfuscation detection (ctf-scramble-v2) | ❌ | ❌ | ❌ | ✅ |
69
+ | Mass typosquatting campaign (vpmdhaj maintainer) | ❌ | ❌ | ❌ | ✅ |
70
+ | Registry poisoning detection (axios fake versions) | ❌ | ❌ | ❌ | ✅ |
71
+ | Attack taxonomy (ATK series) | ❌ | ❌ | ❌ | ✅ |
72
+ | SBOM output (CycloneDX + SPDX) | ❌ | ✅ | ❌ | ✅ |
73
+ | SARIF v2.1 (GitHub Code Scanning) | ❌ | ❌ | ❌ | ✅ |
74
+ | NIST 800-161 compliance reporting | ❌ | ❌ | ❌ | ✅ |
75
+ | EU CRA compliance reporting | ❌ | ❌ | ❌ | ✅ |
76
+ | SIEM export (CEF / ECS / Sentinel / QRadar) | ❌ | ❌ | ❌ | ✅ |
77
+ | Runs entirely locally — no telemetry | ✅ | ❌ | ❌ | ✅ |
78
+ | Policy-as-code (YAML allowlists) | ❌ | ❌ | ❌ | ✅ |
79
+
80
+ > **Privacy first.** All scanning happens on your machine. No code leaves your environment. No telemetry. No cloud dependency.
81
+
82
+ ---
83
+
84
+ ## ✨ Key Features
85
+
86
+ | Icon | Feature | Description |
87
+ |------|---------|-------------|
88
+ | 🕵️ | **Heuristic static analysis** | AST-level inspection catches obfuscation, eval chains, env probing, and suspicious lifecycle scripts that regex-based tools miss |
89
+ | 🧠 | **Behavioral detection** | Identifies conditional triggers (time-based, CI-aware), sandbox evasion, and dormant activation patterns |
90
+ | 🧬 | **ATK attack taxonomy** | 11 classified attack types with NIST 800-161 mappings — versioned, documented, and PR-able |
91
+ | 🪱 | **Worm campaign detection** | Mini Shai-Hulud — 6 sub-checks detecting burst publish, sibling compromise, SLSA attestation mismatch, publisher drift, IOC match, and token exfil across 3 waves (TanStack, AntV/atool, Nx Console) |
92
+ | 🧩 | **VSIX extension scanning** | `npm-scan scan --vsix nrwl.angular-console` — detects VS Code Marketplace supply chain attacks: burst publish, publisher anomaly, activation event risk, orphan commit fetch, known IOC, and exfil patterns (Nx Console 18.95.0 CVE-2026-48027) |
93
+ | 🐍 | **Python vulnerability detection** | CVE-2026-48710 (BadHost) — Starlette Host header injection across 6 Python manifest formats, 15 transitive downstream packages (fastapi, vllm, litellm, MCP), and static `request.url.path` code pattern analysis with `scope["path"]` suppression |
94
+ | 🪤 | **Cross-ecosystem attack detection** | TrapDoor — 9 sub-checks: campaign marker P-2024-001, trap-core.js payload fingerprint, publisher blocklist asdxzxc, Gist-based credential exfil, AI config zero-width Unicode poisoning, crypto/DeFi lure name heuristic, Fernet+ECDH encryption, XOR key cargo-build-helper-2026, STS/GitHub API credential validation |
95
+ | 📡 | **Expired-domain hijack detection** | node-ipc compromise — version blocklist (9.1.6/9.2.3/12.0.1), tarball SHA-256 verification, CJS vs ESM size anomaly with IIFE injection, DNS-over-non-standard-port C2, bootstrap resolver sh.azurestaticprovider.net, DNS TXT exfil zone bt.node.js, setImmediate() runtime trigger, ~/nt-*/ staging artifacts, unauthorized publisher atiertant, lockfile blast-radius with safe pin recommendations |
96
+ | ☣️ | **Malware obfuscation detection** | ctf-scramble-v2 detection — scans package dist/lib for known malware obfuscation patterns (ctf-scramble-v2, ctf-scramble-v3), halts analysis immediately with max severity CRITICAL stop condition |
97
+ | 🎭 | **Mass typosquatting campaign detection** | vpmdhaj maintainer blocklist with stop condition, levenshtein-based typosquat detection (opensearch-setup, env-config-manager), preinstall stager identification (setup.mjs, stager.js, Bun runner), AWS ECS/Vault/GitHub credential exfiltration patterns |
98
+ | ☠️ | **Registry poisoning detection** | Axios version blocklist (1.14.1/0.30.4) with stop condition, decoy dependency discovery (plain-crypto-js), cross-platform RAT payload detection (DLL injection, launchd/systemd/cron persistence, PowerShell IEX, C2 callbacks) |
99
+ | 🔏 | **Provenance audit trail** | Aureus-Elicitor v1.7 framework — HMAC-SHA256 signed detection manifests, content-hash verified audit trails, rule provenance URLs, campaign source attribution. Every finding includes cryptographically verifiable provenance metadata |
100
+ | 📦 | **SBOM generation** | CycloneDX 1.5 and SPDX 2.3 with findings embedded as vulnerabilities |
101
+ | 🔍 | **SARIF output** | GitHub Advanced Security / CodeQL compatible SARIF v2.1 — shows findings directly in Security tab |
102
+ | 🧾 | **Compliance reporting** | NIST SP 800-161 traceability matrix + EU Cyber Resilience Act mapping (free tier) |
103
+ | 🔌 | **SIEM export** | Splunk CEF, Elastic ECS, Microsoft Sentinel, IBM QRadar formats (premium) |
104
+ | 📜 | **Policy-as-code** | YAML/JSON policy engine with allowlists, severity overrides, suppressions, and fail-on thresholds |
105
+ | 🐳 | **Docker + GitHub Action** | Multi-arch images, one-command Compose pipeline, PR scan action |
106
+ | 🛡️ | **Zero telemetry** | No data leaves your machine. No cloud. No callbacks. |
107
+ | 💾 | **Local scan history** | SQLite-backed persistence, zero external dependencies |
108
+ | 🪝 | **Pre-commit hook** | Block threats before commit — one-liner install, scans `package-lock.json` changes |
109
+ | 🤖 | **HF impersonation detection** | Detects typosquatted HuggingFace orgs (Jaro-Winkler), README clones (SimHash), artifact mismatches (`.exe` in model repos), and new-org amplifier — with lazy two-stage evaluation, zero network in Stage 1 |
110
+ | 📎 | **Yarn + pnpm support** | `scan-lockfile` parses `yarn.lock` and `pnpm-lock.yaml` alongside `package-lock.json` |
111
+
112
+ ---
113
+
114
+ ## ⚡ Quick Start
115
+
116
+ ```bash
117
+ # Install globally
118
+ npm install -g @lateos/npm-scan
119
+
120
+ # Scan a single package
121
+ npm-scan scan lodash
122
+
123
+ # Scan your lockfile
124
+ npm-scan scan-lockfile
125
+
126
+ # View latest scans
127
+ npm-scan report
128
+ ```
129
+
130
+ **No install? No problem:**
131
+
132
+ ```bash
133
+ npx @lateos/npm-scan scan commander
134
+ ```
135
+
136
+ ---
137
+
138
+ ## Validation & Accuracy
139
+
140
+ ### Real-World Campaign Detection
141
+
142
+ `@lateos/npm-scan` was validated against 3 active May 2026 supply chain attack campaigns:
143
+
144
+ | Campaign | Packages | Detection Rate | Key Detector |
145
+ |---|---|---|---|
146
+ | **Dependency Confusion** (176-pkg high-version hijack) | 3 | **100%** | D6 (Version Anomaly) |
147
+ | **Mini Shai-Hulud** (Obfuscation + C2) | 2 | **100%** | D7 (Obfuscation Heuristics) |
148
+ | **Bitwarden Impersonation** (Typosquat + lifecycle hooks) | 2 | **100%** | D1 (Typosquat) + D3 (Lifecycle Hook) |
149
+
150
+ ### False Positive Calibration
151
+
152
+ Detector thresholds calibrated against **top 1,000 npm packages by download count**:
153
+
154
+ - **Packages Scanned**: 990 legitimate packages
155
+ - **False Positive Rate**: **0.0%** (0 FPs at production thresholds)
156
+ - **Detector Performance**: See [VALIDATION.md](./VALIDATION.md) for precision/recall per detector
157
+
158
+ ### Per-Detector Confidence
159
+
160
+ | Detector | Avg Confidence | Threshold | Notes |
161
+ |---|---|---|---|
162
+ | D6 (Version Anomaly) | 92.0% | 72 | Z-score >3.0; sentinel patterns (99.99.99) always flag |
163
+ | D7 (Obfuscation Heuristics) | 80.0% | 75 | Entropy + pattern matching; bundlers whitelisted |
164
+ | D5 (Binary Embedding) | 81.3% | 80 | Cross-platform binary sets; rare in legitimate packages |
165
+ | D4 (Lifecycle Hook) | 92.5% | 65 | postinstall/preinstall/prepare scripts analyzed |
166
+ | D3 (Infostealer) | 68.7% | 72 | C2 signatures, credential exfil patterns |
167
+ | D1 (Typosquat) | 87.9% | 85 | Edit-distance scoring; scoped sub-packages exempt |
168
+
169
+ **Full validation report**: [VALIDATION.md](./VALIDATION.md)
170
+
171
+ ---
172
+
173
+ ## 🐳 Run @lateos/npm-scan anywhere with Docker — zero installation
174
+
175
+ ```bash
176
+ # Pull and run a single scan — no Node.js or npm required
177
+ docker run --rm lateos/npm-scan:cli scan lodash
178
+
179
+ # Full pipeline with persistent storage and Compose
180
+ docker compose --profile pipeline up -d
181
+ ```
182
+
183
+ No Node.js. No `npm install`. No global packages. Works on any system with Docker — CI servers, air-gapped environments, Kubernetes clusters. Multi-arch images for `linux/amd64` and `linux/arm64`.
184
+
185
+ ---
186
+
187
+ ## 🛡️ Government & SOC 2 Ready
188
+
189
+ | Feature | SOC 2 Controls | NIST 800-161 | STIG/FedRAMP Alignment |
190
+ |---------|-------|--------------|--------------|
191
+ | Audit logs (--audit-log) | CC6.8 | AU-2 | ✓ |
192
+ | FIPS crypto (--fips) | CC6.1 | SC-13 | ✓ |
193
+ | STIG report (--stig) | CC7.3 | RA-5 | ✓ |
194
+ | Offline cache (--cache-dir) | A1.2 | SC-8 | ✓ |
195
+ | Sigstore provenance | CC6.2 | SI-7 | ✓ |
196
+ | SBOM (SPDX/CycloneDX) | CC7.4 | SA-10 | ✓ |
197
+
198
+ ```bash
199
+ # Air-gapped scan with full compliance
200
+ npm-scan scan-lockfile --cache-dir /offline/cache --audit-log /var/log/npm-scan.audit --fips
201
+ npm-scan report --stig
202
+ ```
203
+
204
+ [![SOC 2 Ready](https://img.shields.io/badge/SOC%202-Ready-green?style=flat-square&logo=aicpa)](https://www.aicpa.org/interestareas/frc/assuranceadvisoryservices/sorhome.html#soc2)
205
+ [![FedRAMP Aligned](https://img.shields.io/badge/FedRAMP-Aligned-blue?style=flat-square&logo=fedramp)](https://fedramp.gov/baselines/)
206
+
207
+ ---
208
+
209
+ ## ☁️ BYOC — Bring Your Own Cloud
210
+
211
+ Deploy npm-scan in your VPC with full data sovereignty. No data leaves your infrastructure.
212
+
213
+ | Feature | Description |
214
+ |---------|-------------|
215
+ | **Self-hosted** | Run on EKS/GKE/AKS in your AWS/Azure/GCP account |
216
+ | **SIEM Export** | CEF/ECS/Sentinel/QRadar to your existing SIEM |
217
+ | **SSO/OIDC** | SAML/OIDC integration with your identity provider |
218
+ | **PDF Reports** | Generate NIST-compliant PDF reports locally |
219
+ | **External DB** | Connect to your existing PostgreSQL/Redis |
220
+
221
+ ```bash
222
+ # Deploy to your VPC with Helm
223
+ git clone https://github.com/lateos-ai/npm-scan.git
224
+ cd npm-scan/deploy/helm
225
+ helm install npm-scan -f values.byoc.yaml .
226
+
227
+ # BYOC values example (see values.byoc.yaml)
228
+ premium:
229
+ enabled: true
230
+ edition: enterprise
231
+ byoc:
232
+ enabled: true
233
+ cloudProvider: aws
234
+ vpcId: vpc-xxx
235
+ region: us-east-1
236
+ ```
237
+
238
+ **Pricing**: Enterprise license $10k/yr — self-supported (docs + GitHub issues).
239
+
240
+ ---
241
+
242
+ ## 📖 Usage Examples
243
+
244
+ ### Scan a single package
245
+
246
+ ```bash
247
+ # Default JSON output with all findings
248
+ npm-scan scan axios
249
+
250
+ # Generate an SBOM alongside the scan
251
+ npm-scan scan express --sbom # CycloneDX JSON
252
+ npm-scan scan express --sbom xml # CycloneDX XML
253
+ npm-scan scan express --sbom spdx # SPDX 2.3
254
+
255
+ # Apply a YAML policy
256
+ npm-scan scan some-package --policy .npm-scan.yml
257
+
258
+ # Scan a local tarball (no registry fetch needed)
259
+ npm-scan scan --file path/to/malicious-package.tgz
260
+
261
+ # Scan a VS Code extension for Marketplace supply chain attacks
262
+ npm-scan scan --vsix nrwl.angular-console
263
+
264
+ # Scan a package AND a VSIX extension together (findings merge)
265
+ npm-scan scan lodash --vsix nrwl.angular-console
266
+ ```
267
+
268
+ ### Scan a lockfile
269
+
270
+ ```bash
271
+ # Scan a single package
272
+ npm-scan scan lodash
273
+
274
+ # Scan your lockfile
275
+ npm-scan scan-lockfile
276
+
277
+ # Scan a VS Code extension for supply chain threats
278
+ npm-scan scan --vsix nrwl.angular-console
279
+
280
+ # View latest scans
281
+ npm-scan report
282
+ ```
283
+
284
+ ### Generate reports
285
+
286
+ ```bash
287
+ # List all recent scans
288
+ npm-scan report
289
+
290
+ # View a specific scan
291
+ npm-scan report -i 42
292
+
293
+ # Generate an HTML report (free) with full findings + NIST table
294
+ npm-scan report -i 42 --html
295
+
296
+ # Print NIST 800-161 compliance table
297
+ npm-scan report -i 42 --nist
298
+
299
+ # Print EU CRA compliance table
300
+ npm-scan report --cra
301
+
302
+ # CSV export for Excel / Sheets (audit-ready)
303
+ npm-scan report --csv risks.csv
304
+ npm-scan scan lodash --csv # CSV to stdout
305
+
306
+ # Text report (free)
307
+ npm-scan report --text
308
+
309
+ # PDF report (premium)
310
+ npm-scan report --pdf --license-key <key>
311
+
312
+ # SIEM export (premium)
313
+ npm-scan report --siem cef # Splunk CEF
314
+ npm-scan report --siem ecs # Elastic ECS
315
+ npm-scan report --siem sentinel # Microsoft Sentinel
316
+ npm-scan report --siem qradar # IBM QRadar
317
+
318
+ # Combine all scans into a single report
319
+ npm-scan report --html # all scans
320
+ npm-scan report --pdf # all scans (premium)
321
+ ```
322
+
323
+ ---
324
+
325
+ ## 🧬 Detection Capabilities (ATK Taxonomy)
326
+
327
+ | ID | Attack Class | Detection Method | Severity | NIST 800-161 |
328
+ |---|---|---|---|---|
329
+ | **ATK-001** | Malicious lifecycle scripts (`preinstall`, `postinstall`, `install`) | Static | 🔴 high | SR-3.1 |
330
+ | **ATK-002** | Obfuscated payload delivery (hex, base64, eval chains) | Static | 🟠 medium | SR-4.2 |
331
+ | **ATK-003** | Credential harvesting (env vars, .npmrc, SSH keys) | Static + Dynamic | 🔴 high | SR-5.3 |
332
+ | **ATK-004** | Persistence via editor/config dirs (.vscode, .claude, .cursor) | Static | 🔴 high | SR-6.4 |
333
+ | **ATK-005** | Network exfiltration (GitHub API, DNS tunneling, HTTP C2) | Static + Dynamic | ⚫ critical | SR-7.5 |
334
+ | **ATK-006** | Dependency confusion / namespace squatting | Static (lockfile) | 🟠 medium | SR-2.2 |
335
+ | **ATK-007** | Typosquatting (edit-distance matching) | Static | 🟢 low | SR-2.1 |
336
+ | **ATK-008** | Tarball tampering (published ≠ source) | Static | 🔴 high | SR-8.1 |
337
+ | **ATK-009** | Conditional/dormant triggers (CI detection, time-based) | Behavioral | 🔴 high | SR-9.2 |
338
+ | **ATK-010** | Sandbox evasion / anti-analysis | Behavioral | 🟠 medium | SR-10.3 |
339
+ | **ATK-011** | Transitive propagation (worm-style lateral spread) | Behavioral | 🔴 high | SR-11.4 |
340
+ | **MEGALODON** | Megalodon CI/CD campaign — workflow C2 exfil, credential harvest, publish velocity spike, publisher drift | Static + Registry | ⚫ critical | SR-3.1, SR-7.5 |
341
+ | **HF_IMPERSONATION** | HuggingFace org spoof detection — Jaro-Winkler similarity against 15 known-good orgs, SimHash README clone detection, artifact mismatch (`.exe`/`.dll` in model repos), postinstall escalation, new-org amplifier | Static + Network (Stage 2) | 🔴 high / ⚫ critical | SR-2.1 |
342
+ | **MINI_SHAI_HULUD** | Mini Shai-Hulud worm campaign — burst publish velocity (≥3 versions/30 min), co-temporal sibling compromise, SLSA attestation mismatch (sub-60s gap, first-ever, builder mismatch), publisher drift (<10 min account change), IOC match (scope/sha512/publisher from seed file), token exfil (NPM_TOKEN/.npmrc/atob patterns), Nx Console downstream detection | Static + Registry | 🔴 high / ⚫ critical | SR-3.1, SR-7.5 |
343
+ | **VSIX_SCAN** | VS Code extension supply chain scan — burst publish (≥2 versions/30 min, hot-pull <20 min), publisher anomaly (account substitution, new-account on high-install ext, 15-min add+publish), activation event risk (onStartupFinished→HIGH, *→CRITICAL, escalation on shell keywords), orphan commit fetch (GitHub API SHA refs, npx git URL, MCP-disguised exfil, Bun install), known IOC (extensionId/publisherAccount/commit hash from seed), exfil patterns (cred paths, DNS tunneling, AES+RSA, anti-analysis, Bun APIs) | Static + Registry | 🟠 medium / 🔴 high / ⚫ critical | SR-3.1, SR-5.3 |
344
+ | **CVE-2026-48710** | BadHost — Starlette authentication bypass via Host header injection (CVE-2026-48710, CVSS 7.0). Python dependency version detection (requirements.txt, pyproject.toml, poetry.lock, Pipfile, setup.py/cfg), transitive heuristic (15 known downstream packages: fastapi, vllm, litellm, MCP servers, etc.), static code pattern scan for dangerous `request.url.path` usage in auth/middleware context with `request.scope["path"]` suppression | Static + Registry | 🔴 high / 🟠 medium / ℹ️ info | SR-3.1, SR-5.3 |
345
+ | **TRAPDOOR** | TrapDoor cross-ecosystem attack — campaign marker P-2024-001 in files, shared payload trap-core.js filename/48,485-byte fingerprint, publisher blocklist asdxzxc, Gist-based credential exfil (ddjidd564.github.io + credential paths), AI context poisoning via zero-width Unicode in .cursorrules/CLAUDE.md, crypto/DeFi lure name heuristic (<30 days), Fernet+ECDH crypto primitives in postinstall, XOR key cargo-build-helper-2026 in lockfiles, STS/GitHub API credential validation in postinstall | Static + Registry | 🟠 medium / 🔴 high / ⚫ critical | SR-3.1, SR-5.3, SR-7.5 |
346
+ | **NODE_IPC_COMPROMISE** | node-ipc supply chain compromise (May 14, 2026) — version blocklist (9.1.6/9.2.3/12.0.1) with safe pins, tarball SHA-256 verification, CJS payload IIFE injection detection (CJS>ESM size differential), injected payload hash match, DNS-over-non-standard-port C2 (setServers + custom resolver), bootstrap resolver sh.azurestaticprovider.net + C2 IP 37.16.75.69, DNS TXT exfiltration zone bt.node.js, setImmediate() runtime trigger, ~/nt-*/ staging artifact detection, unauthorized publisher atiertant, lockfile blast-radius detection with pin recommendations | Static + Registry | ⚫ critical | SR-3.1, SR-5.3, SR-7.5 |
347
+ | **MSH_SUPPLEMENT** | Mini Shai-Hulud supplement — ctf-scramble-v2 obfuscation (HALT on match), daemonization persistence (spawn detached, systemd, cron, launchd, Task Scheduler), geographic killswitch detection (ru_RU/be_BY locale checks via process.env.LANG/LC_ALL/Intl.DateTimeFormat), C2 dead-drop indicators (OhNoWhatsGoingOnWithGitHub keyword, GitHub commit scraping + token exfiltration co-occurrence) | Static + Behavioral | ⚫ critical | SR-3.1, SR-7.5, SR-9.2 |
348
+ | **TYPOSQUAT_VPMDHAJ** | Mass Typosquatting campaign (vpmdhaj) — maintainer blocklist (HALT on match), vpmdhaj-* namespace prefix detection, levenshtein typosquat matching vs popular packages, preinstall stager identification (setup.mjs/stager.js/bun run with generation tracking), cloud credential exfiltration (AWS IMDSv2, ECS task role tokens, Vault credentials, GITHUB_TOKEN/GH_TOKEN with HTTP exfil) | Static + Registry | ⚫ critical | SR-2.1, SR-3.1, SR-5.3 |
349
+ | **AXIOS_POISONING** | Axios Registry Poisoning — version blocklist (1.14.1/0.30.4, HALT on match), decoy dependency injection (plain-crypto-js), crypto-dependency heuristic in non-crypto packages, cross-platform RAT payload detection (PowerShell IEX, launchd, systemd, cron, DLL/LoadLibrary, CreateRemoteThread, binary drops to temp dirs, C2 callbacks) | Static + Behavioral | ⚫ critical | SR-3.1, SR-5.3, SR-7.5 |
350
+
351
+ > **How evasive attacks are caught:** ATK-009 detects packages that check `process.env.CI`, probe hostnames, or use time-based activation. ATK-010 flags `debugger` statements, `os.hostname()` probes, and env fingerprinting. ATK-011 traces peer dependency graphs to detect worm-like propagation patterns.
352
+ > **MEGALODON** campaign detection analyzes bundled `.github/workflows/` files for C2 co-occurrence and base64 decode chains, scans tarball files for credential + outbound network patterns, detects version publish velocity spikes via npm registry metadata, and identifies publisher account drift — all without any network calls beyond the initial package fetch.
353
+ > **HF_IMPERSONATION** detection uses a lazy two-stage evaluation: Stage 1 scans `package.json` scripts and JS/TS sources for HuggingFace references (URLs, `from_pretrained()`, `hub.download()`) and runs Jaro-Winkler similarity against 15 known-good HF orgs — zero network. If spoofs are found, Stage 2 fetches the HF model API, computes SimHash of both READMEs for clone detection, validates artifact type consistency (e.g., `transformers` library with `.exe` files is flagged as critical), applies a new-org amplifier (<30 days), and escalates when the reference appears in a lifecycle script.
354
+ > **MINI_SHAI_HULUD** worm campaign detection uses a lazy two-stage evaluation: Stage 1 runs burst velocity, publisher drift, IOC, and token exfil checks (in-memory, no network). If burst triggers, Stage 2 queries npm attestation endpoints for SLSA anomalies and fetches sibling package registry metadata for co-temporal burst detection. Composite finding includes wave attribution (wave1-tanstack, wave2-antv, wave3-nx-console) and critical severity when SLSA or IOC match. NX_CONSOLE_DOWNSTREAM (D7) flags npm packages with `@nx/*` dependencies and checks for `nrwl.angular-console` in `.vscode/extensions.json`.
355
+ > **VSIX_SCAN** extension scanning wraps both VS Code Marketplace and Open VSX registries with rate-limited (10 req/min), cached (5 min TTL) API clients. All 6 detectors run asynchronously and aggregate into a single composite `VSIX_SCAN` finding. Zero extension code is executed — all analysis is static regex/text-pattern matching. No Bun installation required for Bun pattern detection.
356
+ > **CVE-2026-48710 (BadHost)** detection uses three independent layers: Layer 1 parses 6 Python manifest formats (requirements.txt, pyproject.toml, poetry.lock, Pipfile, setup.py, setup.cfg) with PEP 440 semver-aware version comparison. Layer 2 scans for 15 known Starlette-downstream packages with Tier 1 (HIGH confidence) and Tier 2 (MEDIUM confidence) transitive heuristics, suppressed by explicit `starlette >= 1.0.1` pin. Layer 3 performs function-boundary static analysis on `.py` files for `request.url.path` usage, escalating to MEDIUM severity in auth/middleware contexts and suppressing when `request.scope["path"]` is used in the same function.
357
+ > **TRAPDOOR** campaign detection runs 9 sub-detectors across all package files (README.md, package.json, .md, shell scripts, .cursorrules, CLAUDE.md) for the hardcoded marker P-2024-001, scans for trap-core.js by filename or exact 48,485-byte size, unconditionally blocks publisher asdxzxc, detects outbound references to ddjidd564.github.io or gist.github.com combined with credential-path patterns, scans .cursorrules and CLAUDE.md for zero-width Unicode characters (U+200B, U+200C, U+200D, U+FEFF), flags crypto/DeFi-themed packages <30 days old with no prior version history, detects simultaneous Fernet and ECDH/createECDH usage in postinstall JS, and identifies sts.amazonaws.com and api.github.com/user calls in postinstall hooks.
358
+ > **NODE_IPC_COMPROMISE** detection intercepts the expired-domain takeover attack across 11 dimensions: version blocklist with safe-pin recommendations (9.1.5 for 9.x, 12.0.0 for 12.x), tarball SHA-256 hash verification against known malicious hashes, CJS vs ESM size comparison with IIFE suffix pattern detection for injected payload identification, DNS resolver pattern analysis (dns.promises.Resolver + setServers with non-public IP + resolveTxt), bootstrap domain and C2 IP detection, resolveTxt() with bt.node.js zone references, setImmediate() runtime trigger detection, ~/nt-*/ staging artifact path identification, publisher account verification against unauthorized account atiertant, and lockfile scanning for compromised version resolution with safe-pin suggestions.
359
+ > **MSH_SUPPLEMENT** detection augments the existing Mini Shai-Hulud worm campaign with 4 additional sub-detectors: D1 scans all extracted JS files for `require('ctf-scramble-v2')` or `import 'ctf-scramble-v2'` patterns (including variant ctf-scramble-v3+) and returns a CRITICAL stop condition that halts all further scanning. D2 scans lifecycle scripts (preinstall/install/postinstall) for daemonization APIs (`daemon()`, `fork()`, `spawn({detached: true})`), persistence mechanisms (systemd unit files, crontab, launchd plists, Windows Task Scheduler), and CI environment guards (`!process.env.CI`, `process.env.CI === undefined`). D3 scans code for geographic locale checks targeting sanctioned regions (`ru_RU`, `be_BY`) via `process.env.LANG`, `process.env.LC_ALL`, and `Intl.DateTimeFormat().resolvedOptions().timeZone` with `process.exit(0)` silent termination. D4 detects C2 dead-drop indicators including the hardcoded `OhNoWhatsGoingOnWithGitHub` keyword and co-occurring GitHub token access + GitHub API commit scraping patterns. All sub-detectors produce provenance-attached findings with HMAC-SHA256 signed audit trails.
360
+ > **TYPOSQUAT_VPMDHAJ** campaign detection uses 3 sub-detectors: D1 checks the npm registry metadata for the blocked maintainer `vpmdhaj` (CRITICAL stop condition), scans package names for the `vpmdhaj-*` prefix namespace, and runs levenshtein edit-distance matching against a blocklist of popular packages to detect typosquats (e.g., `env-config-manager` mimicking dotenv, `opensearch-setup` mimicking OpenSearch). D2 scans `package.json` for suspicious `preinstall` lifecycle hooks (unusual — most packages use postinstall), detects `setup.mjs`, `loader.js`, and `stager.js` entry points, and identifies Bun runtime abuse (`bun run stager.js`) as a stealthy loader technique. D3 scans all extracted files for cloud credential exfiltration patterns targeting AWS IMDSv2 (`169.254.169.254`), ECS task role tokens (`AWS_CONTAINER_AUTHORIZATION_TOKEN`, `AWS_CONTAINER_CREDENTIALS_FULL_URI`), HashiCorp Vault (`VAULT_ADDR`, `VAULT_TOKEN`), and GitHub Actions tokens (`GITHUB_TOKEN`, `GH_TOKEN`) combined with HTTP POST exfiltration or base64 obfuscation to attacker-controlled domains. All findings include provenance metadata with HMAC-SHA256 signed manifests.
361
+ > **AXIOS_POISONING** campaign detection uses 3 sub-detectors: D1 checks the package name and version against a hardcoded blocklist of known-malicious axios versions (`1.14.1`, `0.30.4`) with a CRITICAL stop condition that halts all scanning on match. D2 scans axios `package.json` dependencies for injected decoy packages (`plain-crypto-js`), and applies a heuristic that flags crypto-related dependency additions in non-crypto packages as potentially malicious pre-staged decoys. D3 scans all files and lifecycle scripts for cross-platform RAT indicators: Windows (PowerShell `IEX`, `Invoke-Expression`, `cmd.exe`, binary `.exe` drops to temp dirs), macOS (launchd plist installation via `launchctl load/start`), Linux (systemd unit files, crontab persistence), process injection APIs (`CreateRemoteThread`, `VirtualAllocEx`, `LoadLibrary`/`dlopen`), and C2 network callbacks via HTTP/HTTPS/WebSocket to attacker-controlled servers. Only suspicious code patterns (curl, wget, fetch, exec, spawn, eval, etc.) in lifecycle hooks trigger D3 — legitimate build scripts are not flagged.
362
+ > See [`docs/attack-taxonomy.md`](docs/attack-taxonomy.md) for full evasion surface documentation and PoC examples.
363
+
364
+ ---
365
+
366
+ ## 📊 Output & Reports
367
+
368
+ ### Formats
369
+
370
+ | Format | Availability | Description |
371
+ |--------|-------------|-------------|
372
+ | JSON | ✅ Free | Structured machine-readable findings |
373
+ | HTML | ✅ Free | Rich HTML report with NIST compliance table, severity badges, control matrix |
374
+ | Text | ✅ Free | Clean terminal-friendly text report |
375
+ | CycloneDX SBOM | ✅ Free | Industry-standard SBOM with findings as vulnerabilities |
376
+ | SPDX SBOM | ✅ Free | SPDX 2.3 document format |
377
+ | NIST 800-161 | ✅ Free | Control traceability matrix (SR-2.1 → SR-11.4) |
378
+ | EU CRA | ✅ Free | Cyber Resilience Act article mapping |
379
+ | PDF | 🔐 Premium | Multi-page PDF with title page, findings table, NIST compliance matrix |
380
+ | Splunk CEF | 🔐 Premium | Common Event Format for Splunk ingestion |
381
+ | Elastic ECS | 🔐 Premium | Elastic Common Schema format |
382
+ | Microsoft Sentinel | 🔐 Premium | Sentinel-ready formatted output |
383
+ | IBM QRadar | 🔐 Premium | QRadar DSM-ready format with QID mappings |
384
+
385
+ ### Sample output
386
+
387
+ ```json
388
+ {
389
+ "scanId": 1,
390
+ "findings": [
391
+ {
392
+ "id": "ATK-003",
393
+ "severity": "high",
394
+ "title": "Credential harvesting",
395
+ "evidence": "process.env.NPM_TOKEN detected in postinstall.js:17"
396
+ }
397
+ ]
398
+ }
399
+ ```
400
+
401
+ ---
402
+
403
+ ## ⚙️ Configuration & Advanced Usage
404
+
405
+ ### Policy-as-code
406
+
407
+ Define allowlists, severity overrides, suppressions, and fail thresholds in a YAML file:
408
+
409
+ ```yaml
410
+ # .npm-scan.yml
411
+ allowlist:
412
+ - lodash
413
+ - chalk
414
+
415
+ severity_overrides:
416
+ - id: ATK-001
417
+ severity: medium
418
+
419
+ suppress:
420
+ - atk_id: ATK-009
421
+ - package: some-package
422
+
423
+ fail_on: high
424
+ ```
425
+
426
+ ```bash
427
+ npm-scan scan target --policy .npm-scan.yml
428
+ ```
429
+
430
+ ### Environment variables
431
+
432
+ | Variable | Description | Default |
433
+ |----------|-------------|---------|
434
+ | `NPM_SCAN_LICENSE_KEY` | Premium / enterprise license key | — |
435
+ | `NPM_SCAN_DATA_DIR` | Scan history directory | `./.npm-scan` |
436
+ | `NPM_SCAN_LOG_LEVEL` | Log verbosity | `info` |
437
+ | `NPM_SCAN_LICENSE_SECRET` | HMAC key for license generation/validation | `npm-scan-default-dev-key` |
438
+
439
+ ### IOC configuration
440
+
441
+ Campaign detectors use seed IOC files for known-malicious fingerprints:
442
+
443
+ | IOC File | Detector | Types |
444
+ |----------|----------|-------|
445
+ | `backend/detectors/mini-shai-hulud/iocs.json` | Mini Shai-Hulud (Waves 1–3) | `packageScope`, `publisherAccount`, `sha512`, `extensionId` |
446
+ | `backend/detectors/trapdoor/iocs.json` | TrapDoor | `publisherAccount`, `campaignMarker`, `payloadFilename`, `payloadSize`, `xorKey`, `c2Domain`, `gistDomain` |
447
+ | `backend/detectors/node-ipc-compromise/iocs.json` | node-ipc compromise | `publisherAccount`, `c2Domain`, `c2IP`, `exfilZone`, `payloadHash` |
448
+ | `backend/detectors/msh-supplement/` | MSH Supplement | `obfuscationPattern`, `persistenceApi`, `targetedLocale`, `c2Keyword` |
449
+ | `backend/detectors/typosquat-vpmdhaj/` | Typosquat vpmdhaj | `blockedMaintainer`, `typosquatTarget`, `loaderPattern`, `credTarget` |
450
+ | `backend/detectors/axios-poisoning/` | Axios Poisoning | `blockedVersion`, `decoyDependency`, `ratPlatform`, `ratApi` |
451
+ | `backend/vsix-scan/vsix-iocs.json` | VSIX extension scan | `extensionId`, `publisherAccount`, `orphanCommitHash` |
452
+
453
+ IOC files follow a unified schema (`iocs: [{ type, value, ... }]`) and are loaded at module init. Update them from your threat intel feed to extend detection coverage without code changes.
454
+
455
+ ### Premium licensing
456
+
457
+ Contact leo@lateos.ai for a premium/enterprise license key.
458
+
459
+ ```bash
460
+ # Use it
461
+ npm-scan scan target --license-key <key>
462
+ npm-scan report --pdf --license-key <key>
463
+ npm-scan report --siem cef --license-key <key>
464
+ ```
465
+
466
+ ---
467
+
468
+ ## 🔗 Integrations
469
+
470
+ ### GitHub Actions CI (for this repo)
471
+
472
+ Every push and PR runs tests across Node 18, 20, and 22:
473
+
474
+ ```yaml
475
+ # .github/workflows/ci.yml
476
+ name: CI
477
+ on:
478
+ push:
479
+ branches: [ main ]
480
+ pull_request:
481
+ branches: [ main ]
482
+ jobs:
483
+ test:
484
+ runs-on: ubuntu-latest
485
+ strategy:
486
+ matrix:
487
+ node-version: [18, 20, 22]
488
+ steps:
489
+ - uses: actions/checkout@v4
490
+ - uses: actions/setup-node@v4
491
+ with:
492
+ node-version: ${{ matrix.node-version }}
493
+ cache: 'npm'
494
+ - run: npm ci
495
+ - run: npm test
496
+ - run: npm run test:coverage
497
+ - run: node --test test/detectors-corpus.test.js
498
+ - run: npm run lint
499
+ - run: npm run build
500
+ ```
501
+
502
+ ### GitHub Action (for downstream users)
503
+
504
+ Scan your project's `package-lock.json` on every PR — detects typosquats, obfuscated payloads, credential harvesters, and worm propagation before they reach production. **SARIF output shows findings directly in GitHub's Security tab (Code Scanning).**
505
+
506
+ ```yaml
507
+ # .github/workflows/scan.yml
508
+ name: npm-scan
509
+ on:
510
+ pull_request:
511
+ paths:
512
+ - 'package-lock.json'
513
+ - '**/package.json'
514
+ jobs:
515
+ scan:
516
+ runs-on: ubuntu-latest
517
+ steps:
518
+ - uses: actions/checkout@v4
519
+ - uses: lateos/npm-scan@v1
520
+ with:
521
+ scan-type: lockfile
522
+ sarif: results.sarif
523
+ fail-on: high
524
+ - name: Upload SARIF to Security tab
525
+ uses: github/codeql-action/upload-sarif@v3
526
+ with:
527
+ sarif_file: results.sarif
528
+ ```
529
+
530
+ #### Action inputs
531
+
532
+ | Input | Default | Description |
533
+ |-------|---------|-------------|
534
+ | `scan-type` | `lockfile` | `lockfile` to scan `package-lock.json` or `package` to scan a specific npm package |
535
+ | `package` | — | Package name (required when `scan-type=package`) |
536
+ | `fail-on` | `high` | Fail the workflow at this severity threshold: `none`, `low`, `medium`, `high`, `critical` |
537
+ | `policy-file` | — | Path to a YAML/JSON policy file for allowlists, severity overrides, and suppressions |
538
+ | `license-key` | — | Premium license key for SIEM export and PDF reports |
539
+ | `siem-format` | — | SIEM output: `cef`, `ecs`, `sentinel`, `qradar` (premium) |
540
+ | `sbom-format` | — | SBOM output: `json`, `xml`, `spdx` |
541
+
542
+ #### Action outputs
543
+
544
+ | Output | Description |
545
+ |--------|-------------|
546
+ | `findings-count` | Number of findings detected |
547
+ | `scan-id` | Scan ID for later reference in reports |
548
+
549
+ #### Example: scan a specific package with policy + SBOM
550
+
551
+ ```yaml
552
+ - uses: lateos/npm-scan@v1
553
+ with:
554
+ scan-type: package
555
+ package: lodash
556
+ policy-file: .npm-scan.yml
557
+ sbom-format: spdx
558
+ fail-on: critical
559
+ ```
560
+
561
+ #### Example: scan with SIEM export (premium)
562
+
563
+ ```yaml
564
+ - uses: lateos/npm-scan@v1
565
+ with:
566
+ scan-type: lockfile
567
+ siem-format: cef
568
+ license-key: ${{ secrets.NPM_SCAN_LICENSE_KEY }}
569
+ ```
570
+
571
+ ### CI/CD pipeline
572
+
573
+ Integrate directly into your existing pipeline without the composite action:
574
+
575
+ ```bash
576
+ # Scan lockfile, fail build on high severity
577
+ npm-scan scan-lockfile --policy .npm-scan.yml || exit 1
578
+
579
+ # Scan a specific package, fail on critical only
580
+ npm-scan scan lodash --policy .npm-scan.yml || exit 1
581
+
582
+ # Generate SBOM as a build artifact
583
+ npm-scan scan express --sbom spdx > express-sbom.spdx.json
584
+
585
+ # Generate HTML compliance report in CI
586
+ npm-scan report --html > report.html
587
+
588
+ # Upload report as an artifact
589
+ # uses: actions/upload-artifact@v4
590
+ # with:
591
+ # name: npm-scan-report
592
+ # path: report.html
593
+ ```
594
+
595
+ ### Docker
596
+
597
+ See the [Docker quick-start section](#-run-lateosnpm-scan-anywhere-with-docker--zero-installation) above for pull commands, Compose pipeline, and multi-arch images.
598
+
599
+ ---
600
+
601
+ ## 🗺️ Roadmap & Enterprise Features
602
+
603
+ ### Free tier (shipped)
604
+
605
+ - All 11 ATK detectors + **MEGALODON** CI/CD campaign detection (D1–D6) + **HF_IMPERSONATION** detector + **MINI_SHAI_HULUD** worm campaign (D1–D7, 3 waves, with **MSH_SUPPLEMENT** D1–D4 for obfuscation/persistence/geofence/C2) + **VSIX_SCAN** extension supply chain scan (6 detectors) + **CVE-2026-48710 (BadHost)** Python vulnerability detection (3 layers) + **TRAPDOOR** cross-ecosystem attack detection (9 rules) + **NODE_IPC_COMPROMISE** expired-domain hijack detection (11 rules) + **TYPOSQUAT_VPMDHAJ** mass typosquatting campaign (3 rules) + **AXIOS_POISONING** registry poisoning campaign (3 rules)
606
+ - SBOM output (CycloneDX + SPDX)
607
+ - HTML, text, and compliance reports (NIST + EU CRA)
608
+ - Policy-as-code engine (YAML)
609
+ - Local SQLite scan history
610
+ - GitHub Action
611
+ - Pre-commit hook (husky + lint-staged)
612
+ - Docker images + Compose pipeline
613
+ - Watch mode (--watch / --monorepo for auto-rescan)
614
+ - VS Code extension scanning (--vsix flag with Marketplace + Open VSX registries)
615
+
616
+ ### Premium (🔐 license key)
617
+
618
+ - PDF compliance reports with NIST traceability matrix
619
+ - SIEM export (Splunk CEF, Elastic ECS, Microsoft Sentinel, IBM QRadar)
620
+ - Dynamic sandbox (gVisor-based — ATK-008–010)
621
+ - Reachability analysis (call graph filtering)
622
+
623
+ ### Enterprise (🏢 custom license)
624
+
625
+ - SAML 2.0 SSO (Okta, Azure AD, OneLogin, Keycloak)
626
+ - REST API + webhooks (FastAPI)
627
+ - Team RBAC + audit logs
628
+ - Helm chart for Kubernetes deployment
629
+ - PostgreSQL backend for hosted/team tier
630
+ - SLA-backed priority support
631
+
632
+ ---
633
+
634
+ ## 🤝 Contributing
635
+
636
+ We welcome contributions — especially new detectors, improved evasion resistance, and compliance templates.
637
+
638
+ See [`docs/attack-taxonomy.md`](docs/attack-taxonomy.md) for the ATK governance process. Every new detector requires:
639
+
640
+ 1. A proof-of-concept sample
641
+ 2. A detection rule with tests
642
+ 3. False-positive analysis on top-500 npm packages
643
+ 4. NIST 800-161 control mapping
644
+
645
+ ### Testing
646
+
647
+ The project uses the **Node.js native test runner** (`node:test` + `assert/strict`).
648
+
649
+ ```bash
650
+ # Run all tests
651
+ npm test
652
+
653
+ # Run tests with coverage
654
+ npm run test:coverage
655
+
656
+ # Run tests with verbose spec output
657
+ npm run test:verbose
658
+
659
+ # Run local malicious/clean corpus (no network needed)
660
+ node --test test/detectors-corpus.test.js
661
+ ```
662
+
663
+ **Test structure:**
664
+ - `test/fixtures/mock-data.js` — shared mock scans, packages, and code snippets
665
+ - `test/megalodon.test.js` — 30 Megalodon campaign detection tests (D1–D4 + aggregator + runAll integration)
666
+ - `test/db.test.js` — database CRUD (save, query, persist)
667
+ - `test/detectors-edge-cases.test.js` per-detector boundary tests (no-ops, clean clears, severity)
668
+ - `test/detectors-corpus.test.js` — 33 malicious + 50 clean tarball integration (offline)
669
+ - `test/fetch.test.js` — tarball extraction, temp directory cleanup
670
+ - `test/policy-edge-cases.test.js` edge cases in suppress, override, load validation
671
+ - `test/policy.test.js` policy YAML/JSON load, apply, suppress, severity override tests
672
+ - `test/report-snapshots.test.js` — HTML/text/CRA/PDF format assertions
673
+ - `test/report.test.js` SARIF, CSV, STIG, risk score format tests
674
+ - `test/lockfile.test.js` npm/yarn/pnpm parser, auto-detect, ATK-007/011 lockfile tests
675
+ - `test/hf-impersonation.test.js` — 13 HF impersonation detection tests (no-ref, exact match, spoof, README clone, artifact mismatch, postinstall escalation, new-org tag)
676
+ - `test/mini-shai-hulud.test.js` 22 Mini Shai-Hulud worm campaign detection tests (burst, sibling, SLSA, maintainer, IOC, exfil, wave attribution)
677
+ - `test/vsix-scan/burst-publish.test.js` 4 VSIX burst publish tests (threshold, sub-threshold, hot-pull, Open VSX window)
678
+ - `test/vsix-scan/publisher-anomaly.test.js` — 5 publisher anomaly tests (cross-namespace, new-account, add+publish, substitution, silent)
679
+ - `test/vsix-scan/activation-event-risk.test.js` 5 activation event risk tests (onStartupFinished, wildcard, escalation, first-time, silent)
680
+ - `test/vsix-scan/orphan-commit-fetch.test.js` 5 orphan commit tests (GitHub SHA, npx git, MCP exfil, Bun install, silent)
681
+ - `test/vsix-scan/known-ioc.test.js` — 4 known IOC tests (extensionId, publisher window, outside window)
682
+ - `test/vsix-scan/exfil-pattern.test.js` 5 exfil pattern tests (creds, DNS tunnel, AES+RSA, anti-analysis, silent)
683
+ - `test/vsix-scan/integration.test.js` — 4 integration tests (Nx Console CRITICAL, safe version clean, orphan commit, skipNetwork)
684
+ - `test/cve-2026-48710-badhost/manifest.test.js` — 13 Python manifest parsing tests (requirements.txt, pyproject.toml, poetry.lock, version edge cases)
685
+ - `test/cve-2026-48710-badhost/transitive.test.js` — 7 transitive dependency tests (Tier 1/2, fastapi version gating, pin suppression)
686
+ - `test/cve-2026-48710-badhost/codePattern.test.js` — 6 static code pattern tests (auth context, INFO fallthrough, scope suppression)
687
+ - `test/cve-2026-48710-badhost/integration.test.js` — 4 integration tests (end-to-end composite findings, clean project, no Python files)
688
+ - `test/trapdoor.test.js` — 40 TrapDoor campaign detection tests (D1–D9: campaign marker, payload fingerprint, publisher blocklist, Gist exfil, AI poisoning, lure name, crypto primitives, XOR key, credential validation)
689
+ - `test/node-ipc.test.js` — 37 node-ipc compromise detection tests (D1–D11: version blocklist, tarball hash, CJS injection, payload hash, DNS C2 pattern, bootstrap resolver, DNS TXT exfil, runtime trigger, temp artifact, unauthorized publisher, blast radius)
690
+ - `test/msh-supplement.test.js` — 17 MSH supplement tests (ctf-scramble-v2 stop, daemonization, geo killswitch, C2 dead-drop, provenance, false positives)
691
+ - `test/typosquat-vpmdhaj.test.js` 16 typosquatting campaign tests (maintainer block, prefix detection, levenshtein, preinstall stagers, Bun loader, AWS/ECS/Vault/GitHub cred exfil)
692
+ - `test/axios-poisoning.test.js` — 13 axios poisoning tests (version blocklist stop, decoy dependency, crypto heuristic, cross-platform RAT, C2 callback)
693
+ - `test/cli.test.js` — commander integration tests (help, version, scan, report, error handling)
694
+ - `test/cli-lockfile.test.js` scan-lockfile CLI options, yarn/pnpm/monorepo/watch tests
695
+
696
+ ### Need help?
697
+
698
+ - 🔒 See [security policy](SECURITY.md) for vulnerability disclosure
699
+ - 📖 Read the [project plan](docs/project-plan.md)
700
+ - 🧬 Review the [attack taxonomy](docs/attack-taxonomy.md)
701
+ - 🐛 Open an issue or PR
702
+
703
+ ---
704
+
705
+ ## 📄 License
706
+
707
+ Apache-2.0 core + Commons Clause.
708
+ See [`LICENSING.md`](LICENSING.md) for the exact boundary between free and premium features.
709
+
710
+ ---
711
+
712
+ ## 👤 About the Maintainer
713
+
714
+ **Roongrunchai Chongolnee** — creator and maintainer of `@lateos/npm-scan`. Certified security professional (CISSP, CEH, Cisco Security, AWS Cloud Practitioner) with a decade of infrastructure and application security experience at Philips. I built this tool to give the open-source community a practical, detector-driven defense against supply-chain malware — and I'm committed to keeping it transparent, community-owned, and continuously improved.
715
+
716
+ [![LinkedIn](https://img.shields.io/badge/LinkedIn-0A66C2?style=flat-square&logo=linkedin)](https://www.linkedin.com/in/roongrunchai-chong-c-ab9742108/)
717
+ [![GitHub](https://img.shields.io/badge/GitHub-lateos--ai-181717?style=flat-square&logo=github)](https://github.com/lateos-ai/npm-scan)
718
+
719
+ Issues, ideas, and pull requests are always welcome — security is strongest when we collaborate.
720
+
721
+ ---
722
+
723
+ ```
724
+ @lateos/npm-scan — npm supply chain security scanner
725
+ Copyright (C) 2026 Lateos
726
+
727
+ Licensed under the Apache License, Version 2.0 (the "License");
728
+ you may not use this file except in compliance with the License.
729
+
730
+ Unless required by applicable law or agreed to in writing, software
731
+ distributed under the License is distributed on an "AS IS" BASIS,
732
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
733
+ ```
734
+
735
+ ---
736
+
737
+ **Scan your first package now:**
738
+
739
+ ```bash
740
+ npx @lateos/npm-scan scan lodash
741
+ ```