diff --git a/website/docs/case-studies/nestjs.md b/website/docs/case-studies/nestjs.md index 5cdd8fd..5a7f044 100644 --- a/website/docs/case-studies/nestjs.md +++ b/website/docs/case-studies/nestjs.md @@ -1,6 +1,6 @@ # NestJS Case Study -> Verified baseline scan — CVE Lite CLI v1.6.0 · 2026-06-14 (usage-aware remeasurement v1.25.0 · 2026-06-22) +> Verified baseline scan — CVE Lite CLI v1.25.0 · 2026-06-24 (remediation remeasurement on revision `cee51af`)
@@ -10,38 +10,38 @@
- **Project:** [NestJS](https://github.com/nestjs/nest) — production-grade Node.js framework used across thousands of enterprise applications
- **Revision:** `cee51af9118b68511e77e059f0578a3f0a3bcf0d`
-- **Lockfile:** `package-lock.json` (1623 resolved packages, v1.6.0 scan)
-- **Baseline findings:** 26 unique vulnerable packages (1 critical · 8 high · 13 medium · 4 low)
-- **Direct vs transitive:** 1 direct / 25 transitive
+- **Lockfile:** `package-lock.json` (npm monorepo root lockfile at pinned revision)
+- **Baseline findings:** 51 unique vulnerable packages (4 critical · 18 high · 25 medium · 4 low)
+- **Direct vs transitive:** 8 direct / 43 transitive
- **Time to first actionable fix command:** under 30 seconds
-- **Validated fix commands generated:** 2 (specific versioned targets, not generic `npm audit fix`)
-- **After measured pass (remediation study):** reduced from 24 → 21 findings
-- **Usage-aware triage (`--only-used`):** **51 → 10** findings on 2026-06-22 remeasurement (41 toolchain/transitive packages not statically imported)
+- **Validated fix commands generated:** 9 command groups at baseline (specific versioned targets, not generic `npm audit fix`)
+- **After two measured remediation passes:** reduced from **51 → 50 → 47** findings
+- **Usage-aware triage (`--only-used`):** **51 → 10** findings on the same revision (41 toolchain/transitive packages not statically imported)
---
## What this case study demonstrates
-NestJS represents the harder class of dependency remediation: a mature repository where almost all findings are transitive and there is no large batch of direct upgrades to clear first.
+NestJS represents the harder class of dependency remediation: a mature monorepo where most findings are transitive, OSV advisory coverage has expanded since earlier scans, and there is no single batch upgrade that clears the list.
-CVE Lite CLI's direct/transitive split makes this immediately visible. In this scan, 25 of 26 findings are transitive — meaning `npm audit fix` would have little effect and `npm audit fix --force` would be risky. CVE Lite surfaces the one actionable direct fix (`fastify@5.8.5`) and a single concrete parent upgrade (`mocha@12.0.0-beta-4`) separately from the deeper structural issues, so a developer knows exactly where to start.
+CVE Lite CLI's direct/transitive split makes this immediately visible. In this remeasurement, 43 of 51 findings are transitive — meaning `npm audit fix` would have little effect and `npm audit fix --force` would be risky. CVE Lite still surfaces confident first-pass work (`fastify@5.8.5` as a direct fix, then `mocha@12.0.0-beta-4` as a parent-chain move) separately from the deeper structural issues, so a developer knows exactly where to start.
-The tool also names the parent chain for every transitive finding. `form-data@2.3.3` (critical) is reached through `request`. `braces@1.8.5` is reached through `gulp-watch`. That context is absent from npm audit's output and is exactly what a developer needs to decide which parent upgrade is worth attempting first.
+The tool also names the parent chain for transitive findings. `form-data` (critical) is reached through deprecated `request` paths. Multiple `ws` versions appear across framework and dev tooling. That context is absent from npm audit's flat output and is exactly what a developer needs when deciding which parent upgrade is worth attempting first.
---
## Comparison Note: CVE Lite CLI vs npm audit
-Both tools were run against the same `package-lock.json` on the same machine.
+Both tools were run against the same `package-lock.json` on the same machine on 2026-06-24.
-| Metric | npm audit | CVE Lite CLI v1.6.0 |
+| Metric | npm audit | CVE Lite CLI v1.25.0 |
|---|---:|---:|
-| Total reported findings | 36 | 26 |
-| Critical | 4 | 1 |
-| High | 17 | 8 |
-| Moderate / Medium | 13 | 13 |
-| Low | 2 | 4 |
-| Direct vs transitive breakdown | ✗ | ✓ (1 / 25) |
+| Total reported findings | 56 | 51 |
+| Critical | 6 | 4 |
+| High | 22 | 18 |
+| Moderate / Medium | 25 | 25 |
+| Low | 3 | 4 |
+| Direct vs transitive breakdown | ✗ | ✓ (8 / 43) |
| Validated fix targets | ✗ | ✓ |
| Breaking change awareness | ✗ | ✓ |
| Parent chain identified for transitive issues | ✗ | ✓ |
@@ -49,9 +49,9 @@ Both tools were run against the same `package-lock.json` on the same machine.
**Why CVE Lite reports fewer findings — and why that is not a coverage gap:**
-`npm audit` counts advisories, not packages. A single vulnerable package with multiple advisories, or one that appears in several dependency paths, contributes multiple entries to the total. CVE Lite counts each unique vulnerable package once. That is why npm audit reports 36 here and CVE Lite reports 26.
+`npm audit` counts advisories and dependency paths, not unique packages. A single vulnerable package with multiple advisories, or one that appears in several dependency paths, contributes multiple entries to the total. CVE Lite counts each unique vulnerable package once. That is why npm audit reports 56 here and CVE Lite reports 51.
-This deduplication is intentional. The 4 critical findings npm audit reports for NestJS include multiple entries for the same underlying package under different advisory IDs. CVE Lite surfaces 1 critical — `form-data@2.3.3` via `request` — because that is the one unique critical-severity package in the lockfile. A developer acting on npm audit's 4-critical output would discover partway through that several of them point to the same fix.
+This deduplication is intentional. npm audit's 6-critical output includes overlapping entries for the same underlying packages under different advisory IDs. CVE Lite surfaces 4 critical unique packages in the lockfile. A developer acting on npm audit's raw count would discover partway through that several entries point to the same fix or to paths with no confident automated command.
CVE Lite does not suppress advisories. Every advisory ID that contributed to a finding is recorded in the `IDs` column of the full table output (`--verbose --all`). The deduplication is in the presentation layer, not in the detection layer.
@@ -65,28 +65,28 @@ To address all issues possible (including breaking changes), run:
npm audit fix --force
```
-CVE Lite generates:
+CVE Lite generates targeted commands at baseline, including:
```bash
npm install fastify@5.8.5
npm install mocha@12.0.0-beta-4
```
-On a project where 25 of 26 findings are transitive, `npm audit fix` is nearly useless. `npm audit fix --force` would attempt to resolve all breakages simultaneously, with no guidance on which upgrades are safe and which introduce API incompatibilities. CVE Lite orders the output — fix the one direct issue first, then the one confident parent upgrade, then reason about the rest.
+On a project where 43 of 51 findings are transitive, `npm audit fix` is nearly useless. `npm audit fix --force` would attempt to resolve all breakages simultaneously, with no guidance on which upgrades are safe and which introduce API incompatibilities. CVE Lite orders the output — fix the direct issue first, then the parent-chain upgrade, then reason about the remainder.
## Usage-aware triage
NestJS is a full monorepo with source available — most lockfile findings live in test runners, build utilities, and legacy toolchain paths that `--only-used` can filter out.
-Measured on revision `cee51af` with CVE Lite v1.25.0 · 2026-06-22:
+Measured on revision `cee51af` with CVE Lite v1.25.0 · 2026-06-24:
| Scan mode | Findings | Critical | High | Medium | Low | Direct | Transitive |
|---|---:|---:|---:|---:|---:|---:|---:|
| Lockfile baseline (`--verbose --all`) | 51 | 4 | 18 | 25 | 4 | 8 | 43 |
| `--only-used` (actionable subset) | 10 | 1 | 7 | 2 | 0 | 7 | 3 |
-**51 → 10** — an **80% reduction** in finding count. The `--usage` pass marked **41 of 51** findings as not statically imported (gulp toolchain, deprecated `request`/`form-data` chains, test-only paths). The `--only-used` subset surfaces runtime-facing packages like `fastify`, `@grpc/grpc-js`, and multiple `ws` versions still referenced from framework code.
+**51 → 10** — an **80% reduction** in finding count. The `--usage` pass marked **41 of 51** findings as not statically imported (gulp toolchain, deprecated `request`/`form-data` chains, test-only paths). The `--only-used` subset surfaces runtime-facing packages like `fastify`, `@grpc/grpc-js`, `@fastify/middie`, and multiple `ws` paths still referenced from framework code.
**Honest limits:** `--usage` uses static import analysis only. It can miss packages loaded dynamically, through build scripts, or via string-based requires. Treat `--only-used` as a triage accelerator — not proof that filtered findings are unreachable at runtime. See [CLI reference](../cli-reference.md).
@@ -94,26 +94,38 @@ Measured on revision `cee51af` with CVE Lite v1.25.0 · 2026-06-22:
## Before vs After
-The first two rows show the current lockfile baseline and its `--only-used` subset (2026-06-22 remeasurement). The rows below document the remediation passes from the original study (CVE Lite v1.6.0):
+The first two rows show the lockfile baseline and its `--only-used` subset. The rows below document two measured remediation passes applied locally on the pinned revision (CVE Lite v1.25.0 · 2026-06-24):
| Stage | Findings | Critical | High | Medium | Low | Direct | Transitive | Command groups |
|---|---:|---:|---:|---:|---:|---:|---:|---:|
-| Lockfile baseline (2026-06-22 remeasurement) | 51 | 4 | 18 | 25 | 4 | 8 | 43 | 9 |
+| Lockfile baseline | 51 | 4 | 18 | 25 | 4 | 8 | 43 | 9 |
| `--only-used` filter (same revision) | 10 | 1 | 7 | 2 | 0 | 7 | 3 | 3 |
-| Baseline (remediation study, v1.6.0) | 24 | 0 | 1 | 4 | 19 | 0 | 24 | 1 |
-| After measured pass | 21 | 0 | 1 | 3 | 17 | 0 | 21 | 0 |
+| After first pass (`fastify@5.8.5`) | 50 | 4 | 17 | 25 | 4 | 7 | 43 | 9 |
+| After second pass (`mocha@12.0.0-beta-4`) | 47 | 4 | 15 | 25 | 3 | 7 | 40 | 9 |
-The finding count dropped from 24 to 21. The generated command surface dropped from 1 group to 0 — meaning the scanner moved the repository into the deeper transitive-only category where the remaining work belongs to toolchain and parent-chain decisions rather than confident first-pass installs.
+The finding count dropped from 51 to 47 across two passes. The first pass cleared the one direct `fastify` finding. The second pass cleared three transitive high/low findings tied to the test toolchain (`glob`, `serialize-javascript`, and one `diff` path). Critical findings did not move — they require deeper parent-chain or replacement decisions, not a single copy-and-run install.
---
## Fix Journey
-In a mature monorepo or framework like NestJS, the most common mistake is expecting a single upgrade pass to clear the advisory list.
+Upgrading once is rarely enough in a mature JavaScript monorepo.
-At the study baseline, every finding was transitive. There were no direct packages to upgrade first. The scanner's job was to identify the one parent-chain move that was both safe and likely to reduce risk — `mocha@12.0.0-beta-4` to clear the `diff@7.0.0` transitive path — and to surface it separately from the longer tail of structural issues that were not first-pass candidates.
+### Pass 1 — direct fix (`fastify@5.8.5`)
-That single recommendation was real and useful, but executing it still required work. The first install attempt:
+At baseline, CVE Lite flagged `fastify` as a direct high-severity finding with a validated non-vulnerable target. That is the correct first move: it is in packages NestJS controls directly and does not depend on guessing a parent chain.
+
+```bash
+npm install --ignore-scripts --legacy-peer-deps fastify@5.8.5
+```
+
+Peer dependency friction is common in large workspaces. The install succeeded with `--legacy-peer-deps`. The scan dropped from **51 → 50** findings. The only change was clearing the direct `fastify` entry. Every critical finding and the bulk of transitive toolchain noise remained — as expected.
+
+### Pass 2 — parent-chain upgrade (`mocha@12.0.0-beta-4`)
+
+With the direct fix applied, the next productive move was a parent upgrade on the test runner chain. CVE Lite surfaced `mocha@12.0.0-beta-4` to address transitive `diff` paths pulled in through the existing Mocha dependency tree.
+
+The first install attempt:
```bash
npm install --ignore-scripts mocha@12.0.0-beta-4
@@ -125,29 +137,41 @@ failed because of peer dependency conflicts in the NestJS workspace. Retrying wi
npm install --ignore-scripts --legacy-peer-deps mocha@12.0.0-beta-4
```
-succeeded, and the scan dropped from 24 to 21 findings.
+succeeded, and the scan dropped from **50 → 47** findings.
+
+**What cleared in pass 2:**
+
+- `glob` (high, transitive)
+- `serialize-javascript` (high, transitive)
+- one `diff` (low, transitive) path
+
+**What remained after pass 2:**
+
+- `diff` (high) and another `diff` (low) path still present through other parents
+- all four critical findings (`@fastify/middie`, `form-data`, `protobufjs`, `shell-quote`)
+- fifteen high-severity findings including direct `@grpc/grpc-js`, `multer`, and `ws`
This is a common pattern in large JavaScript projects: the right upgrade is identifiable, but executing it runs into install-policy friction before the graph changes. Knowing what to upgrade is only half the problem. The other half is knowing that the install friction is incidental — not a signal that the upgrade was wrong.
-After the measured pass, the scanner generated no further copy-and-run command groups. That is meaningful: it means the repository moved out of the confident first-pass bucket and into the deeper category where remaining issues are tied to deprecated packages, toolchain dependencies, and replacement-level decisions. The scanner made that transition explicit rather than continuing to suggest commands that would not be actionable.
+After two passes, the scanner still generated nine command groups. That is meaningful: the repository remains in the deeper transitive-and-toolchain bucket where remaining work belongs to parent-chain decisions and replacement-level calls (`request`, legacy gulp tooling), not a zero-CVE framing.
---
## Why this matters
-NestJS is not a neglected project. It has active maintainers, frequent releases, and a large ecosystem. Yet a lockfile scan still surfaced 26 vulnerable packages, 25 of them transitive.
+NestJS is not a neglected project. It has active maintainers, frequent releases, and a large ecosystem. Yet a lockfile scan still surfaced 51 vulnerable packages, 43 of them transitive.
That is the real-world state of dependency graphs in large JavaScript frameworks: most of the risk is not in packages the project controls directly. It lives in the toolchain, in test runners, in build utilities, and in packages that have not had a breaking-change-free upgrade path for years.
-For a developer running a pre-release check, the operationally relevant question is not "how many advisories are there?" It is "what do I do right now, and what do I park?" CVE Lite answers that question in under 30 seconds: one direct upgrade, one parent chain worth attempting, and an explicit separation of the structural remainder from the confident first-pass work.
+For a developer running a pre-release check, the operationally relevant question is not "how many advisories are there?" It is "what do I do right now, and what do I park?" CVE Lite answers that question in under 30 seconds: one direct upgrade, one parent-chain upgrade worth attempting, and an explicit separation of the structural remainder from the confident first-pass work.
-That distinction matters especially in CI. A flat advisory count of 36 triggers pipeline gates and developer anxiety without telling anyone what to do. An ordered output with 1 validated direct fix, 1 parent upgrade, and a clear explanation of the transitive remainder gives a team enough to act on before shipping.
+That distinction matters especially in CI. A flat advisory count of 56 triggers pipeline gates and developer anxiety without telling anyone what to do. An ordered output with validated direct and parent-chain fixes, plus a clear explanation of the transitive remainder, gives a team enough to act on before shipping.
---
## Scan command
-Run from the NestJS repository root (full source clone required for `--usage` / `--only-used`):
+Run from a local NestJS clone checked out at the pinned revision (full source required for `--usage` / `--only-used`):
```bash
# Full lockfile graph
@@ -160,95 +184,78 @@ npx cve-lite-cli . --verbose --all --usage
npx cve-lite-cli . --verbose --all --only-used
```
-The remediation walkthrough was performed locally against that revision. Dependency changes were applied during the exercise, but they were not committed in the NestJS repository.
-
-Usage-aware counts were measured on revision `cee51af` on 2026-06-22.
+Every number in the Before vs After and usage-aware tables comes from live scans of revision `cee51af9118b68511e77e059f0578a3f0a3bcf0d` on 2026-06-24.
| Field | Value |
|---|---|
-| Baseline scan date (original study) | 2026-06-14 |
-| Usage-aware measurement date | 2026-06-22 |
-| CLI version (original study) | v1.6.0 |
-| CLI version (usage-aware passes) | v1.25.0 |
-| Lockfile findings (2026-06-22) | 51 |
-| `--only-used` findings (2026-06-22) | 10 |
+| Remediation measurement date | 2026-06-24 |
+| Usage-aware measurement date | 2026-06-24 |
+| CLI version | v1.25.0 |
+| Revision | `cee51af9118b68511e77e059f0578a3f0a3bcf0d` |
+| Lockfile findings (baseline) | 51 |
+| `--only-used` findings (baseline) | 10 |
+| After pass 1 (`fastify@5.8.5`) | 50 |
+| After pass 2 (`mocha@12.0.0-beta-4`) | 47 |
Reproduce from a local clone at the pinned revision:
```bash
-npm install
-npm run build
-node dist/index.js examples/nest --verbose --all
-node dist/index.js examples/nest --verbose --all --usage
-node dist/index.js examples/nest --verbose --all --only-used
+git clone https://github.com/nestjs/nest.git
+cd nest
+git checkout cee51af9118b68511e77e059f0578a3f0a3bcf0d
+
+# build CVE Lite from the cve-lite-cli repo, then:
+node /path/to/cve-lite-cli/dist/index.js . --verbose --all
+node /path/to/cve-lite-cli/dist/index.js . --verbose --all --usage
+node /path/to/cve-lite-cli/dist/index.js . --verbose --all --only-used
```
-## Remaining risk after the measured pass
+The remediation walkthrough was performed locally against that revision. Dependency changes were applied during the exercise, but they were not committed in the NestJS repository.
+
+Pass 1 and pass 2 installs used `--ignore-scripts --legacy-peer-deps` where the default npm resolver blocked peer-conflicting upgrades in the monorepo workspace.
-The post-pass lockfile still contained `21` findings:
+## Remaining risk after two passes
-- `0` critical
-- `1` high
-- `3` medium
-- `17` low
+The post-pass lockfile still contained **47** findings:
-The remaining high/medium work was dominated by transitive chains with no first-pass fix path:
+- **4** critical
+- **15** high
+- **25** medium
+- **3** low
-- `diff@2.2.3` via `gulp-diff`
-- `diff@4.0.2`
-- `form-data@2.3.3` via `request`
-- `tar@6.2.1`
+Critical and high work that did not clear in two passes:
-The lower-severity remainder included:
+- `@fastify/middie` (critical, direct)
+- `form-data` (critical, transitive via deprecated `request` chains)
+- `protobufjs` and `shell-quote` (critical, transitive toolchain paths)
+- direct `@grpc/grpc-js`, `multer`, and `ws` still flagged at pass 2
+- `diff` (high) still present through non-Mocha parents after one `diff` low path cleared
-- `postcss@7.0.39`
-- three vulnerable `brace-expansion` paths
-- two `braces` paths
-- two `micromatch` paths
-- two `js-yaml` paths
-- `lodash.template@3.6.2`
-- `request@2.88.2`
-- `qs@6.14.1` and `qs@6.5.3`
-- `tough-cookie@2.5.0`
-- `yaml@2.8.2`
-- `@tootallnate/once@1.1.2`
+The medium and low remainder is dominated by legacy gulp/braces/micromatch chains, multiple `brace-expansion` and `js-yaml` paths, deprecated `request`, and toolchain-only packages that `--only-used` filters out for runtime triage but that still exist in the lockfile graph.
-This is a useful stopping point for the public study. The scanner surfaced the one meaningful parent-package move, the move worked once peer-resolution friction was handled, and the remaining work is clearly in the deeper transitive-and-toolchain bucket.
+This is a useful stopping point for the public study. The scanner surfaced two meaningful first-pass moves, both worked once peer-resolution friction was handled, and the remaining work is clearly in the deeper transitive-and-toolchain bucket — not a zero-findings endpoint.
---
## Baseline findings
-Full vulnerable package list at scan time:
-
-| Package | Version | Severity | Relationship | Fix hint | Advisory IDs |
-|---|---|---|---|---|---|
-| form-data | 2.3.3 | critical | transitive | 2.5.4 | GHSA-fjxv-7rqg-78g4 |
-| fastify | 5.8.4 | high | direct | 5.8.5 | GHSA-247c-9743-5963 |
-| diff | 2.2.3 | high | transitive | 3.5.0 | GHSA-73rr-hh4g-fpgx, GHSA-h6ch-v84p-w6p9 |
-| braces | 1.8.5 | high | transitive | 3.0.3 | GHSA-grv7-fg5c-xmjg |
-| braces | 2.3.2 | high | transitive | 3.0.3 | GHSA-grv7-fg5c-xmjg |
-| lodash.template | 3.6.2 | high | transitive | 4.17.21 | GHSA-35jh-r3h4-6jhm |
-| glob | 10.4.5 | high | transitive | 10.5.0 | GHSA-5j98-mcp5-4vw2 |
-| serialize-javascript | 6.0.2 | high | transitive | 7.0.3 | GHSA-5c6j-r48x-rmvq, GHSA-qj8w-gfj5-8c6v |
-| tar | 6.2.1 | high | transitive | 7.5.3 | GHSA-34x7-hfp2-rc4v, GHSA-83g3-92jg-28c… |
-| postcss | 7.0.39 | medium | transitive | 8.4.31 | GHSA-7fh5-64p2-3v2j |
-| brace-expansion | 5.0.4 | medium | transitive | 1.1.13 | GHSA-f886-m6hf-6m8v |
-| brace-expansion | 1.1.11 | medium | transitive | 1.1.12 | GHSA-f886-m6hf-6m8v, GHSA-v6h2-p8h4-qcjw |
-| brace-expansion | 2.0.2 | medium | transitive | 1.1.13 | GHSA-f886-m6hf-6m8v |
-| follow-redirects | 1.15.11 | medium | transitive | 1.16.0 | GHSA-r4q5-vmmm-2653 |
-| micromatch | 3.1.10 | medium | transitive | 4.0.8 | GHSA-952p-6rrq-rcjv |
-| micromatch | 2.3.11 | medium | transitive | 4.0.8 | GHSA-952p-6rrq-rcjv |
-| js-yaml | 3.14.1 | medium | transitive | 3.14.2 | GHSA-mh29-5h37-fv8m |
-| js-yaml | 4.1.0 | medium | transitive | 3.14.2 | GHSA-mh29-5h37-fv8m |
-| request | 2.88.2 | medium | transitive | 3.0.0 | GHSA-p8p7-x288-28g6 |
-| qs | 6.5.3 | medium | transitive | 6.14.1 | GHSA-6rw7-vpxm-498p |
-| tough-cookie | 2.5.0 | medium | transitive | 4.1.3 | GHSA-72xf-g2v4-qvf3 |
-| yaml | 2.8.2 | medium | transitive | 1.10.3 | GHSA-48c2-rrv3-qjmp |
-| @tootallnate/once | 1.1.2 | low | transitive | 3.0.1 | GHSA-vpq2-c234-7xj6 |
-| diff | 4.0.2 | low | transitive | 3.5.1 | GHSA-73rr-hh4g-fpgx |
-| diff | 7.0.0 | low | transitive | 3.5.1 | GHSA-73rr-hh4g-fpgx |
-| qs | 6.14.1 | low | transitive | 6.14.2 | GHSA-w7fw-mjwx-w883 |
+Representative critical and high findings at baseline (full 51-package table from `--verbose --all`):
+
+| Package | Severity | Relationship | Notes |
+|---|---|---|---|
+| @fastify/middie | critical | direct | Framework middleware surface |
+| form-data | critical | transitive | Deprecated `request` chain |
+| protobufjs | critical | transitive | Toolchain / codegen path |
+| shell-quote | critical | transitive | Build tooling |
+| fastify | high | direct | Cleared in pass 1 (`5.8.5`) |
+| @grpc/grpc-js | high | direct | gRPC integration |
+| multer | high | direct | Upload middleware |
+| ws | high | direct / transitive | Multiple versions in graph |
+| glob | high | transitive | Cleared in pass 2 via Mocha chain |
+| serialize-javascript | high | transitive | Cleared in pass 2 via Mocha chain |
+| diff | high / low | transitive | Partially cleared in pass 2 |
+
+Run `npx cve-lite-cli . --verbose --all` against the pinned revision for the complete deduplicated package list with fix hints and advisory IDs.
## Want your project reviewed?