fad-checker 1.0.6 → 2.0.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.
- package/CHANGELOG.md +47 -0
- package/CLAUDE.md +27 -9
- package/README.md +27 -13
- package/completions/fad-checker.bash +4 -1
- package/completions/fad-checker.zsh +9 -0
- package/data/eol-mapping.json +17 -0
- package/docs/ARCHITECTURE.md +35 -7
- package/docs/USAGE.md +15 -7
- package/docs/superpowers/plans/2026-05-29-codec-composer.md +556 -0
- package/docs/superpowers/plans/2026-05-29-codec-foundation.md +851 -0
- package/docs/superpowers/plans/2026-05-29-codec-nuget.md +432 -0
- package/docs/superpowers/plans/2026-05-29-codec-pypi.md +450 -0
- package/docs/superpowers/specs/2026-05-29-codecs-multi-ecosystem-design.md +251 -0
- package/fad-checker.js +108 -82
- package/lib/codecs/codec.interface.js +27 -0
- package/lib/codecs/composer/parse.js +59 -0
- package/lib/codecs/composer/registry.js +92 -0
- package/lib/codecs/composer.codec.js +93 -0
- package/lib/codecs/index.js +37 -0
- package/lib/codecs/maven.codec.js +71 -0
- package/lib/{npm → codecs/npm}/collect.js +32 -18
- package/lib/codecs/npm.codec.js +52 -0
- package/lib/codecs/nuget/parse.js +75 -0
- package/lib/codecs/nuget/registry.js +88 -0
- package/lib/codecs/nuget.codec.js +92 -0
- package/lib/codecs/pypi/parse.js +71 -0
- package/lib/codecs/pypi/registry.js +89 -0
- package/lib/codecs/pypi.codec.js +81 -0
- package/lib/codecs/recipes.js +108 -0
- package/lib/codecs/select.js +27 -0
- package/lib/codecs/yarn.codec.js +19 -0
- package/lib/cve-match.js +3 -6
- package/lib/cve-report.js +34 -70
- package/lib/dep-record.js +60 -0
- package/lib/osv.js +22 -14
- package/lib/outdated.js +13 -2
- package/package.json +3 -2
- package/test/cli-ecosystem.test.js +30 -0
- package/test/codec-edge-cases.test.js +131 -0
- package/test/codec-integration.test.js +119 -0
- package/test/codecs.test.js +92 -0
- package/test/composer.test.js +71 -0
- package/test/cve-report.test.js +9 -0
- package/test/dep-record.test.js +31 -0
- package/test/fixtures/csharp-config/packages.config +4 -0
- package/test/fixtures/csharp-csproj/Directory.Packages.props +5 -0
- package/test/fixtures/csharp-csproj/app.csproj +7 -0
- package/test/fixtures/csharp-lock/packages.lock.json +6 -0
- package/test/fixtures/monorepo-mixed/packages/no-lock/package.json +2 -1
- package/test/fixtures/php-app/composer.json +5 -0
- package/test/fixtures/php-app/composer.lock +10 -0
- package/test/fixtures/polyglot/cs/packages.lock.json +1 -0
- package/test/fixtures/polyglot/js/package-lock.json +1 -0
- package/test/fixtures/polyglot/js/package.json +1 -0
- package/test/fixtures/polyglot/mvn/pom.xml +7 -0
- package/test/fixtures/polyglot/php/composer.lock +1 -0
- package/test/fixtures/polyglot/py/poetry.lock +3 -0
- package/test/fixtures/python-pipenv/Pipfile.lock +1 -0
- package/test/fixtures/python-poetry/poetry.lock +7 -0
- package/test/fixtures/python-reqs/requirements.txt +5 -0
- package/test/fixtures/python-uv/uv.lock +3 -0
- package/test/monorepo.test.js +1 -1
- package/test/npm-registry.test.js +1 -1
- package/test/npm.test.js +8 -4
- package/test/nuget.test.js +66 -0
- package/test/osv.test.js +62 -0
- package/test/pypi.test.js +72 -0
- package/test/webjar.test.js +1 -1
- /package/lib/{npm → codecs/npm}/parse.js +0 -0
- /package/lib/{npm → codecs/npm}/registry.js +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `fad-checker` are documented here.
|
|
4
|
+
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [2.0.0]
|
|
7
|
+
|
|
8
|
+
Major release: **codec architecture** + three new ecosystems.
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Codec abstraction** (`lib/codecs/`): every ecosystem now lives behind a single
|
|
12
|
+
interface (`detect` / `collect` / `coordKey` / `formatCoord` / `osvPackageName` /
|
|
13
|
+
`checkRegistry` / `resolveEolProduct` / `recipe` / `nativeScanners`) discovered through
|
|
14
|
+
a registry. OSV, NVD, CPE refinement and endoflife.date are shared, ecosystem-agnostic
|
|
15
|
+
services. Adding an ecosystem is adding a codec — no orchestrator changes.
|
|
16
|
+
- **Composer (PHP)** codec — `composer.lock` / `composer.json`, Packagist `abandoned`,
|
|
17
|
+
EOL (Laravel/Symfony/Drupal), `composer require` fix recipe.
|
|
18
|
+
- **PyPI (Python)** codec — `poetry.lock` / `Pipfile.lock` / `uv.lock` / `pdm.lock` /
|
|
19
|
+
`requirements.txt`, PEP 503 name normalisation, PyPI `yanked` + "Inactive" classifier,
|
|
20
|
+
EOL (Django/NumPy/FastAPI), `pip install` fix recipe.
|
|
21
|
+
- **NuGet (C#/.NET)** codec — `packages.lock.json` / `*.csproj` (+ Central Package
|
|
22
|
+
Management via `Directory.Packages.props`) / `packages.config`, NuGet `deprecation`,
|
|
23
|
+
EOL (.NET/ASP.NET Core/EF Core), `dotnet add package` fix recipe.
|
|
24
|
+
- `--ecosystem` is now a **list** (`auto` | `all` | comma list) and per-codec opt-outs
|
|
25
|
+
`--no-maven` / `--no-npm` / `--no-yarn` / `--no-nuget` / `--no-composer` / `--no-pypi`.
|
|
26
|
+
- Generalized `depRecord` (`ecosystem` / `namespace` / `name` / `coordKey`).
|
|
27
|
+
- Dependency: `smol-toml` (TOML lockfile parsing).
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- **npm no-lockfile behaviour (contract change)**: a `package.json` without a sibling
|
|
31
|
+
`package-lock.json` / `yarn.lock` is now parsed **best-effort** (pinned exact versions
|
|
32
|
+
scanned, ranges skipped, `no-lockfile` warning) instead of being skipped entirely. The
|
|
33
|
+
same lockfile-first, best-effort fallback applies to Composer / PyPI / NuGet.
|
|
34
|
+
- The orchestrator now loops over detected codecs; report sections, labels, coordinate
|
|
35
|
+
formatting and fix recipes are driven by the codec registry.
|
|
36
|
+
- Maven CVE-index (cvelistV5) and retire.js are now `nativeScanners` owned by their codec.
|
|
37
|
+
- `--no-js` is retained as an alias for `--no-npm` + `--no-yarn`.
|
|
38
|
+
|
|
39
|
+
### Notes
|
|
40
|
+
- Maven map keys stay bare `g:a` (collision-free against the prefixed `npm:` / `nuget:` /
|
|
41
|
+
`composer:` / `pypi:` keyspaces) to keep transitive resolution and existing behaviour intact.
|
|
42
|
+
- Verified non-regression against a real Maven + npm project: identical findings modulo
|
|
43
|
+
upstream advisory drift.
|
|
44
|
+
|
|
45
|
+
## [1.x]
|
|
46
|
+
- Maven + npm/Yarn + vendored-JS scanning; CVEProject + OSV + NVD + CPE; EOL / obsolete /
|
|
47
|
+
outdated; HTML + Word report; private-dep cleanup for Snyk.
|
package/CLAUDE.md
CHANGED
|
@@ -7,7 +7,7 @@ Code-level orientation for contributors and Claude Code sessions on this repo.
|
|
|
7
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
|
-
2. Walks every JS package (`package.json` + `package-lock.json` v1/v2/v3 or `yarn.lock` v1) in the same source tree.
|
|
10
|
+
2. Walks every JS package (`package.json` + `package-lock.json` v1/v2/v3 or `yarn.lock` v1), every PHP package (`composer.lock`, or `composer.json` best-effort), and every Python project (`poetry.lock`/`Pipfile.lock`/`uv.lock`/`pdm.lock`, or `requirements.txt` best-effort), and every .NET project (`packages.lock.json`, or `*.csproj`+`Directory.Packages.props`/`packages.config` best-effort) in the same source tree. Each ecosystem is a **codec** (`lib/codecs/`): maven, npm, yarn, composer, pypi, nuget. Adding one is adding a codec.
|
|
11
11
|
3. Scans the union against:
|
|
12
12
|
- the CVEProject `cvelistV5` Maven-relevant index (built locally),
|
|
13
13
|
- OSV.dev (multi-ecosystem),
|
|
@@ -24,7 +24,7 @@ No build tool (`mvn`, `npm install`, `yarn`) is required on PATH — `pom.xml` /
|
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npm install
|
|
27
|
-
npm test #
|
|
27
|
+
npm test # 194 unit tests via node --test
|
|
28
28
|
|
|
29
29
|
# basic cleanup workflow
|
|
30
30
|
node fad-checker.js -s ./proj # read-only, full report
|
|
@@ -51,7 +51,23 @@ Guardrails enforced at startup:
|
|
|
51
51
|
## Architecture (one-liner per file)
|
|
52
52
|
|
|
53
53
|
```
|
|
54
|
-
fad-checker.js Thin CLI: commander parsing, orchestration only.
|
|
54
|
+
fad-checker.js Thin CLI: commander parsing, orchestration only (loops over active codecs).
|
|
55
|
+
lib/codecs/index.js Codec registry: getCodec / allCodecs / detectCodecs.
|
|
56
|
+
lib/codecs/codec.interface.js Codec contract + assertCodecShape() validator.
|
|
57
|
+
lib/codecs/maven.codec.js Maven codec (wraps core.js + transitive.js + CVE-index scanner).
|
|
58
|
+
lib/codecs/npm.codec.js npm codec (wraps lib/codecs/npm/* + retire.js scanner). yarn.codec.js shares it.
|
|
59
|
+
lib/codecs/select.js resolveActiveCodecs(): --ecosystem list + --no-<id> → active codec ids.
|
|
60
|
+
lib/codecs/recipes.js Per-ecosystem fix-recipe specs (pin snippet + direct-update wording).
|
|
61
|
+
lib/codecs/composer.codec.js Composer (PHP) codec.
|
|
62
|
+
lib/codecs/composer/parse.js composer.lock + composer.json parsers.
|
|
63
|
+
lib/codecs/composer/registry.js Packagist query → latest stable + `abandoned` flag.
|
|
64
|
+
lib/codecs/pypi.codec.js PyPI (Python) codec.
|
|
65
|
+
lib/codecs/pypi/parse.js poetry.lock/Pipfile.lock/uv.lock/pdm.lock (smol-toml) + requirements.txt parsers (PEP 503).
|
|
66
|
+
lib/codecs/pypi/registry.js PyPI JSON query → latest + yanked + inactive classifier.
|
|
67
|
+
lib/codecs/nuget.codec.js NuGet (C#/.NET) codec.
|
|
68
|
+
lib/codecs/nuget/parse.js packages.lock.json + *.csproj (+CPM Directory.Packages.props) + packages.config parsers.
|
|
69
|
+
lib/codecs/nuget/registry.js NuGet registration query → latest stable + per-version deprecation.
|
|
70
|
+
lib/dep-record.js makeDepRecord(): generalized depRecord ({ ecosystem, namespace, name, coordKey, … }).
|
|
55
71
|
lib/core.js POM parsing, parent resolution, all-profile merge, rewrite.
|
|
56
72
|
lib/maven-version.js Maven version parsing + range comparison (no external deps).
|
|
57
73
|
lib/cve-download.js Bulk download of CVEProject/cvelistV5 + Maven-relevant index build.
|
|
@@ -65,9 +81,9 @@ lib/nvd.js NIST NVD enrichment (CVSS, references, CPE configur
|
|
|
65
81
|
lib/snyk.js `snyk test --all-projects --json` runner + merge.
|
|
66
82
|
lib/retire.js retire.js (vendored-JS scanner) wrapper + cache + normaliser.
|
|
67
83
|
lib/scan-completeness.js Warnings for deps fad-checker couldn't fully resolve.
|
|
68
|
-
lib/npm/parse.js package.json, package-lock.json (v1/2/3), yarn.lock v1 parsers.
|
|
69
|
-
lib/npm/collect.js Merge across JS manifests → unified resolvedDeps Map.
|
|
70
|
-
lib/npm/registry.js npm registry packument query → per-version deprecation + dist-tags.latest (npm EOL feeds via lib/outdated.js).
|
|
84
|
+
lib/codecs/npm/parse.js package.json, package-lock.json (v1/2/3), yarn.lock v1 parsers.
|
|
85
|
+
lib/codecs/npm/collect.js Merge across JS manifests → unified resolvedDeps Map.
|
|
86
|
+
lib/codecs/npm/registry.js npm registry packument query → per-version deprecation + dist-tags.latest (npm EOL feeds via lib/outdated.js).
|
|
71
87
|
lib/cache-archive.js tar.gz / zip export & import of ~/.fad-checker/.
|
|
72
88
|
lib/config.js Persistent user config in ~/.fad-checker/config.json (mode 0600).
|
|
73
89
|
data/ known-obsolete.json, eol-mapping.json, cpe-coord-map.json, known-public-namespaces.json
|
|
@@ -84,9 +100,11 @@ For the deep dive — pipeline stages, the resolved-deps Map shape, report struc
|
|
|
84
100
|
- **All profiles are merged, never prompted for**: every profile's deps are unioned so Snyk sees every dep any profile could pull in. `activeByDefault` wins only for property value conflicts.
|
|
85
101
|
- **No `process.exit(1)` mid-pipeline**: a parse/rewrite failure for one POM logs and continues so the summary still prints.
|
|
86
102
|
- **HTML report is self-contained**: inline CSS, no external assets. The `.doc` variant is the same HTML with Office XML namespace meta tags — Word opens it natively.
|
|
87
|
-
- **
|
|
103
|
+
- **Codec abstraction**: every ecosystem lives behind a codec (`lib/codecs/*`) implementing one interface (`detect`/`collect`/`coordKey`/`formatCoord`/`osvPackageName`/`checkRegistry`/`resolveEolProduct`/`recipe`/`nativeScanners`). The orchestrator loops over the codecs `detectCodecs()` returns. OSV/NVD/CPE and the endoflife.date fetch are **shared, ecosystem-agnostic** services that ask the codec for a package/product name. CVE-index (maven) and retire.js (npm) are `nativeScanners` owned by their codec. Adding an ecosystem = adding a codec, no orchestrator edits.
|
|
104
|
+
- **Map keys are ecosystem-namespaced** (`dep.coordKey`): Maven uses a **bare** `g:a` (kept prefix-free so `transitive.js` internals and existing tests are untouched); npm uses `npm:name`; new ecosystems use `nuget:`/`composer:vendor/pkg`/`pypi:`. Bare `g:a` is collision-free against those prefixes. Built by `makeDepRecord()`; `groupId`/`artifactId`/`pomPaths` are kept as real duplicated alias fields (not getters — depRecords are spread in hot paths).
|
|
88
105
|
- **Every distinct version is scanned, not just the highest**: when profiles/modules pin the same `g:a` to different versions, the resolved-dep entry keeps `version` = highest (representative for display/EOL/outdated) but `versions` = all distinct concrete versions. CVE matching (`matchOne`) and OSV (`queryOsvForDeps`) iterate `versions` so a vuln affecting only a lower-versioned profile variant isn't missed. Match dedup keys are `g:a:version|cve.id` (version included) to preserve per-version findings.
|
|
89
|
-
- **
|
|
106
|
+
- **npm no-lockfile = best-effort (not skipped)**: `package.json` without a sibling `package-lock.json`/`yarn.lock` is parsed best-effort — pinned exact versions (`"1.2.3"`) are scanned, ranges (`"^1.0.0"`) are skipped, and a `no-lockfile` warning (chapter 0) flags the partial coverage. (Earlier versions skipped such manifests entirely.)
|
|
107
|
+
- **`--ecosystem` is a list**: `auto` (default = `detectCodecs()`) | `all` | comma list `maven,npm,nuget,composer,pypi` (legacy `both`/`maven`/`npm` still parse). Per-codec opt-out via `--no-maven`/`--no-npm`/`--no-yarn`/`--no-nuget`/`--no-composer`/`--no-pypi`; `--no-js` is an alias for `--no-npm`+`--no-yarn`.
|
|
90
108
|
- **Source identifiers**: every match carries `source: "fad" | "osv" | "nvd" | "snyk" | "retire"` (or a `+`-joined combination).
|
|
91
109
|
|
|
92
110
|
## Testing
|
|
@@ -108,7 +126,7 @@ Test fixtures live in `test/fixtures/`:
|
|
|
108
126
|
- 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.
|
|
109
127
|
- `endoflife.date` API responses cached 7 days; Maven Central version lookups cached 24 hours. Cache lives in `~/.fad-checker/`.
|
|
110
128
|
- **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).
|
|
111
|
-
- **`--offline` umbrella flag**: skips every network call (CVE/OSV/NVD/Maven Central/endoflife/npm-registry/retire). Falls back to whatever is already cached. Per-source variants (`--cve-offline`, `--no-osv`, `--no-nvd`, `--no-retire`, `--no-transitive`, `--no-js`) still work independently. npm registry deprecation always runs when online; npm (and Maven) outdated is gated by `--no-all-libs`.
|
|
129
|
+
- **`--offline` umbrella flag**: skips every network call (CVE/OSV/NVD/Maven Central/endoflife/npm-registry/retire). Falls back to whatever is already cached. Per-source variants (`--cve-offline`, `--no-osv`, `--no-nvd`, `--no-retire`, `--no-transitive`) and per-codec toggles (`--no-maven`/`--no-npm`/`--no-yarn`/`--no-nuget`/`--no-composer`/`--no-pypi`, `--no-js`) still work independently. npm registry deprecation always runs when online; npm (and Maven) outdated is gated by `--no-all-libs`.
|
|
112
130
|
- `snyk` is not a hard dep — shells out via `execFile`. `snyk` exits 1 on findings; the JSON is still on stdout.
|
|
113
131
|
- The cleaned POM is the union of every profile's deps. Counts will be larger than the source POM. Intentional — don't "fix" that.
|
|
114
132
|
- Unresolved `${…}` Maven variables stay verbatim in the rewritten POM. `lib/cve-match.js` resolves them lazily via `resolveDepVersion()` when scanning. Deps that *still* can't be resolved (external BOM not in source tree) surface in chapter 0 as `unresolved-versions` warnings.
|
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
> **F**ucking **A**utonomous **D**ependency **C**hecker
|
|
4
4
|
|
|
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.
|
|
5
|
+
`fad-checker` scans **Maven**, **npm**, **Yarn**, **Composer (PHP)**, **PyPI (Python)**, **NuGet (C#/.NET)** 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
|
-
It runs against the source files alone. **No `mvn`, no `npm install`, no `
|
|
7
|
+
It runs against the source files alone. **No `mvn`, no `npm install`, no `composer install`, no `pip`, no `dotnet restore`, no Docker.** It reads `pom.xml`, `package-lock.json`, `yarn.lock`, `composer.lock`, `poetry.lock`/`Pipfile.lock`/`uv.lock`/`pdm.lock`/`requirements.txt`, and `packages.lock.json`/`*.csproj`/`packages.config` directly.
|
|
8
8
|
|
|
9
|
-
> **
|
|
9
|
+
> **Supported ecosystems: Maven, npm, Yarn (v1), Composer, PyPI, NuGet.** Each is a self-contained **codec** (`lib/codecs/`) — adding another is adding a codec, no orchestrator surgery. Vendored JS (jQuery, Bootstrap, PDF.js, etc.) is also scanned via retire.js. Yarn v2/Berry and pnpm are not yet supported — they're surfaced as warnings in chapter 0 so you know they were skipped.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -20,6 +20,9 @@ Because it doesn't need anything you don't already have on disk:
|
|
|
20
20
|
| `mvn dependency:tree` | Same as above. We walk the tree ourselves. |
|
|
21
21
|
| `npm install` / a `node_modules/` | `package-lock.json` (v1/v2/v3) and `yarn.lock` v1 are parsed as text/JSON. Versions come from the lockfile — no installation. |
|
|
22
22
|
| `yarn install` | Same. We read `yarn.lock` v1. |
|
|
23
|
+
| `composer install` | `composer.lock` is parsed directly (concrete versions + transitive). `composer.json` alone → best-effort on pinned versions + warning. |
|
|
24
|
+
| `pip` / `poetry` / a venv | `poetry.lock`, `Pipfile.lock`, `uv.lock`, `pdm.lock` are parsed for concrete versions; `requirements.txt` is best-effort on `==` pins. Names normalised per PEP 503. |
|
|
25
|
+
| `dotnet restore` | `packages.lock.json` is parsed; otherwise `*.csproj` (+ `Directory.Packages.props` Central Package Management) and legacy `packages.config`, best-effort on pinned versions. |
|
|
23
26
|
| `snyk` binary | Built-in CVE matching via 4 independent sources (see below). Snyk is *optional* (`--snyk`). |
|
|
24
27
|
| A network connection | First run downloads CVE / OSV / EOL data; subsequent runs use cached copies (`--offline` to force). |
|
|
25
28
|
|
|
@@ -35,10 +38,10 @@ Exactly **two** runtime dependencies must be on PATH (or installed automatically
|
|
|
35
38
|
| **1. CVE (production)** | CVEProject + OSV.dev + NVD + CPE | Public CVE / GHSA in production deps, per ecosystem, per manifest file |
|
|
36
39
|
| **2. CVE in dev deps** | same | Same, but for `test`/`provided` (Maven) and `dev`/`optional`/`peer` (npm) |
|
|
37
40
|
| **3. Vendored JS** | [retire.js](https://retirejs.github.io/) | Old jQuery/Bootstrap/Angular/PDF.js copies sitting in `static/` or `webapp/` with no lockfile |
|
|
38
|
-
| **4. EOL frameworks** | endoflife.date | Spring Boot 2.5, Hibernate 4.x, EOL JDKs, etc. |
|
|
39
|
-
| **5. Obsolete libraries** | curated list (
|
|
40
|
-
| **6. Outdated libraries** | Maven Central
|
|
41
|
-
| **7. Fix Recommendations** | computed | Per-ecosystem pin recipes: Maven `<dependencyManagement>`, npm `overrides`, yarn `resolutions` |
|
|
41
|
+
| **4. EOL frameworks** | endoflife.date | Spring Boot 2.5, Hibernate 4.x, EOL JDKs, AngularJS, Laravel/Symfony, Django, .NET, etc. |
|
|
42
|
+
| **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` |
|
|
43
|
+
| **6. Outdated libraries** | Maven Central + npm / Packagist / PyPI / NuGet registries | Available newer versions, with release dates |
|
|
44
|
+
| **7. Fix Recommendations** | computed | Per-ecosystem pin recipes: Maven `<dependencyManagement>`, npm `overrides`, yarn `resolutions`, `composer require`, `pip install`, `dotnet add package` |
|
|
42
45
|
|
|
43
46
|
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.
|
|
44
47
|
|
|
@@ -82,8 +85,10 @@ fad-checker -s ./proj --no-all-libs --no-transitive
|
|
|
82
85
|
# Fully offline (uses cached data only)
|
|
83
86
|
fad-checker -s ./proj --offline
|
|
84
87
|
|
|
85
|
-
#
|
|
86
|
-
fad-checker -s ./proj --ecosystem maven
|
|
88
|
+
# Pick ecosystems — --ecosystem is a list: auto (default) | all | comma list
|
|
89
|
+
fad-checker -s ./proj --ecosystem maven # Maven only
|
|
90
|
+
fad-checker -s ./proj --ecosystem maven,npm,pypi # several
|
|
91
|
+
fad-checker -s ./proj --no-nuget --no-composer # or opt out per codec
|
|
87
92
|
```
|
|
88
93
|
|
|
89
94
|
Run `fad-checker --help` for the full flag list.
|
|
@@ -165,7 +170,11 @@ fad-checker --completion zsh > ~/.zsh/completions/_fad-checker
|
|
|
165
170
|
This is the surprising bit. The whole point is that you can run `fad-checker` against a *checkout* with no build environment.
|
|
166
171
|
|
|
167
172
|
- **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
|
-
- **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`.
|
|
173
|
+
- **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`.
|
|
174
|
+
- **Composer (PHP)** — `composer.lock` (`packages` + `packages-dev`) gives concrete + transitive versions; `composer.json` alone is best-effort.
|
|
175
|
+
- **PyPI (Python)** — `poetry.lock` / `Pipfile.lock` / `uv.lock` / `pdm.lock` are parsed (TOML via `smol-toml`, or JSON); `requirements.txt` is best-effort on `==` pins. Package names are PEP 503-normalised (`Flask-SQLAlchemy` → `flask-sqlalchemy`).
|
|
176
|
+
- **NuGet (C#/.NET)** — `packages.lock.json` is authoritative; otherwise `*.csproj` `<PackageReference>` (resolving Central Package Management against `Directory.Packages.props`) and legacy `packages.config`. Ids are case-insensitive.
|
|
177
|
+
- **Lockfile-first, best-effort fallback** — when a lockfile is present it wins. When it's absent, the loose manifest (`package.json` / `composer.json` / `requirements.txt` / `*.csproj`) is still parsed for its **pinned exact versions**, with ranges skipped and a `no-lockfile` warning in chapter 0 flagging the partial coverage.
|
|
169
178
|
- **Vendored JavaScript** — `retire.js` shells out and scans `.js` / `.min.js` files by signature, catching old jQuery / Bootstrap / Angular / PDF.js copies that no lockfile knows about.
|
|
170
179
|
- **CVE data** — three independent sources merged:
|
|
171
180
|
- **CVEProject** (the canonical `cvelistV5` bundle, filtered to Maven-relevant entries)
|
|
@@ -231,10 +240,14 @@ Repos are tried **in declared order, Maven Central last**. Auth is sent as a `Ba
|
|
|
231
240
|
| Source | What we use | License | API / endpoint |
|
|
232
241
|
| --- | --- | --- | --- |
|
|
233
242
|
| [CVEProject `cvelistV5`](https://github.com/CVEProject/cvelistV5) | Daily bulk CVE bundle, filtered to Maven-relevant entries | CC0-1.0 | GitHub release asset (zip) |
|
|
234
|
-
| [OSV.dev](https://osv.dev/) (Google + GitHub Security Lab) | Per-dep vulnerability lookup (Maven
|
|
243
|
+
| [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}` |
|
|
235
244
|
| [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× |
|
|
236
245
|
| [endoflife.date](https://endoflife.date/) | Framework / runtime EOL cycle data | MIT | `GET endoflife.date/api/{product}.json` |
|
|
237
246
|
| [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>` |
|
|
247
|
+
| [npm registry](https://registry.npmjs.org/) | Per-version `deprecated` + `dist-tags.latest` | Free public service | `GET registry.npmjs.org/<pkg>` |
|
|
248
|
+
| [Packagist](https://packagist.org/) | Latest stable + `abandoned` flag | Free public service | `GET packagist.org/packages/<vendor>/<pkg>.json` |
|
|
249
|
+
| [PyPI](https://pypi.org/) | Latest + `yanked` + "Inactive" classifier | Free public service | `GET pypi.org/pypi/<pkg>/json` |
|
|
250
|
+
| [NuGet](https://www.nuget.org/) | Latest stable + per-version `deprecation` | Free public service | `GET api.nuget.org/v3/registration5-gz-semver2/<id>/index.json` |
|
|
238
251
|
| [retire.js](https://retirejs.github.io/retire.js/) | Vendored-JS signature DB + scanner | Apache-2.0 | npm package `retire`, executed locally |
|
|
239
252
|
| [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
253
|
| [MITRE CWE](https://cwe.mitre.org/) | Weakness category links in the report | Free public reference | Linked by URL only, no API call |
|
|
@@ -258,7 +271,7 @@ Built-in guardrails that fire **before** any disk write:
|
|
|
258
271
|
| Tool | What `fad-checker` adds |
|
|
259
272
|
| --- | --- |
|
|
260
273
|
| `mvn dependency:tree` | No Maven needed; multi-source CVE scan; HTML report |
|
|
261
|
-
| `npm audit` | Polyglot (Maven + npm + vendored JS in one report); EOL & obsolete checks; works without
|
|
274
|
+
| `npm audit` | Polyglot (Maven + npm + Composer + PyPI + NuGet + vendored JS in one report); EOL & obsolete checks; works without installing anything |
|
|
262
275
|
| Snyk CLI | Free; offline-capable; integrates Snyk's results if you have it |
|
|
263
276
|
| OWASP DC | Faster (cached); cleaner UI; multi-source dedup |
|
|
264
277
|
|
|
@@ -269,7 +282,8 @@ You don't have to choose — `fad-checker` will use any of them as input (`--sny
|
|
|
269
282
|
## Docs
|
|
270
283
|
|
|
271
284
|
- [`docs/USAGE.md`](docs/USAGE.md) — every flag, every workflow, examples.
|
|
272
|
-
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — internals: collection, matching, report pipeline.
|
|
285
|
+
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — internals: codecs, collection, matching, report pipeline.
|
|
286
|
+
- [`CHANGELOG.md`](CHANGELOG.md) — release history.
|
|
273
287
|
- [`CLAUDE.md`](CLAUDE.md) — code-level orientation for contributors.
|
|
274
288
|
|
|
275
289
|
---
|
|
@@ -6,11 +6,14 @@ _fad_check_complete() {
|
|
|
6
6
|
COMPREPLY=()
|
|
7
7
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
8
8
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
9
|
-
opts="--src --target --exclude --verbose --no-report --no-transitive --no-all-libs --no-osv --no-nvd --report-output --ignore-test --cve-refresh --cve-offline --snyk --transitive-depth --offline --set-nvd-key --show-config --completion --help --version -s -t -e -v"
|
|
9
|
+
opts="--src --target --exclude --verbose --no-report --no-transitive --no-all-libs --no-osv --no-nvd --no-retire --retire-refresh --report-output --ignore-test --cve-refresh --cve-offline --snyk --transitive-depth --offline --ecosystem --no-maven --no-npm --no-yarn --no-nuget --no-composer --no-pypi --no-js --set-nvd-key --show-config --completion --help --version -s -t -e -v"
|
|
10
10
|
case "$prev" in
|
|
11
11
|
--src|-s|--target|-t|--report-output)
|
|
12
12
|
COMPREPLY=( $(compgen -d -- "$cur") )
|
|
13
13
|
return 0 ;;
|
|
14
|
+
--ecosystem)
|
|
15
|
+
COMPREPLY=( $(compgen -W "auto all maven npm yarn nuget composer pypi" -- "$cur") )
|
|
16
|
+
return 0 ;;
|
|
14
17
|
--completion)
|
|
15
18
|
COMPREPLY=( $(compgen -W "bash zsh" -- "$cur") )
|
|
16
19
|
return 0 ;;
|
|
@@ -20,6 +20,15 @@ _fad_check() {
|
|
|
20
20
|
'--cve-refresh[force CVE re-download]'
|
|
21
21
|
'--cve-offline[use cached CVE only]'
|
|
22
22
|
'--snyk[run snyk and merge]'
|
|
23
|
+
'--ecosystem[codecs to run]:list:(auto all maven npm yarn nuget composer pypi)'
|
|
24
|
+
'--no-maven[skip the Maven codec]'
|
|
25
|
+
'--no-npm[skip the npm codec]'
|
|
26
|
+
'--no-yarn[skip the Yarn codec]'
|
|
27
|
+
'--no-nuget[skip the NuGet codec]'
|
|
28
|
+
'--no-composer[skip the Composer codec]'
|
|
29
|
+
'--no-pypi[skip the PyPI codec]'
|
|
30
|
+
'--no-js[alias: skip JS/npm/yarn]'
|
|
31
|
+
'--no-retire[skip retire.js vendored-JS scan]'
|
|
23
32
|
'--completion[print shell completion]:shell:(bash zsh)'
|
|
24
33
|
)
|
|
25
34
|
_arguments $opts
|
package/data/eol-mapping.json
CHANGED
|
@@ -42,5 +42,22 @@
|
|
|
42
42
|
},
|
|
43
43
|
"by_npm_scope": {
|
|
44
44
|
"@angular/": { "product": "angular", "label": "Angular" }
|
|
45
|
+
},
|
|
46
|
+
"by_composer_name": {
|
|
47
|
+
"laravel/framework": { "product": "laravel", "label": "Laravel" },
|
|
48
|
+
"symfony/symfony": { "product": "symfony", "label": "Symfony" },
|
|
49
|
+
"symfony/console": { "product": "symfony", "label": "Symfony" },
|
|
50
|
+
"symfony/http-kernel": { "product": "symfony", "label": "Symfony" },
|
|
51
|
+
"drupal/core": { "product": "drupal", "label": "Drupal" }
|
|
52
|
+
},
|
|
53
|
+
"by_pypi_name": {
|
|
54
|
+
"django": { "product": "django", "label": "Django" },
|
|
55
|
+
"numpy": { "product": "numpy", "label": "NumPy" },
|
|
56
|
+
"fastapi": { "product": "fastapi", "label": "FastAPI" }
|
|
57
|
+
},
|
|
58
|
+
"by_nuget_name": {
|
|
59
|
+
"microsoft.aspnetcore.app": { "product": "aspnetcore", "label": "ASP.NET Core" },
|
|
60
|
+
"microsoft.entityframeworkcore": { "product": "efcore", "label": "EF Core" },
|
|
61
|
+
"microsoft.net.sdk": { "product": "dotnet", "label": ".NET" }
|
|
45
62
|
}
|
|
46
63
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -5,7 +5,12 @@ This is the deep-dive for anyone modifying `fad-checker`'s internals or wonderin
|
|
|
5
5
|
## Module map
|
|
6
6
|
|
|
7
7
|
```
|
|
8
|
-
fad-checker.js Thin CLI: commander parsing
|
|
8
|
+
fad-checker.js Thin CLI: commander parsing + orchestration (loops over active codecs).
|
|
9
|
+
lib/codecs/ Per-ecosystem codecs (maven, npm, yarn, composer, pypi, nuget) + registry + select + recipes (see "Codecs" below).
|
|
10
|
+
lib/codecs/composer/ composer.lock/composer.json parsers + Packagist registry (PHP codec internals).
|
|
11
|
+
lib/codecs/pypi/ poetry/pipfile/uv/pdm/requirements parsers + PyPI registry (Python codec internals).
|
|
12
|
+
lib/codecs/nuget/ packages.lock.json/csproj/packages.config parsers + NuGet registry (.NET codec internals).
|
|
13
|
+
lib/dep-record.js makeDepRecord(): the generalized depRecord shared by all codecs.
|
|
9
14
|
lib/core.js POM parsing, parent resolution, all-profile merge, rewrite.
|
|
10
15
|
lib/maven-version.js Maven version parsing + range comparison (no external deps).
|
|
11
16
|
lib/cve-download.js Bulk download of CVEProject/cvelistV5 + Maven-relevant index build.
|
|
@@ -19,9 +24,9 @@ lib/nvd.js NIST NVD enrichment (CVSS, references, CPE configur
|
|
|
19
24
|
lib/snyk.js `snyk test --all-projects --json` runner + merge.
|
|
20
25
|
lib/retire.js retire.js (vendored-JS scanner) wrapper + cache + normaliser.
|
|
21
26
|
lib/scan-completeness.js Warnings for deps we couldn't fully resolve.
|
|
22
|
-
lib/npm/parse.js package.json, package-lock.json (v1/2/3), yarn.lock v1 parsers.
|
|
23
|
-
lib/npm/collect.js Merge across JS manifests → unified resolvedDeps Map.
|
|
24
|
-
lib/npm/registry.js npm registry packument query → per-version deprecation + dist-tags.latest.
|
|
27
|
+
lib/codecs/npm/parse.js package.json, package-lock.json (v1/2/3), yarn.lock v1 parsers.
|
|
28
|
+
lib/codecs/npm/collect.js Merge across JS manifests → unified resolvedDeps Map.
|
|
29
|
+
lib/codecs/npm/registry.js npm registry packument query → per-version deprecation + dist-tags.latest.
|
|
25
30
|
lib/cache-archive.js tar.gz / zip export & import of ~/.fad-checker/.
|
|
26
31
|
lib/config.js Persistent user config in ~/.fad-checker/config.json (mode 0600).
|
|
27
32
|
data/ Curated JSON: known-obsolete, eol-mapping, cpe-coord-map, known-public-namespaces.
|
|
@@ -29,6 +34,29 @@ completions/ fad-checker.bash, fad-checker.zsh
|
|
|
29
34
|
test/ node:test suite + fixtures (simple, complex-enterprise, monorepo-mixed, …).
|
|
30
35
|
```
|
|
31
36
|
|
|
37
|
+
## Codecs
|
|
38
|
+
|
|
39
|
+
Every ecosystem-specific behaviour lives behind a **codec** (`lib/codecs/*.codec.js`)
|
|
40
|
+
implementing one interface (`lib/codecs/codec.interface.js`):
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
id, label, osvEcosystem, manifestNames,
|
|
44
|
+
detect(dir), collect(dir,opts) → {deps, warnings},
|
|
45
|
+
coordKey(dep), formatCoord(dep), osvPackageName(dep),
|
|
46
|
+
checkRegistry(deps,opts) → {outdated, deprecated},
|
|
47
|
+
resolveEolProduct(dep), recipe, nativeScanners
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- `lib/codecs/index.js` is the registry: `getCodec(id)`, `allCodecs()`, `detectCodecs(dir)`.
|
|
51
|
+
- `lib/codecs/select.js` turns `--ecosystem <list>` + `--no-<id>` into the active codec ids.
|
|
52
|
+
- The orchestrator collects deps by looping the active codecs, then runs the **shared,
|
|
53
|
+
ecosystem-agnostic** services (OSV, NVD, CPE refinement, endoflife.date) which ask the
|
|
54
|
+
codec only for a package/product name. `nativeScanners` are extra scanners a codec owns
|
|
55
|
+
and the orchestrator runs at their pipeline position by `kind`: `cve` (maven → local
|
|
56
|
+
cvelistV5 index, merged into the CVE chapter) and `vendored` (npm → retire.js, its own
|
|
57
|
+
chapter). New ecosystems (NuGet/Composer/PyPI) ship as codecs with no native scanners —
|
|
58
|
+
OSV + NVD cover them — so no orchestrator changes are needed to add one.
|
|
59
|
+
|
|
32
60
|
## The resolved-deps Map
|
|
33
61
|
|
|
34
62
|
The whole pipeline hinges on a single `Map<string, depRecord>` keyed by:
|
|
@@ -78,8 +106,8 @@ The Maven keyspace and npm keyspace never collide — `:lodash` (Maven groupId-l
|
|
|
78
106
|
- Confirms the dep version actually falls in the vulnerable range (else `cpeFiltered: true` — likely false positive).
|
|
79
107
|
- Upgrades match `confidence` from `possible` → `probable` → `exact` when a curated `cpe-coord-map.json` entry confirms vendor:product → dep coord.
|
|
80
108
|
8. **retire.js** (default on) — shells out to `retire --outputformat json --jspath <src>`. Output normalised to fad-checker match shape, with the vendored file path attached so the report can show where the offending `.js` lives. Cache: `~/.fad-checker/retire-cache/<md5(src)>.json`, 24h TTL.
|
|
81
|
-
9. **EOL / Obsolete / Outdated** — `lib/outdated.js` (Maven) + `lib/npm/registry.js` (npm):
|
|
82
|
-
- **WebJars** (`org.webjars*` — client-side JS shipped as Maven artifacts) are reduced to their npm-equivalent coordinate by `webjarToNpm()` (`lib/npm/collect.js`): `org.webjars.npm` is a deterministic npm mirror (`angular__core` → `@angular/core`); classic `org.webjars`/bower names pass through. They then flow through the **same npm paths** below — no WebJar-specific data.
|
|
109
|
+
9. **EOL / Obsolete / Outdated** — `lib/outdated.js` (Maven) + `lib/codecs/npm/registry.js` (npm):
|
|
110
|
+
- **WebJars** (`org.webjars*` — client-side JS shipped as Maven artifacts) are reduced to their npm-equivalent coordinate by `webjarToNpm()` (`lib/codecs/npm/collect.js`): `org.webjars.npm` is a deterministic npm mirror (`angular__core` → `@angular/core`); classic `org.webjars`/bower names pass through. They then flow through the **same npm paths** below — no WebJar-specific data.
|
|
83
111
|
- **EOL**: matches dep coord against `data/eol-mapping.json`, fetches the cycle list from endoflife.date (cached 7d), flags cycles past their EOL date. npm packages and WebJars resolve by JS library name via `by_npm_name` / `by_npm_scope` (e.g. npm `angular`/webjar `angularjs` → AngularJS 1.x, `@angular/*` → Angular, `react`/`jquery`/`vue`/`bootstrap`).
|
|
84
112
|
- **Obsolete**: Maven via curated `data/known-obsolete.json` (log4j 1.x, jackson-mapper-asl, joda-time, commons-httpclient 3.x, …); npm **and WebJars** via the registry's per-version `deprecated` field (authoritative maintainer data — every dep is checked, nothing curated, nothing skipped).
|
|
85
113
|
- **Outdated**: Maven Central Solr query; npm registry `dist-tags.latest` (npm deps and WebJars). Both gated by `--no-all-libs`. Cache 24h. Concurrency 8.
|
|
@@ -142,7 +170,7 @@ The Maven keyspace and npm keyspace never collide — `:lodash` (Maven groupId-l
|
|
|
142
170
|
## Testing
|
|
143
171
|
|
|
144
172
|
```bash
|
|
145
|
-
npm test # full suite (
|
|
173
|
+
npm test # full suite (194 tests)
|
|
146
174
|
node --test test/core.test.js # one file
|
|
147
175
|
```
|
|
148
176
|
|
package/docs/USAGE.md
CHANGED
|
@@ -21,15 +21,23 @@ By default the HTML + Word reports land in `./fad-checker-report/`. Override wit
|
|
|
21
21
|
# Auto-detect (default): scan whatever pom.xml / package(-lock).json / yarn.lock exists
|
|
22
22
|
fad-checker -s .
|
|
23
23
|
|
|
24
|
-
#
|
|
25
|
-
fad-checker -s . --ecosystem maven
|
|
26
|
-
fad-checker -s . --ecosystem npm
|
|
27
|
-
fad-checker -s . --ecosystem
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
# Pick ecosystems (codecs). --ecosystem is a list: auto (default) | all | comma list.
|
|
25
|
+
fad-checker -s . --ecosystem maven # Maven only
|
|
26
|
+
fad-checker -s . --ecosystem maven,npm # both, even if only one is auto-detected
|
|
27
|
+
fad-checker -s . --ecosystem all # every supported codec
|
|
28
|
+
fad-checker -s . --ecosystem both # legacy alias for maven,npm
|
|
29
|
+
|
|
30
|
+
# Opt out of specific codecs (combine freely)
|
|
31
|
+
fad-checker -s . --no-npm # skip npm
|
|
32
|
+
fad-checker -s . --no-js # alias: skip npm + yarn (Maven-only)
|
|
33
|
+
fad-checker -s . --no-pypi --no-nuget # skip Python + C#
|
|
31
34
|
```
|
|
32
35
|
|
|
36
|
+
> **npm without a lockfile**: a `package.json` lacking a sibling
|
|
37
|
+
> `package-lock.json`/`yarn.lock` is now scanned **best-effort** — pinned exact
|
|
38
|
+
> versions are checked, ranges (`^1.0.0`) are skipped, and a `no-lockfile` warning
|
|
39
|
+
> flags the partial coverage. Run `npm install`/`yarn install` for full coverage.
|
|
40
|
+
|
|
33
41
|
## Filtering deps
|
|
34
42
|
|
|
35
43
|
`-e <regex>` filters out coords whose **groupId** (Maven) or **name** (npm) matches the regex. Useful for private/internal libs that you know aren't on a public registry.
|