fad-checker 1.0.6 → 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.
- package/CHANGELOG.md +62 -0
- package/README.md +77 -15
- package/completions/fad-checker.bash +4 -1
- package/completions/fad-checker.zsh +9 -0
- package/data/eol-mapping.json +17 -0
- package/fad-checker.js +353 -241
- 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 +58 -35
- package/lib/{npm → codecs/npm}/parse.js +114 -10
- package/lib/{npm → codecs/npm}/registry.js +4 -3
- 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 +94 -0
- package/lib/codecs/pypi/parse.js +163 -0
- package/lib/codecs/pypi/registry.js +89 -0
- package/lib/codecs/pypi.codec.js +102 -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/core.js +35 -1
- package/lib/cve-match.js +18 -11
- package/lib/cve-report.js +34 -70
- package/lib/dep-record.js +60 -0
- package/lib/deps-descriptor.js +110 -0
- package/lib/nvd.js +4 -3
- package/lib/osv.js +29 -18
- package/lib/outdated.js +20 -4
- package/lib/retire.js +77 -13
- package/lib/transitive.js +3 -3
- package/lib/ui.js +87 -0
- package/package.json +4 -2
- package/CLAUDE.md +0 -129
- package/docs/ARCHITECTURE.md +0 -154
- package/docs/USAGE.md +0 -179
- package/test/core.test.js +0 -153
- package/test/cpe.test.js +0 -166
- package/test/cve-download.test.js +0 -39
- package/test/cve-match.test.js +0 -217
- package/test/cve-report.test.js +0 -171
- package/test/fixtures/complex-enterprise/api/pom.xml +0 -32
- package/test/fixtures/complex-enterprise/build/pom.xml +0 -46
- package/test/fixtures/complex-enterprise/dao/pom.xml +0 -37
- package/test/fixtures/complex-enterprise/pom.xml +0 -66
- package/test/fixtures/complex-enterprise/web/pom.xml +0 -77
- package/test/fixtures/cve-samples/cve-non-java.json +0 -19
- package/test/fixtures/cve-samples/cve-product-only.json +0 -31
- package/test/fixtures/cve-samples/cve-with-packagename.json +0 -37
- package/test/fixtures/cve-samples/nvd-log4shell.json +0 -40
- package/test/fixtures/cve-samples/nvd-npm-lodash.json +0 -22
- package/test/fixtures/monorepo-mixed/libs/common-bom/pom.xml +0 -26
- package/test/fixtures/monorepo-mixed/packages/cli/package.json +0 -14
- package/test/fixtures/monorepo-mixed/packages/cli/yarn.lock +0 -41
- package/test/fixtures/monorepo-mixed/packages/no-lock/package.json +0 -9
- package/test/fixtures/monorepo-mixed/packages/web-app/package-lock.json +0 -71
- package/test/fixtures/monorepo-mixed/packages/web-app/package.json +0 -17
- package/test/fixtures/monorepo-mixed/pom.xml +0 -29
- package/test/fixtures/monorepo-mixed/services/api/pom.xml +0 -27
- package/test/fixtures/monorepo-mixed/services/worker/pom.xml +0 -28
- package/test/fixtures/private-lib-detection/core/pom.xml +0 -26
- package/test/fixtures/private-lib-detection/plugin/pom.xml +0 -23
- package/test/fixtures/private-lib-detection/pom.xml +0 -35
- package/test/fixtures/simple/app/pom.xml +0 -28
- package/test/fixtures/simple/lib/pom.xml +0 -18
- package/test/fixtures/simple/pom.xml +0 -24
- package/test/maven-repo.test.js +0 -111
- package/test/maven-version.test.js +0 -57
- package/test/monorepo.test.js +0 -132
- package/test/npm-registry.test.js +0 -64
- package/test/npm.test.js +0 -146
- package/test/outdated.test.js +0 -101
- package/test/snyk.test.js +0 -64
- package/test/transitive.test.js +0 -305
- package/test/webjar.test.js +0 -33
package/CLAUDE.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# CLAUDE.md
|
|
2
|
-
|
|
3
|
-
Code-level orientation for contributors and Claude Code sessions on this repo.
|
|
4
|
-
|
|
5
|
-
## What this is
|
|
6
|
-
|
|
7
|
-
`fad-checker` — **Fucking Autonomous Dependency Checker**. Node.js CLI (`fad-checker`, or short alias `fad`) that:
|
|
8
|
-
|
|
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.
|
|
11
|
-
3. Scans the union against:
|
|
12
|
-
- the CVEProject `cvelistV5` Maven-relevant index (built locally),
|
|
13
|
-
- OSV.dev (multi-ecosystem),
|
|
14
|
-
- NIST NVD (enrichment: CVSS, CPE configurations, references),
|
|
15
|
-
- retire.js (vendored JS signatures),
|
|
16
|
-
- optionally Snyk (`--snyk`).
|
|
17
|
-
4. Cross-checks every match's NVD CPE configurations against the dep version (`lib/cpe.js`) to filter false positives.
|
|
18
|
-
5. Reports EOL frameworks (endoflife.date — Maven & npm), obsolete libs (curated Maven + npm-registry per-version `deprecated` field — authoritative, skips nothing), outdated libs (Maven Central + npm registry `dist-tags.latest`). **WebJars** (`org.webjars*`) are reduced to their npm coordinate by `webjarToNpm()` and run through the npm EOL/deprecation/outdated paths — so e.g. `org.webjars:angularjs:1.8.3` is flagged EOL.
|
|
19
|
-
6. Produces a self-contained HTML report + Word-compatible `.doc`, organised by ecosystem and by defining manifest, with per-tool fix recipes and an executive summary.
|
|
20
|
-
|
|
21
|
-
No build tool (`mvn`, `npm install`, `yarn`) is required on PATH — `pom.xml` / `package-lock.json` / `yarn.lock` are parsed directly.
|
|
22
|
-
|
|
23
|
-
## Running
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm install
|
|
27
|
-
npm test # 96 unit tests via node --test
|
|
28
|
-
|
|
29
|
-
# basic cleanup workflow
|
|
30
|
-
node fad-checker.js -s ./proj # read-only, full report
|
|
31
|
-
node fad-checker.js -s ./proj -t ../pom-clean -e "^client\\." # write cleaned tree
|
|
32
|
-
node fad-checker.js -s ./proj -t ../pom-clean -e "^client\\." --snyk # also drive snyk
|
|
33
|
-
|
|
34
|
-
# read the full usage doc
|
|
35
|
-
cat docs/USAGE.md
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Binary builds (requires `bun`):
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm run build:linux # → dist/fad-checker-linux
|
|
42
|
-
npm run build:win # → dist/fad-checker.exe
|
|
43
|
-
npm run build # both
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Guardrails enforced at startup:
|
|
47
|
-
- `--target` is required only when you want a cleaned POM tree. Without it the run is read-only.
|
|
48
|
-
- `--target` may not equal or be a subdirectory of `--src`.
|
|
49
|
-
- `--target` is `rimraf`'d before being rewritten — never point at anything precious.
|
|
50
|
-
|
|
51
|
-
## Architecture (one-liner per file)
|
|
52
|
-
|
|
53
|
-
```
|
|
54
|
-
fad-checker.js Thin CLI: commander parsing, orchestration only.
|
|
55
|
-
lib/core.js POM parsing, parent resolution, all-profile merge, rewrite.
|
|
56
|
-
lib/maven-version.js Maven version parsing + range comparison (no external deps).
|
|
57
|
-
lib/cve-download.js Bulk download of CVEProject/cvelistV5 + Maven-relevant index build.
|
|
58
|
-
lib/cve-match.js Resolved-dep collection + 3-tier CVE matching with dedup.
|
|
59
|
-
lib/cve-report.js Self-contained HTML and Word-compatible (.doc) report rendering.
|
|
60
|
-
lib/cpe.js CPE 2.3 parsing + NVD configurations evaluator (post-match refinement).
|
|
61
|
-
lib/outdated.js EOL (endoflife.date), obsolete (curated), outdated (Maven Central).
|
|
62
|
-
lib/transitive.js Maven Central POM walker (transitive resolution).
|
|
63
|
-
lib/osv.js OSV.dev batched query + per-vuln detail fetch.
|
|
64
|
-
lib/nvd.js NIST NVD enrichment (CVSS, references, CPE configurations).
|
|
65
|
-
lib/snyk.js `snyk test --all-projects --json` runner + merge.
|
|
66
|
-
lib/retire.js retire.js (vendored-JS scanner) wrapper + cache + normaliser.
|
|
67
|
-
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).
|
|
71
|
-
lib/cache-archive.js tar.gz / zip export & import of ~/.fad-checker/.
|
|
72
|
-
lib/config.js Persistent user config in ~/.fad-checker/config.json (mode 0600).
|
|
73
|
-
data/ known-obsolete.json, eol-mapping.json, cpe-coord-map.json, known-public-namespaces.json
|
|
74
|
-
completions/ fad-checker.bash, fad-checker.zsh
|
|
75
|
-
test/ node:test suite + fixtures (simple, complex-enterprise, monorepo-mixed, …).
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
For the deep dive — pipeline stages, the resolved-deps Map shape, report structure — see [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
|
|
79
|
-
|
|
80
|
-
## Important conventions
|
|
81
|
-
|
|
82
|
-
- **`coord()` always trims**: real-world POMs occasionally contain whitespace around `<artifactId>`. Every coord-derived lookup goes through `coord()` in `lib/core.js`.
|
|
83
|
-
- **`byId` keys are never polluted with `undefined`**: only indexed when both `groupId` and `artifactId` are present. Enforced by test.
|
|
84
|
-
- **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
|
-
- **No `process.exit(1)` mid-pipeline**: a parse/rewrite failure for one POM logs and continues so the summary still prints.
|
|
86
|
-
- **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
|
-
- **Map keys are ecosystem-namespaced**: Maven uses `g:a`, npm uses `npm:name`. They never collide so they share one resolved-deps Map.
|
|
88
|
-
- **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
|
-
- **Lockfile-only npm**: `package.json` without sibling `package-lock.json`/`yarn.lock` is intentionally skipped (its ranges aren't queryable) and reported in chapter 0.
|
|
90
|
-
- **Source identifiers**: every match carries `source: "fad" | "osv" | "nvd" | "snyk" | "retire"` (or a `+`-joined combination).
|
|
91
|
-
|
|
92
|
-
## Testing
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
node --test test/*.test.js # full suite (96 tests)
|
|
96
|
-
node --test test/core.test.js # one file
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Test fixtures live in `test/fixtures/`:
|
|
100
|
-
- `simple/` — 3 POMs with parent inheritance + property substitution
|
|
101
|
-
- `complex-enterprise/` — Spring Boot parent (external), local BOM via `scope=import`, three profiles
|
|
102
|
-
- `private-lib-detection/` — mixed public/private groupIds, external private parent
|
|
103
|
-
- `monorepo-mixed/` — Maven + npm (package-lock v3) + yarn.lock v1 + a no-lockfile package.json
|
|
104
|
-
- `cve-samples/` — small CVE / NVD JSON files for the matchers
|
|
105
|
-
|
|
106
|
-
## Gotchas / edge cases worth knowing
|
|
107
|
-
|
|
108
|
-
- 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
|
-
- `endoflife.date` API responses cached 7 days; Maven Central version lookups cached 24 hours. Cache lives in `~/.fad-checker/`.
|
|
110
|
-
- **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`.
|
|
112
|
-
- `snyk` is not a hard dep — shells out via `execFile`. `snyk` exits 1 on findings; the JSON is still on stdout.
|
|
113
|
-
- 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
|
-
- 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.
|
|
115
|
-
- **retire.js** doesn't like `--outputpath /dev/stdout`. We write to a temp file and read it back. Exit code 13 means "vulns found" — expected, not an error.
|
|
116
|
-
|
|
117
|
-
### Per-cache TTLs
|
|
118
|
-
|
|
119
|
-
| Cache | Location | TTL |
|
|
120
|
-
|---|---|---|
|
|
121
|
-
| CVEProject bulk index | `~/.fad-checker/cve-data/maven-cve-index.json` | 24 h |
|
|
122
|
-
| OSV per-dep stub list | `~/.fad-checker/osv-cache/<eco>__<g>__<a>__<v>.json` | 12 h |
|
|
123
|
-
| OSV vuln details | `~/.fad-checker/osv-cache/vuln_<id>.json` | 12 h |
|
|
124
|
-
| NVD CVE record | `~/.fad-checker/nvd-cache/<cveId>.json` | 7 d |
|
|
125
|
-
| endoflife.date cycles | `~/.fad-checker/eol-cache.json` | 7 d |
|
|
126
|
-
| Maven Central latest | `~/.fad-checker/version-cache.json` | 24 h |
|
|
127
|
-
| npm registry (deprecation + latest) | `~/.fad-checker/npm-registry-cache.json` | 24 h |
|
|
128
|
-
| Transitive POM | `~/.fad-checker/poms-cache/<g>__<a>__<v>.pom` | ∞ (immutable on Maven Central) |
|
|
129
|
-
| retire.js findings | `~/.fad-checker/retire-cache/<md5(src)>.json` | 24 h |
|
package/docs/ARCHITECTURE.md
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Architecture
|
|
2
|
-
|
|
3
|
-
This is the deep-dive for anyone modifying `fad-checker`'s internals or wondering why a particular finding shows up the way it does. For day-to-day usage, see [`USAGE.md`](USAGE.md).
|
|
4
|
-
|
|
5
|
-
## Module map
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
fad-checker.js Thin CLI: commander parsing, orchestration only.
|
|
9
|
-
lib/core.js POM parsing, parent resolution, all-profile merge, rewrite.
|
|
10
|
-
lib/maven-version.js Maven version parsing + range comparison (no external deps).
|
|
11
|
-
lib/cve-download.js Bulk download of CVEProject/cvelistV5 + Maven-relevant index build.
|
|
12
|
-
lib/cve-match.js Resolved-dep collection + 3-tier CVE matching with dedup.
|
|
13
|
-
lib/cve-report.js Self-contained HTML and Word-compatible (.doc) report rendering.
|
|
14
|
-
lib/cpe.js CPE 2.3 parsing + NVD configurations evaluator (post-match refinement).
|
|
15
|
-
lib/outdated.js EOL (endoflife.date), obsolete (curated), outdated (Maven Central).
|
|
16
|
-
lib/transitive.js Maven Central POM walker (transitive resolution).
|
|
17
|
-
lib/osv.js OSV.dev batched query + per-vuln detail fetch.
|
|
18
|
-
lib/nvd.js NIST NVD enrichment (CVSS, references, CPE configurations).
|
|
19
|
-
lib/snyk.js `snyk test --all-projects --json` runner + merge.
|
|
20
|
-
lib/retire.js retire.js (vendored-JS scanner) wrapper + cache + normaliser.
|
|
21
|
-
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.
|
|
25
|
-
lib/cache-archive.js tar.gz / zip export & import of ~/.fad-checker/.
|
|
26
|
-
lib/config.js Persistent user config in ~/.fad-checker/config.json (mode 0600).
|
|
27
|
-
data/ Curated JSON: known-obsolete, eol-mapping, cpe-coord-map, known-public-namespaces.
|
|
28
|
-
completions/ fad-checker.bash, fad-checker.zsh
|
|
29
|
-
test/ node:test suite + fixtures (simple, complex-enterprise, monorepo-mixed, …).
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## The resolved-deps Map
|
|
33
|
-
|
|
34
|
-
The whole pipeline hinges on a single `Map<string, depRecord>` keyed by:
|
|
35
|
-
- `groupId:artifactId` for Maven entries
|
|
36
|
-
- `npm:<name>` for npm/yarn entries
|
|
37
|
-
|
|
38
|
-
Each `depRecord` carries:
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
{
|
|
42
|
-
groupId, artifactId, version,
|
|
43
|
-
scope, // "compile" | "test" | "import" | "transitive" | "parent" | "prod" | "dev" | "peer" | "optional"
|
|
44
|
-
isDev, // Maven test/provided OR npm dev/devOptional/optional
|
|
45
|
-
ecosystem, // "maven" | "npm"
|
|
46
|
-
ecosystemType, // "maven" | "npm" | "yarn" | "retire"
|
|
47
|
-
pomPaths, // absolute paths to manifests declaring this dep
|
|
48
|
-
manifestPaths, // same as pomPaths but used by the npm collector
|
|
49
|
-
// Transitive-only:
|
|
50
|
-
via, viaPaths, depth,
|
|
51
|
-
// npm-only:
|
|
52
|
-
lockType, resolved, integrity,
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
The Maven keyspace and npm keyspace never collide — `:lodash` (Maven groupId-less) becomes `npm:lodash` so the same Map can hold both ecosystems without overwrites.
|
|
57
|
-
|
|
58
|
-
## Cleanup pipeline (`lib/core.js`)
|
|
59
|
-
|
|
60
|
-
1. `findPomFiles(src)` — recursive walk, skips known output dirs (`target/`, `node_modules/`, `.git/`, `.idea/`, `dist/`, `build-output/`, `out/`, `.next/`, `.nuxt/`, `coverage/`, `.gradle/`, `.mvn/`, `.vscode/`, `bower_components/`, `jspm_packages/`). Note: `build/` is **not** skipped on the Maven side because some multi-module projects use it for a BOM module.
|
|
61
|
-
2. `parsePom()` — xml2js to JSON. Extracts groupId/artifactId/version, `<parent>`, properties, and indexes every profile (recording which one is `activeByDefault`). Templates with literal `\${…}` are skipped.
|
|
62
|
-
3. `getAllInheritedProps()` — merges `<dependencies>`, `<dependencyManagement>`, `<properties>` from **every** `<profile>` (with `activeByDefault` properties winning for value conflicts), follows `<scope>import</scope>` BOMs to other local POMs, and recurses into resolved parents.
|
|
63
|
-
4. `rewritePoms()` — strips everything outside `nodeToKeep`, runs `cleanDeps()` to apply the `-e` regex, rewrites the `<parent>.relativePath` and `version` to the parent's value (not the child's). Skips disk writes when `readOnly`.
|
|
64
|
-
|
|
65
|
-
## Report pipeline (driven by `fad-checker.js` when `--report` is set)
|
|
66
|
-
|
|
67
|
-
1. **Collect** — `collectResolvedDeps()` dedupes by `groupId:artifactId`, keeps the highest version on conflict, includes external parent POMs as `scope='parent'`. `--ignore-test` honored. For npm, `collectNpmDeps()` walks JS manifests (lockfile-only — `package.json` without sibling lockfile is skipped + warned).
|
|
68
|
-
2. **Transitive expansion** (optional, `--transitive`) — `expandWithTransitives()` walks the Maven Central POM graph honouring exclusions, root depMgmt overrides, nearest-wins on version conflict, `--transitive-depth` cap. Skips test + optional scopes by default.
|
|
69
|
-
3. **CVE index** — `ensureCveIndex()` downloads the daily CVEProject zip (via `curl + unzip`, or falls back to `fetch()` + `unzip` / PowerShell `Expand-Archive`), filters to Maven-relevant entries, caches the compact index to `~/.fad-checker/cve-data/maven-cve-index.json`. Fresh for 24h. `--cve-refresh` forces rebuild, `--cve-offline` uses cache only.
|
|
70
|
-
4. **CVE matching** — `matchDepsAgainstCves()` runs three tiers:
|
|
71
|
-
- `exact`: `byPackageName["g:a"]` hit
|
|
72
|
-
- `probable`: `byProduct[artifactId]` + vendor matches groupId (`apache` ↔ `org.apache.*`)
|
|
73
|
-
- `possible`: product-only match
|
|
74
|
-
Dedupes by `(dep, cve.id)` and sorts by severity. npm deps are skipped here — they're scanned by OSV instead.
|
|
75
|
-
5. **OSV** (default on) — `queryOsvForDeps()` POSTs batched queries to `api.osv.dev/v1/querybatch` (Maven ecosystem for Maven deps, npm ecosystem for npm deps). Per-dep stub list cached 12h; per-vuln details cached 12h.
|
|
76
|
-
6. **NVD enrichment** (default on) — for every CVE id matched, fetch the full NVD record (description, CVSS vectors, references categorised by tag, CPE configurations). Rate-limited per NIST policy (5/30s unauthenticated, 50/30s with `NVD_API_KEY`).
|
|
77
|
-
7. **CPE refinement** — `refineMatchesWithCpe()` walks NVD's `configurations[].nodes[]` against each matched dep:
|
|
78
|
-
- Confirms the dep version actually falls in the vulnerable range (else `cpeFiltered: true` — likely false positive).
|
|
79
|
-
- Upgrades match `confidence` from `possible` → `probable` → `exact` when a curated `cpe-coord-map.json` entry confirms vendor:product → dep coord.
|
|
80
|
-
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.
|
|
83
|
-
- **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
|
-
- **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
|
-
- **Outdated**: Maven Central Solr query; npm registry `dist-tags.latest` (npm deps and WebJars). Both gated by `--no-all-libs`. Cache 24h. Concurrency 8.
|
|
86
|
-
10. **Snyk** (optional, `--snyk`) — runs `snyk test --all-projects --json` against the cleaned target dir. Normalised + merged. Findings in both sources tagged `source: "both"`.
|
|
87
|
-
11. **Render** — `writeReports()` produces `cve-report.html` (self-contained, inline CSS, no external assets) and `cve-report.doc` (same HTML with Office XML namespace meta tags so Word opens it natively). Default output dir: `./fad-checker-report/`.
|
|
88
|
-
|
|
89
|
-
## Report structure
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
<Executive Summary> ← global criticality + key bullet counts
|
|
93
|
-
<Summary cards> ← critical / high / medium / low / EOL / obsolete / outdated
|
|
94
|
-
<Toolbar> ← expand-all / collapse-all / expand CVE details
|
|
95
|
-
|
|
96
|
-
0. Warnings & scan-completeness ← chapter 0 if any warnings
|
|
97
|
-
1. CVE Vulnerabilities — production (N)
|
|
98
|
-
1.a Maven (n)
|
|
99
|
-
1.a.0 All (n) ← combined direct + transitive
|
|
100
|
-
By pom.xml (k files) ← wrapper always present
|
|
101
|
-
<relative-path-1> (m) ← direct deps in this pom only
|
|
102
|
-
<relative-path-2> (m)
|
|
103
|
-
…
|
|
104
|
-
1.b npm (package-lock) (n)
|
|
105
|
-
1.b.0 All (n)
|
|
106
|
-
By package-lock.json (k files)
|
|
107
|
-
web/package-lock.json (m)
|
|
108
|
-
2. CVE in dev dependencies (M) ← same structure as 1
|
|
109
|
-
3. Vendored JS scan — retire.js (R)
|
|
110
|
-
4. End-of-Life Frameworks
|
|
111
|
-
5. Obsolete / Deprecated Libraries
|
|
112
|
-
6. Outdated Libraries
|
|
113
|
-
7. Fix Recommendations ← per-ecosystem snippets
|
|
114
|
-
7.a Maven ← dependencyManagement XML
|
|
115
|
-
7.b npm ← package.json overrides
|
|
116
|
-
7.c yarn ← package.json resolutions
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Important conventions
|
|
120
|
-
|
|
121
|
-
- **`coord()` always trims**: real-world POMs occasionally contain whitespace around `<artifactId>` (seen in the wild). Every coord-derived lookup goes through `coord()` in `lib/core.js`.
|
|
122
|
-
- **`byId` keys are never polluted with `undefined`**: we only index a POM by id when both `groupId` and `artifactId` are present. Test `byId does not get polluted with undefined keys` enforces this.
|
|
123
|
-
- **All profiles are merged, never prompted for**: previous versions prompted the user when a POM had multiple profiles. We now union every profile's deps so Snyk sees every dep any profile could pull in. `activeByDefault` wins only for property value conflicts.
|
|
124
|
-
- **No `process.exit(1)` mid-pipeline**: a parse/rewrite failure for one POM logs and continues so the summary still prints.
|
|
125
|
-
- **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.
|
|
126
|
-
- **Map keys are ecosystem-namespaced**: Maven uses `g:a`, npm uses `npm:name`. They never collide so they can share one resolved-deps Map.
|
|
127
|
-
- **Lockfile-only npm**: `package.json` without sibling `package-lock.json`/`yarn.lock` is intentionally skipped (its ranges aren't queryable) and reported in chapter 0. Avoids false negatives on deps that haven't been installed yet.
|
|
128
|
-
- **Source identifiers**: every match carries `source: "fad" | "osv" | "nvd" | "snyk" | "retire"` (or a `+`-joined combination like `"fad+osv+nvd"`). The legacy "mbdc" identifier was renamed to "fad" in 3.0.
|
|
129
|
-
|
|
130
|
-
## Gotchas / edge cases worth knowing
|
|
131
|
-
|
|
132
|
-
- The CVE bundle from CVEProject is ~500 MB unpacked. We shell out to `curl + unzip` (Node built-in fallback to `fetch()` + system `unzip` / PowerShell `Expand-Archive`). The extracted JSON is deleted after the index is built.
|
|
133
|
-
- The bundle ships as `cves.zip.zip` (a zip whose sole content is another zip). `extractZip()` recurses up to 3 levels.
|
|
134
|
-
- `endoflife.date` API responses are cached locally for 7 days; Maven Central and npm registry version lookups for 24 hours.
|
|
135
|
-
- **Persistent config**: `~/.fad-checker/config.json` (mode 0600) stores per-user state, currently the NVD API key. Set via `fad-checker --set-nvd-key <KEY>`.
|
|
136
|
-
- **`--offline` umbrella flag**: skips every network call (CVE index download, OSV queries, NVD enrichment, endoflife.date lookups, Maven Central version queries, npm registry queries, transitive POM fetches, retire.js scans). Falls back to whatever is already cached. Per-source variants (`--cve-offline`, `--no-osv`, `--no-nvd`, `--no-retire`, `--no-js`) still work independently.
|
|
137
|
-
- `snyk` is not a dependency — we shell out via `execFile`. `snyk` exits 1 when it finds vulnerabilities, which is expected (the JSON is still on stdout).
|
|
138
|
-
- The cleaned POM is the union of every profile's deps. Counts will therefore be larger than the source POM. This is intentional — verify your reasoning before "reducing" them.
|
|
139
|
-
- Unresolved `${…}` Maven variables are kept verbatim in the rewritten POM. `lib/cve-match.js` resolves them lazily via `resolveDepVersion()` when collecting deps for the scan. Deps that *still* can't be resolved (external BOM) are surfaced in chapter 0 as `unresolved-versions` warnings.
|
|
140
|
-
- **Per-cache TTLs** are documented in the README's "Caching" table.
|
|
141
|
-
|
|
142
|
-
## Testing
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
npm test # full suite (96 tests)
|
|
146
|
-
node --test test/core.test.js # one file
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Test fixtures live in `test/fixtures/`:
|
|
150
|
-
- `simple/` — 3 POMs with parent inheritance + property substitution
|
|
151
|
-
- `complex-enterprise/` — Spring Boot parent (external), local BOM via `scope=import`, three profiles (two of which inject env-specific JDBC drivers), test-scoped JUnit, jackson-databind via BOM-managed version
|
|
152
|
-
- `private-lib-detection/` — mixed public/private groupIds and an externally-hosted private parent — verifies missing-parent tracking
|
|
153
|
-
- `monorepo-mixed/` — combined Maven (4 POMs: parent + BOM + 2 modules) + JS (npm package-lock v3 + yarn.lock v1 + a no-lockfile package.json to test the warning)
|
|
154
|
-
- `cve-samples/` — small CVE / NVD JSON files to exercise the matchers without the 500 MB real bundle
|
package/docs/USAGE.md
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
# USAGE
|
|
2
|
-
|
|
3
|
-
Every flag, every common workflow, with copy-pasteable commands.
|
|
4
|
-
|
|
5
|
-
## Synopsis
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
fad-checker -s <src> [-t <target>] [-e <regex>] [other options]
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
- `-s, --src <src>` — **required**. Root of the source tree to scan. Contains `pom.xml` and/or `package(-lock).json` / `yarn.lock`.
|
|
12
|
-
- `-t, --target <dir>` — optional. If given, write a parallel directory of "cleaned" POMs (private/excluded deps stripped) to `<dir>` — useful as Snyk input. Without `-t`, the run is read-only.
|
|
13
|
-
|
|
14
|
-
## Output
|
|
15
|
-
|
|
16
|
-
By default the HTML + Word reports land in `./fad-checker-report/`. Override with `--report-output <dir>`.
|
|
17
|
-
|
|
18
|
-
## Ecosystem selection
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
# Auto-detect (default): scan whatever pom.xml / package(-lock).json / yarn.lock exists
|
|
22
|
-
fad-checker -s .
|
|
23
|
-
|
|
24
|
-
# Force one ecosystem
|
|
25
|
-
fad-checker -s . --ecosystem maven
|
|
26
|
-
fad-checker -s . --ecosystem npm
|
|
27
|
-
fad-checker -s . --ecosystem both # scan both even if only one is auto-detected
|
|
28
|
-
|
|
29
|
-
# Disable JS scanning entirely (Maven-only)
|
|
30
|
-
fad-checker -s . --no-js
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Filtering deps
|
|
34
|
-
|
|
35
|
-
`-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.
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
fad-checker -s . -e "^(com\.acme|org\.private)\."
|
|
39
|
-
fad-checker -s . -e "^@acme/"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The excluded coords are listed at the end of the run so you can audit the regex.
|
|
43
|
-
|
|
44
|
-
## Per-source toggles
|
|
45
|
-
|
|
46
|
-
Each data source can be disabled independently:
|
|
47
|
-
|
|
48
|
-
| Flag | Effect |
|
|
49
|
-
| --- | --- |
|
|
50
|
-
| `--no-report` | Skip the report flow entirely (just cleanup) |
|
|
51
|
-
| `--no-transitive` | Don't fetch transitive Maven deps from Maven Central |
|
|
52
|
-
| `--no-all-libs` | Don't query Maven Central for latest versions (skips chapter 6 Outdated and the "missing on Central" check) |
|
|
53
|
-
| `--no-osv` | Skip OSV.dev (Google + GitHub aggregated feed) |
|
|
54
|
-
| `--no-nvd` | Skip NVD enrichment (no full CVSS, no CPE refinement) |
|
|
55
|
-
| `--no-retire` | Skip retire.js vendored-JS scan |
|
|
56
|
-
| `--ignore-test` | Drop test-scoped Maven deps and dev npm deps from the scan entirely (chapter 2 will be empty) |
|
|
57
|
-
|
|
58
|
-
## Offline / cache control
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# Use cached data only, no network (works for everything)
|
|
62
|
-
fad-checker -s . --offline
|
|
63
|
-
|
|
64
|
-
# Per-source offline
|
|
65
|
-
fad-checker -s . --cve-offline # use cached CVE index only
|
|
66
|
-
fad-checker -s . --cve-refresh # force re-download of CVE bundle
|
|
67
|
-
fad-checker -s . --retire-refresh # force re-scan with retire.js (ignore cache)
|
|
68
|
-
|
|
69
|
-
# Cache export / import (useful for air-gapped boxes)
|
|
70
|
-
fad-checker --export-cache fad-cache.tar.gz
|
|
71
|
-
fad-checker --export-cache fad-cache.tar.gz --include-config # bundle NVD key too
|
|
72
|
-
fad-checker --import-cache fad-cache.tar.gz
|
|
73
|
-
fad-checker --import-cache fad-cache.tar.gz --force # replace existing without backup
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## NVD API key
|
|
77
|
-
|
|
78
|
-
NVD's public rate limit is 5 requests / 30s without a key. The free key bumps it to 50 / 30s — **10× faster** for the enrichment step.
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
# Get a key in 30 seconds: https://nvd.nist.gov/developers/request-an-api-key
|
|
82
|
-
fad-checker --set-nvd-key YOUR_KEY # stored in ~/.fad-checker/config.json (mode 0600)
|
|
83
|
-
fad-checker --show-config # confirm it's persisted (key masked)
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Or pass it ad-hoc via the `NVD_API_KEY` env var.
|
|
87
|
-
|
|
88
|
-
## Snyk integration
|
|
89
|
-
|
|
90
|
-
If you have `snyk` installed and authenticated, `fad-checker` can drive it:
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
fad-checker -s ./proj -t ../proj-clean -e "^com\.acme\." --snyk
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
This:
|
|
97
|
-
1. Generates the cleaned POM tree at `../proj-clean/`.
|
|
98
|
-
2. Runs `snyk test --all-projects --json` against it.
|
|
99
|
-
3. Merges Snyk's findings into the report — entries present in both `fad-checker` and Snyk are tagged `source: "both"`.
|
|
100
|
-
|
|
101
|
-
`--snyk` requires `-t` (Snyk needs a real POM tree to scan).
|
|
102
|
-
|
|
103
|
-
## Read-only vs write mode
|
|
104
|
-
|
|
105
|
-
| Mode | Trigger | Disk writes |
|
|
106
|
-
| --- | --- | --- |
|
|
107
|
-
| Read-only | `-t` omitted (default) | Only `~/.fad-checker/` caches and the report dir |
|
|
108
|
-
| Write | `-t <dir>` provided | Above + the cleaned POM tree at `<dir>` (and `<dir>` is `rimraf`'d first!) |
|
|
109
|
-
|
|
110
|
-
The `--target` guardrails refuse:
|
|
111
|
-
- empty `--src`
|
|
112
|
-
- `--target` equal to or a subdirectory of `--src`
|
|
113
|
-
|
|
114
|
-
## Verbosity
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
fad-checker -s . -v # progress per source (OSV batches, NVD pages, retire scan, …)
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Shell completion
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
fad-checker --completion bash > /etc/bash_completion.d/fad-checker
|
|
124
|
-
fad-checker --completion zsh > ~/.zsh/completions/_fad-checker
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## All flags at a glance
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
fad-checker --help
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## Recipes
|
|
134
|
-
|
|
135
|
-
### CI gate: fail the build on any CRITICAL prod CVE
|
|
136
|
-
|
|
137
|
-
`fad-checker` exits 0 even when CVEs are found (it's a reporter, not a gate). Wire your own:
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
fad-checker -s . --no-nvd > /dev/null
|
|
141
|
-
# Then grep the report or parse the structured output (planned).
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
(A `--fail-on critical` flag is a planned addition — track it in issues.)
|
|
145
|
-
|
|
146
|
-
### Diff two runs
|
|
147
|
-
|
|
148
|
-
Keep dated copies of the report:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
fad-checker -s . --report-output reports/$(date +%F)
|
|
152
|
-
diff reports/2026-04-01/cve-report.html reports/2026-05-01/cve-report.html
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Air-gapped scan
|
|
156
|
-
|
|
157
|
-
On a connected machine:
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
fad-checker -s ./dummy-empty-dir # populates ~/.fad-checker/ caches
|
|
161
|
-
fad-checker --export-cache fad-cache.tar.gz --include-config
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Move `fad-cache.tar.gz` to the air-gapped box, then:
|
|
165
|
-
|
|
166
|
-
```bash
|
|
167
|
-
fad-checker --import-cache fad-cache.tar.gz
|
|
168
|
-
fad-checker -s ./real-project --offline
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Monorepo with Maven + JS + vendored JS
|
|
172
|
-
|
|
173
|
-
The melino-style project (Java backend, React frontend in `web/`, vendored jQuery/PDF.js under `web/src/main/webapp/`):
|
|
174
|
-
|
|
175
|
-
```bash
|
|
176
|
-
fad-checker -s . -e "^(com\.captcha|org\.voxaly|com\.voxaly)\."
|
|
177
|
-
# → finds CVE in Maven deps, in web/package-lock.json deps,
|
|
178
|
-
# AND in the vendored .js files under webapp/.
|
|
179
|
-
```
|
package/test/core.test.js
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
const { test } = require("node:test");
|
|
2
|
-
const assert = require("node:assert/strict");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const os = require("os");
|
|
6
|
-
const { parseStringPromise } = require("xml2js");
|
|
7
|
-
|
|
8
|
-
const core = require("../lib/core");
|
|
9
|
-
|
|
10
|
-
const FIXTURES = path.join(__dirname, "fixtures");
|
|
11
|
-
const SIMPLE = path.join(FIXTURES, "simple");
|
|
12
|
-
const COMPLEX = path.join(FIXTURES, "complex-enterprise");
|
|
13
|
-
const PRIVATE_FIX = path.join(FIXTURES, "private-lib-detection");
|
|
14
|
-
|
|
15
|
-
async function pipeline(src, { deps2Exclude } = {}) {
|
|
16
|
-
const store = core.newMetadataStore();
|
|
17
|
-
const props = {};
|
|
18
|
-
const pomFiles = core.findPomFiles(src);
|
|
19
|
-
for (const f of pomFiles) await core.parsePom(f, store);
|
|
20
|
-
for (const f of pomFiles) await core.getAllInheritedProps(f, store, props);
|
|
21
|
-
return { store, props, pomFiles };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
test("findPomFiles skips target/.git/node_modules", () => {
|
|
25
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "fad-checker-test-"));
|
|
26
|
-
fs.mkdirSync(path.join(tmp, "target"));
|
|
27
|
-
fs.writeFileSync(path.join(tmp, "target", "pom.xml"), "<project/>");
|
|
28
|
-
fs.writeFileSync(path.join(tmp, "pom.xml"), "<project/>");
|
|
29
|
-
const found = core.findPomFiles(tmp);
|
|
30
|
-
assert.equal(found.length, 1, "target/ should be skipped");
|
|
31
|
-
assert.equal(found[0], path.join(tmp, "pom.xml"));
|
|
32
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("parsePom extracts groupId/artifactId/version + parent + profiles", async () => {
|
|
36
|
-
const { store } = await pipeline(SIMPLE);
|
|
37
|
-
const root = store.byPath[path.join(SIMPLE, "pom.xml")];
|
|
38
|
-
assert.equal(root.groupId, "com.example.simple");
|
|
39
|
-
assert.equal(root.artifactId, "simple-parent");
|
|
40
|
-
assert.equal(root.version, "1.0.0");
|
|
41
|
-
assert.equal(root.parentInfo, null);
|
|
42
|
-
|
|
43
|
-
const app = store.byPath[path.join(SIMPLE, "app", "pom.xml")];
|
|
44
|
-
assert.equal(app.parentInfo.groupId, "com.example.simple");
|
|
45
|
-
assert.equal(app.parentInfo.artifactId, "simple-parent");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("byId does not get polluted with undefined keys", async () => {
|
|
49
|
-
const { store } = await pipeline(SIMPLE);
|
|
50
|
-
for (const key of Object.keys(store.byId)) {
|
|
51
|
-
assert.ok(!key.includes("undefined"), `byId key has 'undefined': ${key}`);
|
|
52
|
-
assert.ok(!key.includes("null"), `byId key has 'null': ${key}`);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("relativePath as directory resolves to dir/pom.xml", async () => {
|
|
57
|
-
const { store } = await pipeline(SIMPLE);
|
|
58
|
-
const app = store.byPath[path.join(SIMPLE, "app", "pom.xml")];
|
|
59
|
-
// resolveParentPath populates parentDescr
|
|
60
|
-
core.resolveParentPath(app.pomPath, app.parentInfo, store);
|
|
61
|
-
assert.ok(app.parentDescr, "parent descriptor should be resolved");
|
|
62
|
-
assert.equal(app.parentDescr.artifactId, "simple-parent");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("activeByDefault profile is detected and used for property overrides", async () => {
|
|
66
|
-
const { store, props } = await pipeline(COMPLEX);
|
|
67
|
-
const root = store.byPath[path.join(COMPLEX, "pom.xml")];
|
|
68
|
-
assert.equal(root.defaultProfileId, "dev");
|
|
69
|
-
const merged = props[path.join(COMPLEX, "pom.xml")];
|
|
70
|
-
// env.profile should be 'dev' (from activeByDefault) — but properties are
|
|
71
|
-
// stored as xml2js arrays; unwrap when reading.
|
|
72
|
-
const envProfile = merged.properties["env.profile"];
|
|
73
|
-
const val = Array.isArray(envProfile) ? envProfile[0] : envProfile;
|
|
74
|
-
assert.equal(val, "dev");
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("all-profile merge picks up deps from every profile", async () => {
|
|
78
|
-
const { props } = await pipeline(COMPLEX);
|
|
79
|
-
const root = props[path.join(COMPLEX, "pom.xml")];
|
|
80
|
-
const ids = root.dependencies.map(d => `${d.groupId?.[0]}:${d.artifactId?.[0]}`);
|
|
81
|
-
// Each profile contributed a dep — all three must be present.
|
|
82
|
-
assert.ok(ids.includes("com.h2database:h2"), "dev profile (h2) missing");
|
|
83
|
-
assert.ok(ids.includes("org.postgresql:postgresql"), "prod profile (postgres) missing");
|
|
84
|
-
assert.ok(ids.includes("com.acme.private:acme-oracle-driver"), "oracle profile (private) missing");
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("BOM import (scope=import) pulls in managed deps from local BOM", async () => {
|
|
88
|
-
const { props } = await pipeline(COMPLEX);
|
|
89
|
-
const api = props[path.join(COMPLEX, "api", "pom.xml")];
|
|
90
|
-
const mgmtIds = api.dependencyManagement.map(d => `${d.groupId?.[0]}:${d.artifactId?.[0]}`);
|
|
91
|
-
assert.ok(mgmtIds.includes("org.hibernate:hibernate-core"), "BOM hibernate not imported");
|
|
92
|
-
assert.ok(mgmtIds.includes("com.fasterxml.jackson.core:jackson-databind"), "BOM jackson not imported");
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test("rewritePoms writes a clean tree in --target mode, target ≠ src", async () => {
|
|
96
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "fad-checker-target-"));
|
|
97
|
-
const { store, props, pomFiles } = await pipeline(COMPLEX);
|
|
98
|
-
const opts = {
|
|
99
|
-
srcRoot: COMPLEX, targetRoot: tmp,
|
|
100
|
-
deps2Exclude: /^com\.acme\.private$/, verbose: false, readOnly: false,
|
|
101
|
-
};
|
|
102
|
-
let wrote = 0;
|
|
103
|
-
for (const f of pomFiles) if (await core.rewritePoms(f, store, props, opts)) wrote++;
|
|
104
|
-
assert.ok(wrote >= 4, `expected ≥4 POMs written, got ${wrote}`);
|
|
105
|
-
const apiPomOut = path.join(tmp, "api", "pom.xml");
|
|
106
|
-
assert.ok(fs.existsSync(apiPomOut));
|
|
107
|
-
const apiOut = await parseStringPromise(fs.readFileSync(apiPomOut, "utf8"));
|
|
108
|
-
const deps = apiOut.project?.dependencies?.[0]?.dependency || [];
|
|
109
|
-
const ids = deps.map(d => `${d.groupId?.[0]}:${d.artifactId?.[0]}`);
|
|
110
|
-
// Private dep must be filtered out
|
|
111
|
-
assert.ok(!ids.includes("com.acme.private:acme-commons"), "private dep should be excluded");
|
|
112
|
-
// Public dep must remain
|
|
113
|
-
assert.ok(ids.includes("com.fasterxml.jackson.core:jackson-databind"), "public dep dropped unexpectedly");
|
|
114
|
-
// And excludedById must contain the private coord
|
|
115
|
-
assert.ok(store.excludedById["com.acme.private:acme-commons"], "excluded coord not flagged");
|
|
116
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test("rewritePoms in --test (readOnly) mode does not crash with undefined target", async () => {
|
|
120
|
-
const { store, props, pomFiles } = await pipeline(SIMPLE);
|
|
121
|
-
const opts = { srcRoot: SIMPLE, targetRoot: undefined, deps2Exclude: null, verbose: false, readOnly: true };
|
|
122
|
-
// Should not throw despite target being undefined
|
|
123
|
-
for (const f of pomFiles) await core.rewritePoms(f, store, props, opts);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("missing external parent is flagged in missingById", async () => {
|
|
127
|
-
const { store, props, pomFiles } = await pipeline(PRIVATE_FIX);
|
|
128
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "fad-checker-priv-"));
|
|
129
|
-
const opts = {
|
|
130
|
-
srcRoot: PRIVATE_FIX, targetRoot: tmp,
|
|
131
|
-
deps2Exclude: /^(com\.client\.private|org\.megacorp)/,
|
|
132
|
-
verbose: false, readOnly: false,
|
|
133
|
-
};
|
|
134
|
-
for (const f of pomFiles) await core.rewritePoms(f, store, props, opts);
|
|
135
|
-
// The root pom's external parent org.megacorp.parents:megacorp-super-parent must be in missingById
|
|
136
|
-
assert.ok(
|
|
137
|
-
store.missingById["org.megacorp.parents:megacorp-super-parent"] ||
|
|
138
|
-
store.missingById["org.megacorp.parents:megacorp-super-parent:9.9.9-PRIVATE"],
|
|
139
|
-
"external private parent not tracked as missing"
|
|
140
|
-
);
|
|
141
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test("parent version in rewritten POM uses parent's version, not child's", async () => {
|
|
145
|
-
// Simple has child app with no own <version>; the rewritten parent ref should be 1.0.0
|
|
146
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "fad-checker-pv-"));
|
|
147
|
-
const { store, props, pomFiles } = await pipeline(SIMPLE);
|
|
148
|
-
const opts = { srcRoot: SIMPLE, targetRoot: tmp, deps2Exclude: null, verbose: false, readOnly: false };
|
|
149
|
-
for (const f of pomFiles) await core.rewritePoms(f, store, props, opts);
|
|
150
|
-
const appOut = await parseStringPromise(fs.readFileSync(path.join(tmp, "app", "pom.xml"), "utf8"));
|
|
151
|
-
assert.equal(appOut.project.parent[0].version[0], "1.0.0", "parent.version must equal parent's version");
|
|
152
|
-
fs.rmSync(tmp, { recursive: true, force: true });
|
|
153
|
-
});
|