@vyuhlabs/dxkit 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/CHANGELOG.md +223 -0
  2. package/README.md +21 -15
  3. package/dist/analyzers/bom/detailed.d.ts +26 -0
  4. package/dist/analyzers/bom/detailed.d.ts.map +1 -0
  5. package/dist/analyzers/bom/detailed.js +227 -0
  6. package/dist/analyzers/bom/detailed.js.map +1 -0
  7. package/dist/analyzers/bom/gather.d.ts +63 -0
  8. package/dist/analyzers/bom/gather.d.ts.map +1 -0
  9. package/dist/analyzers/bom/gather.js +229 -0
  10. package/dist/analyzers/bom/gather.js.map +1 -0
  11. package/dist/analyzers/bom/index.d.ts +23 -0
  12. package/dist/analyzers/bom/index.d.ts.map +1 -0
  13. package/dist/analyzers/bom/index.js +220 -0
  14. package/dist/analyzers/bom/index.js.map +1 -0
  15. package/dist/analyzers/bom/types.d.ts +91 -0
  16. package/dist/analyzers/bom/types.d.ts.map +1 -0
  17. package/dist/analyzers/bom/types.js +3 -0
  18. package/dist/analyzers/bom/types.js.map +1 -0
  19. package/dist/analyzers/licenses/detailed.d.ts +30 -0
  20. package/dist/analyzers/licenses/detailed.d.ts.map +1 -0
  21. package/dist/analyzers/licenses/detailed.js +194 -0
  22. package/dist/analyzers/licenses/detailed.js.map +1 -0
  23. package/dist/analyzers/licenses/gather.d.ts +15 -0
  24. package/dist/analyzers/licenses/gather.d.ts.map +1 -0
  25. package/dist/analyzers/licenses/gather.js +24 -0
  26. package/dist/analyzers/licenses/gather.js.map +1 -0
  27. package/dist/analyzers/licenses/index.d.ts +21 -0
  28. package/dist/analyzers/licenses/index.d.ts.map +1 -0
  29. package/dist/analyzers/licenses/index.js +146 -0
  30. package/dist/analyzers/licenses/index.js.map +1 -0
  31. package/dist/analyzers/licenses/types.d.ts +30 -0
  32. package/dist/analyzers/licenses/types.d.ts.map +1 -0
  33. package/dist/analyzers/licenses/types.js +13 -0
  34. package/dist/analyzers/licenses/types.js.map +1 -0
  35. package/dist/analyzers/security/detailed.d.ts +0 -3
  36. package/dist/analyzers/security/detailed.d.ts.map +1 -1
  37. package/dist/analyzers/security/detailed.js +37 -6
  38. package/dist/analyzers/security/detailed.js.map +1 -1
  39. package/dist/analyzers/security/gather.d.ts.map +1 -1
  40. package/dist/analyzers/security/gather.js +2 -0
  41. package/dist/analyzers/security/gather.js.map +1 -1
  42. package/dist/analyzers/security/index.d.ts.map +1 -1
  43. package/dist/analyzers/security/index.js +53 -14
  44. package/dist/analyzers/security/index.js.map +1 -1
  45. package/dist/analyzers/security/types.d.ts +5 -0
  46. package/dist/analyzers/security/types.d.ts.map +1 -1
  47. package/dist/analyzers/security/types.js +0 -3
  48. package/dist/analyzers/security/types.js.map +1 -1
  49. package/dist/analyzers/tools/osv.d.ts +49 -5
  50. package/dist/analyzers/tools/osv.d.ts.map +1 -1
  51. package/dist/analyzers/tools/osv.js +99 -17
  52. package/dist/analyzers/tools/osv.js.map +1 -1
  53. package/dist/analyzers/tools/runner.d.ts +11 -0
  54. package/dist/analyzers/tools/runner.d.ts.map +1 -1
  55. package/dist/analyzers/tools/runner.js +54 -0
  56. package/dist/analyzers/tools/runner.js.map +1 -1
  57. package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
  58. package/dist/analyzers/tools/tool-registry.js +77 -0
  59. package/dist/analyzers/tools/tool-registry.js.map +1 -1
  60. package/dist/analyzers/xlsx/bom.d.ts +27 -0
  61. package/dist/analyzers/xlsx/bom.d.ts.map +1 -0
  62. package/dist/analyzers/xlsx/bom.js +141 -0
  63. package/dist/analyzers/xlsx/bom.js.map +1 -0
  64. package/dist/analyzers/xlsx/index.d.ts +27 -0
  65. package/dist/analyzers/xlsx/index.d.ts.map +1 -0
  66. package/dist/analyzers/xlsx/index.js +92 -0
  67. package/dist/analyzers/xlsx/index.js.map +1 -0
  68. package/dist/analyzers/xlsx/licenses.d.ts +40 -0
  69. package/dist/analyzers/xlsx/licenses.d.ts.map +1 -0
  70. package/dist/analyzers/xlsx/licenses.js +140 -0
  71. package/dist/analyzers/xlsx/licenses.js.map +1 -0
  72. package/dist/cli.d.ts.map +1 -1
  73. package/dist/cli.js +178 -1
  74. package/dist/cli.js.map +1 -1
  75. package/dist/languages/capabilities/descriptors.d.ts +4 -1
  76. package/dist/languages/capabilities/descriptors.d.ts.map +1 -1
  77. package/dist/languages/capabilities/descriptors.js +20 -1
  78. package/dist/languages/capabilities/descriptors.js.map +1 -1
  79. package/dist/languages/capabilities/types.d.ts +60 -3
  80. package/dist/languages/capabilities/types.d.ts.map +1 -1
  81. package/dist/languages/csharp.d.ts +48 -0
  82. package/dist/languages/csharp.d.ts.map +1 -1
  83. package/dist/languages/csharp.js +418 -33
  84. package/dist/languages/csharp.js.map +1 -1
  85. package/dist/languages/go.d.ts +35 -0
  86. package/dist/languages/go.d.ts.map +1 -1
  87. package/dist/languages/go.js +367 -26
  88. package/dist/languages/go.js.map +1 -1
  89. package/dist/languages/python.d.ts +49 -0
  90. package/dist/languages/python.d.ts.map +1 -1
  91. package/dist/languages/python.js +372 -11
  92. package/dist/languages/python.js.map +1 -1
  93. package/dist/languages/rust.d.ts +28 -0
  94. package/dist/languages/rust.d.ts.map +1 -1
  95. package/dist/languages/rust.js +287 -28
  96. package/dist/languages/rust.js.map +1 -1
  97. package/dist/languages/types.d.ts +2 -1
  98. package/dist/languages/types.d.ts.map +1 -1
  99. package/dist/languages/typescript.d.ts +17 -0
  100. package/dist/languages/typescript.d.ts.map +1 -1
  101. package/dist/languages/typescript.js +358 -1
  102. package/dist/languages/typescript.js.map +1 -1
  103. package/package.json +7 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,229 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.2.0] - 2026-04-23
11
+
12
+ Minor release adding Snyk-style top-level dep attribution across every
13
+ language pack. Answers "which direct manifest dep do I upgrade to fix
14
+ the most advisories" alongside the existing per-leaf-package reporting.
15
+ Drop-in upgrade — additive `topLevelDep?: string[]` field, no schema
16
+ bump required.
17
+
18
+ ### Added — top-level dep attribution (Phase 10h.4)
19
+
20
+ - **`DepVulnFinding.topLevelDep?: string[]`** — per-advisory list of
21
+ root manifest entries (direct + dev deps) that transitively pull the
22
+ vulnerable package. Coarse name-level attribution (unions across
23
+ multiple parents when the package is reachable from more than one
24
+ top-level). Enables Snyk-style grouping: one advisory against
25
+ `tar@7.5.9` surfaces as "under `@loopback/cli`" rather than just
26
+ "tar has a CVE".
27
+
28
+ - **TypeScript pack** — BFS over `package-lock.json` (v2/v3) from
29
+ each root `dependencies` / `devDependencies` entry. Pure parser
30
+ `buildTsTopLevelDepIndex` unit-tested; benchmark on
31
+ `vyuhlabs-platform`: 71/71 findings attributed across 31 vulnerable
32
+ packages, `@loopback/cli` rollup = 29 advisories (matches Snyk UI).
33
+
34
+ - **Python pack** — BFS over `pip show` graph from packages with empty
35
+ `Required-by`. Pure parsers `parsePipShowOutput` +
36
+ `buildPyTopLevelDepIndex`. Venv detection now includes poetry
37
+ (`poetry env info --path`), pipenv (`pipenv --venv`), and
38
+ `$VIRTUAL_ENV` env var alongside the existing `.venv`/`venv` fast
39
+ path — poetry with default `virtualenvs.in-project = false` now
40
+ resolves.
41
+
42
+ - **Go pack** — BFS over `go mod graph` output, with `go.mod`'s
43
+ `// indirect` markers filtering the seed set so only user-declared
44
+ direct deps become top-levels. Pure parsers `parseGoModDirectDeps` +
45
+ `buildGoTopLevelDepIndex`.
46
+
47
+ - **Rust pack** — BFS over `cargo metadata --format-version 1` resolve
48
+ graph from each direct dep of `resolve.root`. Pure parser
49
+ `buildRustTopLevelDepIndex`; maps package ids → names, collapses
50
+ version variants.
51
+
52
+ - **C# pack** — **two-part expansion**. First,
53
+ `dotnet list package --vulnerable` now uses `--include-transitive`,
54
+ so transitive vulns (previously invisible) are surfaced. Second,
55
+ attribution comes from walking `obj/project.assets.json` — pure
56
+ parsers `parseProjectAssetsJson` + `buildCsharpTopLevelDepIndex`.
57
+ Direct findings carry self-attribution; transitive findings gain
58
+ `topLevelDep` from the assets-json graph. Degrades gracefully when
59
+ the lockfile is absent (user hasn't run `dotnet restore`).
60
+
61
+ ### Added — bom render surfaces top-level grouping
62
+
63
+ - **`BomReport.summary.byTopLevelDep: Record<string, BomTopLevelRollup>`**
64
+ where `BomTopLevelRollup = { advisoryCount, maxSeverity, packages[] }`.
65
+ Multi-parent advisories increment counters for each top-level they
66
+ list, matching Snyk's rollup semantics.
67
+
68
+ - **Markdown "Top-Level Dep Groups" section** in `bom-<date>.md` —
69
+ sorted by severity then advisory count. First row is the single
70
+ upgrade that resolves the most critical/highest-volume issues. Caps
71
+ at 30 top-levels, packages list truncated at 8 with "+N more".
72
+
73
+ - **Xlsx col 12 annotation** — each advisory line gains
74
+ ` via <parent>` (single top-level) or ` via <parent> (+N more)`
75
+ (multi-parent). Reviewer sees upgrade guidance directly in the
76
+ spreadsheet cell. No suffix when `topLevelDep` is unset.
77
+
78
+ ### Fixed — TS dep-vuln finding dedupe
79
+
80
+ - `gatherTsDepVulnsResult` now de-duplicates findings by
81
+ `(package, installedVersion, id)`. npm-audit inlines the same
82
+ advisory on every consumer's `via[]` across the vulnerability tree
83
+ (e.g. minimatch's ReDoS appearing on `@loopback/cli`, `glob-parent`,
84
+ `picomatch` simultaneously); the advisory-emission loop previously
85
+ pushed N copies of one logical finding. Platform count 94 → 71,
86
+ 14 distinct dupe pairs → 0. Pre-existing from 2.1.0; caught during
87
+ 10h.4 evaluation.
88
+
89
+ ### Notes
90
+
91
+ - Every pack degrades gracefully when its dep-graph source is missing:
92
+ TS without `package-lock.json`, Python without a venv, Go without
93
+ `go.mod`, Rust without `cargo metadata`, C# without
94
+ `obj/project.assets.json`. Findings still emit; `topLevelDep` stays
95
+ unset.
96
+
97
+ - Release validated against `vyuhlabs-platform` TypeScript benchmark.
98
+ Python/Go/Rust/C# packs exercised via fixture-based unit tests
99
+ (+53 new tests across the 4 non-TS language test files); real-world
100
+ validation lands with 2.3.0's cross-ecosystem benchmark fixtures.
101
+
102
+ ## [2.1.0] - 2026-04-23
103
+
104
+ Minor release adding two new analyzers and a shared XLSX converter.
105
+ Schema-compatible with 2.0.x for all pre-existing reports; introduces
106
+ two new report kinds (`licenses`, `bom`) and a schema v11 → v12 bump on
107
+ the detailed security report. Drop-in upgrade — no existing consumer
108
+ breaks.
109
+
110
+ ### Added — license inventory
111
+
112
+ - **`vyuh-dxkit licenses [path]`** — per-pack dependency license
113
+ inventory across TypeScript (license-checker-rseidelsohn), Python
114
+ (pip-licenses), Go (go-licenses), Rust (cargo-license), and C#
115
+ (nuget-license). Populates 11 fields per package (name, version,
116
+ description, license type, license text, source URL, supplier,
117
+ release date, etc.). Writes `.ai/reports/licenses-<date>.md`; with
118
+ `--detailed` also a risk-categorized JSON + markdown flagging
119
+ strong-copyleft, weak-copyleft, unknown-license, missing-attribution
120
+ packages. TypeScript provider normalizes source URLs through
121
+ `hosted-git-info` so `git+`/SCP/RFC-SSH variants collapse to canonical
122
+ HTTPS.
123
+ - **`vyuh-dxkit bom [path]`** — Bill of Materials joining `licenses`
124
+ with dependency vulnerabilities on `(package, version)`. One row per
125
+ installed package-version with license metadata (cols 1-9, 15 per
126
+ customer spec) AND per-package vulnerability rollup: max severity
127
+ (col 11), per-advisory list with CVSS scores (col 12), and derived
128
+ Tier-1 resolution proposal (col 13 — "Upgrade X to Y" when every
129
+ advisory has a fixedVersion, "Upgrade <parent> (transitive fix)" when
130
+ the fix is in a parent dep, "No fix available" otherwise). Detailed
131
+ mode (`--detailed`) emits a risk-review markdown with 6 triage
132
+ buckets (critical/high × no-fix/actionable, medium, low, license-
133
+ scanner-gap). `--xlsx` / `to-xlsx` produce the 15-column workbook
134
+ the customer's spreadsheet workflow expects, byte-identical headers.
135
+ - **`vyuh-dxkit to-xlsx <json>`** — shared converter. Reads any
136
+ licenses or bom detailed JSON and emits the canonical 15-col XLSX.
137
+ Lets downstream tooling stash JSON and render on demand without re-
138
+ running the analyzer.
139
+
140
+ ### Added — dependency-vulnerability per-advisory detail
141
+
142
+ - Every language pack's `depVulns` provider now populates
143
+ `DepVulnFinding[]` alongside the existing per-severity counts. Counts
144
+ remain per-package (for `vulnerabilities` command parity); findings
145
+ are per-advisory with id (GHSA/CVE/PYSEC/GO/RUSTSEC), installed +
146
+ fixed versions, CVSS score, aliases, summary, references, and tool
147
+ attribution. `gatherDepVulns` forwards findings into
148
+ `SecurityReport.summary.dependencies.findings` so the
149
+ `vulnerabilities --detailed` command renders per-advisory inventory
150
+ (previously: counts only).
151
+ - `DepVulnFinding` extended with nine optional fields for tier-layered
152
+ enrichment: `tool` (denormalized producer, renamed from unused
153
+ `source`), `cvssScore`, `upgradeAdvice`, `reachable`, `epssScore`,
154
+ `kev`, `riskScore`, `breakingUpgrade`, `aliases`, `summary`,
155
+ `references`. Per-pack Tier-1 providers populate what their native
156
+ tools emit; Tier-2/3/4 enrichment lands in later 10h sub-phases.
157
+ - Cross-pack OSV enhancement: `enrichOsv` (renamed from
158
+ `enrichSeverities`) now returns `{severity, cvssScore}` pairs, and
159
+ a new `resolveCvssScores` helper does batched alias-fallback
160
+ lookups. Fills the CVSS gap for GO-\* records (bulk of which carry
161
+ no severity but whose CVE aliases do) and PYSEC-\* records. TS pack
162
+ is a no-op via this path (npm-audit already ships CVSS at ~100%);
163
+ Python cvssScore coverage jumped from 0% → 100% on the fixture,
164
+ Go from 0% → 55% on vyuhlabs/Tickit.
165
+ - **Go pack parser fix** — `govulncheck -json` emits pretty-printed
166
+ multi-line JSON, not single-line ndjson. Previous `split('\n')`
167
+ parser silently failed on every invocation; new balanced-brace
168
+ `parseJsonStream` helper in `runner.ts` handles both shapes and
169
+ string-literal escapes. Reusable for any future tool that
170
+ pretty-prints.
171
+ - **Python pack manifest gating** — previously `pip-audit` ran with
172
+ no project context and silently scanned dxkit's own graphify-venv.
173
+ Now routes by manifest: `pip-audit <cwd>` for pyproject.toml/setup.py
174
+ projects, `pip-audit -r requirements.txt` for requirements projects,
175
+ null otherwise. Corrected platform audit: 97 → 94 dep vulns (3
176
+ phantom graphify-venv pip findings removed).
177
+
178
+ ### Added — tool registry
179
+
180
+ - TypeScript pack: `license-checker-rseidelsohn` (license inventory)
181
+ - Python pack: `pip-licenses` (license inventory)
182
+ - Go pack: `go-licenses` (license inventory, `go install golang.org/...`)
183
+ - Rust pack: `cargo-license` (license inventory, `cargo install`)
184
+ - C# pack: `nuget-license` (license inventory, `dotnet tool install`)
185
+
186
+ All bundled into per-pack provider commits so `findTool` + provider
187
+ invocation land together (CLAUDE.md rule 1).
188
+
189
+ ### Changed
190
+
191
+ - **Vulnerability report labelling** — Executive Summary now cleanly
192
+ separates "Code Findings" (your team patches source) from
193
+ "Dependency Vulnerabilities" (upgrade the dep) into two tables with
194
+ a combined total. Previously a single table labelled just "Severity
195
+ / Count" implied dep vulns were included, which they weren't. The
196
+ shallow report also now renders a worst-first per-advisory dep-vuln
197
+ table (50-row cap), so `vulnerabilities` without `--detailed` is
198
+ already actionable.
199
+ - **Security detailed schema** — bumps from `"11"` → `"12"` for the
200
+ new `summary.dependencies.findings: DepVulnFinding[]` field in the
201
+ JSON output. Additive — consumers reading just the old keys stay
202
+ compatible.
203
+ - **`DepVulnFinding.source` repurposed to `DepVulnFinding.tool`**.
204
+ The former `'osv.dev' | 'tool-default' | 'tool-reported'` enum was
205
+ dead code (declared, never written or read). Field now holds the
206
+ producer tool name (`npm-audit` / `pip-audit` / `govulncheck` /
207
+ `cargo-audit` / `dotnet-vulnerable`) so per-finding attribution
208
+ survives merges across multiple providers.
209
+
210
+ ### Fixed
211
+
212
+ - **npm-audit `fixAvailable` misinterpretation** — `fix.name` is the
213
+ top-level upgrade target, not the vulnerable package itself. Prior
214
+ code blindly assigned `fix.version` as `fixedVersion` on every
215
+ advisory, producing absurd output like "uuid@13.0.0 → Upgrade to
216
+ 3.2.1". Now branches on `fix.name === pkgName`: direct fix sets
217
+ `fixedVersion`; transitive fix sets `upgradeAdvice` with parent-
218
+ package guidance ("Upgrade @loopback/cli to 5.0.0 [major]
219
+ (transitive fix)"). Surfaced ~20 false positives on platform audit
220
+ covering uuid/octokit/tar/undici/underscore.
221
+ - **bom xlsx col 11/12/13 fill on non-vulnerable rows** — previously
222
+ blank, creating "scanned-clean vs not-scanned" ambiguity. Now fills
223
+ "None" / "No action required" so reviewers see at a glance which
224
+ rows dxkit actually processed.
225
+
226
+ ### Runtime dependencies added
227
+
228
+ - `exceljs ^4.4.0` — XLSX writer. Adds ~80 transitive deps (bumps
229
+ dxkit's own license-checker count 242 → ~325).
230
+ - `hosted-git-info ^9.0.2` + `@types/hosted-git-info ^3.0.5` — URL
231
+ canonicalisation (source URL column of licenses/bom).
232
+
10
233
  ## [2.0.1] - 2026-04-22
11
234
 
12
235
  Patch release following the 2.0.0 smoke-test. No API or schema changes —
package/README.md CHANGED
@@ -32,25 +32,31 @@ The two modes are complementary. The analyzers run anywhere; the scaffolder writ
32
32
 
33
33
  ## Analyzer CLI (`vyuh-dxkit <command>`)
34
34
 
35
- Five deterministic analyzers. Each emits a markdown report to `.ai/reports/` and optional structured JSON.
35
+ Seven deterministic analyzers. Each emits a markdown report to `.ai/reports/` and optional structured JSON.
36
36
 
37
- | Command | What it does | Runtime | Output |
38
- | ----------------- | --------------------------------------------------------------- | ------- | ------------------------------------------ |
39
- | `health` | 6-dimension score (Testing, Quality, Docs, Security, Maint, DX) | 10–20s | `.ai/reports/health-audit-<date>.md` |
40
- | `vulnerabilities` | gitleaks + semgrep + `npm audit` / `pip-audit` | 5–30s | `.ai/reports/vulnerability-scan-<date>.md` |
41
- | `test-gaps` | Coverage artifact → import-graph → filename (strongest wins) | <1s | `.ai/reports/test-gaps-<date>.md` |
42
- | `quality` | Slop score + jscpd duplication + eslint/ruff + hygiene | 5–15s | `.ai/reports/quality-review-<date>.md` |
43
- | `dev-report` | Commits, contributors, hot files, velocity, conventional % | <1s | `.ai/reports/developer-report-<date>.md` |
37
+ | Command | What it does | Runtime | Output |
38
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------ |
39
+ | `health` | 6-dimension score (Testing, Quality, Docs, Security, Maint, DX) | 10–20s | `.ai/reports/health-audit-<date>.md` |
40
+ | `vulnerabilities` | gitleaks + semgrep + per-pack dep-audit (per-advisory detail in `--detailed`) | 5–30s | `.ai/reports/vulnerability-scan-<date>.md` |
41
+ | `test-gaps` | Coverage artifact → import-graph → filename (strongest wins) | <1s | `.ai/reports/test-gaps-<date>.md` |
42
+ | `quality` | Slop score + jscpd duplication + eslint/ruff + hygiene | 5–15s | `.ai/reports/quality-review-<date>.md` |
43
+ | `dev-report` | Commits, contributors, hot files, velocity, conventional % | <1s | `.ai/reports/developer-report-<date>.md` |
44
+ | `licenses` | Dependency license inventory across every active pack (TS/Python/Go/Rust/C#) | 5–20s | `.ai/reports/licenses-<date>.md` |
45
+ | `bom` | **Bill of Materials** — joins licenses + vulnerabilities per package, 15-col XLSX; groups advisories by top-level manifest dep (Snyk-style) | 10–40s | `.ai/reports/bom-<date>.{md,xlsx}` |
46
+
47
+ Plus a converter: `vyuh-dxkit to-xlsx <json-file>` renders any `licenses` or `bom` detailed JSON as the canonical 15-column XLSX.
44
48
 
45
49
  ### Flags (apply to all analyzer commands)
46
50
 
47
- | Flag | Effect |
48
- | ---------------- | ------------------------------------------------------------------------------------- |
49
- | `--detailed` | Also writes `<name>-detailed.md` + `.json` with evidence + ranked remediation actions |
50
- | `--json` | Emit pure JSON on stdout. Logs go to stderr so pipes stay clean |
51
- | `--verbose` | Print per-tool timing to stderr |
52
- | `--no-save` | Skip writing markdown; useful with `--json` |
53
- | `--since <date>` | (`dev-report` only) Analyze commits on or after `YYYY-MM-DD` |
51
+ | Flag | Effect |
52
+ | ---------------- | -------------------------------------------------------------------------------------- |
53
+ | `--detailed` | Also writes `<name>-detailed.md` + `.json` with evidence + ranked remediation actions |
54
+ | `--json` | Emit pure JSON on stdout. Logs go to stderr so pipes stay clean |
55
+ | `--verbose` | Print per-tool timing to stderr |
56
+ | `--no-save` | Skip writing markdown; useful with `--json` |
57
+ | `--xlsx` | (`licenses`, `bom` only) Also write 15-col `.xlsx` drop-in for spreadsheet workflows |
58
+ | `-o <file>` | (`licenses`, `bom`, `to-xlsx`) Override output path for xlsx / converted file |
59
+ | `--since <date>` | (`dev-report` only) Analyze commits on or after `YYYY-MM-DD` |
54
60
 
55
61
  ### Detailed mode — evidence + ranked fixes
56
62
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * BOM analyzer — detailed report.
3
+ *
4
+ * Extends the base BomReport with risk-categorized buckets so the
5
+ * reader can triage the inventory by what needs action first.
6
+ * Mirrors the licenses/detailed pattern (10h.2.2) but the categories
7
+ * are vuln-driven rather than license-driven, since BOM exists
8
+ * primarily to surface upgrade work.
9
+ */
10
+ import type { BomEntry, BomReport, BomSeverity } from './types';
11
+ export interface BomRiskCategory {
12
+ /** Severity tier for ordering. */
13
+ readonly priority: BomSeverity;
14
+ /** Stable category key — for programmatic filtering / dashboards. */
15
+ readonly id: 'critical-no-fix' | 'critical-actionable' | 'high-no-fix' | 'high-actionable' | 'medium-vulns' | 'low-vulns' | 'vuln-only-license-gap';
16
+ readonly title: string;
17
+ readonly rationale: string;
18
+ readonly recommendation: string;
19
+ readonly affected: ReadonlyArray<BomEntry>;
20
+ }
21
+ export interface BomDetailedReport extends BomReport {
22
+ riskCategories: ReadonlyArray<BomRiskCategory>;
23
+ }
24
+ export declare function buildBomDetailed(report: BomReport): BomDetailedReport;
25
+ export declare function formatBomDetailedMarkdown(detailed: BomDetailedReport, elapsed: string): string;
26
+ //# sourceMappingURL=detailed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detailed.d.ts","sourceRoot":"","sources":["../../../src/analyzers/bom/detailed.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,EACP,iBAAiB,GACjB,qBAAqB,GACrB,aAAa,GACb,iBAAiB,GACjB,cAAc,GACd,WAAW,GACX,uBAAuB,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,iBAAkB,SAAQ,SAAS;IAClD,cAAc,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;CAChD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,iBAAiB,CA8HrE;AASD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CA6G9F"}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ /**
3
+ * BOM analyzer — detailed report.
4
+ *
5
+ * Extends the base BomReport with risk-categorized buckets so the
6
+ * reader can triage the inventory by what needs action first.
7
+ * Mirrors the licenses/detailed pattern (10h.2.2) but the categories
8
+ * are vuln-driven rather than license-driven, since BOM exists
9
+ * primarily to surface upgrade work.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.buildBomDetailed = buildBomDetailed;
13
+ exports.formatBomDetailedMarkdown = formatBomDetailedMarkdown;
14
+ function buildBomDetailed(report) {
15
+ const categories = [];
16
+ // Buckets keyed by (severity, has-fix). The `actionable` axis matters
17
+ // because "critical with no fix" is qualitatively different from
18
+ // "critical with a one-line upgrade" — same severity, different
19
+ // remediation cost.
20
+ const buckets = new Map();
21
+ for (const e of report.entries) {
22
+ if (!e.maxSeverity)
23
+ continue;
24
+ const actionable = e.upgradeAdvice.startsWith('PROPOSAL:');
25
+ const key = `${e.maxSeverity}|${actionable ? 'fix' : 'nofix'}`;
26
+ const arr = buckets.get(key) ?? [];
27
+ arr.push(e);
28
+ buckets.set(key, arr);
29
+ }
30
+ const criticalNoFix = buckets.get('critical|nofix') ?? [];
31
+ if (criticalNoFix.length > 0) {
32
+ categories.push({
33
+ priority: 'critical',
34
+ id: 'critical-no-fix',
35
+ title: 'Critical vulns with no published fix',
36
+ rationale: 'These packages have one or more critical-severity advisories ' +
37
+ 'with no upstream fix version. Upgrading the dep does not resolve ' +
38
+ 'them — replacement or vendor pressure is the only remediation path.',
39
+ recommendation: 'Evaluate replacement packages, contact maintainers, or accept ' +
40
+ 'and document the risk. Do not ship to production untreated.',
41
+ affected: criticalNoFix,
42
+ });
43
+ }
44
+ const criticalActionable = buckets.get('critical|fix') ?? [];
45
+ if (criticalActionable.length > 0) {
46
+ categories.push({
47
+ priority: 'critical',
48
+ id: 'critical-actionable',
49
+ title: 'Critical vulns with an upgrade path',
50
+ rationale: 'These packages have critical advisories AND a published fixed ' +
51
+ 'version. The upgrade is the highest-leverage security action ' +
52
+ 'available for this codebase right now.',
53
+ recommendation: 'Schedule the upgrades immediately. Prefer the Tier-1 PROPOSAL ' +
54
+ 'target unless a major-version bump introduces compat risk.',
55
+ affected: criticalActionable,
56
+ });
57
+ }
58
+ const highNoFix = buckets.get('high|nofix') ?? [];
59
+ if (highNoFix.length > 0) {
60
+ categories.push({
61
+ priority: 'high',
62
+ id: 'high-no-fix',
63
+ title: 'High-severity vulns with no published fix',
64
+ rationale: 'High-severity advisories without an upstream fix. Less urgent ' +
65
+ 'than critical-no-fix but still warrants a documented decision.',
66
+ recommendation: 'Plan replacement evaluation alongside critical-no-fix items. ' +
67
+ 'Track the upstream advisory for fix availability.',
68
+ affected: highNoFix,
69
+ });
70
+ }
71
+ const highActionable = buckets.get('high|fix') ?? [];
72
+ if (highActionable.length > 0) {
73
+ categories.push({
74
+ priority: 'high',
75
+ id: 'high-actionable',
76
+ title: 'High-severity vulns with an upgrade path',
77
+ rationale: 'High-severity advisories with a published fixed version.',
78
+ recommendation: 'Schedule upgrades within the next sprint.',
79
+ affected: highActionable,
80
+ });
81
+ }
82
+ const mediumVulns = [
83
+ ...(buckets.get('medium|fix') ?? []),
84
+ ...(buckets.get('medium|nofix') ?? []),
85
+ ];
86
+ if (mediumVulns.length > 0) {
87
+ categories.push({
88
+ priority: 'medium',
89
+ id: 'medium-vulns',
90
+ title: 'Medium-severity vulnerabilities',
91
+ rationale: 'Medium-severity advisories. Plan into routine dep maintenance.',
92
+ recommendation: 'Batch into a recurring "dependency upgrade week" cadence. ' +
93
+ 'Watch for severity bumps — OSV/CVSS revisions can promote ' +
94
+ 'these into the high bucket.',
95
+ affected: mediumVulns,
96
+ });
97
+ }
98
+ const lowVulns = [...(buckets.get('low|fix') ?? []), ...(buckets.get('low|nofix') ?? [])];
99
+ if (lowVulns.length > 0) {
100
+ categories.push({
101
+ priority: 'low',
102
+ id: 'low-vulns',
103
+ title: 'Low-severity vulnerabilities',
104
+ rationale: 'Low-severity advisories. Track but not blocking.',
105
+ recommendation: 'Bundle into routine maintenance; no urgency.',
106
+ affected: lowVulns,
107
+ });
108
+ }
109
+ const vulnOnly = report.entries.filter((e) => !e.joinedFromBoth);
110
+ if (vulnOnly.length > 0) {
111
+ categories.push({
112
+ priority: 'medium',
113
+ id: 'vuln-only-license-gap',
114
+ title: 'License-scanner gap (vuln-only entries)',
115
+ rationale: 'These packages were reported by a vulnerability scanner but ' +
116
+ 'not by the license scanner. The license is shown as UNKNOWN; ' +
117
+ 'the most likely cause is a workspace / sub-package the license ' +
118
+ 'tool did not traverse.',
119
+ recommendation: 'Verify the licenses manually before shipping. If the package ' +
120
+ 'lives in a workspace, surface it in the root manifest or use ' +
121
+ 'the workspace-aware license tool flag.',
122
+ affected: vulnOnly,
123
+ });
124
+ }
125
+ return { ...report, riskCategories: categories };
126
+ }
127
+ const SEV_LABEL = {
128
+ critical: 'CRITICAL',
129
+ high: 'HIGH',
130
+ medium: 'MEDIUM',
131
+ low: 'LOW',
132
+ };
133
+ function formatBomDetailedMarkdown(detailed, elapsed) {
134
+ const L = [];
135
+ const s = detailed.summary;
136
+ L.push('# Bill of Materials (BOM) — Detailed');
137
+ L.push('');
138
+ L.push(`**Date:** ${detailed.analyzedAt.slice(0, 10)}`);
139
+ L.push(`**Repository:** ${detailed.repo}`);
140
+ L.push(`**Branch:** ${detailed.branch} (${detailed.commitSha})`);
141
+ L.push(`**Schema version:** ${detailed.schemaVersion}`);
142
+ L.push('');
143
+ L.push('---');
144
+ L.push('');
145
+ L.push('## Summary');
146
+ L.push('');
147
+ L.push(`- **Total packages:** ${s.totalPackages}`);
148
+ L.push(`- **Vulnerable packages:** ${s.vulnerablePackages}`);
149
+ L.push(`- **Total advisories:** ${s.totalAdvisories} (one package can have many)`);
150
+ L.push(`- **Actionable upgrades (Tier-1 proposals):** ${s.actionableVulns}`);
151
+ if (s.vulnOnlyPackages > 0) {
152
+ L.push(`- **Vuln-only entries (license gap):** ${s.vulnOnlyPackages}`);
153
+ }
154
+ L.push('');
155
+ L.push(`> Reconciles with \`vyuh-dxkit vulnerabilities\`: that command counts ` +
156
+ `per-advisory (${s.totalAdvisories}); bom collapses per-package ` +
157
+ `(${s.vulnerablePackages}) so each xlsx row is one upgrade decision.`);
158
+ L.push('');
159
+ L.push('---');
160
+ L.push('');
161
+ L.push('## Risk Review');
162
+ L.push('');
163
+ if (detailed.riskCategories.length === 0) {
164
+ L.push('No vulnerable packages — nothing to triage.');
165
+ }
166
+ else {
167
+ for (const cat of detailed.riskCategories) {
168
+ L.push(`### ${SEV_LABEL[cat.priority]}: ${cat.title} (${cat.affected.length})`);
169
+ L.push('');
170
+ L.push(`**Why this matters:** ${cat.rationale}`);
171
+ L.push('');
172
+ L.push(`**Recommendation:** ${cat.recommendation}`);
173
+ L.push('');
174
+ L.push('| Package@Version | License | # Vulns | Resolution |');
175
+ L.push('|-----------------|---------|--------:|------------|');
176
+ for (const e of cat.affected) {
177
+ const advice = e.upgradeAdvice.replace(/\|/g, '\\|');
178
+ L.push(`| \`${e.package}@${e.version}\` | ${e.licenseType} | ${e.vulns.length} | ${advice} |`);
179
+ }
180
+ L.push('');
181
+ }
182
+ }
183
+ L.push('---');
184
+ L.push('');
185
+ // Per-advisory inventory across every vuln in every entry. Lets the
186
+ // reader drill from "this package has 3 vulns" up to the actual CVE
187
+ // ids without leaving the markdown.
188
+ L.push('## Per-Advisory Inventory');
189
+ L.push('');
190
+ const SEV_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
191
+ const flat = detailed.entries
192
+ .filter((e) => e.vulns.length > 0)
193
+ .flatMap((e) => e.vulns.map((v) => ({
194
+ severity: v.severity,
195
+ package: e.package,
196
+ version: e.version,
197
+ id: v.id,
198
+ fixedVersion: v.fixedVersion,
199
+ cvssScore: v.cvssScore,
200
+ tool: v.tool,
201
+ summary: v.summary,
202
+ })))
203
+ .sort((a, b) => SEV_RANK[a.severity] - SEV_RANK[b.severity] || a.package.localeCompare(b.package));
204
+ if (flat.length === 0) {
205
+ L.push('No vulnerabilities — empty inventory.');
206
+ }
207
+ else {
208
+ L.push(`${flat.length} advisories total.`);
209
+ L.push('');
210
+ L.push('| Severity | Package@Version | ID | Fix | CVSS | Tool | Summary |');
211
+ L.push('|----------|-----------------|----|-----|-----:|------|---------|');
212
+ for (const v of flat) {
213
+ const summary = (v.summary || '').replace(/\|/g, '\\|').replace(/\n/g, ' ').slice(0, 100);
214
+ const cvss = v.cvssScore !== undefined ? v.cvssScore.toFixed(1) : '—';
215
+ L.push(`| ${SEV_LABEL[v.severity]} | \`${v.package}@${v.version}\` | \`${v.id}\` | ${v.fixedVersion ?? '—'} | ${cvss} | ${v.tool} | ${summary} |`);
216
+ }
217
+ }
218
+ L.push('');
219
+ L.push('---');
220
+ L.push('');
221
+ L.push(`**Tools used:** ${detailed.toolsUsed.join(', ') || '(none)'}`);
222
+ L.push(`**Analysis time:** ${elapsed}s`);
223
+ L.push('');
224
+ L.push('*Generated by [VyuhLabs DXKit](https://www.npmjs.com/package/@vyuhlabs/dxkit) — detailed mode*');
225
+ return L.join('\n');
226
+ }
227
+ //# sourceMappingURL=detailed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detailed.js","sourceRoot":"","sources":["../../../src/analyzers/bom/detailed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * BOM analyzer data-gather. Joins LICENSES + DEP_VULNS via the
3
+ * existing capability gather functions — no new tool invocation logic
4
+ * (CLAUDE.md rule 2). The gather just calls each, then walks both
5
+ * result sets to build a per-package join keyed by `package@version`.
6
+ */
7
+ import type { DepVulnFinding } from '../../languages/capabilities/types';
8
+ import type { BomEntry, BomTopLevelRollup } from './types';
9
+ /**
10
+ * Compare two version strings as semver triples. Strips a leading
11
+ * 'v' (Go-pack convention) and compares dot-separated numeric
12
+ * components. Falls back to lexicographic comparison when either
13
+ * input isn't a parseable triple — preserves a deterministic
14
+ * ordering for non-semver versions (e.g. cargo's "0.10.55+echo.1").
15
+ */
16
+ export declare function compareSemver(a: string, b: string): number;
17
+ /** Highest version in `versions` per compareSemver. Returns the input
18
+ * unchanged when only one version is supplied. */
19
+ export declare function maxSemver(versions: string[]): string;
20
+ /**
21
+ * Tier-1 resolution proposal derived purely from `fixedVersion`
22
+ * data shipped by each pack's depVulns provider. Per checkpoint §9:
23
+ * - no vulns → empty
24
+ * - every vuln has fixedVersion → "PROPOSAL: Upgrade to <maxFix> (resolves N)"
25
+ * - some vuln lacks fixedVersion → "No fix available — evaluate replacement"
26
+ *
27
+ * Higher tiers (10h.4 Snyk, 10h.6 osv-scanner fix) populate
28
+ * `upgradeAdvice` directly on each finding; the bom render layer
29
+ * picks the richest available value per package.
30
+ */
31
+ export declare function deriveTier1Resolution(vulns: DepVulnFinding[]): string;
32
+ /**
33
+ * Build the per-top-level-dep rollup from a flat list of entries.
34
+ * Walks every advisory under every entry; increments the counter for
35
+ * each top-level the advisory lists (a vuln reachable from two
36
+ * top-levels increments both, matching Snyk's grouping semantics).
37
+ *
38
+ * Entries with no topLevelDep attribution contribute nothing — the
39
+ * rollup is best-effort based on what each pack populates. Pure
40
+ * function; unit-testable.
41
+ */
42
+ export declare function buildByTopLevelDep(entries: BomEntry[]): Record<string, BomTopLevelRollup>;
43
+ /**
44
+ * Join LICENSES + DEP_VULNS by (package, version). Strategy:
45
+ * - Primary index: package@version. Both LicenseFinding and
46
+ * DepVulnFinding usually carry both — match exact.
47
+ * - Fallback: package only. DepVulnFindings without
48
+ * installedVersion fall back to package-name match against
49
+ * the licenses list (joins to the single matching version
50
+ * when unambiguous, all matching versions when not).
51
+ *
52
+ * License-only packages emit BomEntry with empty `vulns`. Vuln-only
53
+ * packages (rare — license scanner gap) emit a row with empty
54
+ * license fields and `joinedFromBoth: false` so the detailed report
55
+ * can flag the gap.
56
+ */
57
+ export interface BomGatherResult {
58
+ entries: BomEntry[];
59
+ toolsUsed: string[];
60
+ toolsUnavailable: string[];
61
+ }
62
+ export declare function gatherBomEntries(cwd: string): Promise<BomGatherResult>;
63
+ //# sourceMappingURL=gather.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gather.d.ts","sourceRoot":"","sources":["../../../src/analyzers/bom/gather.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,oCAAoC,CAAC;AACzF,OAAO,KAAK,EAAE,QAAQ,EAAe,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAIxE;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAgB1D;AAED;mDACmD;AACnD,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,MAAM,CAQrE;AAYD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAkCzF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAiF5E"}