@lateos/npm-scan 0.7.5 → 0.8.0

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.
@@ -1,372 +0,0 @@
1
- # Project Plan: npm-scan
2
- ## Enhanced Open-Core npm Supply Chain Security Scanner
3
- ### (Successor to / Evolution of Package-Inferno)
4
-
5
- **Date:** May 2026
6
- **Version:** 1.1
7
- **Author:** Lateos (lateos.ai)
8
-
9
- ---
10
-
11
- ## 1. Project Vision & Objectives
12
-
13
- Build a best-in-class, developer-friendly npm supply chain security tool that detects sophisticated attacks like Mini Shai-Hulud (and future variants) through behavioral, static, and hybrid analysis.
14
-
15
- ### Core Goals
16
-
17
- - Detect malicious patterns: preinstall hooks, obfuscation, credential harvesting, persistence via `.claude`/`.vscode`, GitHub exfiltration, and emerging variants
18
- - Provide enterprise-grade compliance reporting and SIEM integrations
19
- - Follow open-core model: generous free tier + gated premium features
20
- - Distribute as both npm CLI and Docker images
21
- - Be easy to run locally, in CI, and at scale
22
-
23
- ### Differentiation
24
-
25
- Hybrid analysis + a formal, versioned attack taxonomy + a strong compliance/SIEM story + excellent UX.
26
-
27
- ---
28
-
29
- ## 2. Attack Taxonomy (ATK Series) — The Moat
30
-
31
- Before writing detection code, publish and maintain a versioned **npm Attack Taxonomy (ATK)**. This is modeled on the IPI taxonomy pattern and serves as the anchor for all detector development, contributor PRs, and marketing claims.
32
-
33
- Each entry defines: attack class, detection surface (static/dynamic/both), evasion surface, and mapping to NIST 800-161 controls.
34
-
35
- | ID | Class | Detection Surface | Status |
36
- |----|-------|-------------------|--------|
37
- | ATK-001 | Malicious lifecycle scripts (`preinstall`, `postinstall`, `install`) | Static | Phase 1 |
38
- | ATK-002 | Obfuscated payload delivery (hex encoding, base64, `eval`) | Static | Phase 1 |
39
- | ATK-003 | Credential harvesting (env var scraping, `.npmrc`, SSH key access) | Static + Dynamic | Phase 1 |
40
- | ATK-004 | Persistence via editor/tool config dirs (`.vscode`, `.claude`, `.cursor`) | Static | Phase 1 |
41
- | ATK-005 | Network exfiltration (GitHub API, DNS tunneling, HTTP POST to C2) | Static + Dynamic | Phase 1 |
42
- | ATK-006 | Dependency confusion / namespace squatting | Static (lockfile) | Phase 1 |
43
- | ATK-007 | Typosquatting (edit-distance matching against top-N packages) | Static | Phase 1 |
44
- | ATK-008 | Tarball tampering (published tarball ≠ source repo) | Static (diff) | Phase 2 |
45
- | ATK-009 | Conditional/dormant triggers (CI env detection, time-based activation) | Dynamic | Phase 2 |
46
- | ATK-010 | Sandbox evasion detection (anti-analysis behaviors) | Dynamic | Phase 2 |
47
- | ATK-011 | Transitive supply chain propagation (worm-style lateral spread) | Dynamic | Phase 3 |
48
-
49
- > **Governance:** ATK entries are versioned. New entries require a PR with: proof-of-concept sample, detection rule, false positive analysis, and NIST 800-161 control mapping. The taxonomy is published at `docs/attack-taxonomy.md` and referenced in all scan reports.
50
-
51
- ---
52
-
53
- ## 3. Licensing — Decided Before the First PR
54
-
55
- Licensing boundaries must be defined in `LICENSING.md` before accepting any external contributions.
56
-
57
- ### Model: Apache-2.0 core + Commons Clause premium
58
-
59
- - **Core (Apache-2.0):** Static analysis engine, ATK-001–007 detectors, CLI, lockfile scanner, SBOM output (CycloneDX), GitHub Action, Docker images, JSON output, SQLite-backed local storage, basic HTML report.
60
- - **Premium (Apache-2.0 + Commons Clause):** Dynamic sandbox (ATK-008+), advanced compliance reports (PDF, regulatory templates), SIEM connectors, reachability analysis, team dashboard, SSO, audit logs, API/webhooks, on-prem/air-gapped licenses, priority support.
61
-
62
- > **Why Commons Clause over BSL:** Commons Clause is lighter-weight, avoids the community friction HashiCorp and Elasticsearch created with BSL transitions, and the boundary ("you may not sell this software as a service") is unambiguous. BSL is a fallback only if legal counsel recommends it.
63
-
64
- ### Feature Flags
65
-
66
- Premium features are gated by a license key validated at runtime. The key system skeleton ships in Phase 0. Keys are issued per-seat for CLI, per-org for hosted.
67
-
68
- ---
69
-
70
- ## 4. Core Requirements
71
-
72
- ### Free / Open-Source Tier
73
-
74
- - Static analysis (ATK-001–007): obfuscation, credential patterns, lifecycle scripts, YARA
75
- - CLI: `scan`, `scan-lockfile`, `report` commands
76
- - Docker-based full pipeline (Enumerator → Fetcher → Analyzer → Dashboard)
77
- - SBOM output: CycloneDX (Phase 1), SPDX (Phase 2)
78
- - Basic HTML report (Phase 1); PDF report is premium (Phase 2)
79
- - GitHub Action
80
- - Policy-as-code engine (YAML config, free)
81
- - SQLite for local/CLI mode — zero external dependencies
82
-
83
- ### Premium Features (license key or hosted SaaS)
84
-
85
- - Dynamic sandbox / hybrid analysis (ATK-008–011, safe hook execution with syscall monitoring)
86
- - Advanced compliance reports (PDF/HTML, regulatory mapping: NIST 800-161, EU CRA, SOC 2, ISO 27001, DORA)
87
- - Reachability analysis (parse call graphs, surface only reachable findings)
88
- - Full SIEM connectors (Splunk TA, Microsoft Sentinel Solution, Elastic integration, QRadar)
89
- - Team dashboard, SSO, audit logs, high-scale orchestration
90
- - Priority support and on-prem/air-gapped licenses
91
- - OPA/Rego policy engine (YAML for free tier)
92
- - PostgreSQL backend (team/hosted tier)
93
-
94
- > **Note on ML-assisted false-positive reduction:** Explicitly deferred until real scan telemetry exists. Will not appear in roadmap until Phase 4+ with data to justify it.
95
-
96
- ---
97
-
98
- ## 5. Tech Stack
99
-
100
- | Layer | Technology | Notes |
101
- |-------|------------|-------|
102
- | CLI | Node.js + Commander.js | Global `npm install -g npm-scan` |
103
- | Enumerator / Fetcher | Node.js | Extends Package-Inferno structure |
104
- | Analyzer (static) | Node.js + Python 3.12+ | YARA via `yara-python` |
105
- | Dynamic Sandbox | gVisor (runsc) | See §6.1 — not vm2/isolated-vm |
106
- | Local storage (free) | SQLite | Zero-setup, file-based |
107
- | Team/hosted storage | PostgreSQL | SaaS and enterprise tier only |
108
- | Dashboard | Streamlit (MVP stub) | FastAPI + React when first enterprise customer requires it |
109
- | SBOM | cyclonedx-node + cyclonedx-python | CycloneDX 1.5 |
110
- | Reports (free) | Jinja2 → HTML | PDF is premium |
111
- | Reports (premium) | Jinja2 → WeasyPrint → PDF | With NIST 800-161 templates |
112
- | Policy Engine (free) | YAML | Shipped in core |
113
- | Policy Engine (premium) | OPA/Rego | Full enterprise policy-as-code |
114
- | Containerization | Docker + Docker Compose + GHCR | Multi-arch images |
115
- | Observability | OpenTelemetry + structured JSON logging | Opt-in telemetry only |
116
- | Licensing | Feature flags via license key validation | Skeleton in Phase 0 |
117
-
118
- ---
119
-
120
- ## 6. Architecture
121
-
122
- ```
123
- CLI Layer (npm-scan command)
124
- ↓ lightweight mode (static-only, SQLite, no Docker required)
125
- ↓ full mode (delegates to Docker Compose pipeline)
126
-
127
- Docker Compose Pipeline:
128
- [enumerator] → [Redis queue] → [fetcher] → [analyzer-static]
129
-
130
- [analyzer-sandbox] ← premium, gVisor
131
-
132
- [report-generator]
133
-
134
- [api-service (FastAPI)] + [streamlit-dashboard]
135
-
136
- Storage:
137
- SQLite (local / free tier)
138
- PostgreSQL (team / hosted tier)
139
- S3-compatible (tarball cache, optional)
140
-
141
- Output Formats:
142
- JSON (structured findings, machine-readable)
143
- CycloneDX SBOM (Phase 1) + SPDX (Phase 2)
144
- HTML report (free)
145
- PDF report with regulatory mappings (premium)
146
- SIEM formats: OCSF, CEF, ECS (premium, Phase 3)
147
- ```
148
-
149
- ### 6.1 Dynamic Sandbox Architecture — Security-First
150
-
151
- > The sandbox executes malicious code by design. One escape on a user's machine destroys the tool's reputation. This section is non-negotiable.
152
-
153
- **Selected isolation stack: gVisor (runsc)**
154
-
155
- - Kernel-level syscall interception without a full VM — Docker-compatible, production-hardened
156
- - Firecracker microVMs as an optional upgrade for highest-assurance environments
157
- - **Explicitly not used:** vm2 (repeated escapes), isolated-vm (Node-based, insufficient for hostile payloads)
158
-
159
- **Sandbox threat model (required before Phase 2 ships):**
160
-
161
- | Threat | Mitigation |
162
- |--------|------------|
163
- | Syscall escape | gVisor intercepts all syscalls at the gVisor kernel boundary |
164
- | Network exfiltration during analysis | Network namespace isolation; egress blocked except to monitored sink |
165
- | Filesystem escape | Read-only bind mounts; package extracted to ephemeral tmpfs |
166
- | Resource exhaustion (CPU/memory bomb) | cgroup limits: 1 CPU, 512MB RAM, 30s timeout |
167
- | Sandbox detection by malware | Randomized env vars, realistic process tree, no obvious sandbox markers |
168
- | Tarball extraction bomb | Size limits enforced before extraction (uncompressed cap: 500MB) |
169
-
170
- **Anti-sandbox-evasion (ATK-010):** The analyzer checks for behaviors that indicate the package probes its environment before acting (hostname checks, `CI` env var detection, timing attacks). Detection of evasion attempts is itself a high-severity finding.
171
-
172
- ---
173
-
174
- ## 7. Adversarial Posture
175
-
176
- npm-scan is a security tool that will be actively studied by the people it's designed to catch. The plan explicitly addresses this.
177
-
178
- ### Evasion Resistance
179
-
180
- - YARA rules and behavioral signatures are versioned and updated on a defined cadence
181
- - ATK taxonomy includes known evasion techniques per detector (documented in `docs/attack-taxonomy.md`)
182
- - Obfuscation detection uses AST-level analysis, not regex — resistant to trivial reformatting
183
- - Conditional trigger detection (ATK-009) specifically targets code that behaves differently in sandbox vs. production
184
-
185
- ### Supply Chain Integrity of npm-scan Itself
186
-
187
- > npm-scan must not be a supply chain attack vector.
188
-
189
- - All releases signed with `npm provenance` (Sigstore) from day one
190
- - npm-scan's own `package.json` is scanned by npm-scan in CI (self-attestation)
191
- - Lockfile committed and integrity-checked in CI
192
- - SBOM generated and published with every release
193
- - Dependency update PRs gated on passing scan results
194
-
195
- ### Known Evasion Vectors (documented, not hidden)
196
-
197
- The `docs/evasion-known.md` file catalogues known evasion techniques so contributors know what to harden against. Transparency about limitations builds trust; hiding them does not.
198
-
199
- ---
200
-
201
- ## 8. Compliance Strategy — NIST 800-161 First
202
-
203
- > Attempting to map five frameworks simultaneously produces five shallow implementations. One framework done properly is worth more than five done poorly.
204
-
205
- ### Phase 2: NIST SP 800-161r1 (Cybersecurity Supply Chain Risk Management)
206
-
207
- **Why NIST 800-161 first:**
208
- - Supply-chain-specific (directly relevant to the tool's purpose)
209
- - Maps to CMMC Level 2 — existing domain expertise from SecureStack/DockerShield work
210
- - US government and defense contractor buyers have budget and mandate
211
- - NIST → FedRAMP → CMMC creates a coherent enterprise sales story
212
-
213
- **Phase 2 deliverable:** A compliance report template that maps each npm-scan finding to the relevant NIST 800-161 practice (SR-series controls). PDF output with finding → control traceability matrix.
214
-
215
- ### Phase 3+: Additional Frameworks (in order)
216
-
217
- 1. EU Cyber Resilience Act (CRA) — EU enterprise buyers
218
- 2. SOC 2 Type II (CC6.x supply chain controls) — SaaS buyers
219
- 3. ISO 27001:2022 (A.15 supplier relationships) — global enterprise
220
- 4. DORA — financial sector EU buyers
221
-
222
- Each framework addition is a versioned template, not a rewrite of the report engine.
223
-
224
- ---
225
-
226
- ## 9. Phased Roadmap
227
-
228
- ### Phase 0: Foundation (Week 1)
229
-
230
- **Exit criteria:** npm name claimed, repo structured, licensing documented, license key skeleton wired, Docker images published.
231
-
232
- - [ ] Claim `npm-scan` on npm (publish stub immediately)
233
- - [ ] Fork/rename Package-Inferno repo → monorepo structure (`/cli`, `/backend`, `/docker`, `/docs`)
234
- - [ ] Write `LICENSING.md` — define Apache-2.0 + Commons Clause boundary explicitly
235
- - [ ] Write `docs/attack-taxonomy.md` — ATK-001–007 initial entries with NIST 800-161 mappings
236
- - [ ] License key feature-flag skeleton (runtime validation, graceful degradation)
237
- - [ ] SQLite schema for local mode (replaces PostgreSQL for free tier)
238
- - [ ] `CONTRIBUTING.md` referencing ATK taxonomy governance process
239
- - [ ] Initial Docker images published to GHCR (multi-arch)
240
- - [ ] Basic CI/CD pipeline (GitHub Actions): lint, test, image build
241
-
242
- ### Phase 1: MVP — Production Ready Scanner (Weeks 2–4)
243
-
244
- **Exit criteria:** Static scanner runs on any package in <30s; false positive rate <2% on top-500 npm packages; GitHub Action published; SBOM output validated.
245
-
246
- **Priority sequence:**
247
-
248
- 1. **GitHub Action first** — highest-leverage distribution channel
249
- 2. **ATK-001–007 static detectors** hardened against Shai-Hulud patterns
250
- 3. **CLI:** `npm-scan scan <package>`, `npm-scan scan-lockfile`, `npm-scan report`
251
- 4. **SQLite-backed scan history** for local users (no external dependencies)
252
- 5. **CycloneDX SBOM output** with findings embedded as vulnerabilities
253
- 6. **Basic HTML report** (Jinja2 template; PDF deferred to Phase 2)
254
- 7. **YAML policy engine** (allowlists, severity overrides, block-on-severity)
255
- 8. **Docker Compose improvements** — one-command start, health checks
256
- 9. **Test corpus:** 50+ clean packages + 20+ malicious samples including Shai-Hulud variants
257
-
258
- **Not in Phase 1:** PDF reports, PostgreSQL, sandbox, ML.
259
-
260
- ### Phase 2: Hybrid Analysis & Compliance (Weeks 5–7)
261
-
262
- **Exit criteria:** Sandbox threat model documented and reviewed; NIST 800-161 report template covers SR-series controls; dynamic analysis catches ATK-008/009 patterns.
263
-
264
- - Dynamic sandbox service (gVisor-based) — full threat model shipped before first user
265
- - ATK-008–010 behavioral detectors
266
- - NIST 800-161r1 compliance report template (PDF via WeasyPrint)
267
- - SPDX SBOM output (complement to CycloneDX)
268
- - Reachability analysis (lockfile call graph parsing — surfaces only reachable findings)
269
- - Dashboard: "Compliance" tab with NIST control mapping
270
- - ATK taxonomy updated with sandbox-derived evasion findings
271
- - Self-scan of npm-scan in CI using sandbox tier
272
-
273
- ### Phase 3: Enterprise & Integrations (Weeks 8–11)
274
-
275
- **Exit criteria:** At least one Splunkbase listing live; one paying customer.
276
-
277
- - SIEM exporters: Splunk TA, Microsoft Sentinel Solution, Elastic integration, QRadar
278
- - FastAPI-based REST API + webhooks
279
- - Team features: multi-user, RBAC, audit logs
280
- - Feature-flag enforcement (license key hard gating)
281
- - EU CRA compliance report template
282
- - PostgreSQL backend for hosted/team tier
283
- - Kubernetes / Helm chart
284
- - Publish to Splunkbase + Azure Marketplace (content packs)
285
- - ATK-011 transitive propagation detector
286
-
287
- ### Phase 4: Polish & Scale (Ongoing)
288
-
289
- - VS Code extension (surfaces findings inline)
290
- - SOC 2 + ISO 27001 compliance templates
291
- - DORA template (financial sector)
292
- - Hosted SaaS option with usage-based billing
293
- - Opt-in telemetry (aggregate false positive rates feed detector improvement)
294
- - Marketing site + pricing page
295
- - ML-assisted scoring — **only after telemetry data justifies it**
296
-
297
- ---
298
-
299
- ## 10. Distribution & Packaging
300
-
301
- | Channel | Details |
302
- |---------|---------|
303
- | npm (`npm-scan`) | Global CLI + lightweight static-only mode |
304
- | Docker images (GHCR) | Focused images per service + all-in-one + Compose file |
305
- | GitHub Releases | Binaries via `pkg` for offline/air-gapped use |
306
- | GitHub Action | `lateos/npm-scan-action` — Phase 1 priority |
307
- | Splunkbase | Splunk TA for SIEM integration — Phase 3 |
308
- | Azure Marketplace | Sentinel Solution content pack — Phase 3 |
309
-
310
- ---
311
-
312
- ## 11. Success Metrics — Operational, Not Vanity
313
-
314
- Lagging indicators (stars, press) are tracked but not used for go/no-go decisions. Leading indicators drive phase gates:
315
-
316
- | Metric | Target | Phase Gate |
317
- |--------|--------|------------|
318
- | False positive rate on top-500 npm packages | < 2% | Phase 1 exit |
319
- | Static scan time (average package) | < 30 seconds | Phase 1 exit |
320
- | Dynamic scan time (average package) | < 5 minutes | Phase 2 exit |
321
- | ATK taxonomy entries with passing detector tests | 100% | Every phase |
322
- | NIST 800-161 control coverage in compliance report | SR-series complete | Phase 2 exit |
323
- | Paying customers | ≥ 1 | Phase 3 exit |
324
- | Splunkbase listing live | Yes/No | Phase 3 exit |
325
- | npm-scan self-scan passing in CI | Always green | Ongoing |
326
-
327
- ---
328
-
329
- ## 12. Risks & Mitigations
330
-
331
- | Risk | Likelihood | Mitigation |
332
- |------|-----------|------------|
333
- | Sandbox escape damages user environment | Low but catastrophic | gVisor isolation; full threat model before Phase 2 ships; security advisory process |
334
- | False positives erode trust | Medium | Strict test corpus; allowlist support; reachability analysis reduces noise |
335
- | Maintenance burden | High (solo) | Modular design; ATK taxonomy governance keeps contributions structured |
336
- | Legal / license friction | Low | Commons Clause is cleaner than BSL; `LICENSING.md` published Day 1 |
337
- | npm registry rate limiting | Medium | Exponential backoff + S3 tarball cache |
338
- | Adversarial evasion of detectors | High (over time) | ATK taxonomy documents evasion surface; AST-level analysis over regex; evasion findings = high severity |
339
- | npm-scan itself becomes a target | Medium | Sigstore provenance; self-scan in CI; SBOM published with every release |
340
- | Feature scope creep (ML, etc.) | High | ML explicitly gated behind telemetry prerequisite; roadmap is sequential not parallel |
341
-
342
- ---
343
-
344
- ## 13. Deliverables Per Phase
345
-
346
- Each phase ships:
347
-
348
- - Working code + Docker Compose (validated one-command start)
349
- - Comprehensive README with real examples
350
- - Test corpus additions (clean + malicious packages)
351
- - Updated `docs/attack-taxonomy.md`
352
- - CI/CD pipeline passing (including self-scan for Phase 2+)
353
- - `CHANGELOG.md` entry referencing ATK IDs addressed
354
-
355
- ---
356
-
357
- ## 14. Immediate Next Steps (Phase 0, Day 1)
358
-
359
- Ordered by dependency and leverage:
360
-
361
- 1. `npm publish npm-scan` — claim the name before someone else does
362
- 2. Write `LICENSING.md` — unblocks external contributions
363
- 3. Write `docs/attack-taxonomy.md` — ATK-001–007, NIST mappings — unblocks detector PRs
364
- 4. Wire license key skeleton — unblocks premium feature development
365
- 5. SQLite schema for local mode — removes PostgreSQL dependency from free tier
366
- 6. Monorepo structure (`/cli`, `/backend`, `/docker`, `/docs`)
367
- 7. Initial Docker images to GHCR
368
- 8. GitHub Action stub — distribution channel, even before full detection logic
369
-
370
- ---
371
-
372
- *This document is the canonical project plan for npm-scan v1.x. Changes require updating both this document and the affected ATK taxonomy entries.*
@@ -1,91 +0,0 @@
1
- # Sandbox Threat Model — npm-scan Dynamic Analysis
2
-
3
- ## Overview
4
-
5
- The dynamic sandbox executes untrusted npm packages in an isolated environment to detect behavioral attacks (ATK-008–010). Escape would compromise the host, so isolation is the highest-priority design constraint.
6
-
7
- ## Isolation Stack
8
-
9
- **Primary: gVisor (runsc)**
10
- - Kernel-level syscall interception without a full VM
11
- - Docker-compatible, production-hardened by Google
12
- - Each analysis runs in a separate sandbox container
13
-
14
- **Fallback: Firecracker microVMs**
15
- - For highest-assurance / air-gapped environments
16
- - Slower startup, stronger isolation boundary
17
-
18
- **Explicitly excluded:**
19
- - `vm2` — repeated CVEs (CVE-2023-29017, etc.), known escape surface
20
- - `isolated-vm` — Node-based V8 isolate, insufficient for hostile native code
21
-
22
- ## Threat Model
23
-
24
- | Threat | Severity | Mitigation |
25
- |--------|----------|------------|
26
- | Syscall escape | Critical | gVisor intercepts all syscalls at kernel boundary; no uncontained syscalls reach host kernel |
27
- | Network exfiltration during analysis | Critical | Network namespace isolation; egress blocked except to monitored sink/HTTPBun |
28
- | Filesystem escape | High | Read-only bind mounts; package extracted to ephemeral tmpfs; no persistent mounts |
29
- | Resource exhaustion (CPU/memory bomb) | Medium | cgroup limits: 1 CPU, 512MB RAM, 30s timeout; SIGKILL on timeout |
30
- | Sandbox detection by malware | Medium | Randomized env vars, realistic process tree, no obvious sandbox markers in `/proc` |
31
- | Tarball extraction bomb | Medium | Size limits enforced before extraction (uncompressed cap: 500MB) |
32
- | Container escape via Docker socket | Critical | Sandbox containers get no Docker socket mount; `--privileged` never used |
33
- | Filesystem write amplification | Low | tmpfs capped at 512MB; no disk-backed volumes |
34
-
35
- ## Network Isolation
36
-
37
- - **Default mode:** Full egress blocked. Container has loopback only.
38
- - **Monitor mode (opt-in):** Egress allowed through a MITM proxy (HTTPBun/sink) that records all requests for post-analysis review. The malware believes it has network access; actually all traffic is captured.
39
-
40
- ## Filesystem Layout
41
-
42
- ```
43
- /tmp/analysis/{id}/
44
- ├── package/ # extracted tarball (read-only bind mount)
45
- ├── run.js # runner script that executes the package
46
- └── output.json # structured findings after timeout
47
- ```
48
-
49
- ## Timeouts and Lifecycle
50
-
51
- 1. Container starts → package extracted → 30s countdown begins
52
- 2. Runner runs `npm install && node index.js` (or entry point from package.json)
53
- 3. All syscalls logged via gVisor's `strace`-style logging
54
- 4. On timeout (30s): SIGKILL, logs collected, findings generated
55
- 5. Container destroyed, tmpfs unmounted
56
-
57
- ## Analysis Pipeline
58
-
59
- ```
60
- [fetch tarball] → [verify size < 500MB] → [extract to tmpfs]
61
- → [gVisor sandbox: execute entry point for 30s]
62
- → [collect syscall log + file system diffs]
63
- → [run ATK-008/009/010 behavioral detectors on trace]
64
- → [merge static + dynamic findings]
65
- → [cleanup: destroy container, unmount tmpfs]
66
- ```
67
-
68
- ## Anti-Sandbox-Evasion (ATK-010)
69
-
70
- The analyzer checks for behaviors indicating the package probes its environment:
71
-
72
- - Hostname checks (expects randomized, non-obvious hostnames)
73
- - `CI` env var detection (sandbox should NOT set CI-like env vars)
74
- - Timing attacks (`performance.now()`, `process.hrtime()`)
75
- - Process tree inspection (`process.ppid`, `/proc/self/status`)
76
- - Stack trace capture (Error().stack inspection)
77
-
78
- Detection of evasion attempts is itself a high-severity finding.
79
-
80
- ## Threat Model Review Cadence
81
-
82
- | Type | Frequency | Owner |
83
- |------|-----------|-------|
84
- | Full threat model review | Every major release (quarterly) | Security lead |
85
- | gVisor CVE monitoring | Continuous (automated alerts) | DevOps |
86
- | Sandbox escape test | Every PR adding a new ATK detector | CI pipeline |
87
- | Penetration test | Annual (third-party) | External firm |
88
-
89
- ---
90
-
91
- *This document is a living artifact. Update when: gVisor version changes, new ATK entries require dynamic analysis, or a CVSS 7+ CVE affects the isolation stack.*
@@ -1,93 +0,0 @@
1
- import { globSync } from 'glob';
2
- import { readFileSync, mkdtempSync } from 'fs';
3
- import { execSync } from 'child_process';
4
- import { fetchPackage, cleanup } from '../../backend/fetch.js';
5
- import { runAll } from '../../backend/detectors/index.js';
6
- import os from 'os';
7
- import path from 'path';
8
-
9
- function scanLocalTarball(tarPath) {
10
- const tmpDir = mkdtempSync(path.join(os.tmpdir(), 'npm-scan-corpus-'));
11
- execSync(`tar xzf "${tarPath}" -C "${tmpDir}"`, { stdio: 'pipe' });
12
- const pkgPath = globSync(path.join(tmpDir, '**', 'package.json'), { nodir: true })[0];
13
- if (!pkgPath) throw new Error(`No package.json in ${tarPath}`);
14
- const pkgJson = JSON.parse(readFileSync(pkgPath, 'utf8'));
15
- const pkgDir = path.dirname(pkgPath);
16
- const jsFiles = globSync(path.join(pkgDir, '**', '*.js'), { nodir: true }).map(p => ({
17
- path: p,
18
- content: readFileSync(p, 'utf8')
19
- }));
20
- return { pkgJson, jsFiles, tmpDir };
21
- }
22
-
23
- let cleanFails = 0;
24
- let malFails = 0;
25
- let cleanTotal = 0;
26
- let malTotal = 0;
27
-
28
- console.log('--- Clean corpus (remote) ---');
29
- for (const pkg of ['lodash', 'chalk', 'react', 'axios', 'express']) {
30
- cleanTotal++;
31
- try {
32
- const { pkgJson, jsFiles, tmpDir } = await fetchPackage(pkg);
33
- const findings = await runAll(pkgJson, jsFiles);
34
- const bad = findings.filter(f => f.severity === 'high' || f.severity === 'critical');
35
- const badIds = bad.map(f => f.id).join(', ');
36
- if (bad.length > 0) {
37
- console.log(` FAIL ${pkg}: ${bad.length} high/crit (${badIds})`);
38
- cleanFails++;
39
- } else {
40
- console.log(` OK ${pkg} (no high/crit findings)`);
41
- }
42
- cleanup(tmpDir);
43
- } catch (e) {
44
- console.log(` ERR ${pkg}: ${e.message}`);
45
- cleanFails++;
46
- }
47
- }
48
-
49
- console.log('--- Malicious corpus (local) ---');
50
- const malTars = globSync('tests/corpus/malicious/*.tgz');
51
- malTotal = malTars.length;
52
- for (const tar of malTars) {
53
- const name = path.basename(tar, '.tgz');
54
- try {
55
- const { pkgJson, jsFiles } = scanLocalTarball(tar);
56
- const findings = await runAll(pkgJson, jsFiles);
57
- const ids = findings.map(f => f.id).join(', ');
58
- if (findings.length === 0) {
59
- console.log(` FAIL ${name}: no findings`);
60
- console.log(` scripts: ${JSON.stringify(pkgJson.scripts || {})}`);
61
- console.log(` deps: ${JSON.stringify(pkgJson.dependencies || {})}`);
62
- console.log(` js files: ${jsFiles.length}`);
63
- malFails++;
64
- } else {
65
- console.log(` OK ${name}: ${ids}`);
66
- }
67
- } catch (e) {
68
- console.log(` ERR ${name}: ${e.message}`);
69
- malFails++;
70
- }
71
- }
72
-
73
- const fpRate = cleanTotal > 0 ? (cleanFails / cleanTotal * 100).toFixed(1) : 'N/A';
74
- const malDetectRate = malTotal > 0 ? ((malTotal - malFails) / malTotal * 100).toFixed(1) : 'N/A';
75
- console.log(`\n=== Corpus Results ===`);
76
- console.log(`Clean packages: ${cleanTotal}, Malicious samples: ${malTotal}`);
77
- console.log(`Clean FP rate: ${fpRate}% (${cleanFails}/${cleanTotal} high/crit)`);
78
- console.log(`Mal detect rate: ${malDetectRate}% (${malTotal - malFails}/${malTotal})`);
79
-
80
- let exitCode = 0;
81
- if (fpRate !== 'N/A' && Number(fpRate) >= 2) {
82
- console.log(`FP <2% : FAIL (${fpRate}% exceeds 2%)`);
83
- exitCode = 1;
84
- } else {
85
- console.log('FP <2% : PASS');
86
- }
87
- if (malDetectRate !== 'N/A' && Number(malDetectRate) < 100) {
88
- console.log(`Mal 100% : FAIL (${malDetectRate}%)`);
89
- exitCode = 1;
90
- } else {
91
- console.log('Mal 100% : PASS');
92
- }
93
- process.exit(exitCode);