fad-checker 2.0.0 → 2.0.1

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.
Files changed (93) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +58 -10
  3. package/data/eol-mapping.json +5 -5
  4. package/fad-checker.js +289 -203
  5. package/lib/codecs/npm/collect.js +26 -17
  6. package/lib/codecs/npm/parse.js +114 -10
  7. package/lib/codecs/npm/registry.js +4 -3
  8. package/lib/codecs/npm.codec.js +1 -1
  9. package/lib/codecs/nuget.codec.js +4 -2
  10. package/lib/codecs/pypi/parse.js +104 -12
  11. package/lib/codecs/pypi.codec.js +27 -6
  12. package/lib/core.js +35 -1
  13. package/lib/cve-match.js +15 -5
  14. package/lib/deps-descriptor.js +110 -0
  15. package/lib/nvd.js +4 -3
  16. package/lib/osv.js +7 -4
  17. package/lib/outdated.js +7 -2
  18. package/lib/retire.js +77 -13
  19. package/lib/transitive.js +3 -3
  20. package/lib/ui.js +87 -0
  21. package/package.json +3 -2
  22. package/CLAUDE.md +0 -147
  23. package/docs/ARCHITECTURE.md +0 -182
  24. package/docs/USAGE.md +0 -187
  25. package/docs/superpowers/plans/2026-05-29-codec-composer.md +0 -556
  26. package/docs/superpowers/plans/2026-05-29-codec-foundation.md +0 -851
  27. package/docs/superpowers/plans/2026-05-29-codec-nuget.md +0 -432
  28. package/docs/superpowers/plans/2026-05-29-codec-pypi.md +0 -450
  29. package/docs/superpowers/specs/2026-05-29-codecs-multi-ecosystem-design.md +0 -251
  30. package/test/cli-ecosystem.test.js +0 -30
  31. package/test/codec-edge-cases.test.js +0 -131
  32. package/test/codec-integration.test.js +0 -119
  33. package/test/codecs.test.js +0 -92
  34. package/test/composer.test.js +0 -71
  35. package/test/core.test.js +0 -153
  36. package/test/cpe.test.js +0 -166
  37. package/test/cve-download.test.js +0 -39
  38. package/test/cve-match.test.js +0 -217
  39. package/test/cve-report.test.js +0 -180
  40. package/test/dep-record.test.js +0 -31
  41. package/test/fixtures/complex-enterprise/api/pom.xml +0 -32
  42. package/test/fixtures/complex-enterprise/build/pom.xml +0 -46
  43. package/test/fixtures/complex-enterprise/dao/pom.xml +0 -37
  44. package/test/fixtures/complex-enterprise/pom.xml +0 -66
  45. package/test/fixtures/complex-enterprise/web/pom.xml +0 -77
  46. package/test/fixtures/csharp-config/packages.config +0 -4
  47. package/test/fixtures/csharp-csproj/Directory.Packages.props +0 -5
  48. package/test/fixtures/csharp-csproj/app.csproj +0 -7
  49. package/test/fixtures/csharp-lock/packages.lock.json +0 -6
  50. package/test/fixtures/cve-samples/cve-non-java.json +0 -19
  51. package/test/fixtures/cve-samples/cve-product-only.json +0 -31
  52. package/test/fixtures/cve-samples/cve-with-packagename.json +0 -37
  53. package/test/fixtures/cve-samples/nvd-log4shell.json +0 -40
  54. package/test/fixtures/cve-samples/nvd-npm-lodash.json +0 -22
  55. package/test/fixtures/monorepo-mixed/libs/common-bom/pom.xml +0 -26
  56. package/test/fixtures/monorepo-mixed/packages/cli/package.json +0 -14
  57. package/test/fixtures/monorepo-mixed/packages/cli/yarn.lock +0 -41
  58. package/test/fixtures/monorepo-mixed/packages/no-lock/package.json +0 -10
  59. package/test/fixtures/monorepo-mixed/packages/web-app/package-lock.json +0 -71
  60. package/test/fixtures/monorepo-mixed/packages/web-app/package.json +0 -17
  61. package/test/fixtures/monorepo-mixed/pom.xml +0 -29
  62. package/test/fixtures/monorepo-mixed/services/api/pom.xml +0 -27
  63. package/test/fixtures/monorepo-mixed/services/worker/pom.xml +0 -28
  64. package/test/fixtures/php-app/composer.json +0 -5
  65. package/test/fixtures/php-app/composer.lock +0 -10
  66. package/test/fixtures/polyglot/cs/packages.lock.json +0 -1
  67. package/test/fixtures/polyglot/js/package-lock.json +0 -1
  68. package/test/fixtures/polyglot/js/package.json +0 -1
  69. package/test/fixtures/polyglot/mvn/pom.xml +0 -7
  70. package/test/fixtures/polyglot/php/composer.lock +0 -1
  71. package/test/fixtures/polyglot/py/poetry.lock +0 -3
  72. package/test/fixtures/private-lib-detection/core/pom.xml +0 -26
  73. package/test/fixtures/private-lib-detection/plugin/pom.xml +0 -23
  74. package/test/fixtures/private-lib-detection/pom.xml +0 -35
  75. package/test/fixtures/python-pipenv/Pipfile.lock +0 -1
  76. package/test/fixtures/python-poetry/poetry.lock +0 -7
  77. package/test/fixtures/python-reqs/requirements.txt +0 -5
  78. package/test/fixtures/python-uv/uv.lock +0 -3
  79. package/test/fixtures/simple/app/pom.xml +0 -28
  80. package/test/fixtures/simple/lib/pom.xml +0 -18
  81. package/test/fixtures/simple/pom.xml +0 -24
  82. package/test/maven-repo.test.js +0 -111
  83. package/test/maven-version.test.js +0 -57
  84. package/test/monorepo.test.js +0 -132
  85. package/test/npm-registry.test.js +0 -64
  86. package/test/npm.test.js +0 -150
  87. package/test/nuget.test.js +0 -66
  88. package/test/osv.test.js +0 -62
  89. package/test/outdated.test.js +0 -101
  90. package/test/pypi.test.js +0 -72
  91. package/test/snyk.test.js +0 -64
  92. package/test/transitive.test.js +0 -305
  93. package/test/webjar.test.js +0 -33
package/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to `fad-checker` are documented here.
4
4
  This project adheres to [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [2.0.1]
7
+
8
+ ### Fixed
9
+ - **EOL detection for PyPI / NuGet** used dead endoflife.date product slugs
10
+ (`fastapi`, `aspnetcore`, `efcore` → HTTP 404), so no .NET / FastAPI EOL was ever
11
+ flagged. NuGet ASP.NET Core / EF Core packages now map to the `dotnet` product
12
+ (their versions track .NET cycles); `fastapi` removed (no endoflife.date source).
13
+
14
+ ### Added
15
+ - Detailed capability test suite (`test/codec-capabilities.test.js`): end-to-end EOL
16
+ per ecosystem (seeded cycles), registry findings (abandoned / yanked / inactive /
17
+ deprecation) + outdated gating, cycle-matching logic, fix recipes, and report
18
+ rendering of EOL/Obsolete/Outdated. Plus a guard asserting every eol-mapping product
19
+ slug is a known-valid endoflife.date product.
20
+
6
21
  ## [2.0.0]
7
22
 
8
23
  Major release: **codec architecture** + three new ecosystems.
package/README.md CHANGED
@@ -4,9 +4,9 @@
4
4
 
5
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 `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.
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`, `pnpm-lock.yaml`, `composer.lock`, `poetry.lock`/`Pipfile.lock`/`uv.lock`/`pdm.lock`/`pyproject.toml`/`requirements.txt`, and `packages.lock.json`/`*.csproj`/`*.fsproj`/`*.vbproj`/`packages.config` directly.
8
8
 
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.
9
+ > **Supported ecosystems: Maven, npm, Yarn (v1 + Berry/v2+), pnpm, 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.
10
10
 
11
11
  ---
12
12
 
@@ -18,11 +18,11 @@ Because it doesn't need anything you don't already have on disk:
18
18
  | --- | --- |
19
19
  | 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). |
20
20
  | `mvn dependency:tree` | Same as above. We walk the tree ourselves. |
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
- | `yarn install` | Same. We read `yarn.lock` v1. |
21
+ | `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. |
22
+ | `yarn install` / `pnpm install` | Same. We read `yarn.lock` (v1 + Berry) and `pnpm-lock.yaml` directly. |
23
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. |
24
+ | `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. |
25
+ | `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. |
26
26
  | `snyk` binary | Built-in CVE matching via 4 independent sources (see below). Snyk is *optional* (`--snyk`). |
27
27
  | A network connection | First run downloads CVE / OSV / EOL data; subsequent runs use cached copies (`--offline` to force). |
28
28
 
@@ -170,11 +170,11 @@ fad-checker --completion zsh > ~/.zsh/completions/_fad-checker
170
170
  This is the surprising bit. The whole point is that you can run `fad-checker` against a *checkout* with no build environment.
171
171
 
172
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.
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`.
173
+ - **npm / Yarn / pnpm** — `package-lock.json` (v1, v2, v3), `yarn.lock` (v1 + Berry/v2+, via `js-yaml`) and `pnpm-lock.yaml` (v5/v6/v9, via `js-yaml`) are parsed directly. Lockfiles already contain every transitive version. No `node_modules/` traversal, no `npm install`.
174
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.
175
+ - **PyPI (Python)** — `poetry.lock` / `Pipfile.lock` / `uv.lock` / `pdm.lock` are parsed (TOML via `smol-toml`, or JSON); `pyproject.toml` (PEP 621 `[project]` + `[tool.poetry]`) and `requirements.txt` (following `-r`/`-c` includes recursively, with `-c` constraint pins applied to ranges) are best-effort on exact pins. Package names are PEP 503-normalised (`Flask-SQLAlchemy` → `flask-sqlalchemy`).
176
+ - **NuGet (C#/.NET)** — `packages.lock.json` is authoritative; otherwise `*.csproj` / `*.fsproj` / `*.vbproj` `<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` / `pyproject.toml` / `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.
178
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.
179
179
  - **CVE data** — three independent sources merged:
180
180
  - **CVEProject** (the canonical `cvelistV5` bundle, filtered to Maven-relevant entries)
@@ -198,6 +198,7 @@ All cached data lives in `~/.fad-checker/`:
198
198
  | Maven Central latest versions | `version-cache.json` | 24 h |
199
199
  | Transitive POMs from Maven Central | `poms-cache/<g>__<a>__<v>.pom` | ∞ (immutable) |
200
200
  | retire.js findings | `retire-cache/<md5(src)>.json` | 24 h |
201
+ | retire.js signature DB | `retire-signatures/jsrepository-v5.json` | warmed online, used offline |
201
202
  | User config (NVD key) | `config.json` (mode 0600) | — |
202
203
 
203
204
  Export the lot to share between machines:
@@ -210,6 +211,53 @@ fad-checker --import-cache fad-cache.tar.gz
210
211
 
211
212
  `--include-config` ships the NVD API key too (off by default).
212
213
 
214
+ > The cache export bundles **everything** under `~/.fad-checker/` (except `config.json`),
215
+ > including the retire.js findings **and** the warmed retire.js signature DB — so a
216
+ > machine that imports it can scan vendored JavaScript fully offline.
217
+
218
+ ---
219
+
220
+ ## Air-gapped / PASSI audits: anonymized dependency descriptor
221
+
222
+ When the audited system is **offline / confidential** (typical of a PASSI engagement) it
223
+ can't reach OSV / NVD / Maven Central / npm. Split the work across machines while keeping
224
+ **zero environment information** off the secure enclave: an anonymized descriptor carries
225
+ only **public package coordinates** — no filesystem paths, no registry URLs, no
226
+ hostnames/usernames — and the **detailed report is produced back on the offline machine**.
227
+
228
+ The transfer relies on a property of fad-checker's caches: they are keyed by *coordinate*
229
+ or *vuln id*, never by path, so they are **machine-independent**. The online step just
230
+ **warms the caches**; the offline step replays the scan and gets cache hits.
231
+
232
+ ```bash
233
+ # ── Phase 1 — OFFLINE (audited machine): export the anonymized descriptor ──
234
+ # Exclude private/internal packages with -e (offline we can't tell private from public).
235
+ fad-checker -s ./proj -e "^(client|internal)\." --export-anonymized deps.json
236
+ # → deps.json: public coordinates only. Review it before it leaves the enclave.
237
+
238
+ # ── Phase 2 — ONLINE (any machine, no source needed): warm the caches ──
239
+ fad-checker --import-anonymized deps.json # scans coordinates → OSV/NVD/CVE/registry/EOL + retire signatures
240
+ fad-checker --export-cache fad-cache.tar.gz # bundle the warmed ~/.fad-checker/
241
+
242
+ # ── Phase 3 — OFFLINE (audited machine): full report, all local context ──
243
+ fad-checker --import-cache fad-cache.tar.gz
244
+ fad-checker -s ./proj --offline # re-collect locally (real paths) + cache hits
245
+ # → full HTML/.doc report with manifests & structure, generated inside the enclave.
246
+ ```
247
+
248
+ What the descriptor (`fad-deps/1`) contains vs. drops:
249
+
250
+ | Kept (needed to scan) | Dropped (environment) |
251
+ | --- | --- |
252
+ | ecosystem, ecosystemType | manifest paths / pom paths |
253
+ | namespace, name | resolved registry URLs |
254
+ | version, versions | integrity hashes |
255
+ | scope, isDev | parent chains, lockfile type |
256
+
257
+ The online phase report is itself path-free; vendored-JavaScript (retire.js) findings are
258
+ produced **offline in phase 3**, since retire needs the actual `.js` files — its signature
259
+ DB is warmed online (phase 2) and carried by `--export-cache`.
260
+
213
261
  ---
214
262
 
215
263
  ## Custom Maven repositories
@@ -52,12 +52,12 @@
52
52
  },
53
53
  "by_pypi_name": {
54
54
  "django": { "product": "django", "label": "Django" },
55
- "numpy": { "product": "numpy", "label": "NumPy" },
56
- "fastapi": { "product": "fastapi", "label": "FastAPI" }
55
+ "numpy": { "product": "numpy", "label": "NumPy" }
57
56
  },
58
57
  "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" }
58
+ "microsoft.aspnetcore.app": { "product": "dotnet", "label": "ASP.NET Core (.NET)" },
59
+ "microsoft.aspnetcore.all": { "product": "dotnet", "label": "ASP.NET Core (.NET)" },
60
+ "microsoft.netcore.app": { "product": "dotnet", "label": ".NET" },
61
+ "microsoft.entityframeworkcore": { "product": "dotnet", "label": "EF Core (.NET)" }
62
62
  }
63
63
  }