feat(report): keep dependency findings from distinct manifests separate in dedupe

This commit is contained in:
Ahmed Allam
2026-08-06 02:20:46 +03:00
committed by Ahmed Allam
parent 72cb15a20a
commit b69af37cb2
3 changed files with 90 additions and 2 deletions
+20
View File
@@ -183,6 +183,24 @@ def _dependency_identity(report: dict[str, Any]) -> tuple[str, str, str] | None:
return cve, ecosystem, package_name
def _manifest_path(report: dict[str, Any]) -> str:
metadata = report.get("dependency_metadata")
if not isinstance(metadata, dict):
return ""
return str(metadata.get("manifest_path") or "").strip()
def _distinct_manifest_paths(candidate: dict[str, Any], report: dict[str, Any]) -> bool:
"""Same CVE/package observed in two different manifests is two findings.
Only applies when both sides carry a manifest_path; a missing path keeps
the legacy CVE/package/ecosystem identity.
"""
candidate_path = _manifest_path(candidate)
report_path = _manifest_path(report)
return bool(candidate_path and report_path and candidate_path != report_path)
def _report_cve(report: dict[str, Any]) -> str:
return str(report.get("cve") or "").strip().upper()
@@ -228,6 +246,8 @@ def _check_dependency_duplicate(
report_cve, report_ecosystem, report_package_name = report_identity
if (report_cve, report_package_name) != (cve, package_name):
continue
if _distinct_manifest_paths(candidate, report):
continue
if report_ecosystem == ecosystem:
return {
"is_duplicate": True,
@@ -77,8 +77,10 @@ For each entry under `.Results[].Vulnerabilities[]` in `trivy-sca.json`, collect
- `CVSS` — the published advisory base score
- `PrimaryURL` / references — to verify the advisory
Deduplicate by `(CVE, PkgName, InstalledVersion)`. File one
`create_dependency_report` per CVE — do not batch multiple CVEs into one report.
Deduplicate by `(CVE, PkgName, Target)` — the same CVE/package observed in two
different manifests (e.g. two workspaces of a monorepo) is two findings, one
per manifest. File one `create_dependency_report` per CVE — do not batch
multiple CVEs into one report.
### Attribute transitive CVEs to the direct dependency
+66
View File
@@ -653,6 +653,72 @@ async def test_dependency_dedupe_rejects_same_cve_package_identity() -> None:
assert result["confidence"] == 1.0
async def test_dependency_dedupe_keeps_findings_from_distinct_manifests() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in sample",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/api/package-lock.json",
},
}
]
candidate = {
"title": "CVE-2024-0001 in sample (web)",
"description": "Same advisory observed in a second workspace.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/web/package-lock.json",
},
}
result = await check_duplicate(candidate, existing)
assert result["is_duplicate"] is False
assert result["confidence"] == 1.0
async def test_dependency_dedupe_rejects_same_manifest_identity() -> None:
existing = [
{
"id": "vuln-0001",
"title": "CVE-2024-0001 in sample",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/api/package-lock.json",
},
}
]
candidate = {
"title": "CVE-2024-0001 in sample re-reported",
"description": "Same advisory, same manifest.",
"target": "repo/package.json",
"cve": "CVE-2024-0001",
"dependency_metadata": {
"package_name": "sample",
"installed_version": "1.0.0",
"package_ecosystem": "npm",
"manifest_path": "services/api/package-lock.json",
},
}
result = await check_duplicate(candidate, existing)
assert result["is_duplicate"] is True
assert result["duplicate_id"] == "vuln-0001"
async def test_dependency_dedupe_detects_legacy_same_cve_package() -> None:
existing = [
{