@lateos/npm-scan 0.9.6 → 0.9.9
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/CHANGELOG.md +199 -0
- package/README.de.md +52 -0
- package/README.fr.md +52 -0
- package/README.ja.md +48 -0
- package/README.md +48 -7
- package/README.zh.md +52 -0
- package/SECURITY.md +73 -0
- package/backend/fetch.js +100 -1
- package/backend/report.js +97 -0
- package/cli/cli.js +105 -9
- package/package.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to [@lateos/npm-scan](https://github.com/lateos-ai/npm-scan) are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `scan --file <path>` flag to analyze local `.tgz` tarballs without fetching from npm registry
|
|
11
|
+
- `scan --fail-on <level>` flag to exit with code 1 when findings >= severity (CI/CD integration)
|
|
12
|
+
- `scan --sarif [file]` to output SARIF v2.1 format for GitHub Advanced Security, VS Code, Azure DevOps
|
|
13
|
+
- `scan --csv [file]` and `report --csv [file]` to export tabular CSV for Excel/Sheets import
|
|
14
|
+
- `scan --score-only` to output only risk score (0-10), auto-added to JSON output
|
|
15
|
+
- Government/SOC 2 features: `--audit-log`, `--fips`, `--stig`, `--cache-dir` for air-gapped/federal compliance
|
|
16
|
+
|
|
17
|
+
## [0.9.7] — 2026-05-12
|
|
18
|
+
|
|
19
|
+
- Sigstore provenance attestation on every publish via new GitHub Actions workflow
|
|
20
|
+
- Fix duplicate Docker section in README.md
|
|
21
|
+
- Add SECURITY.md with vulnerability disclosure policy and PGP key
|
|
22
|
+
|
|
23
|
+
## [0.9.6] — 2026-05-12
|
|
24
|
+
|
|
25
|
+
- Add Docker badge (`ghcr.io/lateos/npm-scan`) to all 5 READMEs
|
|
26
|
+
- Add dedicated Docker quick-start section in all languages
|
|
27
|
+
- Replace duplicate Docker pull instructions in Integrations with cross-references
|
|
28
|
+
|
|
29
|
+
## [0.9.5] — 2026-05-12
|
|
30
|
+
|
|
31
|
+
- Fix literal `\n` escape sequences in LICENSING.md (replaced with real newlines)
|
|
32
|
+
|
|
33
|
+
## [0.9.4] — 2026-05-11
|
|
34
|
+
|
|
35
|
+
- Fix language badge links to use absolute GitHub URLs so they work from npm web UI
|
|
36
|
+
- Fix GitHub organization links from `lateos` to `lateos-ai` across all READMEs
|
|
37
|
+
|
|
38
|
+
## [0.9.3] — 2026-05-11
|
|
39
|
+
|
|
40
|
+
- Add multi-language README: Chinese (`README.zh.md`), Japanese (`README.ja.md`), French (`README.fr.md`), German (`README.de.md`)
|
|
41
|
+
- Language-switcher badges with absolute GitHub URLs in all 5 READMEs
|
|
42
|
+
|
|
43
|
+
## [0.9.2] — 2026-05-11
|
|
44
|
+
|
|
45
|
+
- **222 tests across 8 test files** (212 passing, 10 skipped for known FPs)
|
|
46
|
+
- **85% line coverage** with Node.js native test runner
|
|
47
|
+
- New test files: `test/db.test.js`, `test/detectors-edge-cases.test.js`, `test/detectors-corpus.test.js`, `test/report-snapshots.test.js`, `test/fetch.test.js`, `test/policy-edge-cases.test.js`, `test/cli.test.js`, `test/fixtures/mock-data.js`
|
|
48
|
+
- `backend/db.js:close()` resets `initPromise = null` for test isolation
|
|
49
|
+
- GitHub Actions CI with Node 18/20/22 matrix, corpus tests, and self-scan
|
|
50
|
+
- GitHub Actions PR lockfile scanner with `fail-on: high`
|
|
51
|
+
|
|
52
|
+
## [0.9.1] — 2026-05-11
|
|
53
|
+
|
|
54
|
+
- Remove `node-fetch` import and dependency (replaced in 0.9.0)
|
|
55
|
+
|
|
56
|
+
## [0.9.0] — 2026-05-11
|
|
57
|
+
|
|
58
|
+
- **Replace `node-fetch` with native `fetch`** (Node 18+) — removes external HTTP dependency
|
|
59
|
+
- **Replace `better-sqlite3` with `sql.js`** (WASM) — zero native compilation, fixes `npx` silent failure on systems without build tools
|
|
60
|
+
- Add 404 check in `backend/fetch.js` for robust registry lookups
|
|
61
|
+
- Reduce ATK-009 false positives on `lodash`/`axios`/`express`
|
|
62
|
+
- Fix ATK-002/011 false positives — stricter eval+decode rules, remove self-referential checks
|
|
63
|
+
- Fix ATK-008 `knownRepos` for `vue`
|
|
64
|
+
|
|
65
|
+
## [0.8.0] — 2026-05-11
|
|
66
|
+
|
|
67
|
+
- **YAML/JSON policy-as-code engine** — allowlists, severity overrides, suppressions, `fail_on` threshold
|
|
68
|
+
- **Text report generator** (free tier)
|
|
69
|
+
- **PDF report generator** (premium, via `pdf-lib`)
|
|
70
|
+
- **Docker**: multi-stage builds, Compose profiles, health checks, validation script, Makefile
|
|
71
|
+
- Comprehensive README rewrite with comparison table, ATK taxonomy, usage examples, integrations
|
|
72
|
+
- `.npmignore` cleanup for smaller package
|
|
73
|
+
|
|
74
|
+
## [0.7.6] — 2026-05-10
|
|
75
|
+
|
|
76
|
+
- **GitHub Action** (`action.yml`) — scan on push/PR with lockfile or package mode, fail-on severity threshold, SIEM/SBOM output support
|
|
77
|
+
- **28 comprehensive tests** covering SIEM exporters (CEF, ECS, Sentinel, QRadar), EU CRA compliance, SBOM (CycloneDX + SPDX), License key gen/validation/edition/tamper/expiry, Report/NIST (HTML, SR-series table, severity badges, all 11 ATK IDs)
|
|
78
|
+
- Fix tampered key test determinism
|
|
79
|
+
|
|
80
|
+
## [0.7.5] — 2026-05-10
|
|
81
|
+
|
|
82
|
+
- Add Elastic ECS, Microsoft Sentinel, and IBM QRadar SIEM exporters
|
|
83
|
+
|
|
84
|
+
## [0.7.4] — 2026-05-10
|
|
85
|
+
|
|
86
|
+
- Version bump only; no functional changes
|
|
87
|
+
|
|
88
|
+
## [0.7.3] — 2026-05-10
|
|
89
|
+
|
|
90
|
+
- Version bump only; no functional changes
|
|
91
|
+
|
|
92
|
+
## [0.7.2] — 2026-05-10
|
|
93
|
+
|
|
94
|
+
- Fix duplicate Enterprise Features section in README
|
|
95
|
+
|
|
96
|
+
## [0.7.1] — 2026-05-10
|
|
97
|
+
|
|
98
|
+
- Add SAML SSO and REST API sections to README
|
|
99
|
+
|
|
100
|
+
## [0.7.0] — 2026-05-10
|
|
101
|
+
|
|
102
|
+
- **Enterprise SAML SSO integration**
|
|
103
|
+
|
|
104
|
+
## [0.6.0] — 2026-05-10
|
|
105
|
+
|
|
106
|
+
- **License key enforcement** — HMAC-signed keys with community/premium/enterprise editions
|
|
107
|
+
- Feature gating for SIEM, CRA, REST API, Helm, PostgreSQL backend, SSO, audit logs
|
|
108
|
+
- **PostgreSQL schema** — teams, users, RBAC, audit log, webhooks, API keys, materialized `package_risk` view
|
|
109
|
+
- **FastAPI REST API** — scan/list/retrieve endpoints, webhook CRUD with HMAC-signed dispatch
|
|
110
|
+
- **Webhook engine** — event dispatch with retry, signature verification header
|
|
111
|
+
- **Helm chart** — API + worker + PostgreSQL deployments, secrets, ingress, PVC
|
|
112
|
+
- CLI hardened: premium features blocked without valid license key
|
|
113
|
+
|
|
114
|
+
## [0.5.0] — 2026-05-10
|
|
115
|
+
|
|
116
|
+
- **ATK-011 (Transitive Propagation)** detector
|
|
117
|
+
- **SIEM CEF export** for Splunk and ArcSight integration
|
|
118
|
+
- **EU CRA compliance report** — EU Cyber Resilience Act readiness assessment
|
|
119
|
+
- Phase 3 enterprise foundation
|
|
120
|
+
|
|
121
|
+
## [0.4.1] — 2026-05-10
|
|
122
|
+
|
|
123
|
+
- Update README for Phase 3 (ATK-011, SIEM, CRA)
|
|
124
|
+
|
|
125
|
+
## [0.4.0] — 2026-05-10
|
|
126
|
+
|
|
127
|
+
- **ATK-008 (Tarball Tampering)**, **ATK-009 (Dormant Trigger)**, **ATK-010 (Sandbox Evasion)** detectors
|
|
128
|
+
- **SPDX 2.3 SBOM** support alongside CycloneDX
|
|
129
|
+
- **NIST SP 800-161 compliance report** — supply chain risk management controls
|
|
130
|
+
- Sandbox threat model and gVisor isolation strategy
|
|
131
|
+
|
|
132
|
+
## [0.3.3] — 2026-05-10
|
|
133
|
+
|
|
134
|
+
- Fix report HTML/SBOM generation to use `atk_id`, description, package name, dynamic version
|
|
135
|
+
|
|
136
|
+
## [0.3.2] — 2026-05-10
|
|
137
|
+
|
|
138
|
+
- Update README for Phase 2 (ATK-008–010, SPDX, NIST)
|
|
139
|
+
|
|
140
|
+
## [0.3.1] — 2026-05-10
|
|
141
|
+
|
|
142
|
+
- Fix schema literal newlines
|
|
143
|
+
- Fix CLI SBOM defaults
|
|
144
|
+
- Fix SBOM finding IDs
|
|
145
|
+
|
|
146
|
+
## [0.3.0] — 2026-05-10
|
|
147
|
+
|
|
148
|
+
- **ATK-001 (Lifecycle Script)** detector — detects `preinstall`, `postinstall`, `preuninstall` hooks with suspicious commands
|
|
149
|
+
- **ATK-002 (Obfuscated Payload)** detector — hex/base64/decode-driven eval, regex obfuscation
|
|
150
|
+
- **ATK-003 (Credential Harvester)** detector — env var exfiltration, filesystem credential scraping
|
|
151
|
+
- **ATK-004 (Persistence Mechanism)** detector — cron jobs, startup scripts, `postinstall` service installs
|
|
152
|
+
- **ATK-005 (Data Exfiltration)** detector — DNS tunneling, HTTP beaconing, unexpected network calls
|
|
153
|
+
- **ATK-006 (Dependency Confusion)** detector — internal package name heuristics
|
|
154
|
+
- **ATK-007 (Typosquatting)** detector — edit-distance based package name similarity
|
|
155
|
+
|
|
156
|
+
## [0.2.5] — 2026-05-10
|
|
157
|
+
|
|
158
|
+
- Fix `.npmignore` to exclude corpus tarballs from published package
|
|
159
|
+
|
|
160
|
+
## [0.2.4] — 2026-05-10
|
|
161
|
+
|
|
162
|
+
- Version bump only; no functional changes
|
|
163
|
+
|
|
164
|
+
## [0.2.2] — 2026-05-10
|
|
165
|
+
|
|
166
|
+
- **Corpus test suite** — 50 clean packages (0% FP) + 22 malicious PoC (100% detect rate)
|
|
167
|
+
- **HTML report generator** with CLI `--html` flag
|
|
168
|
+
- ATK-007 edit-distance typosquatting implementation
|
|
169
|
+
- Switch from `adm-zip` to `tar` for tgz extraction
|
|
170
|
+
- ATK detectors hardened for fewer false positives
|
|
171
|
+
- `README.md`, `.gitignore`, corpus download scripts
|
|
172
|
+
- **Phase 1 exit**: FP < 2%, passes unit tests + corpus
|
|
173
|
+
|
|
174
|
+
## [0.2.1] — 2026-05-10
|
|
175
|
+
|
|
176
|
+
- Version bump only; no functional changes
|
|
177
|
+
|
|
178
|
+
## [0.2.0] — 2026-05-10
|
|
179
|
+
|
|
180
|
+
- **Commander.js CLI** with `scan`, `scan-lockfile`, `report` commands
|
|
181
|
+
- **ATK-001–007 detector stubs** via `backend/detectors/index.js` (`runAll`)
|
|
182
|
+
- **SQLite persistence** via `better-sqlite3` — scan auto-save, report by ID/recent
|
|
183
|
+
- **CycloneDX SBOM** — JSON and XML output with ATK vulnerability references
|
|
184
|
+
- `.github/workflows/scan.yml` — GitHub Action example for PR scanning
|
|
185
|
+
- Dependencies: `commander`, `adm-zip`, `acorn`, `node-fetch`
|
|
186
|
+
|
|
187
|
+
## [0.1.0] — 2026-05-09
|
|
188
|
+
|
|
189
|
+
- **Initial foundation**
|
|
190
|
+
- Monorepo structure (`cli/`, `backend/`, `docker/`, `docs/`)
|
|
191
|
+
- `LICENSING.md` — Apache-2.0 core + Commons Clause for premium features
|
|
192
|
+
- `CONTRIBUTING.md`
|
|
193
|
+
- `docs/attack-taxonomy.md` — ATK-001 through ATK-011 stubs
|
|
194
|
+
- `backend/license.js` skeleton for HMAC-signed license key gating
|
|
195
|
+
- `backend/db/schema.sql`
|
|
196
|
+
- `docker/Dockerfile.cli` + `docker-compose.yml`
|
|
197
|
+
- npm scripts (lint, test stubs)
|
|
198
|
+
- `.github/workflows/ci.yml`
|
|
199
|
+
- `AGENTS.md` — project instructions
|
package/README.de.md
CHANGED
|
@@ -108,6 +108,25 @@ Kein Node.js. Kein `npm install`. Keine globalen Pakete. Funktioniert auf jedem
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
+
## 🛡️ Behörden- & SOC 2 L2-bereit
|
|
112
|
+
|
|
113
|
+
| Funktion | SOC 2 | NIST 800-161 | STIG/FedRAMP |
|
|
114
|
+
|----------|-------|--------------|--------------|
|
|
115
|
+
| Audit-Protokolle (--audit-log) | CC6.8 | AU-2 | ✓ |
|
|
116
|
+
| FIPS-Krypto (--fips) | CC6.1 | SC-13 | ✓ |
|
|
117
|
+
| STIG-Bericht (--stig) | CC7.3 | RA-5 | ✓ |
|
|
118
|
+
| Offline-Cache (--cache-dir) | A1.2 | SC-8 | ✓ |
|
|
119
|
+
| Sigstore-Herleitung | CC6.2 | SI-7 | ✓ |
|
|
120
|
+
| SBOM (SPDX/CycloneDX) | CC7.4 | SA-10 | ✓ |
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Vollständig konformer Scan in luftdichten Umgebungen
|
|
124
|
+
npm-scan scan-lockfile --cache-dir /offline/cache --audit-log /var/log/npm-scan.audit --fips
|
|
125
|
+
npm-scan report --stig
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
111
130
|
## 📖 Verwendungsbeispiele
|
|
112
131
|
|
|
113
132
|
### Ein einzelnes Paket scannen
|
|
@@ -123,6 +142,9 @@ npm-scan scan express --sbom spdx # SPDX 2.3
|
|
|
123
142
|
|
|
124
143
|
# Eine YAML-Policy anwenden
|
|
125
144
|
npm-scan scan some-package --policy .npm-scan.yml
|
|
145
|
+
|
|
146
|
+
# Lokales Tarball scannen (kein Registry-Abruf nötig)
|
|
147
|
+
npm-scan scan --file path/to/malicious-package.tgz
|
|
126
148
|
```
|
|
127
149
|
|
|
128
150
|
### Eine Lock-Datei scannen
|
|
@@ -133,6 +155,18 @@ npm-scan scan-lockfile
|
|
|
133
155
|
|
|
134
156
|
# Eine bestimmte Lock-Datei scannen
|
|
135
157
|
npm-scan scan-lockfile -f ./path/to/package-lock.json
|
|
158
|
+
|
|
159
|
+
# CI/CD bei hohen oder kritischen Problemen fehlschlagen (Exit-Code 1)
|
|
160
|
+
npm-scan scan-lockfile --fail-on high
|
|
161
|
+
|
|
162
|
+
# Bei allen Erkenntnissen fehlschlagen (low und höher)
|
|
163
|
+
npm-scan scan-lockfile --fail-on low
|
|
164
|
+
|
|
165
|
+
# SARIF v2.1-Ausgabe für GitHub Advanced Security / VS Code generieren
|
|
166
|
+
npm-scan scan-lockfile --sarif results.sarif
|
|
167
|
+
|
|
168
|
+
# Nur Risiko-Score ausgeben (0-10) für Dashboards/Schwellenwerte
|
|
169
|
+
npm-scan scan-lockfile --score-only
|
|
136
170
|
```
|
|
137
171
|
|
|
138
172
|
### Berichte generieren
|
|
@@ -153,6 +187,10 @@ npm-scan report -i 42 --nist
|
|
|
153
187
|
# EU-CRA-Compliance-Tabelle ausgeben
|
|
154
188
|
npm-scan report --cra
|
|
155
189
|
|
|
190
|
+
# CSV-Export für Excel / Sheets (audit-bereit)
|
|
191
|
+
npm-scan report --csv risks.csv
|
|
192
|
+
npm-scan scan lodash --csv # CSV nach stdout
|
|
193
|
+
|
|
156
194
|
# Textbericht (kostenlos)
|
|
157
195
|
npm-scan report --text
|
|
158
196
|
|
|
@@ -576,6 +614,7 @@ node --test test/detectors-corpus.test.js
|
|
|
576
614
|
|
|
577
615
|
### Hilfe benötigt?
|
|
578
616
|
|
|
617
|
+
- 🔒 Siehe [Sicherheitsrichtlinie](SECURITY.md) für die Offenlegung von Schwachstellen
|
|
579
618
|
- 📖 Lesen Sie den [Projektplan](docs/project-plan.md)
|
|
580
619
|
- 🧬 Überprüfen Sie die [Angriffstaxonomie](docs/attack-taxonomy.md)
|
|
581
620
|
- 🐛 Öffnen Sie ein Issue oder PR
|
|
@@ -587,6 +626,19 @@ node --test test/detectors-corpus.test.js
|
|
|
587
626
|
Apache-2.0 Core + Commons Clause.
|
|
588
627
|
Siehe [`LICENSING.md`](LICENSING.md) für die genaue Grenze zwischen kostenlosen und Premium-Funktionen.
|
|
589
628
|
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## 👤 Über den Maintainer
|
|
632
|
+
|
|
633
|
+
**Roongrunchai Chongolnee** — Ersteller und Maintainer von `@lateos/npm-scan`. Zertifizierter Sicherheitsexperte (CISSP, CEH, Cisco Security, AWS Cloud Practitioner) mit einem Jahrzehnt Erfahrung in Infrastruktur- und Anwendungssicherheit bei Philips. Ich habe dieses Tool entwickelt, um der Open-Source-Community eine praktische, detektorgesteuerte Abwehr gegen Supply-Chain-Malware zu bieten — und ich bin bestrebt, es transparent, gemeinschaftseigen und kontinuierlich verbessert zu halten.
|
|
634
|
+
|
|
635
|
+
[](https://www.linkedin.com/in/roongrunchai-chong-c-ab9742108/)
|
|
636
|
+
[](https://github.com/lateos-ai/npm-scan)
|
|
637
|
+
|
|
638
|
+
Issues, Ideen und Pull-Requests sind immer willkommen — Sicherheit ist am stärksten, wenn wir zusammenarbeiten.
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
590
642
|
```
|
|
591
643
|
@lateos/npm-scan — npm supply chain security scanner
|
|
592
644
|
Copyright (C) 2026 Lateos
|
package/README.fr.md
CHANGED
|
@@ -108,6 +108,25 @@ Pas de Node.js. Pas de `npm install`. Pas de paquets globaux. Fonctionne sur tou
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
+
## 🛡️ Prêt pour le Gouvernement et SOC 2 L2
|
|
112
|
+
|
|
113
|
+
| Fonctionnalité | SOC 2 | NIST 800-161 | STIG/FedRAMP |
|
|
114
|
+
|----------------|-------|--------------|--------------|
|
|
115
|
+
| Journaux d'audit (--audit-log) | CC6.8 | AU-2 | ✓ |
|
|
116
|
+
| Crypto FIPS (--fips) | CC6.1 | SC-13 | ✓ |
|
|
117
|
+
| Rapport STIG (--stig) | CC7.3 | RA-5 | ✓ |
|
|
118
|
+
| Cache hors ligne (--cache-dir) | A1.2 | SC-8 | ✓ |
|
|
119
|
+
| Provenance Sigstore | CC6.2 | SI-7 | ✓ |
|
|
120
|
+
| SBOM (SPDX/CycloneDX) | CC7.4 | SA-10 | ✓ |
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Scan conforme en environnement hermétique
|
|
124
|
+
npm-scan scan-lockfile --cache-dir /offline/cache --audit-log /var/log/npm-scan.audit --fips
|
|
125
|
+
npm-scan report --stig
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
111
130
|
## 📖 Exemples d'utilisation
|
|
112
131
|
|
|
113
132
|
### Scanner un seul paquet
|
|
@@ -123,6 +142,9 @@ npm-scan scan express --sbom spdx # SPDX 2.3
|
|
|
123
142
|
|
|
124
143
|
# Appliquer une politique YAML
|
|
125
144
|
npm-scan scan some-package --policy .npm-scan.yml
|
|
145
|
+
|
|
146
|
+
# Scanner un fichier tarball local (pas de téléchargement depuis le registre)
|
|
147
|
+
npm-scan scan --file path/to/malicious-package.tgz
|
|
126
148
|
```
|
|
127
149
|
|
|
128
150
|
### Scanner un fichier de verrouillage
|
|
@@ -133,6 +155,18 @@ npm-scan scan-lockfile
|
|
|
133
155
|
|
|
134
156
|
# Scanner un fichier de verrouillage spécifique
|
|
135
157
|
npm-scan scan-lockfile -f ./path/to/package-lock.json
|
|
158
|
+
|
|
159
|
+
# Échouer en CI/CD sur les découvertes de severity haute ou critique (code de sortie 1)
|
|
160
|
+
npm-scan scan-lockfile --fail-on high
|
|
161
|
+
|
|
162
|
+
# Échouer sur toute découverte (low et au-delà)
|
|
163
|
+
npm-scan scan-lockfile --fail-on low
|
|
164
|
+
|
|
165
|
+
# Générer une sortie SARIF v2.1 pour GitHub Advanced Security / VS Code
|
|
166
|
+
npm-scan scan-lockfile --sarif results.sarif
|
|
167
|
+
|
|
168
|
+
# Afficher uniquement le score de risque (0-10) pour les tableaux de bord/seuils
|
|
169
|
+
npm-scan scan-lockfile --score-only
|
|
136
170
|
```
|
|
137
171
|
|
|
138
172
|
### Générer des rapports
|
|
@@ -153,6 +187,10 @@ npm-scan report -i 42 --nist
|
|
|
153
187
|
# Afficher le tableau de conformité EU CRA
|
|
154
188
|
npm-scan report --cra
|
|
155
189
|
|
|
190
|
+
# Export CSV pour Excel / Sheets (prêt pour audit)
|
|
191
|
+
npm-scan report --csv risks.csv
|
|
192
|
+
npm-scan scan lodash --csv # CSV vers stdout
|
|
193
|
+
|
|
156
194
|
# Rapport texte (gratuit)
|
|
157
195
|
npm-scan report --text
|
|
158
196
|
|
|
@@ -576,6 +614,7 @@ node --test test/detectors-corpus.test.js
|
|
|
576
614
|
|
|
577
615
|
### Besoin d'aide ?
|
|
578
616
|
|
|
617
|
+
- 🔒 Voir la [politique de sécurité](SECURITY.md) pour la divulgation des vulnérabilités
|
|
579
618
|
- 📖 Lire le [plan du projet](docs/project-plan.md)
|
|
580
619
|
- 🧬 Consulter la [taxonomie des attaques](docs/attack-taxonomy.md)
|
|
581
620
|
- 🐛 Ouvrir une issue ou une PR
|
|
@@ -587,6 +626,19 @@ node --test test/detectors-corpus.test.js
|
|
|
587
626
|
Apache-2.0 core + Commons Clause.
|
|
588
627
|
Voir [`LICENSING.md`](LICENSING.md) pour la limite exacte entre les fonctionnalités gratuites et premium.
|
|
589
628
|
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## 👤 À propos du mainteneur
|
|
632
|
+
|
|
633
|
+
**Roongrunchai Chongolnee** — créateur et mainteneur de `@lateos/npm-scan`. Professionnel de la sécurité certifié (CISSP, CEH, Cisco Security, AWS Cloud Practitioner) avec une décennie d'expérience en sécurité des infrastructures et des applications chez Philips. J'ai construit cet outil pour offrir à la communauté open-source une défense pratique et pilotée par des détecteurs contre les logiciels malveillants de la chaîne d'approvisionnement — et je m'engage à le maintenir transparent, détenu par la communauté et en amélioration continue.
|
|
634
|
+
|
|
635
|
+
[](https://www.linkedin.com/in/roongrunchai-chong-c-ab9742108/)
|
|
636
|
+
[](https://github.com/lateos-ai/npm-scan)
|
|
637
|
+
|
|
638
|
+
Les issues, idées et pull requests sont toujours les bienvenus — la sécurité est plus forte quand nous collaborons.
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
590
642
|
```
|
|
591
643
|
@lateos/npm-scan — npm supply chain security scanner
|
|
592
644
|
Copyright (C) 2026 Lateos
|
package/README.ja.md
CHANGED
|
@@ -108,6 +108,25 @@ Node.js不要。`npm install`不要。グローバルパッケージ不要。Doc
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
+
## 🛡️ 政府機関・SOC 2 L2 対応
|
|
112
|
+
|
|
113
|
+
| 機能 | SOC 2 | NIST 800-161 | STIG/FedRAMP |
|
|
114
|
+
|------|-------|--------------|--------------|
|
|
115
|
+
| 監査ログ (--audit-log) | CC6.8 | AU-2 | ✓ |
|
|
116
|
+
| FIPS暗号化 (--fips) | CC6.1 | SC-13 | ✓ |
|
|
117
|
+
| STIGレポート (--stig) | CC7.3 | RA-5 | ✓ |
|
|
118
|
+
| オフラインキャッシュ (--cache-dir) | A1.2 | SC-8 | ✓ |
|
|
119
|
+
| Sigstoreプロvenes | CC6.2 | SI-7 | ✓ |
|
|
120
|
+
| SBOM (SPDX/CycloneDX) | CC7.4 | SA-10 | ✓ |
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# エアギャップ環境での完全なコンプライアンススキャンを実行
|
|
124
|
+
npm-scan scan-lockfile --cache-dir /offline/cache --audit-log /var/log/npm-scan.audit --fips
|
|
125
|
+
npm-scan report --stig
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
111
130
|
## 📖 使用例
|
|
112
131
|
|
|
113
132
|
### 単一パッケージのスキャン
|
|
@@ -123,6 +142,9 @@ npm-scan scan express --sbom spdx # SPDX 2.3
|
|
|
123
142
|
|
|
124
143
|
# YAMLポリシーを適用
|
|
125
144
|
npm-scan scan some-package --policy .npm-scan.yml
|
|
145
|
+
|
|
146
|
+
# ローカルtarballをスキャン(レジストリからの取得不要)
|
|
147
|
+
npm-scan scan --file path/to/malicious-package.tgz
|
|
126
148
|
```
|
|
127
149
|
|
|
128
150
|
### ロックファイルのスキャン
|
|
@@ -133,6 +155,18 @@ npm-scan scan-lockfile
|
|
|
133
155
|
|
|
134
156
|
# 特定のロックファイルをスキャン
|
|
135
157
|
npm-scan scan-lockfile -f ./path/to/package-lock.json
|
|
158
|
+
|
|
159
|
+
# 高重大または致命的な問題でCI/CDを失敗させる(終了コード1)
|
|
160
|
+
npm-scan scan-lockfile --fail-on high
|
|
161
|
+
|
|
162
|
+
# 任何の発見項目でビルドを失敗させる(low以上)
|
|
163
|
+
npm-scan scan-lockfile --fail-on low
|
|
164
|
+
|
|
165
|
+
# SARIF v2.1出力を生成(GitHub Advanced Security / VS Code向け)
|
|
166
|
+
npm-scan scan-lockfile --sarif results.sarif
|
|
167
|
+
|
|
168
|
+
# リスクスコアのみを出力(0-10)(ダッシュボード/閾値向け)
|
|
169
|
+
npm-scan scan-lockfile --score-only
|
|
136
170
|
```
|
|
137
171
|
|
|
138
172
|
### レポートの生成
|
|
@@ -576,6 +610,7 @@ node --test test/detectors-corpus.test.js
|
|
|
576
610
|
|
|
577
611
|
### ヘルプが必要ですか?
|
|
578
612
|
|
|
613
|
+
- 🔒 [セキュリティポリシー](SECURITY.md)で脆弱性の開示方法を確認
|
|
579
614
|
- 📖 [プロジェクト計画](docs/project-plan.md)を読む
|
|
580
615
|
- 🧬 [攻撃分類](docs/attack-taxonomy.md)を確認
|
|
581
616
|
- 🐛 IssueまたはPRを開く
|
|
@@ -587,6 +622,19 @@ node --test test/detectors-corpus.test.js
|
|
|
587
622
|
Apache-2.0コア+Commons Clause。
|
|
588
623
|
無料版とプレミアム版機能の正確な境界については[`LICENSING.md`](LICENSING.md)を参照してください。
|
|
589
624
|
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## 👤 メンテナーについて
|
|
628
|
+
|
|
629
|
+
**Roongrunchai Chongolnee** — `@lateos/npm-scan` の作成者兼メンテナー。CISSP、CEH、Cisco Security、AWS Cloud Practitioner の認定を持つセキュリティ専門家で、Philips で10年間のインフラおよびアプリケーションセキュリティの経験があります。このツールは、オープンソースコミュニティに実用的で検出器駆動型のサプライチェーン型マルウェア防御を提供するために構築しました。透明性、コミュニティ所有、継続的改善に取り組んでいます。
|
|
630
|
+
|
|
631
|
+
[](https://www.linkedin.com/in/roongrunchai-chong-c-ab9742108/)
|
|
632
|
+
[](https://github.com/lateos-ai/npm-scan)
|
|
633
|
+
|
|
634
|
+
Issue、アイデア、PRはいつでも歓迎します——セキュリティは協力によって最も強力になります。
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
590
638
|
```
|
|
591
639
|
@lateos/npm-scan — npm supply chain security scanner
|
|
592
640
|
Copyright (C) 2026 Lateos
|
package/README.md
CHANGED
|
@@ -107,17 +107,25 @@ No Node.js. No `npm install`. No global packages. Works on any system with Docke
|
|
|
107
107
|
|
|
108
108
|
---
|
|
109
109
|
|
|
110
|
-
##
|
|
110
|
+
## 🛡️ Government & SOC 2 L2 Ready
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
| Feature | SOC 2 | NIST 800-161 | STIG/FedRAMP |
|
|
113
|
+
|---------|-------|--------------|--------------|
|
|
114
|
+
| Audit logs (--audit-log) | CC6.8 | AU-2 | ✓ |
|
|
115
|
+
| FIPS crypto (--fips) | CC6.1 | SC-13 | ✓ |
|
|
116
|
+
| STIG report (--stig) | CC7.3 | RA-5 | ✓ |
|
|
117
|
+
| Offline cache (--cache-dir) | A1.2 | SC-8 | ✓ |
|
|
118
|
+
| Sigstore provenance | CC6.2 | SI-7 | ✓ |
|
|
119
|
+
| SBOM (SPDX/CycloneDX) | CC7.4 | SA-10 | ✓ |
|
|
115
120
|
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
```bash
|
|
122
|
+
# Air-gapped scan with full compliance
|
|
123
|
+
npm-scan scan-lockfile --cache-dir /offline/cache --audit-log /var/log/npm-scan.audit --fips
|
|
124
|
+
npm-scan report --stig
|
|
118
125
|
```
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
[](https://www.aicpa.org/interestareas/frc/assuranceadvisoryservices/sorhome.html)
|
|
128
|
+
[](https://fedramp.gov/)
|
|
121
129
|
|
|
122
130
|
---
|
|
123
131
|
|
|
@@ -136,6 +144,9 @@ npm-scan scan express --sbom spdx # SPDX 2.3
|
|
|
136
144
|
|
|
137
145
|
# Apply a YAML policy
|
|
138
146
|
npm-scan scan some-package --policy .npm-scan.yml
|
|
147
|
+
|
|
148
|
+
# Scan a local tarball (no registry fetch needed)
|
|
149
|
+
npm-scan scan --file path/to/malicious-package.tgz
|
|
139
150
|
```
|
|
140
151
|
|
|
141
152
|
### Scan a lockfile
|
|
@@ -146,6 +157,18 @@ npm-scan scan-lockfile
|
|
|
146
157
|
|
|
147
158
|
# Scan a specific lockfile
|
|
148
159
|
npm-scan scan-lockfile -f ./path/to/package-lock.json
|
|
160
|
+
|
|
161
|
+
# Fail CI/CD on high or critical findings (exit code 1)
|
|
162
|
+
npm-scan scan-lockfile --fail-on high
|
|
163
|
+
|
|
164
|
+
# Fail on any findings (low and above)
|
|
165
|
+
npm-scan scan-lockfile --fail-on low
|
|
166
|
+
|
|
167
|
+
# Generate SARIF v2.1 output for GitHub Advanced Security / VS Code
|
|
168
|
+
npm-scan scan-lockfile --sarif results.sarif
|
|
169
|
+
|
|
170
|
+
# Output only risk score (0-10) for dashboards/thresholds
|
|
171
|
+
npm-scan scan-lockfile --score-only
|
|
149
172
|
```
|
|
150
173
|
|
|
151
174
|
### Generate reports
|
|
@@ -166,6 +189,10 @@ npm-scan report -i 42 --nist
|
|
|
166
189
|
# Print EU CRA compliance table
|
|
167
190
|
npm-scan report --cra
|
|
168
191
|
|
|
192
|
+
# CSV export for Excel / Sheets (audit-ready)
|
|
193
|
+
npm-scan report --csv risks.csv
|
|
194
|
+
npm-scan scan lodash --csv # CSV to stdout
|
|
195
|
+
|
|
169
196
|
# Text report (free)
|
|
170
197
|
npm-scan report --text
|
|
171
198
|
|
|
@@ -589,6 +616,7 @@ node --test test/detectors-corpus.test.js
|
|
|
589
616
|
|
|
590
617
|
### Need help?
|
|
591
618
|
|
|
619
|
+
- 🔒 See [security policy](SECURITY.md) for vulnerability disclosure
|
|
592
620
|
- 📖 Read the [project plan](docs/project-plan.md)
|
|
593
621
|
- 🧬 Review the [attack taxonomy](docs/attack-taxonomy.md)
|
|
594
622
|
- 🐛 Open an issue or PR
|
|
@@ -600,6 +628,19 @@ node --test test/detectors-corpus.test.js
|
|
|
600
628
|
Apache-2.0 core + Commons Clause.
|
|
601
629
|
See [`LICENSING.md`](LICENSING.md) for the exact boundary between free and premium features.
|
|
602
630
|
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
## 👤 About the Maintainer
|
|
634
|
+
|
|
635
|
+
**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.
|
|
636
|
+
|
|
637
|
+
[](https://www.linkedin.com/in/roongrunchai-chong-c-ab9742108/)
|
|
638
|
+
[](https://github.com/lateos-ai/npm-scan)
|
|
639
|
+
|
|
640
|
+
Issues, ideas, and pull requests are always welcome — security is strongest when we collaborate.
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
603
644
|
```
|
|
604
645
|
@lateos/npm-scan — npm supply chain security scanner
|
|
605
646
|
Copyright (C) 2026 Lateos
|
package/README.zh.md
CHANGED
|
@@ -108,6 +108,25 @@ docker compose --profile pipeline up -d
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
+
## 🛡️ 政府与 SOC 2 L2 就绪
|
|
112
|
+
|
|
113
|
+
| 功能 | SOC 2 | NIST 800-161 | STIG/FedRAMP |
|
|
114
|
+
|------|-------|--------------|--------------|
|
|
115
|
+
| 审计日志 (--audit-log) | CC6.8 | AU-2 | ✓ |
|
|
116
|
+
| FIPS 加密 (--fips) | CC6.1 | SC-13 | ✓ |
|
|
117
|
+
| STIG 报告 (--stig) | CC7.3 | RA-5 | ✓ |
|
|
118
|
+
| 离线缓存 (--cache-dir) | A1.2 | SC-8 | ✓ |
|
|
119
|
+
| Sigstore 溯源 | CC6.2 | SI-7 | ✓ |
|
|
120
|
+
| SBOM (SPDX/CycloneDX) | CC7.4 | SA-10 | ✓ |
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# 气隙环境下的完整合规扫描
|
|
124
|
+
npm-scan scan-lockfile --cache-dir /offline/cache --audit-log /var/log/npm-scan.audit --fips
|
|
125
|
+
npm-scan report --stig
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
111
130
|
## 📖 使用示例
|
|
112
131
|
|
|
113
132
|
### 扫描单个包
|
|
@@ -123,6 +142,9 @@ npm-scan scan express --sbom spdx # SPDX 2.3
|
|
|
123
142
|
|
|
124
143
|
# 应用 YAML 策略
|
|
125
144
|
npm-scan scan some-package --policy .npm-scan.yml
|
|
145
|
+
|
|
146
|
+
# 扫描本地 tarball(无需从注册表获取)
|
|
147
|
+
npm-scan scan --file path/to/malicious-package.tgz
|
|
126
148
|
```
|
|
127
149
|
|
|
128
150
|
### 扫描锁定文件
|
|
@@ -133,6 +155,18 @@ npm-scan scan-lockfile
|
|
|
133
155
|
|
|
134
156
|
# 扫描特定锁定文件
|
|
135
157
|
npm-scan scan-lockfile -f ./path/to/package-lock.json
|
|
158
|
+
|
|
159
|
+
# 在高危或严重问题时使 CI/CD 失败(退出码 1)
|
|
160
|
+
npm-scan scan-lockfile --fail-on high
|
|
161
|
+
|
|
162
|
+
# 任何发现项都使构建失败(low 及以上)
|
|
163
|
+
npm-scan scan-lockfile --fail-on low
|
|
164
|
+
|
|
165
|
+
# 生成 SARIF v2.1 输出,用于 GitHub Advanced Security / VS Code
|
|
166
|
+
npm-scan scan-lockfile --sarif results.sarif
|
|
167
|
+
|
|
168
|
+
# 仅输出风险分数(0-10)用于仪表板/阈值
|
|
169
|
+
npm-scan scan-lockfile --score-only
|
|
136
170
|
```
|
|
137
171
|
|
|
138
172
|
### 生成报告
|
|
@@ -153,6 +187,10 @@ npm-scan report -i 42 --nist
|
|
|
153
187
|
# 打印 EU CRA 合规表格
|
|
154
188
|
npm-scan report --cra
|
|
155
189
|
|
|
190
|
+
# CSV 导出用于 Excel / Sheets(审计就绪)
|
|
191
|
+
npm-scan report --csv risks.csv
|
|
192
|
+
npm-scan scan lodash --csv # CSV 输出到标准输出
|
|
193
|
+
|
|
156
194
|
# 文本报告(免费)
|
|
157
195
|
npm-scan report --text
|
|
158
196
|
|
|
@@ -576,6 +614,7 @@ node --test test/detectors-corpus.test.js
|
|
|
576
614
|
|
|
577
615
|
### 需要帮助?
|
|
578
616
|
|
|
617
|
+
- 🔒 查看[安全策略](SECURITY.md)了解漏洞披露流程
|
|
579
618
|
- 📖 阅读[项目计划](docs/project-plan.md)
|
|
580
619
|
- 🧬 查看[攻击分类](docs/attack-taxonomy.md)
|
|
581
620
|
- 🐛 提交 issue 或 PR
|
|
@@ -587,6 +626,19 @@ node --test test/detectors-corpus.test.js
|
|
|
587
626
|
Apache-2.0 核心 + Commons Clause。
|
|
588
627
|
请参阅 [`LICENSING.md`](LICENSING.md) 了解免费版和高级版功能之间的确切界限。
|
|
589
628
|
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## 👤 关于维护者
|
|
632
|
+
|
|
633
|
+
**Roongrunchai Chongolnee** — `@lateos/npm-scan` 的创建者和维护者。持有 CISSP、CEH、思科安全、AWS 云从业者认证的安全专业人士,在飞利浦拥有十年的基础设施和应用安全经验。我构建这个工具是为了让开源社区拥有一个实用、检测器驱动的供应链恶意软件防御方案——我致力于保持其透明、社区拥有和持续改进。
|
|
634
|
+
|
|
635
|
+
[](https://www.linkedin.com/in/roongrunchai-chong-c-ab9742108/)
|
|
636
|
+
[](https://github.com/lateos-ai/npm-scan)
|
|
637
|
+
|
|
638
|
+
欢迎提交 issue、想法和 PR——安全在协作中最强大。
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
590
642
|
```
|
|
591
643
|
@lateos/npm-scan — npm supply chain security scanner
|
|
592
644
|
Copyright (C) 2026 Lateos
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
Only the **latest published minor version** on npm receives security patches. Keep `@lateos/npm-scan` up to date:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm update -g @lateos/npm-scan
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Version | Supported |
|
|
12
|
+
|---------|-----------|
|
|
13
|
+
| 0.9.x | ✅ Active |
|
|
14
|
+
| < 0.9 | ❌ |
|
|
15
|
+
|
|
16
|
+
## Reporting a Vulnerability
|
|
17
|
+
|
|
18
|
+
Use **GitHub Private Vulnerability Reporting**:
|
|
19
|
+
|
|
20
|
+
1. Go to [github.com/lateos-ai/npm-scan/security/advisories/new](https://github.com/lateos-ai/npm-scan/security/advisories/new)
|
|
21
|
+
2. Describe the vulnerability in detail (ideally with a proof of concept)
|
|
22
|
+
3. Allow **72 hours** for an initial acknowledgment
|
|
23
|
+
|
|
24
|
+
For encrypted follow-up outside of GitHub, use our PGP key:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Fingerprint: 1BC6 998B 879B BDE0 D778 629E D9CF F5EF 1F7C 557B
|
|
28
|
+
Key ID: 1F7C557B
|
|
29
|
+
Email: leo@lateos.ai
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Scope
|
|
33
|
+
|
|
34
|
+
**In scope:**
|
|
35
|
+
- Detector logic (ATK-001 through ATK-011)
|
|
36
|
+
- Code execution in the scanner engine (`backend/fetch.js`, `cli/cli.js`)
|
|
37
|
+
- CI/CD pipeline and publish process (provenance bypass, supply chain)
|
|
38
|
+
- Configuration injection via `policy.yaml` or command-line flags
|
|
39
|
+
|
|
40
|
+
**Out of scope:**
|
|
41
|
+
- CVEs in third-party dependencies — report upstream
|
|
42
|
+
- Vulnerabilities in the npm registry itself — report to npm
|
|
43
|
+
- Malicious packages detected by the scanner (that's working as designed)
|
|
44
|
+
|
|
45
|
+
## Security Practices
|
|
46
|
+
|
|
47
|
+
`@lateos/npm-scan` follows these practices to protect its own supply chain:
|
|
48
|
+
|
|
49
|
+
- **Sigstore provenance** on every npm publish — verifiable via `npm view @lateos/npm-scan provenance`
|
|
50
|
+
- **Self-scanning in CI** — every commit scans the project's own `package-lock.json` for the full ATK taxonomy
|
|
51
|
+
- **SBOM per release** — CycloneDX and SPDX 2.3 Bill of Materials published with every version
|
|
52
|
+
- **2FA** enforced on the npm publisher account
|
|
53
|
+
- **Docker multi-arch images** signed and pushed via CI, not manually
|
|
54
|
+
- **All code public** — no security-by-obscurity
|
|
55
|
+
|
|
56
|
+
## Self-Scanning
|
|
57
|
+
|
|
58
|
+
As a supply chain security scanner, `@lateos/npm-scan` dogfoods its own detectors. Every CI run executes:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx @lateos/npm-scan scan-lockfile --fail-on medium
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If a future update to a dependency triggers one of our detectors (e.g., typosquat, obfuscated lifecycle script), the build **fails** before the change reaches npm.
|
|
65
|
+
|
|
66
|
+
## Safe Harbor
|
|
67
|
+
|
|
68
|
+
We consider security research conducted under this policy as authorized and will not pursue legal action against researchers who:
|
|
69
|
+
|
|
70
|
+
- Report vulnerabilities through GitHub Private Vulnerability Reporting
|
|
71
|
+
- Do not access or modify user data beyond what's necessary to demonstrate the vulnerability
|
|
72
|
+
- Do not exploit the vulnerability beyond demonstrating it
|
|
73
|
+
- Act in good faith to improve the security of the project
|
package/backend/fetch.js
CHANGED
|
@@ -6,7 +6,18 @@ import zlib from 'zlib';
|
|
|
6
6
|
import { Readable } from 'stream';
|
|
7
7
|
import { pipeline } from 'stream/promises';
|
|
8
8
|
|
|
9
|
-
export async function fetchPackage(target) {
|
|
9
|
+
export async function fetchPackage(target, options = {}) {
|
|
10
|
+
const { cacheDir, cacheTTL = 604800, cacheMaxSize = 1000000000 } = options;
|
|
11
|
+
|
|
12
|
+
// Check cache if enabled
|
|
13
|
+
if (cacheDir) {
|
|
14
|
+
const cached = getFromCache(cacheDir, target, cacheTTL);
|
|
15
|
+
if (cached) {
|
|
16
|
+
const tmpDir = path.join(os.tmpdir(), 'npm-scan-cache-' + Date.now());
|
|
17
|
+
return { ...(await extractTarball(cached, tmpDir)), meta: null };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
const metaRes = await fetch(`https://registry.npmjs.org/${target}/latest`);
|
|
11
22
|
const meta = await metaRes.json();
|
|
12
23
|
|
|
@@ -19,7 +30,95 @@ export async function fetchPackage(target) {
|
|
|
19
30
|
const buffer = Buffer.from(await tarRes.arrayBuffer());
|
|
20
31
|
if (buffer.length > 500 * 1024 * 1024) throw new Error('Tarball too large');
|
|
21
32
|
|
|
33
|
+
// Save to cache if enabled
|
|
34
|
+
if (cacheDir) {
|
|
35
|
+
saveToCache(cacheDir, target, buffer, cacheTTL, cacheMaxSize);
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
const tmpDir = path.join(os.tmpdir(), 'npm-scan-' + Date.now());
|
|
39
|
+
return { ...(await extractTarball(buffer, tmpDir)), meta };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getFromCache(cacheDir, target, ttl) {
|
|
43
|
+
const cachePath = path.join(cacheDir, `${target.replace('/', '-')}.tgz`);
|
|
44
|
+
const metaPath = path.join(cacheDir, `${target.replace('/', '-')}.meta.json`);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
if (!fs.existsSync(cachePath) || !fs.existsSync(metaPath)) return null;
|
|
48
|
+
|
|
49
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
|
|
50
|
+
const age = (Date.now() - meta.timestamp) / 1000;
|
|
51
|
+
|
|
52
|
+
if (age > ttl) {
|
|
53
|
+
fs.unlinkSync(cachePath);
|
|
54
|
+
fs.unlinkSync(metaPath);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return fs.readFileSync(cachePath);
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function saveToCache(cacheDir, target, buffer, ttl, maxSize) {
|
|
65
|
+
try {
|
|
66
|
+
if (!fs.existsSync(cacheDir)) {
|
|
67
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Prune if needed
|
|
71
|
+
pruneCache(cacheDir, maxSize);
|
|
72
|
+
|
|
73
|
+
const safeName = target.replace('/', '-');
|
|
74
|
+
const cachePath = path.join(cacheDir, `${safeName}.tgz`);
|
|
75
|
+
const metaPath = path.join(cacheDir, `${safeName}.meta.json`);
|
|
76
|
+
|
|
77
|
+
fs.writeFileSync(cachePath, buffer);
|
|
78
|
+
fs.writeFileSync(metaPath, JSON.stringify({ timestamp: Date.now(), size: buffer.length }));
|
|
79
|
+
} catch (e) {
|
|
80
|
+
// Cache write failure - continue without caching
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function pruneCache(cacheDir, maxSize) {
|
|
85
|
+
try {
|
|
86
|
+
const files = fs.readdirSync(cacheDir).filter(f => f.endsWith('.meta.json'));
|
|
87
|
+
let totalSize = 0;
|
|
88
|
+
const fileInfos = [];
|
|
89
|
+
|
|
90
|
+
for (const f of files) {
|
|
91
|
+
const meta = JSON.parse(fs.readFileSync(path.join(cacheDir, f), 'utf8'));
|
|
92
|
+
const tarFile = f.replace('.meta.json', '.tgz');
|
|
93
|
+
const size = meta.size || 0;
|
|
94
|
+
totalSize += size;
|
|
95
|
+
fileInfos.push({ tarFile, metaFile: f, timestamp: meta.timestamp, size });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (totalSize > maxSize) {
|
|
99
|
+
// Sort by oldest first and remove until under limit
|
|
100
|
+
fileInfos.sort((a, b) => a.timestamp - b.timestamp);
|
|
101
|
+
for (const info of fileInfos) {
|
|
102
|
+
if (totalSize <= maxSize * 0.8) break; // Leave 20% margin
|
|
103
|
+
try {
|
|
104
|
+
fs.unlinkSync(path.join(cacheDir, info.tarFile));
|
|
105
|
+
fs.unlinkSync(path.join(cacheDir, info.metaFile));
|
|
106
|
+
totalSize -= info.size;
|
|
107
|
+
} catch {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// Prune failure - ignore
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function scanLocalTarball(filePath) {
|
|
116
|
+
const buffer = fs.readFileSync(filePath);
|
|
117
|
+
const tmpDir = path.join(os.tmpdir(), 'npm-scan-local-' + Date.now());
|
|
118
|
+
return await extractTarball(buffer, tmpDir);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function extractTarball(buffer, tmpDir) {
|
|
23
122
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
24
123
|
|
|
25
124
|
const stream = Readable.from(buffer);
|
package/backend/report.js
CHANGED
|
@@ -155,4 +155,101 @@ function generateNistTable(scans) {
|
|
|
155
155
|
<thead><tr><th>NIST Control</th><th>Control Title</th><th>Status</th><th>ATK ID</th></tr></thead>
|
|
156
156
|
<tbody>${rows}</tbody>
|
|
157
157
|
</table>`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function generateSARIF(scan, format = 'json') {
|
|
161
|
+
const findings = scan.findings || [];
|
|
162
|
+
const runs = [{
|
|
163
|
+
tool: {
|
|
164
|
+
driver: {
|
|
165
|
+
name: 'npm-scan',
|
|
166
|
+
version: '0.9.7',
|
|
167
|
+
informationUri: 'https://github.com/lateos-ai/npm-scan',
|
|
168
|
+
rules: Array.from(new Set(findings.map(f => f.id))).map(id => ({
|
|
169
|
+
id,
|
|
170
|
+
name: `ATK-${id.replace('ATK-', '')}`,
|
|
171
|
+
shortDescription: { text: findings.find(f => f.id === id)?.title || id },
|
|
172
|
+
fullDescription: { text: findings.find(f => f.id === id)?.description || '' },
|
|
173
|
+
defaultConfiguration: { enabled: true }
|
|
174
|
+
}))
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
results: findings.map(f => {
|
|
178
|
+
const severityMap = { critical: 'error', high: 'error', medium: 'warning', low: 'note' };
|
|
179
|
+
return {
|
|
180
|
+
ruleId: f.id,
|
|
181
|
+
level: severityMap[f.severity] || 'note',
|
|
182
|
+
message: { text: f.description || f.title },
|
|
183
|
+
locations: [{
|
|
184
|
+
physicalLocation: {
|
|
185
|
+
artifactLocation: { uri: f.evidence || 'unknown' },
|
|
186
|
+
region: { startLine: 1, startColumn: 1 }
|
|
187
|
+
}
|
|
188
|
+
}]
|
|
189
|
+
};
|
|
190
|
+
})
|
|
191
|
+
}];
|
|
192
|
+
|
|
193
|
+
const sarif = {
|
|
194
|
+
version: '2.1.0',
|
|
195
|
+
schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
196
|
+
runs
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
return format === 'pretty' ? JSON.stringify(sarif, null, 2) : JSON.stringify(sarif);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function generateCSV(scans) {
|
|
203
|
+
const headers = 'id,severity,title,description,evidence,package_name,version\n';
|
|
204
|
+
const rows = (scans || []).flatMap(s =>
|
|
205
|
+
(s.findings || []).map(f => [
|
|
206
|
+
f.id,
|
|
207
|
+
f.severity || '',
|
|
208
|
+
(f.title || '').replace(/,/g, ';'),
|
|
209
|
+
(f.description || '').replace(/,/g, ';'),
|
|
210
|
+
(f.evidence || '').replace(/,/g, ';'),
|
|
211
|
+
s.package_name || '',
|
|
212
|
+
s.version || ''
|
|
213
|
+
].join(','))
|
|
214
|
+
).join('\n');
|
|
215
|
+
return headers + rows;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function calculateRiskScore(findings, totalPackages = 1) {
|
|
219
|
+
const weights = { low: 1, medium: 3, high: 7, critical: 10 };
|
|
220
|
+
const rawScore = findings.reduce((sum, f) => sum + (weights[f.severity] || 0), 0) / totalPackages;
|
|
221
|
+
return Math.min(rawScore, 10).toFixed(1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const STIG_MAP = {
|
|
225
|
+
'SRG-APP-000141': { title: 'Application Malware Detection', atk: 'ATK-001', desc: 'Lifecycle script detection' },
|
|
226
|
+
'SRG-APP-000142': { title: 'Application Code Obfuscation', atk: 'ATK-002', desc: 'Obfuscated payload detection' },
|
|
227
|
+
'SRG-APP-000143': { title: 'Credential Harvesting', atk: 'ATK-003', desc: 'Credential exfiltration detection' },
|
|
228
|
+
'SRG-APP-000144': { title: 'Persistence Mechanisms', atk: 'ATK-004', desc: 'Malicious persistence detection' },
|
|
229
|
+
'SRG-APP-000145': { title: 'Data Exfiltration', atk: 'ATK-005', desc: 'Network exfiltration detection' },
|
|
230
|
+
'SRG-APP-000146': { title: 'Dependency Confusion', atk: 'ATK-006', desc: 'Internal package detection' },
|
|
231
|
+
'SRG-APP-000147': { title: 'Typosquatting', atk: 'ATK-007', desc: 'Malicious package name detection' },
|
|
232
|
+
'SRG-APP-000148': { title: 'Tarball Tampering', atk: 'ATK-008', desc: 'Modified package detection' },
|
|
233
|
+
'SRG-APP-000149': { title: 'Dormant Triggers', atk: 'ATK-009', desc: 'Conditional execution detection' },
|
|
234
|
+
'SRG-APP-000150': { title: 'Sandbox Evasion', atk: 'ATK-010', desc: 'Environment detection evasion' },
|
|
235
|
+
'SRG-APP-000151': { title: 'Transitive Propagation', atk: 'ATK-011', desc: 'Dependency chain attacks' }
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export function generateSTIG(scans) {
|
|
239
|
+
const rows = [];
|
|
240
|
+
for (const [stigId, info] of Object.entries(STIG_MAP)) {
|
|
241
|
+
const findings = scans.flatMap(s => (s.findings || []).filter(f => f.id === info.atk));
|
|
242
|
+
const status = findings.length > 0 ? 'NOT APPLICABLE' : 'COMPLETE';
|
|
243
|
+
const findingsList = findings.map(f => `${f.severity.toUpperCase()}: ${f.title}`).join('; ') || 'None';
|
|
244
|
+
rows.push(`| ${stigId} | ${info.title} | ${status} | ${findingsList} |`);
|
|
245
|
+
}
|
|
246
|
+
return `# STIG Compliance Report
|
|
247
|
+
Generated: ${new Date().toISOString()}
|
|
248
|
+
|
|
249
|
+
| STIG ID | Control Title | Status | Findings |
|
|
250
|
+
|---------|--------------|--------|----------|
|
|
251
|
+
${rows.join('\n')}
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
*This report maps application security controls to DISA STIG requirements.*`;
|
|
158
255
|
}
|
package/cli/cli.js
CHANGED
|
@@ -15,51 +15,124 @@ function requirePremium(feature, licenseKey) {
|
|
|
15
15
|
const program = new Command()
|
|
16
16
|
.name('npm-scan')
|
|
17
17
|
.description('npm supply chain security scanner')
|
|
18
|
-
.version('0.9.
|
|
18
|
+
.version('0.9.7');
|
|
19
19
|
|
|
20
20
|
program
|
|
21
21
|
.command('scan')
|
|
22
22
|
.description('Scan a package')
|
|
23
|
-
.argument('
|
|
23
|
+
.argument('[target]', 'package name')
|
|
24
|
+
.option('-f, --file <path>', 'local tarball path')
|
|
24
25
|
.option('-l, --license-key <key>', 'Premium license')
|
|
25
26
|
.option('--sbom [format]', 'Generate SBOM (json/xml/spdx)')
|
|
26
27
|
.option('-p, --policy <path>', 'Policy file (YAML/JSON)')
|
|
28
|
+
.option('--fail-on <level>', 'Exit with code 1 if findings >= level (low|medium|high|critical)', 'none')
|
|
29
|
+
.option('--sarif [file]', 'Output SARIF v2.1 format to file or stdout')
|
|
30
|
+
.option('--csv [file]', 'Output CSV format to file or stdout')
|
|
31
|
+
.option('--score-only', 'Output only the risk score (0-10)')
|
|
32
|
+
.option('--audit-log <file>', 'Append scan record to immutable audit log (JSONL format)')
|
|
33
|
+
.option('--fips', 'Enable FIPS 140-2/3 crypto mode (requires FIPS-enabled Node.js)')
|
|
34
|
+
.option('--cache-dir <path>', 'Cache directory for offline/air-gapped scans')
|
|
35
|
+
.option('--cache-ttl <seconds>', 'Cache TTL in seconds (default: 604800 = 7 days)', '604800')
|
|
36
|
+
.option('--cache-size <bytes>', 'Max cache size in bytes (default: 1GB)', '1000000000')
|
|
27
37
|
.action(async (target, options) => {
|
|
28
38
|
try {
|
|
39
|
+
if (options.fips) {
|
|
40
|
+
process.env.NODE_OPTIONS = (process.env.NODE_OPTIONS || '') + ' --enable-fips';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const fetchOptions = {
|
|
44
|
+
cacheDir: options.cacheDir,
|
|
45
|
+
cacheTTL: parseInt(options.cacheTtl || '604800'),
|
|
46
|
+
cacheMaxSize: parseInt(options.cacheSize || '1000000000')
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (!target && !options.file) {
|
|
50
|
+
console.error('Error: specify a package name or --file <path>');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
const policy = options.policy
|
|
30
55
|
? await import('../backend/policy.js').then(m => m.loadPolicy(options.policy))
|
|
31
56
|
: null;
|
|
32
57
|
|
|
33
58
|
if (policy) {
|
|
34
59
|
const { isAllowed } = await import('../backend/policy.js');
|
|
35
|
-
if (isAllowed(target, policy)) {
|
|
60
|
+
if (target && isAllowed(target, policy)) {
|
|
36
61
|
console.log(JSON.stringify({ scanId: null, findings: [], skipped: true, reason: `Package '${target}' is in policy allowlist` }));
|
|
37
62
|
return;
|
|
38
63
|
}
|
|
39
64
|
}
|
|
40
65
|
|
|
41
|
-
const { pkgJson, jsFiles, tmpDir } =
|
|
66
|
+
const { pkgJson, jsFiles, tmpDir } = options.file
|
|
67
|
+
? await import('../backend/fetch.js').then(m => m.scanLocalTarball(options.file))
|
|
68
|
+
: await import('../backend/fetch.js').then(m => m.fetchPackage(target, fetchOptions));
|
|
69
|
+
const pkgName = target || pkgJson.name || 'unknown';
|
|
42
70
|
const findings = await import('../backend/detectors/index.js').then(m => m.runAll(pkgJson, jsFiles));
|
|
43
71
|
const { saveScan } = await import('../backend/db.js');
|
|
44
|
-
const scanId = await saveScan(
|
|
72
|
+
const scanId = await saveScan(pkgName, 'latest', findings);
|
|
45
73
|
|
|
46
74
|
let outputFindings = findings;
|
|
47
75
|
let blocked = false;
|
|
48
76
|
|
|
49
77
|
if (policy) {
|
|
50
78
|
const { applyPolicy } = await import('../backend/policy.js');
|
|
51
|
-
const result = applyPolicy(findings,
|
|
79
|
+
const result = applyPolicy(findings, pkgName, policy);
|
|
52
80
|
outputFindings = result.findings;
|
|
53
81
|
blocked = result.blocked;
|
|
54
82
|
}
|
|
55
83
|
|
|
56
|
-
|
|
84
|
+
const { calculateRiskScore } = await import('../backend/report.js');
|
|
85
|
+
const riskScore = calculateRiskScore(outputFindings);
|
|
86
|
+
|
|
87
|
+
if (options.scoreOnly) {
|
|
88
|
+
console.log(riskScore);
|
|
89
|
+
import('../backend/fetch.js').then(m => m.cleanup(tmpDir));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (options.sarif) {
|
|
94
|
+
const { generateSARIF } = await import('../backend/report.js');
|
|
95
|
+
const scan = { package_name: pkgName, version: pkgJson.version || 'latest', findings: outputFindings };
|
|
96
|
+
const sarifOutput = generateSARIF(scan);
|
|
97
|
+
if (options.sarif === true || !options.sarif) {
|
|
98
|
+
console.log(sarifOutput);
|
|
99
|
+
} else {
|
|
100
|
+
const { writeFileSync } = await import('fs');
|
|
101
|
+
writeFileSync(options.sarif, sarifOutput);
|
|
102
|
+
console.log(`SARIF output written to ${options.sarif}`);
|
|
103
|
+
}
|
|
104
|
+
} else if (options.csv) {
|
|
105
|
+
const { generateCSV } = await import('../backend/report.js');
|
|
106
|
+
const scan = { package_name: pkgName, version: pkgJson.version || 'latest', findings: outputFindings };
|
|
107
|
+
const csvOutput = generateCSV([scan]);
|
|
108
|
+
if (options.csv === true || !options.csv) {
|
|
109
|
+
console.log(csvOutput);
|
|
110
|
+
} else {
|
|
111
|
+
const { writeFileSync } = await import('fs');
|
|
112
|
+
writeFileSync(options.csv, csvOutput);
|
|
113
|
+
console.log(`CSV output written to ${options.csv}`);
|
|
114
|
+
}
|
|
115
|
+
} else if (options.sbom) {
|
|
57
116
|
const { generateSBOM } = await import('../backend/sbom.js');
|
|
58
|
-
const pkg = { name:
|
|
117
|
+
const pkg = { name: pkgName, version: pkgJson.version || 'latest' };
|
|
59
118
|
const sbom = generateSBOM(pkg, outputFindings, options.sbom === true ? 'json' : options.sbom);
|
|
60
119
|
console.log(sbom);
|
|
61
120
|
} else {
|
|
62
|
-
console.log(JSON.stringify({scanId, findings: outputFindings, blocked}, null, 2));
|
|
121
|
+
console.log(JSON.stringify({scanId, findings: outputFindings, blocked, riskScore}, null, 2));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (options.auditLog) {
|
|
125
|
+
const { writeFileSync, appendFileSync } = await import('fs');
|
|
126
|
+
const entry = {
|
|
127
|
+
timestamp: new Date().toISOString(),
|
|
128
|
+
command: `scan ${target || options.file}`,
|
|
129
|
+
package: pkgName,
|
|
130
|
+
version: pkgJson.version || 'latest',
|
|
131
|
+
riskScore,
|
|
132
|
+
findingsCount: outputFindings.length,
|
|
133
|
+
exitCode: 0
|
|
134
|
+
};
|
|
135
|
+
appendFileSync(options.auditLog, JSON.stringify(entry) + '\n');
|
|
63
136
|
}
|
|
64
137
|
|
|
65
138
|
if (blocked) {
|
|
@@ -67,6 +140,16 @@ program
|
|
|
67
140
|
process.exit(1);
|
|
68
141
|
}
|
|
69
142
|
|
|
143
|
+
if (options.failOn !== 'none') {
|
|
144
|
+
const severityLevels = { low: 1, medium: 2, high: 3, critical: 4 };
|
|
145
|
+
const failLevel = severityLevels[options.failOn] || 0;
|
|
146
|
+
const hasBlockingFindings = outputFindings.some(f => (severityLevels[f.severity] || 0) >= failLevel);
|
|
147
|
+
if (hasBlockingFindings) {
|
|
148
|
+
console.error(`Fail: findings with severity >= ${options.failOn} detected`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
70
153
|
import('../backend/fetch.js').then(m => m.cleanup(tmpDir));
|
|
71
154
|
} catch (e) {
|
|
72
155
|
console.error(e.message);
|
|
@@ -78,6 +161,9 @@ program
|
|
|
78
161
|
.command('scan-lockfile')
|
|
79
162
|
.description('Scan package-lock.json')
|
|
80
163
|
.option('-f, --file <path>', 'lockfile path', 'package-lock.json')
|
|
164
|
+
.option('--fail-on <level>', 'Exit with code 1 if findings >= level (low|medium|high|critical)', 'none')
|
|
165
|
+
.option('--csv [file]', 'Output CSV format to file or stdout')
|
|
166
|
+
.option('--sarif [file]', 'Output SARIF v2.1 format to file or stdout')
|
|
81
167
|
.action((options) => {
|
|
82
168
|
console.log('Scanning lockfile:', options.file);
|
|
83
169
|
});
|
|
@@ -89,8 +175,10 @@ program
|
|
|
89
175
|
.option('--sbom [format]', 'SBOM format (json/xml/spdx)')
|
|
90
176
|
.option('--html', 'HTML report')
|
|
91
177
|
.option('--text', 'Plain text report')
|
|
178
|
+
.option('--csv [file]', 'CSV export to file or stdout')
|
|
92
179
|
.option('--nist', 'NIST 800-161 compliance report')
|
|
93
180
|
.option('--cra', 'EU CRA compliance report')
|
|
181
|
+
.option('--stig', 'STIG compliance report (DISA SRG-APP)')
|
|
94
182
|
.option('--siem <format>', 'SIEM format (cef|ecs|sentinel|qradar)')
|
|
95
183
|
.option('--pdf', 'PDF report (premium)')
|
|
96
184
|
.option('-o, --output <path>', 'Output file path')
|
|
@@ -133,6 +221,10 @@ program
|
|
|
133
221
|
const { generateHTML } = await import('../backend/report.js');
|
|
134
222
|
const html = generateHTML(scan ? [scan] : []);
|
|
135
223
|
console.log(html);
|
|
224
|
+
} else if (options.stig) {
|
|
225
|
+
const { generateSTIG } = await import('../backend/report.js');
|
|
226
|
+
const stig = generateSTIG(scan ? [scan] : []);
|
|
227
|
+
console.log(stig);
|
|
136
228
|
} else {
|
|
137
229
|
console.log(JSON.stringify(findings, null, 2));
|
|
138
230
|
}
|
|
@@ -163,6 +255,10 @@ program
|
|
|
163
255
|
const { generateHTML } = await import('../backend/report.js');
|
|
164
256
|
const html = generateHTML(scansWithFindings);
|
|
165
257
|
console.log(html);
|
|
258
|
+
} else if (options.stig) {
|
|
259
|
+
const { generateSTIG } = await import('../backend/report.js');
|
|
260
|
+
const stig = generateSTIG(scansWithFindings);
|
|
261
|
+
console.log(stig);
|
|
166
262
|
} else {
|
|
167
263
|
console.log('Recent scans:', JSON.stringify(scans, null, 2));
|
|
168
264
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lateos/npm-scan",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"description": "Modern npm supply chain security scanner — detects obfuscated payloads, credential stealers, conditional triggers, sandbox evasion, and worm-like propagation. 11 attack types, SBOM, NIST/EU CRA compliance reporting.",
|
|
5
5
|
"main": "backend/index.js",
|
|
6
6
|
"bin": {
|