mirror of
https://github.com/usestrix/strix.git
synced 2026-08-21 18:52:47 +02:00
feat(report): keep dependency findings from distinct manifests separate in dedupe
This commit is contained in:
@@ -183,6 +183,24 @@ def _dependency_identity(report: dict[str, Any]) -> tuple[str, str, str] | None:
|
|||||||
return cve, ecosystem, package_name
|
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:
|
def _report_cve(report: dict[str, Any]) -> str:
|
||||||
return str(report.get("cve") or "").strip().upper()
|
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
|
report_cve, report_ecosystem, report_package_name = report_identity
|
||||||
if (report_cve, report_package_name) != (cve, package_name):
|
if (report_cve, report_package_name) != (cve, package_name):
|
||||||
continue
|
continue
|
||||||
|
if _distinct_manifest_paths(candidate, report):
|
||||||
|
continue
|
||||||
if report_ecosystem == ecosystem:
|
if report_ecosystem == ecosystem:
|
||||||
return {
|
return {
|
||||||
"is_duplicate": True,
|
"is_duplicate": True,
|
||||||
|
|||||||
@@ -77,8 +77,10 @@ For each entry under `.Results[].Vulnerabilities[]` in `trivy-sca.json`, collect
|
|||||||
- `CVSS` — the published advisory base score
|
- `CVSS` — the published advisory base score
|
||||||
- `PrimaryURL` / references — to verify the advisory
|
- `PrimaryURL` / references — to verify the advisory
|
||||||
|
|
||||||
Deduplicate by `(CVE, PkgName, InstalledVersion)`. File one
|
Deduplicate by `(CVE, PkgName, Target)` — the same CVE/package observed in two
|
||||||
`create_dependency_report` per CVE — do not batch multiple CVEs into one report.
|
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
|
### Attribute transitive CVEs to the direct dependency
|
||||||
|
|
||||||
|
|||||||
@@ -653,6 +653,72 @@ async def test_dependency_dedupe_rejects_same_cve_package_identity() -> None:
|
|||||||
assert result["confidence"] == 1.0
|
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:
|
async def test_dependency_dedupe_detects_legacy_same_cve_package() -> None:
|
||||||
existing = [
|
existing = [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user