fad-checker 1.0.0 → 1.0.2
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/CLAUDE.md +21 -21
- package/CRITICAL-REVIEW.md +343 -0
- package/README.md +37 -37
- package/completions/{fad-check.bash → fad-checker.bash} +3 -3
- package/completions/{fad-check.zsh → fad-checker.zsh} +2 -2
- package/docs/ARCHITECTURE.md +10 -10
- package/docs/USAGE.md +35 -35
- package/{fad-check.js → fad-checker.js} +25 -21
- package/lib/cache-archive.js +9 -9
- package/lib/config.js +3 -3
- package/lib/cpe.js +23 -5
- package/lib/cve-download.js +3 -3
- package/lib/cve-match.js +13 -7
- package/lib/cve-report.js +4 -4
- package/lib/maven-repo.js +3 -3
- package/lib/maven-version.js +6 -0
- package/lib/nvd.js +62 -17
- package/lib/osv.js +53 -28
- package/lib/outdated.js +25 -5
- package/lib/retire.js +5 -5
- package/lib/scan-completeness.js +2 -2
- package/lib/snyk.js +4 -4
- package/lib/transitive.js +15 -7
- package/package.json +6 -6
- package/test/core.test.js +4 -4
- package/test/cpe.test.js +18 -0
- package/test/cve-match.test.js +56 -0
- package/test/cve-report.test.js +1 -1
- package/test/maven-version.test.js +9 -0
- package/test/monorepo.test.js +1 -1
- package/test/snyk.test.js +1 -1
- package/test/transitive.test.js +2 -2
package/CLAUDE.md
CHANGED
|
@@ -4,7 +4,7 @@ Code-level orientation for contributors and Claude Code sessions on this repo.
|
|
|
4
4
|
|
|
5
5
|
## What this is
|
|
6
6
|
|
|
7
|
-
`fad-
|
|
7
|
+
`fad-checker` — **Fucking Autonomous Dependency Checker**. Node.js CLI (`fad-checker`, or short alias `fad`) that:
|
|
8
8
|
|
|
9
9
|
1. Walks a multi-module Maven tree, removes private/excluded dependencies (regex on groupId), writes a parallel directory of "cleaned" POMs that can be fed to Snyk.
|
|
10
10
|
2. Walks every JS package (`package.json` + `package-lock.json` v1/v2/v3 or `yarn.lock` v1) in the same source tree.
|
|
@@ -27,9 +27,9 @@ npm install
|
|
|
27
27
|
npm test # 96 unit tests via node --test
|
|
28
28
|
|
|
29
29
|
# basic cleanup workflow
|
|
30
|
-
node fad-
|
|
31
|
-
node fad-
|
|
32
|
-
node fad-
|
|
30
|
+
node fad-checker.js -s ./proj # read-only, full report
|
|
31
|
+
node fad-checker.js -s ./proj -t ../pom-clean -e "^client\\." # write cleaned tree
|
|
32
|
+
node fad-checker.js -s ./proj -t ../pom-clean -e "^client\\." --snyk # also drive snyk
|
|
33
33
|
|
|
34
34
|
# read the full usage doc
|
|
35
35
|
cat docs/USAGE.md
|
|
@@ -38,8 +38,8 @@ cat docs/USAGE.md
|
|
|
38
38
|
Binary builds (requires `bun`):
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
npm run build:linux # → dist/fad-
|
|
42
|
-
npm run build:win # → dist/fad-
|
|
41
|
+
npm run build:linux # → dist/fad-checker-linux
|
|
42
|
+
npm run build:win # → dist/fad-checker.exe
|
|
43
43
|
npm run build # both
|
|
44
44
|
```
|
|
45
45
|
|
|
@@ -51,7 +51,7 @@ Guardrails enforced at startup:
|
|
|
51
51
|
## Architecture (one-liner per file)
|
|
52
52
|
|
|
53
53
|
```
|
|
54
|
-
fad-
|
|
54
|
+
fad-checker.js Thin CLI: commander parsing, orchestration only.
|
|
55
55
|
lib/core.js POM parsing, parent resolution, all-profile merge, rewrite.
|
|
56
56
|
lib/maven-version.js Maven version parsing + range comparison (no external deps).
|
|
57
57
|
lib/cve-download.js Bulk download of CVEProject/cvelistV5 + Maven-relevant index build.
|
|
@@ -64,13 +64,13 @@ lib/osv.js OSV.dev batched query + per-vuln detail fetch.
|
|
|
64
64
|
lib/nvd.js NIST NVD enrichment (CVSS, references, CPE configurations).
|
|
65
65
|
lib/snyk.js `snyk test --all-projects --json` runner + merge.
|
|
66
66
|
lib/retire.js retire.js (vendored-JS scanner) wrapper + cache + normaliser.
|
|
67
|
-
lib/scan-completeness.js Warnings for deps fad-
|
|
67
|
+
lib/scan-completeness.js Warnings for deps fad-checker couldn't fully resolve.
|
|
68
68
|
lib/npm/parse.js package.json, package-lock.json (v1/2/3), yarn.lock v1 parsers.
|
|
69
69
|
lib/npm/collect.js Merge across JS manifests → unified resolvedDeps Map.
|
|
70
|
-
lib/cache-archive.js tar.gz / zip export & import of ~/.fad-
|
|
71
|
-
lib/config.js Persistent user config in ~/.fad-
|
|
70
|
+
lib/cache-archive.js tar.gz / zip export & import of ~/.fad-checker/.
|
|
71
|
+
lib/config.js Persistent user config in ~/.fad-checker/config.json (mode 0600).
|
|
72
72
|
data/ known-obsolete.json, eol-mapping.json, cpe-coord-map.json, known-public-namespaces.json
|
|
73
|
-
completions/ fad-
|
|
73
|
+
completions/ fad-checker.bash, fad-checker.zsh
|
|
74
74
|
test/ node:test suite + fixtures (simple, complex-enterprise, monorepo-mixed, …).
|
|
75
75
|
```
|
|
76
76
|
|
|
@@ -104,8 +104,8 @@ Test fixtures live in `test/fixtures/`:
|
|
|
104
104
|
## Gotchas / edge cases worth knowing
|
|
105
105
|
|
|
106
106
|
- CVE bundle from CVEProject is ~500 MB unpacked. Shells out to `curl + unzip` (fallback to `fetch()` + `unzip` / `Expand-Archive`). Extracted JSON deleted after index build. Ships as `cves.zip.zip` (nested zip) — `extractZip()` recurses up to 3 levels.
|
|
107
|
-
- `endoflife.date` API responses cached 7 days; Maven Central version lookups cached 24 hours. Cache lives in `~/.fad-
|
|
108
|
-
- **Persistent config**: `~/.fad-
|
|
107
|
+
- `endoflife.date` API responses cached 7 days; Maven Central version lookups cached 24 hours. Cache lives in `~/.fad-checker/`.
|
|
108
|
+
- **Persistent config**: `~/.fad-checker/config.json` (mode 0600). Set NVD key via `fad-checker --set-nvd-key <KEY>` (free, instant from <https://nvd.nist.gov/developers/request-an-api-key> — bumps rate limit from 5/30s to 50/30s).
|
|
109
109
|
- **`--offline` umbrella flag**: skips every network call (CVE/OSV/NVD/Maven Central/endoflife/retire). Falls back to whatever is already cached. Per-source variants (`--cve-offline`, `--no-osv`, `--no-nvd`, `--no-retire`, `--no-transitive`) still work independently.
|
|
110
110
|
- `snyk` is not a hard dep — shells out via `execFile`. `snyk` exits 1 on findings; the JSON is still on stdout.
|
|
111
111
|
- The cleaned POM is the union of every profile's deps. Counts will be larger than the source POM. Intentional — don't "fix" that.
|
|
@@ -116,11 +116,11 @@ Test fixtures live in `test/fixtures/`:
|
|
|
116
116
|
|
|
117
117
|
| Cache | Location | TTL |
|
|
118
118
|
|---|---|---|
|
|
119
|
-
| CVEProject bulk index | `~/.fad-
|
|
120
|
-
| OSV per-dep stub list | `~/.fad-
|
|
121
|
-
| OSV vuln details | `~/.fad-
|
|
122
|
-
| NVD CVE record | `~/.fad-
|
|
123
|
-
| endoflife.date cycles | `~/.fad-
|
|
124
|
-
| Maven Central latest | `~/.fad-
|
|
125
|
-
| Transitive POM | `~/.fad-
|
|
126
|
-
| retire.js findings | `~/.fad-
|
|
119
|
+
| CVEProject bulk index | `~/.fad-checker/cve-data/maven-cve-index.json` | 24 h |
|
|
120
|
+
| OSV per-dep stub list | `~/.fad-checker/osv-cache/<eco>__<g>__<a>__<v>.json` | 12 h |
|
|
121
|
+
| OSV vuln details | `~/.fad-checker/osv-cache/vuln_<id>.json` | 12 h |
|
|
122
|
+
| NVD CVE record | `~/.fad-checker/nvd-cache/<cveId>.json` | 7 d |
|
|
123
|
+
| endoflife.date cycles | `~/.fad-checker/eol-cache.json` | 7 d |
|
|
124
|
+
| Maven Central latest | `~/.fad-checker/version-cache.json` | 24 h |
|
|
125
|
+
| Transitive POM | `~/.fad-checker/poms-cache/<g>__<a>__<v>.pom` | ∞ (immutable on Maven Central) |
|
|
126
|
+
| retire.js findings | `~/.fad-checker/retire-cache/<md5(src)>.json` | 24 h |
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# fad-checker — Analyse critique complète
|
|
2
|
+
|
|
3
|
+
**Date :** 2026-05-22
|
|
4
|
+
**Périmètre :** intégralité du code (`fad-checker.js` + `lib/**/*.js` + `data/*.json` + `test/`)
|
|
5
|
+
**Méthode :** 4 audits parallèles à effort xhigh (reuse / quality / efficiency / **faux positifs**)
|
|
6
|
+
**Verdict global :** architecture saine, mais **4 chemins fail-open en cascade dans le matcher CVE** produisent un taux de faux positifs estimé entre **20 % et 60 %** selon le niveau de confiance retenu. À traiter en priorité avant toute exploitation en production.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## TL;DR — Risque de faux positifs
|
|
11
|
+
|
|
12
|
+
| Tier de confiance | Taux de FP estimé sur ~500 deps | Recommandation |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `exact` uniquement | 5–10 % | Utilisable (triage léger) |
|
|
15
|
+
| `exact` + `probable` | 20–30 % | Triage manuel obligatoire |
|
|
16
|
+
| Tous tiers (`exact` + `probable` + `possible`) | **40–60 %** | Inexploitable sans tri humain |
|
|
17
|
+
|
|
18
|
+
Quatre fail-open en cascade :
|
|
19
|
+
|
|
20
|
+
1. **H1** — `isVersionAffected` retourne `true` quand le CVE n'a aucune borne de version (`lib/maven-version.js:101-124`).
|
|
21
|
+
2. **H2** — `vendorMatchesGroup` fait du *substring matching* (`g.includes(v) || v.includes(g)`) — `vendor="open"` matche `org.opensaml.*` (`lib/cve-match.js:164-173`).
|
|
22
|
+
3. **H3** — Tier-3 `possible` émis pour toute collision d'`artifactId`, même quand le vendor est totalement étranger (`lib/cve-match.js:191-198`).
|
|
23
|
+
4. **H4** — Le filtre CPE (« primary FP filter ») se contente de **marquer** `cpeFiltered=true`, il ne supprime jamais (`lib/cpe.js:269-272`).
|
|
24
|
+
|
|
25
|
+
Ces quatre interagissent de manière pathologique : H3 laisse passer une masse de matches `possible`, H2 en remonte beaucoup en `probable`, H1 valide toutes les versions, et H4 ne filtre plus rien en aval.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## A. Faux positifs (critère #1 demandé)
|
|
30
|
+
|
|
31
|
+
### HIGH
|
|
32
|
+
|
|
33
|
+
#### H1 — `isVersionAffected` fail-open quand pas de bornes
|
|
34
|
+
**Fichier :** `lib/maven-version.js:101-124`
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
if (spec.version && spec.version !== "0" && spec.version !== "*") { /* lower */ }
|
|
38
|
+
if (spec.lessThan) { ... }
|
|
39
|
+
if (spec.lessThanOrEqual) { ... }
|
|
40
|
+
if (!spec.lessThan && !spec.lessThanOrEqual && spec.version && …) { /* exact */ }
|
|
41
|
+
return true; // ← fall-through "affected"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Empiriquement : `isVersionAffected("2.14", { status: "affected" })` → `true`. CVEProject contient régulièrement des entrées `versions:[{status:"affected"}]` sans bornes (stub mal formé). Combiné à H2, **toute dep qui hit le `byProduct` tier-2 avec un range sparse est flaggée sur toute version**.
|
|
45
|
+
|
|
46
|
+
**FP concret :** CVE listant `affected: [{ product: "log4j", versions: [{ status: "affected" }] }]` → flag `log4j-core 2.99.0` (patché) car pas de borne haute, pas d'exact-match, fall-through → `true`.
|
|
47
|
+
|
|
48
|
+
**Fix :**
|
|
49
|
+
```js
|
|
50
|
+
if (!spec.version && !spec.lessThan && !spec.lessThanOrEqual) return false;
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
#### H2 — `vendorMatchesGroup` substring-match → cross-vendor leak
|
|
56
|
+
**Fichier :** `lib/cve-match.js:164-173`
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
if (g.includes(v) || v.includes(g)) return true;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`vendorMatchesGroup("spring", "foospringbar")` → `true`. NVD/CVEProject contiennent régulièrement des vendors `"a"`, `"oss"`, `"com"`, `"java"`, `"go"`, `"web"`, `"open"`, `"ibm"`, `"net"` — tous présents en sous-chaîne dans des groupIds réels.
|
|
63
|
+
|
|
64
|
+
**FP concrets :**
|
|
65
|
+
- CVE `vendor="open"` matche `org.opensaml:opensaml-core` (`"opensaml".includes("open")`).
|
|
66
|
+
- CVE `vendor="ibm"` matche `com.ibmcloudant:cloudant-client`.
|
|
67
|
+
- CVE `vendor="spring"` matche `org.springframework.*` ET `com.foospringbar.*`.
|
|
68
|
+
|
|
69
|
+
**Fix :** restreindre à (a) `g === v`, (b) `g.split(".").includes(v)`, (c) `v.includes(g)` seulement si `g.length >= 4`. Drop des branches substring non bornées.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
#### H3 — Tier-3 `possible` émis avec vendor totalement étranger
|
|
74
|
+
**Fichier :** `lib/cve-match.js:191-198`
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
const productMatches = byProduct[dep.artifactId.toLowerCase()] || [];
|
|
78
|
+
for (const e of productMatches) {
|
|
79
|
+
if (vendorMatchesGroup(e.vendor, dep.groupId)) {
|
|
80
|
+
all.push(...matchOne(dep, [e], "probable"));
|
|
81
|
+
} else {
|
|
82
|
+
all.push(...matchOne(dep, [e], "possible")); // ← émis quand même
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Toute dep dont l'`artifactId` collide avec un produit non relié produit un match `possible`. Collisions fréquentes : `core`, `common`, `api`, `client`, `utils`, `parser`, `web`. Une CVE avec `product="core"` flagge **toutes** les deps `*-core`.
|
|
88
|
+
|
|
89
|
+
**Fix :** drop le tier `possible` ou ne l'afficher que si la refinement CPE (`refineMatchesWithCpe`) l'a confirmé.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
#### H4 — CPE refinement flagge sans supprimer
|
|
94
|
+
**Fichier :** `lib/cpe.js:269-272`
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
if (!affected && rec.configurations.length) {
|
|
98
|
+
m.cpeFiltered = true; // ← marqué, jamais supprimé
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`cpeFiltered` est un soft tag. Si `lib/cve-report.js` ne strippe pas `cpeFiltered === true` (à vérifier dans le rendu), **les matches prouvés faux positifs restent dans la punch list**.
|
|
103
|
+
|
|
104
|
+
De plus, le skip `if (!rec.configurations.length && !rec.cpes.length) continue;` (`cpe.js:266`) est fail-open : une CVE sans configurations NVD (CVE récente ou réservée) est laissée au tier que le matcher lui a donné.
|
|
105
|
+
|
|
106
|
+
**Fix :** (a) filtrer par défaut `cpeFiltered=true` avec flag `--show-filtered`, ou (b) section séparée « likely false positive » dans le report.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
#### H5 — Le qualificateur CPE `update` (RC, alpha) est ignoré
|
|
111
|
+
**Fichier :** `lib/cpe.js:81-100` (`matchVersionRange`)
|
|
112
|
+
|
|
113
|
+
Le champ index 6 du CPE 2.3 (`update`) est parsé mais jamais comparé. NVD liste fréquemment les pré-releases via le champ update (`cpe:2.3:a:vendor:product:1.0.0:rc1:*…`). Le hard-pin compare littéralement la `version` ignorant `update` :
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
if (parsed.version && parsed.version !== "*" && parsed.version !== "-") {
|
|
117
|
+
try { return compareMavenVersions(depVersion, parsed.version) === 0; }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
→ `criteria: cpe:2.3:a:apache:foo:1.0.0:beta1:*…` matche `dep=1.0.0` (release). FP.
|
|
121
|
+
|
|
122
|
+
**Fix :** si `update` n'est ni `*` ni `-`, concaténer (`${version}-${update}`) avant `compareMavenVersions` (qui gère déjà `1.0.0-beta1`).
|
|
123
|
+
|
|
124
|
+
### MEDIUM
|
|
125
|
+
|
|
126
|
+
#### M1 — `compareMavenVersions` accepte du garbage sans broncher
|
|
127
|
+
**Fichier :** `lib/maven-version.js:25-94`
|
|
128
|
+
|
|
129
|
+
`compareMavenVersions("${foo}", "1.0")` → `-1`. Une variable Maven non résolue est comparée comme une vraie version. La pipeline filtre `/\$\{/.test(dep.version)` dans `expandWithTransitives`, `queryOsvForDeps`, `checkOutdatedDeps` — **mais pas dans `matchDepsAgainstCves`** (`cve-match.js:151-162`). Donc dep avec version `${foo}` → `isVersionAffected("${foo}", range)` → fall-through `true` (H1).
|
|
130
|
+
|
|
131
|
+
**Fix :** skipper les deps avec `${…}` non résolu dans `matchDepsAgainstCves`.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
#### M2 — OSV : stubs cachés ré-émis sur la mauvaise version
|
|
136
|
+
**Fichier :** `lib/osv.js:242-265` (`runMatches`)
|
|
137
|
+
|
|
138
|
+
`vulnToMatch` ne refait pas de check range local. Le cache key `(g, a, v)` est versionné, donc pour la même dep+version c'est sûr. Mais le branch « stub only » émet `severity: "UNKNOWN"` sans description — vrai FP si la dep a été upgradée entre la build du cache et le scan.
|
|
139
|
+
|
|
140
|
+
`dep.version` avec `1.0.0.Final` ou `1.0.0.RELEASE` est envoyée verbatim à OSV qui ne normalise pas → recall patchy (FN, pas FP).
|
|
141
|
+
|
|
142
|
+
**Fix :** toujours évaluer `vuln.affected[].ranges` localement avant d'émettre. Drop des matches stub-only.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
#### M3 — `parseRange` exporté mais jamais appelé
|
|
147
|
+
**Fichier :** `lib/maven-version.js:131-146`
|
|
148
|
+
|
|
149
|
+
`parseRange` traite les ranges Maven (`[1.0,2.0)`) mais aucun caller. Une `<version>[1.0,2.0)</version>` est passée verbatim à `compareMavenVersions("[1.0,2.0)", spec.version)`. Le tokenizer split sur `[.\-]`, `[1` devient un segment string → ranking sub-release → typiquement FN, mais peut devenir FP via H1.
|
|
150
|
+
|
|
151
|
+
**Fix :** câbler `parseRange` dans la pipeline ; sinon reporter ces deps en `unresolved-versions`.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
#### M4 — `cpeMatchesDep` ré-introduit le substring leak de H2
|
|
156
|
+
**Fichier :** `lib/cpe.js:170-173`
|
|
157
|
+
|
|
158
|
+
Le filtre CPE supposé *narrow* contient lui-même la même heuristique substring que H2. Après que H2 fait passer un match, ce leak l'empêche de le re-filtrer.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
#### M5 — retire.js passé sans validation locale
|
|
163
|
+
**Fichier :** `lib/retire.js:148-193`
|
|
164
|
+
|
|
165
|
+
Le wrapper a accès à `below`/`atOrAbove` mais ne les compare jamais. Les régressions historiques des signatures retire (matchs sur des bundles non reliés) passent. Pas d'équivalent à `--includeOsvData` / `--ignorefile`.
|
|
166
|
+
|
|
167
|
+
retire émet aussi un CVE id synthétique `RETIRE-<component>-<version>` qui ne dédup jamais contre fad/osv/nvd → **même vuln rapportée 2 fois**.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
#### M6 — Cohérence des clés `byProduct`
|
|
172
|
+
**Fichier :** `lib/cve-download.js:163-164` + `lib/cve-match.js:191`
|
|
173
|
+
|
|
174
|
+
Index keyé sur `a.product` brut, lookup sur `.toLowerCase()`. Actuellement safe car `extractAffectedRanges` lowercase upstream — mais fragile. À documenter ou normaliser explicitement.
|
|
175
|
+
|
|
176
|
+
### LOW
|
|
177
|
+
|
|
178
|
+
- **L1** — `data/known-obsolete.json:92-96` : `struts2-core` flaggé HIGH sans gate de version (Struts 2.5.30 patché remonte « obsolete »).
|
|
179
|
+
- **L2** — `findCycleForVersion` (`lib/outdated.js:79-86`) prefix-match OK, pas de FP trouvé.
|
|
180
|
+
- **L3** — Tests : aucun ne couvre les edge cases ci-dessus (no-bounds, substring vendor leak, possible tier, update CPE, garbage `${...}`, `parseRange`).
|
|
181
|
+
|
|
182
|
+
### Verdict faux positifs
|
|
183
|
+
|
|
184
|
+
**Architecture saine, implémentation fail-open.** Les 4 cascades (H1+H2+H3+H4) doivent être patchées avant prod.
|
|
185
|
+
|
|
186
|
+
Patches prioritaires (ordre) :
|
|
187
|
+
1. H1 → one-liner, plus gros gain
|
|
188
|
+
2. H2 → tightening de `vendorMatchesGroup`
|
|
189
|
+
3. H3 → cacher le tier `possible` par défaut
|
|
190
|
+
4. H4 → strip `cpeFiltered:true` du report par défaut
|
|
191
|
+
5. M1 → skip `${...}` dans `matchDepsAgainstCves`
|
|
192
|
+
6. M2 → re-eval local des OSV ranges
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## B. Code reuse
|
|
197
|
+
|
|
198
|
+
### HIGH
|
|
199
|
+
|
|
200
|
+
- **Helper de cache disque triplicé** : `lib/osv.js:25-44`, `lib/nvd.js:28-45`, `lib/retire.js:27-45`. Pattern identique (`_fetchedAt` + TTL + JSON). `lib/outdated.js:22-37` même chose avec shape différent.
|
|
201
|
+
→ Extraire `lib/cache-disk.js` exportant `makeCache(dir, ttlMs)`.
|
|
202
|
+
|
|
203
|
+
- **`severityFromScore` + `SEVERITY_RANK` dupliqués 7+ fois** : `lib/nvd.js:64-71`, `lib/cve-download.js:82-89`, `lib/osv.js:60-66`, `lib/cve-match.js:9`, `lib/snyk.js:112`, `lib/cve-report.js:{634, 761, 1085, 1280}`, `fad-checker.js:657`.
|
|
204
|
+
→ `lib/severity.js` + `sortMatchesBySeverity()` helper.
|
|
205
|
+
|
|
206
|
+
- **Merge-by-source logic dupliquée** : `fad-checker.js:628-665` (`mergeBySource`) vs `lib/snyk.js:97-120` (`mergeWithFadResults`). Le `"both"` de Snyk désaccorde avec le `"fad+osv"` ailleurs.
|
|
207
|
+
→ Promouvoir `mergeBySource` dans `lib/cve-match.js`.
|
|
208
|
+
|
|
209
|
+
### MEDIUM
|
|
210
|
+
|
|
211
|
+
- **`depLabel` / `depToKey` réinventés 6 fois** : `fad-checker.js:551`, `lib/cpe.js:177-184`, `lib/osv.js:100-102`, `lib/cve-report.js:{648, 669, 773, 1296}`.
|
|
212
|
+
- **Walker de répertoires + `SKIP_DIRS` dupliqués 4 fois** : `lib/core.js:9-37`, `lib/npm/parse.js:253-284`, `lib/npm/collect.js:202-222`, `lib/retire.js:77-81`.
|
|
213
|
+
- **Deux parsers POM divergents** : `lib/core.js:49-117` vs `lib/transitive.js:111-158`. Légitime (besoins différents) mais extraction parent/coord en commun possible.
|
|
214
|
+
- **CLI hand-roll argv parsing** : `fad-checker.js:25-150` walk de `process.argv` pour `--completion`, `--set-nvd-key`, etc. — devrait être des subcommands `commander`.
|
|
215
|
+
|
|
216
|
+
### LOW
|
|
217
|
+
|
|
218
|
+
- 3 comparateurs de versions divergents : `compareMavenVersions`, `compareVersionsLoose` (`cve-report.js:705-715`), `semverCompare` (`npm/collect.js:25-37`).
|
|
219
|
+
- `parseMavenMetadataLatest` (`outdated.js:212-224`) en regex au lieu d'xml2js (OK car XML trivial).
|
|
220
|
+
|
|
221
|
+
### Modules clean
|
|
222
|
+
`maven-version.js`, `maven-repo.js`, `config.js`, `scan-completeness.js`, `cache-archive.js`.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## C. Code quality
|
|
227
|
+
|
|
228
|
+
### HIGH
|
|
229
|
+
|
|
230
|
+
- **`fad-checker.js` (665 lignes) = god script** : pré-parse argv mélangé à l'orchestration, business logic dans la CLI.
|
|
231
|
+
- **`lib/cve-report.js` (1455 lignes) = mega-module** : CSS inline (~200 lignes), JS inline (`TOGGLE_SCRIPT`), business logic (`buildFixRecommendations`, `versionJump`, `pickTopCriticalMatches`) et rendu HTML dans le même fichier. `RENDER_CTX` module-global (lignes 247-249) reset à chaque `buildBody` — comment auto-avoue le workaround.
|
|
232
|
+
→ Splitter en `lib/report/{css,html,recommendations,word}.js`. Passer `srcRoot` en paramètre.
|
|
233
|
+
- **Stringly-typed partout** : `"fad" | "osv" | "nvd" | "snyk" | "retire"` en chaînes nues + class CSS `.source.snyk` qui couple le HTML à ces littéraux. Renommer une source casse silencieusement le CSS.
|
|
234
|
+
→ `lib/keys.js` + `SOURCES = Object.freeze({...})`.
|
|
235
|
+
|
|
236
|
+
### MEDIUM
|
|
237
|
+
|
|
238
|
+
- **Param sprawl sur `writeReports` / `buildBody` / `renderExecutiveSummary`** — 10 champs disjoints recomposés à chaque appel.
|
|
239
|
+
- **CVE matcher : duplication structurelle tier-2/tier-3** (`cve-match.js:175-219`) — flatten en single loop avec ternaire.
|
|
240
|
+
- **Lockfile parsers v1 vs v2/v3 collés en un seul `parsePackageLock`** (`npm/parse.js:76-176`) — splitter.
|
|
241
|
+
- **~30 `catch {}` silencieux** dans tout le code. Beaucoup légitimes, certains masquent des bugs réels (`osv.js:233`, `outdated.js:187,201`, `fad-checker.js:259`).
|
|
242
|
+
- **`RENDER_CTX` global** → thread `srcRoot` via les signatures (3 niveaux max).
|
|
243
|
+
- **Skip-dir lists copy-collées 4x** avec divergence `build/` (Maven keep, JS skip) — documenter une fois.
|
|
244
|
+
- **`lib/core.js:230-235`** repeat parent-resolution dans `rewritePoms` — consolider.
|
|
245
|
+
|
|
246
|
+
### LOW
|
|
247
|
+
|
|
248
|
+
- Cache helpers ré-implémentés (cf. reuse).
|
|
249
|
+
- Commentaires narratifs sans valeur (`cve-report.js:404-406`, `cve-report.js:519-521`, `transitive.js:204-206`, `fad-checker.js:202`).
|
|
250
|
+
- Pollution `byId` potentielle via `excludedById`/`missingById` (`core.js:240-285`) — appliquer le guard du CLAUDE.md.
|
|
251
|
+
- `lib/osv.js:188` arithmétique d'index fragile (`(batchIdx - 1) * BATCH_SIZE + j`) — utiliser `i + j`.
|
|
252
|
+
- Verbosité booléenne threadée 4+ niveaux → `lib/log.js`.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## D. Efficiency
|
|
257
|
+
|
|
258
|
+
### HIGH
|
|
259
|
+
|
|
260
|
+
- **CVE index loadé eagerly** (`lib/cve-download.js:299-301`) : `JSON.parse(readFileSync)` synchrone même pour un projet de 3 deps. Charger async + lazy par bucket.
|
|
261
|
+
- **`cpe.js` re-parse les CPE et walk 2 fois** (`cpe.js:194-245`) : `parseCpe23` appelé répétitivement, walk pour confidence après le walk d'évaluation. 2M+ string-splits sur un projet 800 deps × 50 matches.
|
|
262
|
+
→ Cache `m._parsed ||= parseCpe23(m.criteria)` + retour du match satisfaisant.
|
|
263
|
+
- **NVD enrichment 100 % serial** (`nvd.js:186-203`) : `sleep(600|6000)` entre chaque appel. 200 CVEs uniques = 2 min avec clé, **20 min sans**. NIST policy = 50/30s window → permet parallélisme.
|
|
264
|
+
- **TOCTOU `existsSync` + `readFile`** dans `transitive.js:52`, `nvd.js:34`, `osv.js:33`, `retire.js:32-37`, `outdated.js:22-25`. Doublé syscalls.
|
|
265
|
+
- **`outdated.checkOutdatedDeps` cache reset global** (`outdated.js:230`) : `cache.entries = {}` si meta TTL périmé → 600 deps refetch même si entrées fraîches.
|
|
266
|
+
→ TTL par-entrée.
|
|
267
|
+
|
|
268
|
+
### MEDIUM
|
|
269
|
+
|
|
270
|
+
- `findEolProduct` re-sort la prefix-list dans la boucle (`outdated.js:46-52`) — sort once at module load.
|
|
271
|
+
- `outByKey` Map rebuild 6x dans `cve-report.js` (`:633-679`, `:760-795`, `:993-1003`) — builder once dans `buildBody`.
|
|
272
|
+
- CVE bulk JSON `readFileSync` per file (`cve-download.js:274-287`) — borné par cache 24h, MEDIUM.
|
|
273
|
+
- `cve-report.js` : `renderDetailPanel` (`:417-493`) + `groupExternalRefs` (`:340`) re-alloués par row → 45k allocations sur un report 5k rows. Pré-classifier à l'enrichment NVD.
|
|
274
|
+
- **POMs parsés 2x sur le chemin `rewritePoms`** (`core.js:50` puis re-read à `:219`). `structuredClone` ou skip re-parse en read-only.
|
|
275
|
+
- **`transitive.js:314` : `queue.shift()` O(n)** sur 4000+ transitives → 16M memmoves. Switch en cursor.
|
|
276
|
+
- **Maven Central : pas de batch endpoint** (`outdated.js:176`) — solrsearch accepte OR boolean. 600 deps → 1 requête au lieu de 600.
|
|
277
|
+
- **OSV detail fetch serial** dans `queryBatch` (`osv.js:218-236`) — `p-limit(10)` similaire à fad-checker.js.
|
|
278
|
+
|
|
279
|
+
### LOW
|
|
280
|
+
|
|
281
|
+
- `getAllInheritedProps` rebuild via spread — fine en dessous de 10k POMs.
|
|
282
|
+
- `xml2js` 3-5x plus lent que `fast-xml-parser` — hors scope.
|
|
283
|
+
- Eager `require("chalk")` etc. — invisible.
|
|
284
|
+
|
|
285
|
+
### Note positive
|
|
286
|
+
Le matcher CVE (`matchDepsAgainstCves`) est **Map-indexé correctement** (`byPackageName` / `byProduct` pré-bucketés) — pas de O(n²) sur le hot path.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## E. Recommandations priorisées
|
|
291
|
+
|
|
292
|
+
| # | Action | Severity | Effort | Impact |
|
|
293
|
+
|---|---|---|---|---|
|
|
294
|
+
| 1 | Patch `isVersionAffected` fail-closed (H1) | HIGH FP | 1 ligne | Énorme |
|
|
295
|
+
| 2 | Tighten `vendorMatchesGroup` substring (H2) | HIGH FP | 10 lignes | Énorme |
|
|
296
|
+
| 3 | Drop ou hide tier `possible` (H3) | HIGH FP | 5 lignes | Énorme |
|
|
297
|
+
| 4 | Strip `cpeFiltered:true` du report (H4) | HIGH FP | 3 lignes | Gros |
|
|
298
|
+
| 5 | Handle CPE `update` qualifier (H5) | HIGH FP | 15 lignes | Moyen |
|
|
299
|
+
| 6 | Skip `${...}` dans matcher (M1) | MED FP | 3 lignes | Moyen |
|
|
300
|
+
| 7 | OSV ranges re-eval local (M2) | MED FP | 30 lignes | Moyen |
|
|
301
|
+
| 8 | Extract `lib/severity.js` + `SEVERITY_RANK` | HIGH reuse | 1h | Maintenance |
|
|
302
|
+
| 9 | Extract `lib/cache-disk.js` | HIGH reuse | 1h | Maintenance |
|
|
303
|
+
| 10 | NVD enrichment parallèle (token-bucket) | HIGH perf | 30min | 10x speedup |
|
|
304
|
+
| 11 | Batch Maven Central Solr (#13 efficiency) | HIGH perf | 1h | 100x speedup |
|
|
305
|
+
| 12 | `parseCpe23` memoization (CPE perf) | HIGH perf | 10min | 2-3x speedup |
|
|
306
|
+
| 13 | Splitter `cve-report.js` en sous-modules | HIGH quality | 2h | Maintenance |
|
|
307
|
+
| 14 | Tests des edge cases FP (`update`, no-bounds, substring) | LOW | 2h | Filet de sécurité |
|
|
308
|
+
|
|
309
|
+
**Quick wins (< 1 jour cumulé)** : #1, #2, #3, #4, #6, #12 → diviserait le taux de FP par ~3-5 et accélérerait le rendu de ~2-3×.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## F. Verdict final
|
|
314
|
+
|
|
315
|
+
- **Architecture :** **bonne**. Séparation Maven/npm, 3-tier matching, post-CPE refinement, multi-source dedup, caches TTL — tout est en place.
|
|
316
|
+
- **Implémentation matcher :** **fail-open systémique**. Les 4 cascades H1→H4 transforment un outil correctement architecturé en générateur de faux positifs.
|
|
317
|
+
- **Performance :** correcte en CPU (Map-indexed), **mauvaise en I/O** (NVD serial, Solr unitaire, double-parse POM).
|
|
318
|
+
- **Qualité :** 2 mega-modules (`cve-report.js` 1455 LoC, `fad-checker.js` 665 LoC) à splitter, ~30 `catch {}` à auditer, 7+ duplications de `SEVERITY_RANK`.
|
|
319
|
+
- **Tests :** 96 tests existants mais happy-path-heavy ; aucune couverture des edge cases FP listés ci-dessus.
|
|
320
|
+
|
|
321
|
+
**Recommandation pratique :** sans les patches H1-H4, le rapport doit être traité comme une **liste de départ pour triage manuel**, pas comme un inventaire de vulnérabilités exploitable. Le tier `exact` reste fiable. Tout le reste demande des yeux humains sur la description CVE avant action.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Annexe — Références fichier:ligne
|
|
326
|
+
|
|
327
|
+
**Faux positifs :**
|
|
328
|
+
- `lib/maven-version.js:25-94, 101-124, 131-146`
|
|
329
|
+
- `lib/cve-match.js:9, 99-109, 151-162, 164-173, 175-219, 191-198`
|
|
330
|
+
- `lib/cpe.js:25, 36-68, 81-100, 161-184, 194-245, 266, 269-272`
|
|
331
|
+
- `lib/osv.js:25-44, 60-66, 100-102, 188, 218-236, 242-265, 273`
|
|
332
|
+
- `lib/nvd.js:28-45, 64-71, 145, 186-203`
|
|
333
|
+
- `lib/retire.js:24-45, 77-81, 148-193`
|
|
334
|
+
- `lib/outdated.js:22-37, 46-52, 79-86, 168-187, 212-254`
|
|
335
|
+
- `lib/cve-download.js:82-89, 163-164, 274-301`
|
|
336
|
+
- `lib/transitive.js:51-101, 111-158, 204-235, 273-320`
|
|
337
|
+
- `lib/core.js:9-37, 49-149, 219-285`
|
|
338
|
+
- `lib/npm/{parse.js:76-291, collect.js:25-224}`
|
|
339
|
+
- `lib/cve-report.js:48-247, 340, 404-493, 519-521, 559, 633-1455`
|
|
340
|
+
- `fad-checker.js:25-150, 202, 259, 551, 628-665`
|
|
341
|
+
- `data/{cpe-coord-map.json, known-obsolete.json:92-96}`
|
|
342
|
+
|
|
343
|
+
**Tests manquants :** `test/{cpe,cve-match,maven-version}.test.js` — ajouter edge cases `update`, no-bounds, substring vendor leak, garbage version.
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# fad-
|
|
1
|
+
# fad-checker
|
|
2
2
|
|
|
3
3
|
> **F**ucking **A**utonomous **D**ependency **C**hecker
|
|
4
4
|
|
|
5
|
-
`fad-
|
|
5
|
+
`fad-checker` scans **Maven**, **npm**, **Yarn** and **vendored JavaScript** in any source tree — multi-module, monorepo, polyglot, whatever you've got — and produces a single self-contained HTML report with CVE, EOL, obsolete and outdated findings, plus per-ecosystem fix recipes.
|
|
6
6
|
|
|
7
7
|
It runs against the source files alone. **No `mvn`, no `npm install`, no `yarn`, no Docker.** It reads `pom.xml`, `package-lock.json` and `yarn.lock` directly.
|
|
8
8
|
|
|
@@ -47,16 +47,16 @@ The HTML report opens in any browser, contains every detail (CVSS vectors, refer
|
|
|
47
47
|
## Quick start
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
npm install -g fad-
|
|
51
|
-
fad-
|
|
50
|
+
npm install -g fad-checker
|
|
51
|
+
fad-checker -s ./my-project
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
That's it. The report lands in `./fad-
|
|
54
|
+
That's it. The report lands in `./fad-checker-report/cve-report.html`.
|
|
55
55
|
|
|
56
56
|
Want a 10× faster NVD enrichment? [Get a free NVD API key](https://nvd.nist.gov/developers/request-an-api-key) (instant), then:
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
fad-
|
|
59
|
+
fad-checker --set-nvd-key YOUR_KEY
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
---
|
|
@@ -65,28 +65,28 @@ fad-check --set-nvd-key YOUR_KEY
|
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
# Read-only full scan (default: all sources on)
|
|
68
|
-
fad-
|
|
68
|
+
fad-checker -s ./proj
|
|
69
69
|
|
|
70
70
|
# Exclude private/internal libs by groupId regex
|
|
71
|
-
fad-
|
|
71
|
+
fad-checker -s ./proj -e "^(com\.acme|org\.private)\."
|
|
72
72
|
|
|
73
73
|
# Also write cleaned POMs (private deps stripped, ready for Snyk)
|
|
74
|
-
fad-
|
|
74
|
+
fad-checker -s ./proj -t ../proj-clean -e "^com\.acme\."
|
|
75
75
|
|
|
76
76
|
# Then run Snyk on the cleaned tree and merge findings
|
|
77
|
-
fad-
|
|
77
|
+
fad-checker -s ./proj -t ../proj-clean -e "^com\.acme\." --snyk
|
|
78
78
|
|
|
79
79
|
# Faster: skip Maven Central / no transitive walk
|
|
80
|
-
fad-
|
|
80
|
+
fad-checker -s ./proj --no-all-libs --no-transitive
|
|
81
81
|
|
|
82
82
|
# Fully offline (uses cached data only)
|
|
83
|
-
fad-
|
|
83
|
+
fad-checker -s ./proj --offline
|
|
84
84
|
|
|
85
85
|
# Only one ecosystem
|
|
86
|
-
fad-
|
|
86
|
+
fad-checker -s ./proj --ecosystem maven # or npm | both | auto (default)
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
Run `fad-
|
|
89
|
+
Run `fad-checker --help` for the full flag list.
|
|
90
90
|
|
|
91
91
|
---
|
|
92
92
|
|
|
@@ -131,38 +131,38 @@ Each CVE row shows: severity badge · CVE / GHSA id · dep coord & version · wh
|
|
|
131
131
|
### As a global CLI
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
|
-
npm install -g fad-
|
|
134
|
+
npm install -g fad-checker
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
### From source
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
git clone <repo-url> fad-
|
|
141
|
-
cd fad-
|
|
140
|
+
git clone <repo-url> fad-checker
|
|
141
|
+
cd fad-checker
|
|
142
142
|
npm install
|
|
143
|
-
node fad-
|
|
143
|
+
node fad-checker.js --help
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
### Single-binary build (no Node required)
|
|
147
147
|
|
|
148
148
|
```bash
|
|
149
149
|
npm install # one-time, brings in bun
|
|
150
|
-
npm run build # → dist/fad-
|
|
150
|
+
npm run build # → dist/fad-checker-linux + dist/fad-checker.exe
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
### Shell completion
|
|
154
154
|
|
|
155
155
|
```bash
|
|
156
|
-
fad-
|
|
156
|
+
fad-checker --completion bash > /etc/bash_completion.d/fad-checker
|
|
157
157
|
# or for zsh:
|
|
158
|
-
fad-
|
|
158
|
+
fad-checker --completion zsh > ~/.zsh/completions/_fad-checker
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
---
|
|
162
162
|
|
|
163
163
|
## How it scans without any build tool
|
|
164
164
|
|
|
165
|
-
This is the surprising bit. The whole point is that you can run `fad-
|
|
165
|
+
This is the surprising bit. The whole point is that you can run `fad-checker` against a *checkout* with no build environment.
|
|
166
166
|
|
|
167
167
|
- **Maven** — `pom.xml` files are parsed with xml2js. Property substitution (`${jackson.version}`), parent inheritance, local BOM imports (`<scope>import</scope>`) and every profile are resolved in-process. Transitive deps are walked by fetching child POMs from Maven Central (cached forever — POMs are immutable). When the project uses an **external BOM** (`spring-boot-dependencies` etc.), the deps whose version comes from that BOM can't be resolved without `mvn` itself — those are surfaced in chapter 0 as "unresolved-versions" so you know what's missing.
|
|
168
168
|
- **npm / Yarn** — `package-lock.json` (v1, v2, v3) and `yarn.lock` v1 are parsed directly. Lockfiles already contain every transitive version. No `node_modules/` traversal, no `npm install`. A package.json *without* a sibling lockfile is intentionally skipped (its ranges aren't queryable) and reported in chapter 0.
|
|
@@ -177,7 +177,7 @@ This is the surprising bit. The whole point is that you can run `fad-check` agai
|
|
|
177
177
|
|
|
178
178
|
## Caching
|
|
179
179
|
|
|
180
|
-
All cached data lives in `~/.fad-
|
|
180
|
+
All cached data lives in `~/.fad-checker/`:
|
|
181
181
|
|
|
182
182
|
| Cache | Path | TTL |
|
|
183
183
|
| --- | --- | --- |
|
|
@@ -194,9 +194,9 @@ All cached data lives in `~/.fad-check/`:
|
|
|
194
194
|
Export the lot to share between machines:
|
|
195
195
|
|
|
196
196
|
```bash
|
|
197
|
-
fad-
|
|
197
|
+
fad-checker --export-cache fad-cache.tar.gz
|
|
198
198
|
# on the other box:
|
|
199
|
-
fad-
|
|
199
|
+
fad-checker --import-cache fad-cache.tar.gz
|
|
200
200
|
```
|
|
201
201
|
|
|
202
202
|
`--include-config` ships the NVD API key too (off by default).
|
|
@@ -205,19 +205,19 @@ fad-check --import-cache fad-cache.tar.gz
|
|
|
205
205
|
|
|
206
206
|
## Custom Maven repositories
|
|
207
207
|
|
|
208
|
-
Out of the box `fad-
|
|
208
|
+
Out of the box `fad-checker` queries Maven Central for transitive POMs and latest versions. If your project depends on artifacts that live on a private Nexus / Artifactory / JBoss repo, add them so transitive resolution and outdated checks work end-to-end.
|
|
209
209
|
|
|
210
210
|
```bash
|
|
211
|
-
# Persist a repo (lives in ~/.fad-
|
|
212
|
-
fad-
|
|
213
|
-
fad-
|
|
214
|
-
fad-
|
|
215
|
-
fad-
|
|
211
|
+
# Persist a repo (lives in ~/.fad-checker/config.json)
|
|
212
|
+
fad-checker --add-repo nexus https://nexus.acme.com/repository/maven-public/
|
|
213
|
+
fad-checker --add-repo nexus-priv https://nexus.acme.com/repository/maven-private/ --auth alice:s3cr3t
|
|
214
|
+
fad-checker --list-repos
|
|
215
|
+
fad-checker --remove-repo nexus-priv
|
|
216
216
|
|
|
217
217
|
# One-off (not persisted) — repeatable
|
|
218
|
-
fad-
|
|
218
|
+
fad-checker -s ./proj --repo https://nexus.acme.com/repository/maven-public/
|
|
219
219
|
# Inline auth in the URL also works:
|
|
220
|
-
fad-
|
|
220
|
+
fad-checker -s ./proj --repo https://alice:s3cr3t@nexus.acme.com/repository/maven-public/
|
|
221
221
|
```
|
|
222
222
|
|
|
223
223
|
Repos are tried **in declared order, Maven Central last**. Auth is sent as a `Basic <base64>` header. POMs and `maven-metadata.xml` are cached per coord, so subsequent runs are free even against a private repo.
|
|
@@ -226,7 +226,7 @@ Repos are tried **in declared order, Maven Central last**. Auth is sent as a `Ba
|
|
|
226
226
|
|
|
227
227
|
## Data sources & acknowledgments
|
|
228
228
|
|
|
229
|
-
`fad-
|
|
229
|
+
`fad-checker` is glue around several outstanding public datasets. Each is used per its license terms.
|
|
230
230
|
|
|
231
231
|
| Source | What we use | License | API / endpoint |
|
|
232
232
|
| --- | --- | --- | --- |
|
|
@@ -239,7 +239,7 @@ Repos are tried **in declared order, Maven Central last**. Auth is sent as a `Ba
|
|
|
239
239
|
| [Snyk](https://snyk.io/) (optional) | Additional CVE source via `snyk test --all-projects --json` | Per Snyk EULA; needs a Snyk account | Local CLI `snyk` |
|
|
240
240
|
| [MITRE CWE](https://cwe.mitre.org/) | Weakness category links in the report | Free public reference | Linked by URL only, no API call |
|
|
241
241
|
|
|
242
|
-
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-
|
|
242
|
+
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.
|
|
243
243
|
|
|
244
244
|
---
|
|
245
245
|
|
|
@@ -255,14 +255,14 @@ Built-in guardrails that fire **before** any disk write:
|
|
|
255
255
|
|
|
256
256
|
## Compared to…
|
|
257
257
|
|
|
258
|
-
| Tool | What `fad-
|
|
258
|
+
| Tool | What `fad-checker` adds |
|
|
259
259
|
| --- | --- |
|
|
260
260
|
| `mvn dependency:tree` | No Maven needed; multi-source CVE scan; HTML report |
|
|
261
261
|
| `npm audit` | Polyglot (Maven + npm + vendored JS in one report); EOL & obsolete checks; works without `npm install` |
|
|
262
262
|
| Snyk CLI | Free; offline-capable; integrates Snyk's results if you have it |
|
|
263
263
|
| OWASP DC | Faster (cached); cleaner UI; multi-source dedup |
|
|
264
264
|
|
|
265
|
-
You don't have to choose — `fad-
|
|
265
|
+
You don't have to choose — `fad-checker` will use any of them as input (`--snyk`) and merge results.
|
|
266
266
|
|
|
267
267
|
---
|
|
268
268
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Bash completion for fad-
|
|
3
|
-
# Source this file or copy it to /etc/bash_completion.d/fad-
|
|
2
|
+
# Bash completion for fad-checker (Fucking Autonomous Dependency Checker)
|
|
3
|
+
# Source this file or copy it to /etc/bash_completion.d/fad-checker
|
|
4
4
|
_fad_check_complete() {
|
|
5
5
|
local cur prev opts
|
|
6
6
|
COMPREPLY=()
|
|
@@ -20,4 +20,4 @@ _fad_check_complete() {
|
|
|
20
20
|
return 0
|
|
21
21
|
fi
|
|
22
22
|
}
|
|
23
|
-
complete -F _fad_check_complete fad-
|
|
23
|
+
complete -F _fad_check_complete fad-checker
|