fad-checker 2.2.2 → 2.2.4
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 +45 -310
- package/data/popular-packages.json +39 -0
- package/fad-checker.js +48 -4
- package/lib/charts.js +293 -0
- package/lib/core.js +25 -3
- package/lib/cve-match.js +9 -2
- package/lib/cve-report.js +243 -80
- package/lib/json-export.js +5 -0
- package/lib/malware.js +100 -0
- package/lib/osv-db.js +160 -0
- package/lib/version-overlay.js +8 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,42 +5,47 @@
|
|
|
5
5
|
[](https://github.com/n8tz/fad-checker/blob/main/package.json)
|
|
6
6
|
[](https://nodejs.org)
|
|
7
7
|
|
|
8
|
-
> **F**
|
|
9
|
-
|
|
10
|
-
`fad-checker` scans **Maven**, **npm**, **Yarn**, **Composer (PHP)**, **PyPI (Python)**, **NuGet (C#/.NET)**, **Go**, **Ruby**, **vendored JavaScript** and **committed native binaries** in any source tree — multi-module, monorepo, polyglot, whatever you've got — and produces a single self-contained HTML report with CVE (prioritised by **EPSS + CISA KEV**), EOL, obsolete, outdated and **license** findings, plus per-ecosystem fix recipes. It also exports a **CycloneDX 1.6 SBOM** and a **CSAF 2.0 VEX**.
|
|
8
|
+
> **F**abulous **A**utonomous **D**ependency **C**hecker<br>
|
|
9
|
+
> AKA **F**uckin' **A**utonomous **D**ependency **C**hecker<br>
|
|
11
10
|
|
|
11
|
+
`fad-checker` audits **Maven · npm · Yarn · pnpm · Composer · PyPI · NuGet · Go · Ruby**, vendored JavaScript and committed native binaries in any source tree — multi-module, monorepo, polyglot — and produces a self-contained **HTML + Word report** (CVE prioritised by EPSS + CISA KEV, EOL, obsolete, outdated, licenses) plus **CycloneDX SBOM / CSAF VEX / SARIF / JSON** exports. **No build tools, no Docker, no network needed** — it reads lockfiles and manifests straight off disk.
|
|
12
12
|
|
|
13
13
|
🌐 **[Project site & docs →](https://n8tz.github.io/fad-checker/)**
|
|
14
14
|
|
|
15
|
-
<p align="center"><img src="docs/assets/
|
|
15
|
+
<p align="center"><img src="docs/assets/demo.gif" alt="fad-checker animated terminal demo — a [n/N] checklist warming each vulnerability database, then CVE findings coloured by severity with KEV badges" height="600"></p>
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
## Features
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
- **9 ecosystems in one pass** — Maven, npm/Yarn/pnpm, Composer (PHP), PyPI, NuGet, Go, Ruby; plus **vendored JS** (retire.js), committed **native binaries** (`.dll`/`.exe`/`.so`/`.dylib`, identified by checksum via deps.dev + CIRCL) and **embedded JARs** (fat-jars/war/ear, unzipped in-memory).
|
|
20
|
+
- **No build tools** — reads `pom.xml`, `package-lock`/`yarn.lock`/`pnpm-lock`, `composer.lock`, `poetry`/`Pipfile`/`uv`/`pdm` locks, `packages.lock.json`/`*.csproj`, `go.mod`, `Gemfile.lock` directly. No `mvn`/`npm install`/`pip`/`dotnet restore`/`go build`/`bundle`, no `node_modules/`. → [how it stays build-free](docs/COMPARISON.md#how-its-autonomous-no-build-tools)
|
|
21
|
+
- **CVE, merged & prioritised** — CVEProject + OSV.dev + NVD, CPE/version cross-checked to cut false positives, ranked **CISA KEV → EPSS → CVSS**.
|
|
22
|
+
- **Per-module Maven version mediation** — recovers vulnerable transitive versions that a global `<dependencyManagement>` pin hides in another module (lifted Snyk-corroborated coverage **156 → 181** on a real 25-module reactor, finding CVEs a single Snyk scan missed).
|
|
23
|
+
- **Air-gapped / PASSI** — **zero network under `--offline`** (regression-tested), offline Maven transitive resolution, and `--osv-db` for cache-independent offline OSV recall. → [Air-gapped](#air-gapped--passi-audits)
|
|
24
|
+
- **Supply-chain risk** — known-**malicious** advisories (`MAL-`, always block the CI gate) + suspected **typosquats** (`--typosquat`).
|
|
25
|
+
- **Lifecycle** — EOL (endoflife.date), obsolete/deprecated, outdated — across every ecosystem.
|
|
26
|
+
- **Licenses** *(opt-in `--licenses`)* — SPDX-normalised, copyleft/proprietary flagged.
|
|
27
|
+
- **Outputs & CI** — HTML + Word `.doc`, CycloneDX 1.6 SBOM, CSAF 2.0 VEX, SARIF 2.1.0, JSON; gate with `--fail-on`, triage with `--ignore`/`--vex`.
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
📖 **[Usage & all flags](docs/USAGE.md)** · **[Architecture](docs/ARCHITECTURE.md)** · **[Comparison vs other tools](docs/COMPARISON.md)** · **[Data sources](docs/DATA-SOURCES.md)**
|
|
22
30
|
|
|
23
|
-
##
|
|
31
|
+
## Quick start
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g fad-checker
|
|
35
|
+
fad-checker -s ./my-project # → ./fad-checker-report/cve-report.html
|
|
36
|
+
```
|
|
26
37
|
|
|
27
|
-
|
|
28
|
-
| --- | --- |
|
|
29
|
-
| Maven installed | `pom.xml` files are parsed directly with xml2js. Properties, profiles and local BOMs are resolved in-process. Transitive deps fetched from Maven Central if `--transitive` (cached forever). |
|
|
30
|
-
| `mvn dependency:tree` | Same as above. We walk the tree ourselves. |
|
|
31
|
-
| `npm install` / a `node_modules/` | `package-lock.json` (v1/v2/v3), `yarn.lock` (v1 + Berry/v2+) and `pnpm-lock.yaml` (v5/v6/v9) are parsed as text/JSON/YAML. Versions come from the lockfile — no installation. |
|
|
32
|
-
| `yarn install` / `pnpm install` | Same. We read `yarn.lock` (v1 + Berry) and `pnpm-lock.yaml` directly. |
|
|
33
|
-
| `composer install` | `composer.lock` is parsed directly (concrete versions + transitive). `composer.json` alone → best-effort on pinned versions + warning. |
|
|
34
|
-
| `pip` / `poetry` / a venv | `poetry.lock`, `Pipfile.lock`, `uv.lock`, `pdm.lock` are parsed for concrete versions; `pyproject.toml` (PEP 621 + poetry) and `requirements.txt` (following `-r`/`-c` includes) are best-effort on exact pins. Names normalised per PEP 503. |
|
|
35
|
-
| `dotnet restore` | `packages.lock.json` is parsed; otherwise `*.csproj`/`*.fsproj`/`*.vbproj` (+ `Directory.Packages.props` Central Package Management) and legacy `packages.config`, best-effort on pinned versions. |
|
|
36
|
-
| `go build` / a Go toolchain | `go.mod` is parsed (the full pruned graph on Go ≥1.17, `// indirect` → transitive); `go.sum` is the fallback. No module download. |
|
|
37
|
-
| `bundle install` | `Gemfile.lock` is parsed for the resolved gem set. No Ruby, no bundler. |
|
|
38
|
-
| `snyk` binary | Built-in CVE matching via CVEProject + OSV + NVD (merged), prioritised with EPSS + CISA KEV (see below). Snyk is *optional* (`--snyk`). |
|
|
39
|
-
| A network connection | First run downloads CVE / OSV / EOL data; subsequent runs use cached copies (`--offline` to force). |
|
|
38
|
+
A free [NVD API key](https://nvd.nist.gov/developers/request-an-api-key) (instant) gives 10× faster enrichment: `fad-checker --set-nvd-key YOUR_KEY`. A few common runs — full list via `fad-checker --help` or [docs/USAGE.md](docs/USAGE.md):
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
```bash
|
|
41
|
+
fad-checker -s ./proj -e "^com\.acme\." # exclude private libs (coord regex)
|
|
42
|
+
fad-checker -s ./proj -t ../clean -e "^com\.acme\." --snyk # cleaned POM tree + merge Snyk
|
|
43
|
+
fad-checker -s ./proj --offline # fully offline (zero network)
|
|
44
|
+
fad-checker -s ./proj --osv-db --typosquat # offline-complete OSV + typosquat
|
|
45
|
+
fad-checker -s ./proj --licenses --fail-on high # license chapter + CI gate
|
|
46
|
+
```
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
A single self-contained binary (no Node), from-source install and shell completion are in → [docs/USAGE.md](docs/USAGE.md).
|
|
44
49
|
|
|
45
50
|
## What it finds
|
|
46
51
|
|
|
@@ -53,6 +58,7 @@ Exactly **one** runtime dependencies must be on PATH (or installed automatically
|
|
|
53
58
|
| **1D. Unmanaged / vendored JavaScript** | [retire.js](https://retirejs.github.io/) (`--verbose`) | **Inventory of every standalone JS lib** committed into the tree (jQuery, Bootstrap, PDF.js, …) that no package manager governs — vulnerable *or not*. A cyber-hygiene constat: unknown provenance/integrity/patch story. `--no-vendored-js-inventory` to skip |
|
|
54
59
|
| **2. Vendored JS (vulnerable)** | [retire.js](https://retirejs.github.io/) | The subset of the above with known CVEs/advisories — old jQuery/Bootstrap/Angular/PDF.js copies with no lockfile |
|
|
55
60
|
| **3. CVE in dev deps** | same | Same as chapter 1, but for `test`/`provided` (Maven) and `dev`/`optional`/`peer` (npm) |
|
|
61
|
+
| **Supply-chain risk** | OSV `MAL-…` + name heuristic | **Known-malicious** packages (always block the CI gate, any `--fail-on` level) and **suspected typosquats** (`--typosquat`: an npm/PyPI name one edit from a popular package — `lodahs`↔`lodash`) |
|
|
56
62
|
| **4. EOL frameworks** | endoflife.date | Spring Boot 2.5, Hibernate 4.x, EOL JDKs, AngularJS, Laravel/Symfony, Django, .NET, etc. |
|
|
57
63
|
| **5. Obsolete libraries** | curated list (Maven) + registry maintainer flags | log4j 1.x, jackson-mapper-asl, joda-time, …; npm `deprecated`, Composer `abandoned`, PyPI `yanked`/inactive, NuGet `deprecation` |
|
|
58
64
|
| **6. Outdated libraries** | Maven Central + npm / Packagist / PyPI / NuGet registries | Available newer versions, with release dates |
|
|
@@ -61,171 +67,18 @@ Exactly **one** runtime dependencies must be on PATH (or installed automatically
|
|
|
61
67
|
|
|
62
68
|
The HTML report opens in any browser, contains every detail (CVSS vectors, references, full descriptions, CPE configurations, via-paths for transitives) and ships a Word-compatible `.doc` twin. Every match carries a **composite priority** (KEV-exploited > EPSS likelihood > CVSS severity), and the run can additionally emit a **CycloneDX 1.6 SBOM** (`--report-sbom`, vulnerabilities inline) and a **CSAF 2.0 VEX** (`--report-csaf`) for downstream tooling.
|
|
63
69
|
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Quick start
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
npm install -g fad-checker
|
|
70
|
-
fad-checker -s ./my-project
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
That's it. The report lands in `./fad-checker-report/cve-report.html`.
|
|
74
|
-
|
|
75
|
-
Want a 10× faster NVD enrichment? [Get a free NVD API key](https://nvd.nist.gov/developers/request-an-api-key) (instant), then:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
fad-checker --set-nvd-key YOUR_KEY
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## Common runs
|
|
84
|
-
|
|
85
|
-
```bash
|
|
86
|
-
# Read-only full scan (default: all sources on)
|
|
87
|
-
fad-checker -s ./proj
|
|
88
|
-
|
|
89
|
-
# Exclude private/internal libs by groupId/name regex
|
|
90
|
-
fad-checker -s ./proj -e "^(com\.acme|org\.private)\."
|
|
91
|
-
|
|
92
|
-
# Ignore whole sub-paths during the walk (gitignore-style glob, relative to -s)
|
|
93
|
-
fad-checker -s ./proj --exclude-path "packages/legacy/**" --exclude-path "**/fixtures/**"
|
|
94
|
-
fad-checker -s ./proj --no-default-excludes # walk everything (incl. node_modules/.git)
|
|
95
|
-
|
|
96
|
-
# Also write cleaned POMs (private deps stripped, ready for Snyk)
|
|
97
|
-
fad-checker -s ./proj -t ../proj-clean -e "^com\.acme\."
|
|
98
|
-
|
|
99
|
-
# Then run Snyk on the cleaned tree and merge findings
|
|
100
|
-
fad-checker -s ./proj -t ../proj-clean -e "^com\.acme\." --snyk
|
|
101
|
-
|
|
102
|
-
# Faster: skip Maven Central / no transitive walk
|
|
103
|
-
fad-checker -s ./proj --no-all-libs --no-transitive
|
|
104
|
-
|
|
105
|
-
# Fully offline (uses cached data only)
|
|
106
|
-
fad-checker -s ./proj --offline
|
|
107
|
-
|
|
108
|
-
# License + copyleft-policy chapter (off by default)
|
|
109
|
-
fad-checker -s ./proj --licenses
|
|
110
|
-
|
|
111
|
-
# Pick ecosystems — --ecosystem is a list: auto (default) | all | comma list
|
|
112
|
-
fad-checker -s ./proj --ecosystem maven # Maven only
|
|
113
|
-
fad-checker -s ./proj --ecosystem maven,npm,pypi # several
|
|
114
|
-
fad-checker -s ./proj --no-nuget --no-composer # or opt out per codec
|
|
115
|
-
fad-checker -s ./proj --no-binaries # skip the native-binary scan
|
|
116
|
-
|
|
117
|
-
# Private registry + reusable defaults (see "Configuration file & environment")
|
|
118
|
-
fad-checker --add-repo npm verdaccio https://npm.acme.com/ --token "$NPM_TOKEN"
|
|
119
|
-
fad-checker --config ./ci/fad-env.json # all options from a JSON file
|
|
120
|
-
fad-checker --source ./proj # --source / --src are aliases
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Run `fad-checker --help` for the full flag list.
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
## What a report looks like
|
|
128
|
-
|
|
129
70
|
<p align="center"><img src="docs/assets/report.png" alt="fad-checker HTML report — executive summary with severity tiles and a detailed CVE table with CWE, descriptions and fix versions" width="900"></p>
|
|
130
71
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
╭───────────────────────────────────────────────────╮
|
|
135
|
-
│ fad-checker v2.2.1 · Autonomous Dependency Checker │
|
|
136
|
-
╰───────────────────────────────────────────────────╯
|
|
137
|
-
Executive Summary [CRITICAL] — 1708 dependencies scanned
|
|
138
|
-
• 81 CVE in production deps (critical=5, high=53, medium=12, low=11)
|
|
139
|
-
• 32 CVE in dev/test deps · 17 vulnerable vendored JS (retire.js)
|
|
140
|
-
• 2 EOL frameworks · 13 obsolete/deprecated · 172 outdated
|
|
141
|
-
• 4 scan-completeness alerts — see chapter 0
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
The HTML report organises this into chapters (0 Warnings, 1 CVE prod, 1B embedded JARs,
|
|
145
|
-
1C native binaries, 1D vendored-JS inventory, 2 vendored-JS vulns, 3 dev CVE, 4 EOL,
|
|
146
|
-
5 obsolete, 6 outdated, 7 Licenses *(opt-in)*, 8 Fix Recommendations), each grouped by
|
|
147
|
-
ecosystem and manifest. Every CVE row carries: severity badge · CVE/GHSA id · coord &
|
|
148
|
-
version · declaring manifest · source(s) · fix-version · priority (KEV/EPSS) — click for
|
|
149
|
-
the full panel (CVSS vectors, references, transitive paths, CPE configs).
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## Install
|
|
154
|
-
|
|
155
|
-
### As a global CLI
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
npm install -g fad-checker
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### From source
|
|
162
|
-
|
|
163
|
-
```bash
|
|
164
|
-
git clone <repo-url> fad-checker
|
|
165
|
-
cd fad-checker
|
|
166
|
-
npm install
|
|
167
|
-
node fad-checker.js --help
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Single-binary build (no Node required)
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
npm install # one-time, brings in bun
|
|
174
|
-
npm run build # → dist/fad-checker-linux + dist/fad-checker.exe
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Shell completion
|
|
178
|
-
|
|
179
|
-
```bash
|
|
180
|
-
fad-checker --completion bash > /etc/bash_completion.d/fad-checker
|
|
181
|
-
# or for zsh:
|
|
182
|
-
fad-checker --completion zsh > ~/.zsh/completions/_fad-checker
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
## How it scans without any build tool
|
|
188
|
-
|
|
189
|
-
**TL;DR** — `fad-checker` never runs your build. For each ecosystem it reads the **lockfile** (or, failing that, the manifest's pinned versions) straight off disk to learn the exact dependency versions, then matches those coordinates against vulnerability/EOL/registry data over the network (cached, `--offline`-able). No `mvn`, `npm install`, `pip`, `dotnet restore`, `go build` or `bundle` — and no `node_modules/`.
|
|
190
|
-
|
|
191
|
-
| Ecosystem | Read directly | Transitive versions come from |
|
|
192
|
-
| --- | --- | --- |
|
|
193
|
-
| Maven | `pom.xml` (+ parents, BOMs, profiles) | child POMs fetched from Maven Central (cached) |
|
|
194
|
-
| npm / Yarn / pnpm | `package-lock.json` · `yarn.lock` (v1+Berry) · `pnpm-lock.yaml` | the lockfile itself |
|
|
195
|
-
| Composer | `composer.lock` (else `composer.json`) | the lockfile |
|
|
196
|
-
| PyPI | `poetry.lock` · `Pipfile.lock` · `uv.lock` · `pdm.lock` (else `pyproject.toml`/`requirements.txt`) | the lockfile |
|
|
197
|
-
| NuGet | `packages.lock.json` (else `*.csproj`/`packages.config`) | the lockfile |
|
|
198
|
-
| Go | `go.mod` (`// indirect` → transitive; `go.sum` fallback) | the module graph in `go.mod` |
|
|
199
|
-
| Ruby | `Gemfile.lock` (`specs:`) | the lockfile |
|
|
200
|
-
| Vendored JS / binaries | the committed `.js` / `.jar` / `.so` files themselves | n/a (read in place) |
|
|
201
|
-
|
|
202
|
-
Highlights of the matching layer: **three CVE sources merged** (CVEProject + OSV.dev + NVD), **CPE/version cross-check** to drop false positives, **EPSS + CISA KEV** prioritisation, lockfile-first with a **best-effort pinned-version fallback** when no lockfile, in-memory **embedded-JAR** unzip (no disk, no zip-slip), and **checksum identity** for native binaries. Outputs: HTML + `.doc` by default, plus opt-in `--report-sbom`/`-csaf`/`-json`/`-sarif`; CI gating via `--fail-on` with `--ignore`/`--vex` triage.
|
|
203
|
-
|
|
204
|
-
> Full per-ecosystem and per-stage detail lives in **[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)** and **[`docs/USAGE.md`](docs/USAGE.md)**.
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
## Caching
|
|
209
|
-
|
|
210
|
-
All cached data lives in `~/.fad-checker/` — every source is hit at most once per its
|
|
211
|
-
TTL (12 h–7 d, transitive POMs forever as they're immutable). Full per-cache TTL table
|
|
212
|
-
in [`docs/USAGE.md`](docs/USAGE.md). Export the lot to share between machines:
|
|
72
|
+
## Air-gapped / PASSI audits
|
|
213
73
|
|
|
214
|
-
|
|
215
|
-
fad-checker
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
> The cache export bundles **everything** under `~/.fad-checker/` (except `config.json`),
|
|
223
|
-
> including the retire.js findings **and** the warmed retire.js signature DB — so a
|
|
224
|
-
> machine that imports it can scan vendored JavaScript fully offline.
|
|
225
|
-
|
|
226
|
-
---
|
|
227
|
-
|
|
228
|
-
## Air-gapped / PASSI audits: anonymized dependency descriptor
|
|
74
|
+
> **Zero-data-sent guarantee.** Under `--offline`, fad-checker makes **no network calls
|
|
75
|
+
> whatsoever** — it reads only the warmed `~/.fad-checker/` caches and never transmits a
|
|
76
|
+
> dependency, path or finding off the machine. It is regression-tested
|
|
77
|
+
> (`test/offline-guarantee.test.js`, a tripwire fetcher that throws if touched) and
|
|
78
|
+
> auditor-reproducible: `unshare -rn node fad-checker.js -s ./proj --offline …` runs it in a
|
|
79
|
+
> namespace with **no network interface** and yields byte-identical findings. Unlike the
|
|
80
|
+
> mainstream OSS scanners, fad also resolves the **Maven transitive graph** offline — so on
|
|
81
|
+
> an air-gapped multi-module project it finds the transitive CVEs they can't.
|
|
229
82
|
|
|
230
83
|
When the audited system is **offline / confidential** (typical of a PASSI engagement) it
|
|
231
84
|
can't reach OSV / NVD / Maven Central / npm. Split the work across machines while keeping
|
|
@@ -264,134 +117,16 @@ What the descriptor (`fad-deps/1`) contains vs. drops:
|
|
|
264
117
|
|
|
265
118
|
The online phase report is itself path-free; vendored-JavaScript (retire.js) findings are
|
|
266
119
|
produced **offline in phase 3**, since retire needs the actual `.js` files — its signature
|
|
267
|
-
DB is warmed online (phase 2) and carried by `--export-cache`.
|
|
268
|
-
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-
## Custom repositories & registries
|
|
272
|
-
|
|
273
|
-
Register private **Nexus / Artifactory / Verdaccio / devpi / GOPROXY** registries (for **`maven`, `npm`, `pypi`, `ruby`, `go`**) so transitive resolution, outdated/deprecation and license lookups reach them:
|
|
274
|
-
|
|
275
|
-
```bash
|
|
276
|
-
fad-checker --add-repo maven nexus https://nexus.acme.com/repository/maven-public/ --auth alice:s3cr3t
|
|
277
|
-
fad-checker --add-repo npm verdaccio https://npm.acme.com/ --token "$NPM_TOKEN"
|
|
278
|
-
fad-checker -s ./proj --repo npm=https://npm.acme.com/ # one-off, repeatable
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
Tried in declared order, public registry last; auth via `--auth user:pass` / `--token TOK` / inline URL; responses cached per coordinate. NuGet/Composer private feeds aren't supported yet. **Full details, the PyPI/Ruby JSON-API caveat and `--list-repos`/`--remove-repo` → [`docs/USAGE.md`](docs/USAGE.md).**
|
|
282
|
-
|
|
283
|
-
---
|
|
284
|
-
|
|
285
|
-
## Configuration file & environment
|
|
286
|
-
|
|
287
|
-
Don't retype flags every run. Precedence: **CLI flag > `--config <file.json>` / `./.fad-env.json` (JSON, keys = camelCase options) > `FAD_CHECKER_ENV` (a string of CLI flags) > `~/.fad-checker/config.json` > defaults.** A file/env value only fills an option you didn't pass; **registries are merged across every layer**.
|
|
288
|
-
|
|
289
|
-
```bash
|
|
290
|
-
fad-checker --config ./ci/fad-env.json
|
|
291
|
-
FAD_CHECKER_ENV='--fail-on high --no-nuget' fad-checker -s ./proj
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
`-s` / `--src` / `--source` are aliases. **`-e/--exclude`** is a regex on the **coordinate** (drops deps); **`--exclude-path`** is a gitignore-style glob on the **path** (prunes the walk). Full `.fad-env.json` schema → [`docs/USAGE.md`](docs/USAGE.md).
|
|
295
|
-
|
|
296
|
-
---
|
|
297
|
-
|
|
298
|
-
## Data sources & acknowledgments
|
|
299
|
-
|
|
300
|
-
`fad-checker` is glue around several outstanding public datasets. Each is used per its license terms.
|
|
301
|
-
|
|
302
|
-
| Source | What we use | License | API / endpoint |
|
|
303
|
-
| --- | --- | --- | --- |
|
|
304
|
-
| [CVEProject `cvelistV5`](https://github.com/CVEProject/cvelistV5) | Daily bulk CVE bundle, filtered to Maven-relevant entries | CC0-1.0 | GitHub release asset (zip) |
|
|
305
|
-
| [OSV.dev](https://osv.dev/) (Google + GitHub Security Lab) | Per-dep vulnerability lookup (Maven, npm, Packagist, PyPI, NuGet, …) | CC-BY 4.0 | `POST api.osv.dev/v1/querybatch`, `GET api.osv.dev/v1/vulns/{id}` |
|
|
306
|
-
| [NIST NVD](https://nvd.nist.gov/) | Canonical CVE description + CVSS vectors + CPE configurations + CWE | US-gov public domain | `GET services.nvd.nist.gov/rest/json/cves/2.0?cveId=…` — free [API key](https://nvd.nist.gov/developers/request-an-api-key) bumps the rate limit 10× |
|
|
307
|
-
| [FIRST.org EPSS](https://www.first.org/epss/) | Exploit-prediction score + percentile per CVE | CC-BY 4.0 | `GET api.first.org/data/v1/epss?cve=…` (batched) |
|
|
308
|
-
| [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) | Known-exploited-vulnerability catalogue membership | US-gov public domain | `GET cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json` |
|
|
309
|
-
| [endoflife.date](https://endoflife.date/) | Framework / runtime EOL cycle data | MIT | `GET endoflife.date/api/{product}.json` |
|
|
310
|
-
| [Maven Central](https://search.maven.org/) | Latest-version lookups + transitive POM fetches | Free public service | Solr `search.maven.org/solrsearch/select?q=…` + `repo1.maven.org/maven2/<coord>` |
|
|
311
|
-
| [npm registry](https://registry.npmjs.org/) | Per-version `deprecated` + `dist-tags.latest` | Free public service | `GET registry.npmjs.org/<pkg>` |
|
|
312
|
-
| [Packagist](https://packagist.org/) | Latest stable + `abandoned` flag | Free public service | `GET packagist.org/packages/<vendor>/<pkg>.json` |
|
|
313
|
-
| [PyPI](https://pypi.org/) | Latest + `yanked` + "Inactive" classifier | Free public service | `GET pypi.org/pypi/<pkg>/json` |
|
|
314
|
-
| [NuGet](https://www.nuget.org/) | Latest stable + per-version `deprecation` | Free public service | `GET api.nuget.org/v3/registration5-gz-semver2/<id>/index.json` |
|
|
315
|
-
| [Go module proxy](https://proxy.golang.org/) | Latest module version (outdated) | Free public service | `GET proxy.golang.org/<module>/@latest` |
|
|
316
|
-
| [RubyGems](https://rubygems.org/) | Latest stable + licenses | Free public service | `GET rubygems.org/api/v1/gems/<gem>.json` |
|
|
317
|
-
| [deps.dev](https://deps.dev/) | Native-binary identity by checksum (→ package coordinate) | Free public API (CC-BY) | `GET api.deps.dev/v3/query?hash.type=SHA1&hash.value=<base64>` |
|
|
318
|
-
| [CIRCL hashlookup](https://hashlookup.circl.lu/) | Known-good file identity (NSRL/distro/CDN) + KnownMalicious | Free public service | `GET hashlookup.circl.lu/lookup/sha256/<hash>` |
|
|
319
|
-
| [retire.js](https://retirejs.github.io/retire.js/) | Vendored-JS signature DB + scanner | Apache-2.0 | npm package `retire`, executed locally |
|
|
320
|
-
| [Snyk](https://snyk.io/) (optional) | Additional CVE source via `snyk test --all-projects --json` | Per Snyk EULA; needs a Snyk account | Local CLI `snyk` |
|
|
321
|
-
| [MITRE CWE](https://cwe.mitre.org/) | Weakness category links in the report | Free public reference | Linked by URL only, no API call |
|
|
322
|
-
|
|
323
|
-
Persistent caches mean each source is hit at most once per its TTL (see [Caching](#caching) table). No telemetry, no third-party analytics — every request listed above is made directly to the named endpoint with a `User-Agent: fad-checker-*` header.
|
|
324
|
-
|
|
325
|
-
---
|
|
326
|
-
|
|
327
|
-
## Safety rails
|
|
328
|
-
|
|
329
|
-
Built-in guardrails that fire **before** any disk write:
|
|
330
|
-
|
|
331
|
-
- `--target` is required unless you're running read-only (no `-t`).
|
|
332
|
-
- `--target` may not equal or be a subdirectory of `--src`.
|
|
333
|
-
- `--target` is `rimraf`'d before being rewritten — never point it at anything precious.
|
|
334
|
-
|
|
335
|
-
---
|
|
336
|
-
|
|
337
|
-
## Comparison
|
|
338
|
-
|
|
339
|
-
`fad-checker` is **not** a Trivy/Grype competitor — those are container-and-SBOM supply-chain
|
|
340
|
-
scanners. It targets a narrower job: a **zero-setup, multi-ecosystem audit of a source
|
|
341
|
-
checkout, with an audit-ready report and a confidential / air-gapped workflow** — the kind
|
|
342
|
-
of thing a security consultant or an ANSSI-PASSI engagement needs.
|
|
343
|
-
|
|
344
|
-
| | **fad-checker** | OSV-Scanner | Trivy | Grype + Syft | OWASP DC | Snyk OSS |
|
|
345
|
-
| --- | --- | --- | --- | --- | --- | --- |
|
|
346
|
-
| Ecosystems it targets¹ | Maven, npm, Yarn, **pnpm**, Composer, PyPI, NuGet, Go, Ruby + vendored JS + **native binaries** | 11+ langs / 19+ lockfiles | 20+ | 20+ | Java/.NET (others exp.) | many |
|
|
347
|
-
| Reads lockfiles without `install`/build | ✅ | ✅ | ✅ | ✅ | ⚠️ Java needs Maven Central/build | ❌ build required |
|
|
348
|
-
| Best-effort when **no lockfile** (pinned versions) | ✅ | ❌ | ❌ | ❌ | ⚠️ | ⚠️ |
|
|
349
|
-
| Vulnerability sources | CVEProject + OSV + NVD + EPSS + KEV + retire.js (+ Snyk), merged | OSV.dev | Aqua DB | Anchore DB | NVD / CPE | Snyk DB |
|
|
350
|
-
| False-positive control | CPE/version cross-check | ecosystem-aware | ecosystem-aware | ecosystem-aware | ⚠️ CPE → noisy | ecosystem-aware |
|
|
351
|
-
| **EOL** (end-of-life) detection | ✅ endoflife.date | ❌ | ❌ | ❌ | ❌ | ~ |
|
|
352
|
-
| **Outdated / deprecated** | ✅ registries + curated | ❌ | ❌ | ❌ | ❌ | ~ |
|
|
353
|
-
| Containers / OS packages | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
354
|
-
| SBOM (CycloneDX/SPDX) | ✅ CycloneDX 1.6 (+ CSAF 2.0 VEX) | ✅ | ✅ | ✅ (Syft) | ~ | ✅ |
|
|
355
|
-
| License compliance | ✅ SPDX + copyleft policy | ~ | ✅ | ~ | ❌ | ✅ |
|
|
356
|
-
| EPSS / KEV prioritization | ✅ FIRST.org EPSS + CISA KEV | ~ | ✅ | ✅ | ❌ | ✅ |
|
|
357
|
-
| CI gating (`--fail-on`) + triage | ✅ severity/KEV + ignore/VEX | ✅ | ✅ | ✅ | ⚠️ | ✅ |
|
|
358
|
-
| Auto-remediation / PRs | ❌ (fix recipes only) | ✅ `fix` | ❌ | ❌ | ❌ | ✅ |
|
|
359
|
-
| Offline | ✅ cache | ✅ local DB | ✅ | ✅ | ✅ feed | ❌ mostly online |
|
|
360
|
-
| **Scan without exposing the codebase**² | ✅ anonymized descriptor | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
361
|
-
| **Maven private-dep cleanup** (→ Snyk) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
362
|
-
| Output | **HTML + Word `.doc`** + JSON / SARIF / CycloneDX / CSAF | table/JSON/SARIF | table/JSON/SARIF | table/JSON/SARIF | HTML/XML/JSON | JSON / cloud UI |
|
|
363
|
-
|
|
364
|
-
¹ Narrower language coverage — no Rust/Dart/Swift (Go and Ruby are now covered).
|
|
365
|
-
|
|
366
|
-
² Phase 1 exports only public coordinates; the online scan never sees your source tree —
|
|
367
|
-
see [Air-gapped / PASSI](#air-gapped--passi-audits-anonymized-dependency-descriptor). OSV-Scanner
|
|
368
|
-
has an offline mode, but it still needs the **source on the scanning machine**.
|
|
369
|
-
|
|
370
|
-
**Where it fits:** a one-shot audit of a polyglot checkout you may not be able to build, a
|
|
371
|
-
presentable HTML/Word deliverable, and confidential / air-gapped engagements.
|
|
372
|
-
|
|
373
|
-
**Where it doesn't:** container/OS scanning,
|
|
374
|
-
reachability analysis, auto-fix PRs — reach for **Trivy** or **Grype + Syft**.
|
|
375
|
-
|
|
376
|
-
You don't have to choose — `fad-checker` takes Snyk's results as input (`--snyk`) and merges them.
|
|
377
|
-
|
|
378
|
-
> Sources: [OSV-Scanner lockfiles](https://google.github.io/osv-scanner/supported-languages-and-lockfiles/) ·
|
|
379
|
-
> [Trivy Java/`pom.xml` (Maven Central, `--offline-scan`)](https://trivy.dev/docs/latest/coverage/language/java/) ·
|
|
380
|
-
> [Syft `java-pom-cataloger` (source dirs)](https://github.com/anchore/syft/issues/676) ·
|
|
381
|
-
> [OWASP DC needs internet/build for Java](https://jeremylong.github.io/DependencyCheck/data/index.html) ·
|
|
382
|
-
> [Snyk requires building the project](https://docs.snyk.io/supported-languages/technical-specifications-and-guidance) ·
|
|
383
|
-
> [EOL/outdated "most tools skip" (Aikido)](https://www.aikido.dev/code/outdated-eol-software)
|
|
384
|
-
|
|
385
|
-
---
|
|
120
|
+
DB is warmed online (phase 2) and carried by `--export-cache`. Full offline/cache control →
|
|
121
|
+
[`docs/USAGE.md`](docs/USAGE.md).
|
|
386
122
|
|
|
387
123
|
## Docs
|
|
388
124
|
|
|
389
|
-
- [`docs/USAGE.md`](docs/USAGE.md) — every flag
|
|
125
|
+
- [`docs/USAGE.md`](docs/USAGE.md) — every flag and workflow: offline/cache control, private registries, config files, recipes, safety rails.
|
|
390
126
|
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — internals: codecs, collection, matching, report pipeline.
|
|
391
|
-
- [`
|
|
392
|
-
- [`
|
|
393
|
-
|
|
394
|
-
---
|
|
127
|
+
- [`docs/COMPARISON.md`](docs/COMPARISON.md) — vs OSV-Scanner / Trivy / Grype / OWASP DC / Snyk, and how it stays build-free.
|
|
128
|
+
- [`docs/DATA-SOURCES.md`](docs/DATA-SOURCES.md) — the public datasets fad-checker uses + their licenses.
|
|
129
|
+
- [`CHANGELOG.md`](CHANGELOG.md) · [`CLAUDE.md`](CLAUDE.md) — release history · code-level orientation for contributors.
|
|
395
130
|
|
|
396
131
|
## License
|
|
397
132
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Curated list of widely-used npm + PyPI packages used as TYPOSQUAT TARGETS by lib/malware.js#detectTyposquats. A resolved dep whose name is edit-distance 1 from one of these (but isn't it) is flagged as a suspected typosquat/slopsquat. Not exhaustive — focused on the most-impersonated packages. Add names here to widen proactive coverage.",
|
|
3
|
+
"npm": [
|
|
4
|
+
"react", "react-dom", "lodash", "express", "axios", "chalk", "commander", "request",
|
|
5
|
+
"debug", "async", "moment", "underscore", "jquery", "webpack", "babel-core", "vue",
|
|
6
|
+
"angular", "typescript", "eslint", "prettier", "jest", "mocha", "chai", "bluebird",
|
|
7
|
+
"yargs", "glob", "minimist", "colors", "dotenv", "body-parser", "cors", "mongoose",
|
|
8
|
+
"redux", "next", "gatsby", "rxjs", "classnames", "prop-types", "uuid", "bcrypt",
|
|
9
|
+
"jsonwebtoken", "passport", "nodemon", "node-fetch", "cross-env", "rimraf", "fs-extra",
|
|
10
|
+
"semver", "validator", "winston", "morgan", "helmet", "sequelize", "knex", "mysql",
|
|
11
|
+
"redis", "ioredis", "dayjs", "date-fns", "ramda", "immutable", "styled-components",
|
|
12
|
+
"tailwindcss", "postcss", "autoprefixer", "browserify", "rollup", "parcel", "esbuild",
|
|
13
|
+
"husky", "lint-staged", "concurrently", "nanoid", "form-data", "tslib", "core-js",
|
|
14
|
+
"socket.io", "nodemailer", "puppeteer", "playwright", "cheerio", "inquirer", "ora",
|
|
15
|
+
"execa", "chokidar", "ws", "got", "undici", "zod", "yup", "formik", "react-router",
|
|
16
|
+
"react-router-dom", "react-redux", "vuex", "pinia", "svelte", "preact", "ant-design",
|
|
17
|
+
"bootstrap", "jquery-ui", "three", "d3", "chart.js", "leaflet", "highlight.js",
|
|
18
|
+
"marked", "handlebars", "ejs", "pug", "nunjucks", "graphql", "apollo-server",
|
|
19
|
+
"mongodb", "pg", "typeorm", "prisma", "jsdom", "supertest", "sinon", "nyc", "ava",
|
|
20
|
+
"babel-loader", "ts-loader", "css-loader", "style-loader", "html-webpack-plugin",
|
|
21
|
+
"colord"
|
|
22
|
+
],
|
|
23
|
+
"pypi": [
|
|
24
|
+
"requests", "numpy", "pandas", "flask", "django", "scipy", "matplotlib", "pillow",
|
|
25
|
+
"setuptools", "wheel", "urllib3", "certifi", "pytest", "six", "click", "jinja2",
|
|
26
|
+
"werkzeug", "sqlalchemy", "boto3", "botocore", "pyyaml", "cryptography",
|
|
27
|
+
"beautifulsoup4", "lxml", "scikit-learn", "tensorflow", "keras", "scrapy",
|
|
28
|
+
"celery", "psycopg2", "pymongo", "fastapi", "uvicorn", "pydantic", "aiohttp",
|
|
29
|
+
"httpx", "tqdm", "colorama", "rich", "typer", "python-dateutil", "pytz", "attrs",
|
|
30
|
+
"packaging", "idna", "charset-normalizer", "markupsafe", "openpyxl", "tornado",
|
|
31
|
+
"gunicorn", "redis", "selenium", "paramiko", "fabric", "pyjwt", "passlib", "bcrypt",
|
|
32
|
+
"marshmallow", "flask-sqlalchemy", "flask-cors", "flask-restful", "django-rest-framework",
|
|
33
|
+
"djangorestframework", "gevent", "greenlet", "asgiref", "starlette", "anyio",
|
|
34
|
+
"httpcore", "sniffio", "wrapt", "decorator", "more-itertools", "toml", "tomli",
|
|
35
|
+
"pyparsing", "jsonschema", "protobuf", "grpcio", "boto", "awscli", "google-api-python-client",
|
|
36
|
+
"opencv-python", "imageio", "networkx", "sympy", "statsmodels", "seaborn", "plotly",
|
|
37
|
+
"dash", "streamlit", "jupyter", "ipython", "notebook", "nbconvert", "cython"
|
|
38
|
+
]
|
|
39
|
+
}
|
package/fad-checker.js
CHANGED
|
@@ -236,7 +236,10 @@ program
|
|
|
236
236
|
.option("--ignore-test", "skip test-scoped dependencies in report")
|
|
237
237
|
.option("--cve-refresh", "force re-download of CVE database")
|
|
238
238
|
.option("--cve-offline", "use cached CVE index only (no download)")
|
|
239
|
+
.option("--osv-db", "import + match the full local OSV database (Maven) — offline-complete recall, independent of the per-dep OSV cache")
|
|
240
|
+
.option("--osv-db-refresh", "force re-download of the local OSV database")
|
|
239
241
|
.option("--snyk", "run snyk on cleaned POMs and merge into report (requires --target)")
|
|
242
|
+
.option("--typosquat", "flag npm/PyPI deps whose name is one edit from a popular package (heuristic typosquat/slopsquat detection)")
|
|
240
243
|
.option("--no-retire", "skip retire.js vendored-JS scan")
|
|
241
244
|
.option("--no-vendored-js-inventory", "don't list ALL identified vendored JS libs (chapter 1D) — keep only the vulnerable ones (chapter 2)")
|
|
242
245
|
.option("--retire-refresh", "ignore retire cache and re-scan")
|
|
@@ -750,6 +753,9 @@ async function runReportFlow(resolved, ecoFlags = {}) {
|
|
|
750
753
|
? require("./lib/maven-bom").collectImportBoms(mavenPropsByPom) : [];
|
|
751
754
|
const willBom = importBoms.length > 0;
|
|
752
755
|
const willOsv = !!options.osv;
|
|
756
|
+
// Local OSV DB import (Maven): offline-complete OSV recall, independent of the per-dep
|
|
757
|
+
// OSV.dev cache. Opt-in (downloads ~9 MB once); then matches online or offline.
|
|
758
|
+
const willOsvDb = !!options.osvDb && runMaven;
|
|
753
759
|
const willOutdated = !!options.allLibs;
|
|
754
760
|
const willNvd = !!options.nvd;
|
|
755
761
|
const willEpss = !!options.epss;
|
|
@@ -760,7 +766,7 @@ async function runReportFlow(resolved, ecoFlags = {}) {
|
|
|
760
766
|
const willBinaryId = [...resolved.values()].some(d => d.provenance === "binary");
|
|
761
767
|
// License detection piggybacks on the registry passes (same fetched metadata),
|
|
762
768
|
// so it adds no progress step of its own.
|
|
763
|
-
const totalSteps = [willBom, willTransitive, willOverlay, willCve, /*EOL*/ true, willOutdated, /*npm reg*/ true, ...otherRegistryIds.map(() => true), willOsv, willNvd, willEpss, willKev, willRetire, willBinaryId].filter(Boolean).length;
|
|
769
|
+
const totalSteps = [willBom, willTransitive, willOverlay, willCve, /*EOL*/ true, willOutdated, /*npm reg*/ true, ...otherRegistryIds.map(() => true), willOsv, willOsvDb, willNvd, willEpss, willKev, willRetire, willBinaryId].filter(Boolean).length;
|
|
764
770
|
const progress = new ui.Progress(totalSteps);
|
|
765
771
|
|
|
766
772
|
if (willBom) {
|
|
@@ -912,6 +918,25 @@ async function runReportFlow(resolved, ecoFlags = {}) {
|
|
|
912
918
|
}
|
|
913
919
|
}
|
|
914
920
|
|
|
921
|
+
// 4b'. Local OSV database (Maven) — offline-COMPLETE recall. The per-dep OSV.dev
|
|
922
|
+
// queries above only cover deps cached online; the imported full OSV DB matches every
|
|
923
|
+
// dep offline, deterministically, regardless of cache warmth (the OSV-Scanner model).
|
|
924
|
+
if (willOsvDb) {
|
|
925
|
+
const st = progress.start("OSV database (local, Maven)");
|
|
926
|
+
try {
|
|
927
|
+
const { ensureOsvDb, matchOsvDbDeps } = require("./lib/osv-db");
|
|
928
|
+
const index = await ensureOsvDb({ offline, refresh: !!options.osvDbRefresh, verbose, ecosystem: "maven" });
|
|
929
|
+
if (!index) {
|
|
930
|
+
st.done(offline ? "no local OSV DB (run once online with --osv-db)" : "unavailable");
|
|
931
|
+
} else {
|
|
932
|
+
const dbMatches = matchOsvDbDeps(resolved, index);
|
|
933
|
+
const before = cveMatches.length;
|
|
934
|
+
cveMatches = mergeBySource(cveMatches, dbMatches);
|
|
935
|
+
st.done(`${index.count} advisories · ${dbMatches.length} matched · +${cveMatches.length - before} new`);
|
|
936
|
+
}
|
|
937
|
+
} catch (err) { st.fail(err.message); }
|
|
938
|
+
}
|
|
939
|
+
|
|
915
940
|
// 4c. NVD enrichment — canonical description + full CVSS for matched CVEs.
|
|
916
941
|
if (willNvd) {
|
|
917
942
|
const st = progress.start("NVD enrichment");
|
|
@@ -1019,6 +1044,22 @@ async function runReportFlow(resolved, ecoFlags = {}) {
|
|
|
1019
1044
|
}
|
|
1020
1045
|
}
|
|
1021
1046
|
|
|
1047
|
+
// 6b. Supply-chain risk lane (pure + offline): flag KNOWN-MALICIOUS advisories
|
|
1048
|
+
// (OSV MAL-… already in the match set) always, and detect suspected TYPOSQUATS
|
|
1049
|
+
// (opt-in --typosquat, heuristic — names one edit from a popular package).
|
|
1050
|
+
const { flagMalicious, detectTyposquats } = require("./lib/malware");
|
|
1051
|
+
const maliciousCount = flagMalicious(cveMatches);
|
|
1052
|
+
const typosquats = options.typosquat ? detectTyposquats(resolved) : [];
|
|
1053
|
+
if (maliciousCount || typosquats.length) {
|
|
1054
|
+
ui.section("Supply-chain risk");
|
|
1055
|
+
if (maliciousCount) ui.warn(chalk.red.bold(`${maliciousCount} KNOWN-MALICIOUS package advisory(ies) (MAL-…) — treat the build as compromised`));
|
|
1056
|
+
if (typosquats.length) {
|
|
1057
|
+
ui.warn(`${typosquats.length} suspected typosquat(s) — heuristic, verify each is intentional:`);
|
|
1058
|
+
for (const t of typosquats.slice(0, 15)) console.log(" " + chalk.yellow(t.name) + chalk.dim(` (${t.ecosystem}) ≈ ${t.resembles}`));
|
|
1059
|
+
if (typosquats.length > 15) console.log(chalk.dim(` …and ${typosquats.length - 15} more (see JSON export)`));
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1022
1063
|
// Split prod vs dev based on the dep's isDev flag (set at collection time
|
|
1023
1064
|
// from Maven scope=test/provided and npm dev/devOptional/optional). Keep the
|
|
1024
1065
|
// full per-bucket list (including cpeFiltered) so the HTML report can render
|
|
@@ -1253,7 +1294,7 @@ async function runReportFlow(resolved, ecoFlags = {}) {
|
|
|
1253
1294
|
try {
|
|
1254
1295
|
const { writeFindings } = require("./lib/json-export");
|
|
1255
1296
|
await ensureDir(out.json);
|
|
1256
|
-
writeFindings({ cveMatches, retireMatches, vendoredJsInventory, eolResults, obsoleteResults, outdatedResults, licenseResults, resolvedDeps: resolved, projectInfo, toolVersion: pkg.version }, out.json);
|
|
1297
|
+
writeFindings({ cveMatches, retireMatches, vendoredJsInventory, eolResults, obsoleteResults, outdatedResults, licenseResults, resolvedDeps: resolved, projectInfo, toolVersion: pkg.version, typosquats }, out.json);
|
|
1257
1298
|
wrote.push(["Findings JSON", out.json]);
|
|
1258
1299
|
} catch (err) { ui.warn(`JSON export failed: ${err.message}`); }
|
|
1259
1300
|
}
|
|
@@ -1280,9 +1321,12 @@ async function runReportFlow(resolved, ecoFlags = {}) {
|
|
|
1280
1321
|
const { evaluateGate } = require("./lib/gate");
|
|
1281
1322
|
// Embedded-binary findings are real production risk → gate on them too.
|
|
1282
1323
|
const gate = evaluateGate([...prodActive, ...embeddedActive], options.failOn);
|
|
1283
|
-
|
|
1324
|
+
// A known-malicious package always blocks, regardless of the --fail-on level.
|
|
1325
|
+
const maliciousActive = [...prodActive, ...embeddedActive].filter(m => m.malicious);
|
|
1326
|
+
if (gate.failed || maliciousActive.length) {
|
|
1284
1327
|
ui.section("Gate");
|
|
1285
|
-
console.log(chalk.red(`✗
|
|
1328
|
+
if (maliciousActive.length) console.log(chalk.red.bold(`✗ ${maliciousActive.length} known-malicious package(s) detected — always blocks`));
|
|
1329
|
+
if (gate.failed) console.log(chalk.red(`✗ --fail-on ${options.failOn}: ${gate.reason}`));
|
|
1286
1330
|
process.exitCode = 1;
|
|
1287
1331
|
} else if (verbose) {
|
|
1288
1332
|
ui.info(chalk.dim(`--fail-on ${options.failOn}: no blocking finding`));
|