Skip to content

Commit fcc5cfa

Browse files
authored
Improve VulnerabilityMatcher (#783)
* Improve VulnerabilityMatcher
1 parent e8120e1 commit fcc5cfa

3 files changed

Lines changed: 41 additions & 9 deletions

File tree

doc/changes/unreleased.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
# Unreleased
22

33
## Summary
4+
5+
## Features
6+
7+
* #777: Improved VulnerabilityMatcher to handle packages with multiple vulnerabilities

exasol/toolbox/util/dependencies/track_vulnerabilities.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections import defaultdict
12
from inspect import cleandoc
23

34
from pydantic import (
@@ -10,12 +11,11 @@
1011

1112
class VulnerabilityMatcher:
1213
def __init__(self, current_vulnerabilities: list[Vulnerability]):
13-
# Dict of current vulnerabilities:
14-
# * keys: package names
15-
# * values: set of each vulnerability's references
16-
self._references = {
17-
v.package.name: set(v.references) for v in current_vulnerabilities
18-
}
14+
# Dictionary mapping package names to a unified set of all active
15+
# vulnerability references (IDs, CVEs, aliases) for that package.
16+
self._references = defaultdict(set)
17+
for v in current_vulnerabilities:
18+
self._references[v.package.name].update(v.references)
1919

2020
def is_resolved(self, vuln: Vulnerability) -> bool:
2121
"""
@@ -34,8 +34,8 @@ def is_resolved(self, vuln: Vulnerability) -> bool:
3434
vulnerability.
3535
"""
3636
refs = set(vuln.references)
37-
current = self._references.get(vuln.package.name, set())
38-
return not refs.intersection(current)
37+
current_refs = self._references.get(vuln.package.name, set())
38+
return refs.isdisjoint(current_refs)
3939

4040

4141
class DependenciesAudit(BaseModel):

test/unit/util/dependencies/track_vulnerabilities_test.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def test_changed_id_not_resolved(
3939
self, sample_vulnerability, flipped_id_vulnerability
4040
):
4141
"""
42-
Simulate a vulnerability to be still present, but it's ID having
42+
Simulate a vulnerability to be still present, but its ID having
4343
changed over time.
4444
4545
The test verifies that the vulnerability (using the original ID) is
@@ -56,6 +56,34 @@ def test_resolved(self, sample_vulnerability):
5656
matcher = VulnerabilityMatcher(current_vulnerabilities=[])
5757
assert matcher.is_resolved(vuln)
5858

59+
def test_no_resolution_same_package(self):
60+
"""
61+
Scenario: 'cryptography' has two vulnerabilities.
62+
One is resolved (removed from the current list), the other remains.
63+
"""
64+
pkg_data = {"name": "cryptography", "version": "46.0.6"}
65+
66+
vuln_1 = Vulnerability(
67+
package=pkg_data,
68+
id="GHSA-m959-cc7f-wv43",
69+
aliases=["CVE-2026-34073"],
70+
fix_versions=["46.0.6"],
71+
description="Dummy description",
72+
)
73+
74+
vuln_2 = Vulnerability(
75+
package=pkg_data,
76+
id="GHSA-p423-j2cm-9vmq",
77+
aliases=["CVE-2026-39892"],
78+
fix_versions=["46.0.7"],
79+
description="Dummy description",
80+
)
81+
82+
matcher = VulnerabilityMatcher(current_vulnerabilities=[vuln_1, vuln_2])
83+
84+
assert matcher.is_resolved(vuln_1) is False
85+
assert matcher.is_resolved(vuln_2) is False
86+
5987

6088
class TestDependenciesAudit:
6189
def test_no_vulnerabilities_for_previous_and_current(self):

0 commit comments

Comments
 (0)