fad-checker 2.0.1 → 2.1.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 (66) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +91 -17
  3. package/completions/fad-checker.bash +1 -1
  4. package/completions/fad-checker.zsh +14 -1
  5. package/data/license-policy.json +97 -0
  6. package/fad-checker-report/cve-report.doc +630 -0
  7. package/fad-checker-report/cve-report.html +748 -0
  8. package/fad-checker.js +278 -53
  9. package/lib/cache-archive.js +3 -0
  10. package/lib/codecs/codec.interface.js +3 -0
  11. package/lib/codecs/composer/parse.js +3 -0
  12. package/lib/codecs/composer/registry.js +13 -5
  13. package/lib/codecs/composer.codec.js +3 -0
  14. package/lib/codecs/go/parse.js +65 -0
  15. package/lib/codecs/go/registry.js +74 -0
  16. package/lib/codecs/go.codec.js +76 -0
  17. package/lib/codecs/index.js +7 -2
  18. package/lib/codecs/maven/jar-scan.js +199 -0
  19. package/lib/codecs/maven.codec.js +17 -1
  20. package/lib/codecs/npm/collect.js +11 -0
  21. package/lib/codecs/npm/parse.js +3 -0
  22. package/lib/codecs/npm/registry.js +10 -2
  23. package/lib/codecs/npm.codec.js +3 -0
  24. package/lib/codecs/nuget/parse.js +3 -0
  25. package/lib/codecs/nuget/registry.js +11 -4
  26. package/lib/codecs/nuget.codec.js +3 -0
  27. package/lib/codecs/pypi/parse.js +7 -2
  28. package/lib/codecs/pypi/registry.js +24 -5
  29. package/lib/codecs/pypi.codec.js +3 -0
  30. package/lib/codecs/recipes.js +28 -1
  31. package/lib/codecs/ruby/parse.js +42 -0
  32. package/lib/codecs/ruby/registry.js +76 -0
  33. package/lib/codecs/ruby.codec.js +66 -0
  34. package/lib/codecs/select.js +3 -0
  35. package/lib/codecs/yarn.codec.js +3 -0
  36. package/lib/config.js +3 -0
  37. package/lib/core.js +3 -0
  38. package/lib/cpe.js +30 -5
  39. package/lib/csaf-export.js +159 -0
  40. package/lib/cve-download.js +3 -0
  41. package/lib/cve-match.js +12 -1
  42. package/lib/cve-report.js +157 -28
  43. package/lib/dep-record.js +15 -2
  44. package/lib/deps-descriptor.js +3 -0
  45. package/lib/epss.js +115 -0
  46. package/lib/gate.js +45 -0
  47. package/lib/json-export.js +110 -0
  48. package/lib/kev.js +88 -0
  49. package/lib/license-policy.js +110 -0
  50. package/lib/maven-license.js +52 -0
  51. package/lib/maven-repo.js +3 -0
  52. package/lib/maven-version.js +8 -3
  53. package/lib/nvd.js +13 -3
  54. package/lib/osv.js +68 -19
  55. package/lib/outdated.js +3 -0
  56. package/lib/priority.js +90 -0
  57. package/lib/purl.js +77 -0
  58. package/lib/retire.js +3 -0
  59. package/lib/sarif-export.js +134 -0
  60. package/lib/sbom-export.js +153 -0
  61. package/lib/scan-completeness.js +3 -0
  62. package/lib/snyk.js +3 -0
  63. package/lib/suppress.js +113 -0
  64. package/lib/transitive.js +3 -0
  65. package/lib/ui.js +3 -0
  66. package/package.json +48 -2
package/CHANGELOG.md CHANGED
@@ -3,6 +3,56 @@
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.1.0]
7
+
8
+ ### Added
9
+ - **Embedded-binary scanning (chapter 1B).** The Maven codec now discovers Maven
10
+ coordinates inside committed `.jar`/`.war`/`.ear` archives — vendored libs,
11
+ Spring-Boot fat-jars, shaded uber-jars — by unzipping them **in memory** (via
12
+ `fflate`, recursing into nested jars without touching disk, so there is no
13
+ zip-slip risk). Each artifact's coordinate is read from
14
+ `META-INF/maven/.../pom.properties` → `MANIFEST.MF` → file name; unidentifiable
15
+ archives are flagged in chapter 0 rather than scanned blindly. Findings carry
16
+ `provenance:"embedded"`, report in a dedicated **Embedded binaries** chapter
17
+ (grouped by containing archive), feed the `--fail-on` gate, and are labelled in
18
+ the SBOM (`fad:provenance`/`fad:location` + unique `bom-ref`), SARIF
19
+ (`provenance` + nested-jar location) and JSON exports. Auto when archives are
20
+ present; `--no-jars` disables it.
21
+
22
+ ### Changed
23
+ - **Unified output flags.** Every output now has its own `--report-<type>` flag
24
+ taking an OPTIONAL path (omit it → a default name under `--report-output`):
25
+ `--report-html`, `--report-doc`, `--report-sbom`, `--report-csaf`,
26
+ `--report-json`, `--report-sarif`. With no `--report-*` flag, HTML + `.doc` are
27
+ written as before; selecting any flag writes exactly that set.
28
+ **BREAKING:** the old `--export-sbom`/`--export-csaf`/`--export-json`/`--export-sarif`
29
+ flags are removed — use `--report-sbom`/`-csaf`/`-json`/`-sarif`. (The unrelated
30
+ `--export-cache` / `--export-anonymized` flags are unchanged.)
31
+ - **`--no-report` now writes NO output files at all** (gate-only / CI mode) — the
32
+ scan, terminal summary and `--fail-on` gate still run. Previously it
33
+ short-circuited the whole flow, so `--no-report --fail-on …` silently passed.
34
+
35
+ ### Fixed
36
+ - **Catastrophic data loss**: `--target` being a *parent* of `--src` passed the
37
+ guardrail and `rimraf`'d the source tree. The guard now rejects overlap in both
38
+ directions.
39
+ - **Missed npm/yarn/pnpm CVEs**: only the highest version of a duplicated package
40
+ was scanned; nested-`node_modules` lower versions are now accumulated.
41
+ - **CPE false negatives**: AND-configurations with a `vulnerable:false` platform
42
+ node wrongly dropped real findings.
43
+ - **VEX over-suppression**: an unmappable product id suppressed a CVE for every
44
+ dependency.
45
+ - **CSAF/SBOM/SARIF scoring**: OSV CVSS *vectors* were mis-read as the score
46
+ (`3.1`), the NVD CVSS version label was malformed (`CVSS:V31`), and an NVD record
47
+ without metrics clobbered an OSV-derived vector — so CSAF emitted no scores and
48
+ SBOM showed `method:other`. CVSS v3 base scores are now computed from the vector,
49
+ labels normalised, and exports stay schema-valid (no empty `known_affected`, no
50
+ `UNKNOWN` baseSeverity, no v4 vector under a v3 score).
51
+ - **CI / parsing**: an invalid `--fail-on <level>` (typo) now hard-fails instead of
52
+ silently disabling the gate; Maven version ordering for dot-aligned qualifiers
53
+ (`5.0.0.RC1` vs `5.0.0.5`) corrected; classic poetry.lock `category="dev"` and
54
+ `go.sum` highest-version selection fixed.
55
+
6
56
  ## [2.0.1]
7
57
 
8
58
  ### Fixed
package/README.md CHANGED
@@ -1,12 +1,21 @@
1
1
  # fad-checker
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/fad-checker.svg)](https://www.npmjs.com/package/fad-checker)
4
+ [![npm downloads](https://img.shields.io/npm/dm/fad-checker.svg)](https://www.npmjs.com/package/fad-checker)
5
+ [![license](https://img.shields.io/npm/l/fad-checker.svg)](https://github.com/n8tz/fad-checker/blob/main/package.json)
6
+ [![node](https://img.shields.io/node/v/fad-checker.svg)](https://nodejs.org)
7
+
3
8
  > **F**ucking **A**utonomous **D**ependency **C**hecker
4
9
 
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.
10
+ `fad-checker` scans **Maven**, **npm**, **Yarn**, **Composer (PHP)**, **PyPI (Python)**, **NuGet (C#/.NET)**, **Go**, **Ruby** 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 (prioritised by **EPSS + CISA KEV**), EOL, obsolete, outdated and **license** findings, plus per-ecosystem fix recipes. It also exports a **CycloneDX 1.6 SBOM** and a **CSAF 2.0 VEX**.
11
+
12
+ 🌐 **[Project site & docs →](https://n8tz.github.io/fad-checker/)**
13
+
14
+ <p align="center"><img src="docs/assets/cli.png" alt="fad-checker terminal output — a [n/N] checklist warming each vulnerability database, then CVE findings coloured by severity" width="760"></p>
6
15
 
7
16
  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
17
 
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.
18
+ > **Supported ecosystems: Maven, npm, Yarn (v1 + Berry/v2+), pnpm, Composer, PyPI, NuGet, Go, Ruby.** 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. **Embedded JARs** committed into the tree — vendored libs, Spring-Boot fat-jars, shaded uber-jars inside `.jar`/`.war`/`.ear` — are unzipped in-memory and their Maven coordinates scanned too (disable with `--no-jars`).
10
19
 
11
20
  ---
12
21
 
@@ -23,10 +32,12 @@ Because it doesn't need anything you don't already have on disk:
23
32
  | `composer install` | `composer.lock` is parsed directly (concrete versions + transitive). `composer.json` alone → best-effort on pinned versions + warning. |
24
33
  | `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
34
  | `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
- | `snyk` binary | Built-in CVE matching via 4 independent sources (see below). Snyk is *optional* (`--snyk`). |
35
+ | `go build` / a Go toolchain | `go.mod` is parsed (the full pruned graph on Go ≥1.17, `// indirect` transitive); `go.sum` is the fallback. No module download. |
36
+ | `bundle install` | `Gemfile.lock` is parsed for the resolved gem set. No Ruby, no bundler. |
37
+ | `snyk` binary | Built-in CVE matching via CVEProject + OSV + NVD (merged), prioritised with EPSS + CISA KEV (see below). Snyk is *optional* (`--snyk`). |
27
38
  | A network connection | First run downloads CVE / OSV / EOL data; subsequent runs use cached copies (`--offline` to force). |
28
39
 
29
- Exactly **two** runtime dependencies must be on PATH (or installed automatically through npm): Node ≥ 20 and `retire` (the npm package, installed by `npm install`). Everything else is bundled or fetched lazily.
40
+ Exactly **one** runtime dependencies must be on PATH (or installed automatically through npm): Node ≥ 20. Everything else is bundled or fetched lazily.
30
41
 
31
42
  ---
32
43
 
@@ -35,15 +46,17 @@ Exactly **two** runtime dependencies must be on PATH (or installed automatically
35
46
  | Chapter | Source | What it catches |
36
47
  | --- | --- | --- |
37
48
  | **0. Warnings** | local heuristics | Missing lockfiles, unresolved Maven versions (BOM-managed), private libs not on Maven Central |
38
- | **1. CVE (production)** | CVEProject + OSV.dev + NVD + CPE | Public CVE / GHSA in production deps, per ecosystem, per manifest file |
49
+ | **1. CVE (production)** | CVEProject + OSV.dev + NVD + CPE | Public CVE / GHSA in production deps, per ecosystem, per manifest file — each row **prioritised** by CISA KEV + EPSS + CVSS |
50
+ | **1B. Embedded binaries** | same, on coords read from archives | CVEs in libraries **shipped inside committed `.jar`/`.war`/`.ear`** (vendored libs, Spring-Boot fat-jars, shaded uber-jars) — not declared in any `pom.xml`. Grouped by containing archive |
39
51
  | **2. CVE in dev deps** | same | Same, but for `test`/`provided` (Maven) and `dev`/`optional`/`peer` (npm) |
40
52
  | **3. Vendored JS** | [retire.js](https://retirejs.github.io/) | Old jQuery/Bootstrap/Angular/PDF.js copies sitting in `static/` or `webapp/` with no lockfile |
41
53
  | **4. EOL frameworks** | endoflife.date | Spring Boot 2.5, Hibernate 4.x, EOL JDKs, AngularJS, Laravel/Symfony, Django, .NET, etc. |
42
54
  | **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
55
  | **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` |
56
+ | **7. Licenses** | registry metadata + Maven POMs → SPDX policy | Each dep's license normalised to SPDX and classified; copyleft (GPL/AGPL/LGPL/MPL), proprietary and unknown flagged for review |
57
+ | **8. Fix Recommendations** | computed | Per-ecosystem pin recipes: Maven `<dependencyManagement>`, npm `overrides`, yarn `resolutions`, `composer require`, `pip install`, `dotnet add package` |
45
58
 
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.
59
+ The HTML report opens in any browser, contains every detail (CVSS vectors, references, full descriptions, CPE configurations, via-paths for transitives) and ships a Word-compatible `.doc` twin. Every match carries a **composite priority** (KEV-exploited > EPSS likelihood > CVSS severity), and the run can additionally emit a **CycloneDX 1.6 SBOM** (`--report-sbom`, vulnerabilities inline) and a **CSAF 2.0 VEX** (`--report-csaf`) for downstream tooling.
47
60
 
48
61
  ---
49
62
 
@@ -97,6 +110,10 @@ Run `fad-checker --help` for the full flag list.
97
110
 
98
111
  ## What a report looks like
99
112
 
113
+ <p align="center"><img src="docs/assets/report.png" alt="fad-checker HTML report — executive summary with severity tiles and a detailed CVE table with CWE, descriptions and fix versions" width="900"></p>
114
+
115
+ The console prints a summary; the full detail lives in the self-contained HTML/`.doc`:
116
+
100
117
  ```
101
118
  Executive Summary [CRITICAL] — 1708 dependencies scanned
102
119
  • 81 CVE in production deps (critical=5, high=53, medium=12, low=11)
@@ -174,13 +191,20 @@ This is the surprising bit. The whole point is that you can run `fad-checker` ag
174
191
  - **Composer (PHP)** — `composer.lock` (`packages` + `packages-dev`) gives concrete + transitive versions; `composer.json` alone is best-effort.
175
192
  - **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
193
  - **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.
194
+ - **Go** — `go.mod` `require` entries are the selected versions (full pruned graph on Go ≥1.17; `// indirect` → transitive), `go.sum` as fallback. OSV "Go" ecosystem for recall, the Go module proxy for outdated.
195
+ - **Ruby** — `Gemfile.lock` `specs:` give the resolved gem set. OSV "RubyGems" for recall, the RubyGems API for outdated + licenses.
177
196
  - **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
197
  - **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.
198
+ - **Embedded JARs** — committed `.jar` / `.war` / `.ear` archives are unzipped **in memory** (via `fflate` — nested fat-jar libs are recursed without ever touching disk, so there's no zip-slip risk) and each artifact's Maven coordinate is read from `META-INF/maven/.../pom.properties` (authoritative), then `MANIFEST.MF`, then the file name. Those coordinates run through the same CVE/OSV/NVD matching as declared deps but report in their own **Embedded binaries** chapter, grouped by containing archive. An archive whose coordinate can't be resolved is flagged in chapter 0 rather than scanned blindly. Auto when archives are present; disable with `--no-jars`. (Embedded coords don't trigger Maven Central transitive resolution — a fat-jar already ships its dependencies, which the recursion finds directly.)
179
199
  - **CVE data** — three independent sources merged:
180
200
  - **CVEProject** (the canonical `cvelistV5` bundle, filtered to Maven-relevant entries)
181
201
  - **OSV.dev** (Google + GitHub Security Lab, multi-ecosystem)
182
202
  - **NVD** (official NIST records, used for enrichment: full CVSS, references, CPE configurations)
183
203
  - **CPE refinement** — once a CVE is matched, its NVD CPE configurations are checked against the dep version range. A match outside the vulnerable range is flagged `cpeFiltered: true` (likely false positive). A curated `data/cpe-coord-map.json` maps CPE `vendor:product` to Maven `g:a` (60+ entries seeded: log4j, jackson, spring, tomcat, jetty, netty, …).
204
+ - **Prioritization** — each matched CVE is enriched with **EPSS** (FIRST.org exploit-prediction percentile) and **CISA KEV** (known-exploited catalogue), then scored: KEV (exploited in the wild) outranks EPSS-weighted CVSS. The report sorts by this composite priority and badges KEV/EPSS.
205
+ - **Licenses** — each dependency's license is resolved (registry metadata, no extra request; Maven from cached POMs), normalised to SPDX and classified against a copyleft policy (`data/license-policy.json`) — permissive / weak / strong / network copyleft / proprietary / unknown.
206
+ - **Unified outputs** — one `--report-<type>` flag per output, each with an OPTIONAL path (omit it → a default name under `--report-output`): `--report-html`, `--report-doc`, plus the machine-readable `--report-sbom` (**CycloneDX 1.6**, vulnerabilities inline / VDR), `--report-csaf` (**CSAF 2.0 VEX**), `--report-json` (flat findings, diff-friendly) and `--report-sarif` (**SARIF 2.1.0** for GitHub/GitLab code scanning). With no `--report-*` flag, HTML + `.doc` are written by default; `--no-report` writes nothing (gate-only). purls per ecosystem.
207
+ - **CI gating & triage** — `--fail-on <low|medium|high|critical|kev>` sets a non-zero exit code (`kev` = fail only on a CISA-known-exploited finding). `--ignore <file>` (CVE/coord/glob rules) and `--vex <file>` (ingest a CSAF VEX) suppress accepted-risk / false-positive findings from the report and the gate, while keeping them flagged in the exports — so re-audits stay signal-rich.
184
208
 
185
209
  ---
186
210
 
@@ -194,6 +218,10 @@ All cached data lives in `~/.fad-checker/`:
194
218
  | OSV per-dep lookups | `osv-cache/<ecosystem>__<g>__<a>__<v>.json` | 12 h |
195
219
  | OSV vuln details | `osv-cache/vuln_<id>.json` | 12 h |
196
220
  | NVD CVE records | `nvd-cache/<cveId>.json` | 7 d |
221
+ | EPSS scores (FIRST.org) | `epss-cache.json` | 24 h |
222
+ | CISA KEV catalogue | `kev-cache.json` | 24 h |
223
+ | Go module proxy (latest) | `go-proxy-cache.json` | 24 h |
224
+ | RubyGems (latest + licenses) | `rubygems-cache.json` | 24 h |
197
225
  | endoflife.date cycles | `eol-cache.json` | 7 d |
198
226
  | Maven Central latest versions | `version-cache.json` | 24 h |
199
227
  | Transitive POMs from Maven Central | `poms-cache/<g>__<a>__<v>.pom` | ∞ (immutable) |
@@ -290,12 +318,16 @@ Repos are tried **in declared order, Maven Central last**. Auth is sent as a `Ba
290
318
  | [CVEProject `cvelistV5`](https://github.com/CVEProject/cvelistV5) | Daily bulk CVE bundle, filtered to Maven-relevant entries | CC0-1.0 | GitHub release asset (zip) |
291
319
  | [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}` |
292
320
  | [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× |
321
+ | [FIRST.org EPSS](https://www.first.org/epss/) | Exploit-prediction score + percentile per CVE | CC-BY 4.0 | `GET api.first.org/data/v1/epss?cve=…` (batched) |
322
+ | [CISA KEV](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) | Known-exploited-vulnerability catalogue membership | US-gov public domain | `GET cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json` |
293
323
  | [endoflife.date](https://endoflife.date/) | Framework / runtime EOL cycle data | MIT | `GET endoflife.date/api/{product}.json` |
294
324
  | [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>` |
295
325
  | [npm registry](https://registry.npmjs.org/) | Per-version `deprecated` + `dist-tags.latest` | Free public service | `GET registry.npmjs.org/<pkg>` |
296
326
  | [Packagist](https://packagist.org/) | Latest stable + `abandoned` flag | Free public service | `GET packagist.org/packages/<vendor>/<pkg>.json` |
297
327
  | [PyPI](https://pypi.org/) | Latest + `yanked` + "Inactive" classifier | Free public service | `GET pypi.org/pypi/<pkg>/json` |
298
328
  | [NuGet](https://www.nuget.org/) | Latest stable + per-version `deprecation` | Free public service | `GET api.nuget.org/v3/registration5-gz-semver2/<id>/index.json` |
329
+ | [Go module proxy](https://proxy.golang.org/) | Latest module version (outdated) | Free public service | `GET proxy.golang.org/<module>/@latest` |
330
+ | [RubyGems](https://rubygems.org/) | Latest stable + licenses | Free public service | `GET rubygems.org/api/v1/gems/<gem>.json` |
299
331
  | [retire.js](https://retirejs.github.io/retire.js/) | Vendored-JS signature DB + scanner | Apache-2.0 | npm package `retire`, executed locally |
300
332
  | [Snyk](https://snyk.io/) (optional) | Additional CVE source via `snyk test --all-projects --json` | Per Snyk EULA; needs a Snyk account | Local CLI `snyk` |
301
333
  | [MITRE CWE](https://cwe.mitre.org/) | Weakness category links in the report | Free public reference | Linked by URL only, no API call |
@@ -314,16 +346,58 @@ Built-in guardrails that fire **before** any disk write:
314
346
 
315
347
  ---
316
348
 
317
- ## Compared to…
318
-
319
- | Tool | What `fad-checker` adds |
320
- | --- | --- |
321
- | `mvn dependency:tree` | No Maven needed; multi-source CVE scan; HTML report |
322
- | `npm audit` | Polyglot (Maven + npm + Composer + PyPI + NuGet + vendored JS in one report); EOL & obsolete checks; works without installing anything |
323
- | Snyk CLI | Free; offline-capable; integrates Snyk's results if you have it |
324
- | OWASP DC | Faster (cached); cleaner UI; multi-source dedup |
325
-
326
- You don't have to choose `fad-checker` will use any of them as input (`--snyk`) and merge results.
349
+ ## Comparison
350
+
351
+ `fad-checker` is **not** a Trivy/Grype competitor — those are container-and-SBOM supply-chain
352
+ scanners. It targets a narrower job: a **zero-setup, multi-ecosystem audit of a source
353
+ checkout, with an audit-ready report and a confidential / air-gapped workflow** the kind
354
+ of thing a security consultant or an ANSSI-PASSI engagement needs.
355
+
356
+ | | **fad-checker** | OSV-Scanner | Trivy | Grype + Syft | OWASP DC | Snyk OSS |
357
+ | --- | --- | --- | --- | --- | --- | --- |
358
+ | Ecosystems it targets¹ | Maven, npm, Yarn, **pnpm**, Composer, PyPI, NuGet, Go, Ruby + vendored JS | 11+ langs / 19+ lockfiles | 20+ | 20+ | Java/.NET (others exp.) | many |
359
+ | Reads lockfiles without `install`/build² | ✅ | ✅ | ✅ | ✅ | ⚠️ Java needs Maven Central/build | ❌ build required |
360
+ | Best-effort when **no lockfile** (pinned versions) | ✅ | ❌ | ❌ | ❌ | ⚠️ | ⚠️ |
361
+ | Vulnerability sources | CVEProject + OSV + NVD + EPSS + KEV + retire.js (+ Snyk), merged | OSV.dev | Aqua DB | Anchore DB | NVD / CPE | Snyk DB |
362
+ | False-positive control | CPE/version cross-check | ecosystem-aware | ecosystem-aware | ecosystem-aware | ⚠️ CPE → noisy | ecosystem-aware |
363
+ | **EOL** (end-of-life) detection | ✅ endoflife.date | ❌ | ❌ | ❌ | ❌ | ~ |
364
+ | **Outdated / deprecated** | ✅ registries + curated | ❌ | ❌ | ❌ | ❌ | ~ |
365
+ | Containers / OS packages | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ |
366
+ | SBOM (CycloneDX/SPDX) | ✅ CycloneDX 1.6 (+ CSAF 2.0 VEX) | ✅ | ✅ | ✅ (Syft) | ~ | ✅ |
367
+ | License compliance | ✅ SPDX + copyleft policy | ~ | ✅ | ~ | ❌ | ✅ |
368
+ | EPSS / KEV prioritization | ✅ FIRST.org EPSS + CISA KEV | ~ | ✅ | ✅ | ❌ | ✅ |
369
+ | CI gating (`--fail-on`) + triage | ✅ severity/KEV + ignore/VEX | ✅ | ✅ | ✅ | ⚠️ | ✅ |
370
+ | Auto-remediation / PRs | ❌ (fix recipes only) | ✅ `fix` | ❌ | ❌ | ❌ | ✅ |
371
+ | Offline | ✅ cache | ✅ local DB | ✅ | ✅ | ✅ feed | ❌ mostly online |
372
+ | **Scan without exposing the codebase**³ | ✅ anonymized descriptor | ❌ | ❌ | ❌ | ❌ | ❌ |
373
+ | **Maven private-dep cleanup** (→ Snyk) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
374
+ | Output | **HTML + Word `.doc`** + JSON / SARIF / CycloneDX / CSAF | table/JSON/SARIF | table/JSON/SARIF | table/JSON/SARIF | HTML/XML/JSON | JSON / cloud UI |
375
+
376
+ ¹ Narrower language coverage — no Rust/Dart/Swift (Go and Ruby are now covered).
377
+ ² Reading **lockfiles** without a build is the norm today: OSV-Scanner, Trivy and Grype/Syft
378
+ do it too. For **Maven `pom.xml`** specifically, *every* tool — `fad-checker` included — must
379
+ reach Maven Central (or rely on a real build / CycloneDX SBOM) to resolve transitive versions;
380
+ Trivy can resolve wrong transitive versions in that mode, while `fad-checker` flags what it
381
+ can't resolve in chapter 0. The genuine "no build" win is **vs Snyk** (requires building the
382
+ project) and **OWASP DC** (needs Maven Central access for Java accuracy).
383
+ ³ Phase 1 exports only public coordinates; the online scan never sees your source tree —
384
+ see [Air-gapped / PASSI](#air-gapped--passi-audits-anonymized-dependency-descriptor). OSV-Scanner
385
+ has an offline mode, but it still needs the **source on the scanning machine**.
386
+
387
+ **Where it fits:** a one-shot audit of a polyglot checkout you may not be able to build, a
388
+ presentable HTML/Word deliverable, and confidential / air-gapped engagements.
389
+ **Where it doesn't:** continuous CI supply-chain security, container/OS scanning,
390
+ reachability analysis, auto-fix PRs — reach for **Trivy** or **Grype + Syft**. (It now
391
+ *does* emit CycloneDX/CSAF and flag licenses + EPSS/KEV, but it isn't a gating CI daemon.)
392
+
393
+ You don't have to choose — `fad-checker` takes Snyk's results as input (`--snyk`) and merges them.
394
+
395
+ > Sources: [OSV-Scanner lockfiles](https://google.github.io/osv-scanner/supported-languages-and-lockfiles/) ·
396
+ > [Trivy Java/`pom.xml` (Maven Central, `--offline-scan`)](https://trivy.dev/docs/latest/coverage/language/java/) ·
397
+ > [Syft `java-pom-cataloger` (source dirs)](https://github.com/anchore/syft/issues/676) ·
398
+ > [OWASP DC needs internet/build for Java](https://jeremylong.github.io/DependencyCheck/data/index.html) ·
399
+ > [Snyk requires building the project](https://docs.snyk.io/supported-languages/technical-specifications-and-guidance) ·
400
+ > [EOL/outdated "most tools skip" (Aikido)](https://www.aikido.dev/code/outdated-eol-software)
327
401
 
328
402
  ---
329
403
 
@@ -6,7 +6,7 @@ _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 --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"
9
+ opts="--src --target --exclude --verbose --no-report --no-transitive --no-all-libs --no-osv --no-nvd --no-epss --no-kev --no-licenses --no-retire --retire-refresh --report-output --report-html --report-doc --report-sbom --report-csaf --report-json --report-sarif --fail-on --ignore --vex --ignore-test --cve-refresh --cve-offline --snyk --transitive-depth --offline --ecosystem --no-maven --no-npm --no-yarn --no-nuget --no-composer --no-pypi --no-go --no-ruby --no-jars --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") )
@@ -15,7 +15,20 @@ _fad_check() {
15
15
  '--verbose[verbose]'
16
16
  '--test[read-only]'
17
17
  '--report[generate CVE/EOL/obsolete report]'
18
- '--report-output[report output dir]:directory:_directories'
18
+ '--report-output[base dir for default-named outputs]:directory:_directories'
19
+ '--report-html[write HTML report]::file:_files'
20
+ '--report-doc[write Word .doc report]::file:_files'
21
+ '--report-sbom[write CycloneDX 1.6 SBOM]::file:_files'
22
+ '--report-csaf[write CSAF 2.0 VEX]::file:_files'
23
+ '--report-json[write flat findings JSON]::file:_files'
24
+ '--report-sarif[write SARIF 2.1.0 log]::file:_files'
25
+ '--no-report[write no output files (gate-only)]'
26
+ '--no-jars[skip embedded .jar/.war/.ear scanning]'
27
+ '--no-go[skip the Go codec]'
28
+ '--no-ruby[skip the Ruby codec]'
29
+ '--fail-on[CI gate level]:level:(none low medium high critical kev)'
30
+ '--ignore[suppress findings file]:file:_files'
31
+ '--vex[ingest CSAF VEX]:file:_files'
19
32
  '--ignore-test[skip test-scoped deps]'
20
33
  '--cve-refresh[force CVE re-download]'
21
34
  '--cve-offline[use cached CVE only]'
@@ -0,0 +1,97 @@
1
+ {
2
+ "_comment": "SPDX license id → policy category, plus a free-form alias map used to normalize registry/POM license strings. Categories: permissive | weak-copyleft | strong-copyleft | network-copyleft | proprietary | unknown. Edit to tune the policy.",
3
+ "categories": {
4
+ "MIT": "permissive",
5
+ "MIT-0": "permissive",
6
+ "Apache-2.0": "permissive",
7
+ "Apache-1.1": "permissive",
8
+ "BSD-2-Clause": "permissive",
9
+ "BSD-3-Clause": "permissive",
10
+ "0BSD": "permissive",
11
+ "ISC": "permissive",
12
+ "Zlib": "permissive",
13
+ "Unlicense": "permissive",
14
+ "WTFPL": "permissive",
15
+ "CC0-1.0": "permissive",
16
+ "PSF-2.0": "permissive",
17
+ "Python-2.0": "permissive",
18
+ "BSL-1.0": "permissive",
19
+ "Artistic-2.0": "permissive",
20
+ "LGPL-2.1": "weak-copyleft",
21
+ "LGPL-2.1-only": "weak-copyleft",
22
+ "LGPL-2.1-or-later": "weak-copyleft",
23
+ "LGPL-3.0": "weak-copyleft",
24
+ "LGPL-3.0-only": "weak-copyleft",
25
+ "LGPL-3.0-or-later": "weak-copyleft",
26
+ "MPL-1.1": "weak-copyleft",
27
+ "MPL-2.0": "weak-copyleft",
28
+ "EPL-1.0": "weak-copyleft",
29
+ "EPL-2.0": "weak-copyleft",
30
+ "CDDL-1.0": "weak-copyleft",
31
+ "CDDL-1.1": "weak-copyleft",
32
+ "GPL-2.0": "strong-copyleft",
33
+ "GPL-2.0-only": "strong-copyleft",
34
+ "GPL-2.0-or-later": "strong-copyleft",
35
+ "GPL-3.0": "strong-copyleft",
36
+ "GPL-3.0-only": "strong-copyleft",
37
+ "GPL-3.0-or-later": "strong-copyleft",
38
+ "AGPL-3.0": "network-copyleft",
39
+ "AGPL-3.0-only": "network-copyleft",
40
+ "AGPL-3.0-or-later": "network-copyleft",
41
+ "SSPL-1.0": "network-copyleft",
42
+ "proprietary": "proprietary",
43
+ "UNLICENSED": "proprietary",
44
+ "commercial": "proprietary"
45
+ },
46
+ "aliases": {
47
+ "the mit license": "MIT",
48
+ "mit license": "MIT",
49
+ "apache 2": "Apache-2.0",
50
+ "apache 2.0": "Apache-2.0",
51
+ "apache-2": "Apache-2.0",
52
+ "apache license 2.0": "Apache-2.0",
53
+ "apache license, version 2.0": "Apache-2.0",
54
+ "the apache software license, version 2.0": "Apache-2.0",
55
+ "asl 2.0": "Apache-2.0",
56
+ "apache software license": "Apache-2.0",
57
+ "bsd": "BSD-3-Clause",
58
+ "bsd license": "BSD-3-Clause",
59
+ "new bsd license": "BSD-3-Clause",
60
+ "bsd 3-clause": "BSD-3-Clause",
61
+ "bsd-3": "BSD-3-Clause",
62
+ "the 3-clause bsd license": "BSD-3-Clause",
63
+ "bsd 2-clause": "BSD-2-Clause",
64
+ "bsd-2": "BSD-2-Clause",
65
+ "simplified bsd license": "BSD-2-Clause",
66
+ "gnu gpl v2": "GPL-2.0",
67
+ "gplv2": "GPL-2.0",
68
+ "gpl-2": "GPL-2.0",
69
+ "gpl 2.0": "GPL-2.0",
70
+ "gnu gpl v3": "GPL-3.0",
71
+ "gplv3": "GPL-3.0",
72
+ "gpl-3": "GPL-3.0",
73
+ "gpl 3.0": "GPL-3.0",
74
+ "gnu general public license v3.0": "GPL-3.0",
75
+ "gnu lgpl v3": "LGPL-3.0",
76
+ "lgplv3": "LGPL-3.0",
77
+ "lgpl-3": "LGPL-3.0",
78
+ "gnu lgpl v2.1": "LGPL-2.1",
79
+ "lgplv2.1": "LGPL-2.1",
80
+ "lgpl-2.1": "LGPL-2.1",
81
+ "gnu agpl v3": "AGPL-3.0",
82
+ "agplv3": "AGPL-3.0",
83
+ "agpl-3": "AGPL-3.0",
84
+ "mozilla public license 2.0": "MPL-2.0",
85
+ "mpl 2.0": "MPL-2.0",
86
+ "mpl-2": "MPL-2.0",
87
+ "eclipse public license 2.0": "EPL-2.0",
88
+ "eclipse public license - v 2.0": "EPL-2.0",
89
+ "eclipse public license 1.0": "EPL-1.0",
90
+ "eclipse public license - v 1.0": "EPL-1.0",
91
+ "isc license": "ISC",
92
+ "python software foundation license": "PSF-2.0",
93
+ "psf": "PSF-2.0",
94
+ "the unlicense": "Unlicense",
95
+ "public domain": "CC0-1.0"
96
+ }
97
+ }