feat(html-reporter): show duplicate package versions in HTML report - #845
Conversation
When the same vulnerable package is installed at more than one version, the findings table lists each install separately and there is no single place that surfaces the duplication. This adds a "Duplicate package versions" card above the findings table: it groups vulnerable findings by package name, keeps the ones present at two or more versions, and shows each version with its severity and where it comes from (a direct dependency, or the parent it is pulled in through). The card is computed at render time from the findings already in the report, so there is no scan-logic change and report.json keeps its current shape. It is omitted when nothing is installed at multiple versions. Closes OWASP#235
sonukapoor
left a comment
There was a problem hiding this comment.
Really nice card @ELHart05 - reusing sev-badge, escaping everything, and hiding it when there are no duplicates is exactly right, and CI is green.
One correctness fix before it goes in: the origin line derives the parent from path[path.length - 2] (the immediate parent), but we already compute the top-level responsible dependency as finding.primaryParent and render it as "Parent: X" in the findings table just above. Using the immediate parent means the same finding can say "Parent: webpack" in the table but "via loader-utils" in this card, and for a hoisted 2-node transitive path (e.g. ['my-app','lodash']) it prints "via my-app" - naming the project itself as the parent. primaryParent is already on SerializedFinding, so it is a one-line swap that also drops the re-derivation:
function duplicateOrigin(finding: SerializedFinding): string {
if (finding.relationship === 'direct') return 'direct dependency';
if (finding.primaryParent) return `via ${finding.primaryParent}`;
return finding.relationship === 'transitive' ? 'transitive dependency' : 'unknown origin';
}Could you also add a test with a 4+ node path and a 2-node hoisted transitive path? The current fixtures all use 3-node paths where the immediate parent and primaryParent happen to coincide, so the difference does not show up.
Two optional nits if you are in there anyway: pluralize(duplicates.length, "package") is already imported and would replace the manual plural, and sorting the version rows (by severity then version) makes the output deterministic. Thanks again - genuinely useful addition once the origin is squared away.
Use finding.primaryParent for the duplicate-versions origin line instead of re-deriving the immediate parent from the dependency path. The immediate parent could disagree with the Parent line in the findings table, and on a hoisted 2-node transitive path it named the project itself. Also sort the version rows (severity then version) for deterministic output and reuse pluralize for the package count. Adds tests for a 4-node path and a hoisted 2-node transitive path, where the immediate parent and primaryParent differ.
|
Thanks for the careful review. You're right, the immediate parent was the wrong thing to show.
Pushed. Let me know if you'd like anything else. |
sonukapoor
left a comment
There was a problem hiding this comment.
This is exactly right now. primaryParent matches the table, and the two new tests (the 4-node deep path and the 2-node hoisted path) nail the specific cases that were silently broken before. Both optional nits are in too (pluralize and the deterministic sort). Thanks for working through this @ELHart05.
|
Merged - thank you @ELHart05! |
Summary
When the same vulnerable package is installed at more than one version, the findings table lists each install on its own row and nothing ties them together. This adds a "Duplicate package versions" card above the findings table so you can see, in one place, which vulnerable packages exist at multiple versions and where each version comes from.
Why this change
Issue #235: the report had no consolidated view of a vulnerable package that appears at several versions across the tree. That is exactly the case where a single upgrade or an override can collapse several findings at once, but today you have to spot it by scanning the table yourself.
What changed
Validation
npm run build(stricttsc) passes.User-facing impact
Does this change:
Notes
The consolidation is a view over existing finding data, so it does not add a field to report.json; the raw per-version findings are already there. If you would rather also expose a computed duplicates array in report.json for tooling, I am happy to add that as a follow-up.
Closes #235