Skip to content

Commit 5db9eef

Browse files
committed
Add ResolvedVulnerabilities to detect if vulnerability has been
resolved from previous work to the present
1 parent f732bc1 commit 5db9eef

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from pydantic import (
2+
BaseModel,
3+
ConfigDict,
4+
)
5+
6+
from exasol.toolbox.util.dependencies.audit import Vulnerability
7+
8+
9+
class ResolvedVulnerabilities(BaseModel):
10+
model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True)
11+
12+
previous_vulnerabilities: list[Vulnerability]
13+
current_vulnerabilities: list[Vulnerability]
14+
15+
def _is_resolved(self, previous_vuln: Vulnerability):
16+
"""
17+
Detects if a vulnerability has been resolved.
18+
19+
A vulnerability is said to be resolved when it cannot be found
20+
in the `current_vulnerabilities`. In order to see if a vulnerability
21+
is still present, its id and aliases are compared to values in the
22+
`current_vulnerabilities`. While one could hope that the IDs
23+
are the same, it's possible between pip-audit versions that
24+
there are differences.
25+
"""
26+
previous_vuln_set = {previous_vuln.id, *previous_vuln.aliases}
27+
for current_vuln in self.current_vulnerabilities:
28+
if previous_vuln.name == current_vuln.name:
29+
current_vuln_id_set = {current_vuln.id, *current_vuln.aliases}
30+
if previous_vuln_set.intersection(current_vuln_id_set):
31+
return False
32+
return True
33+
34+
@property
35+
def resolutions(self) -> list[Vulnerability]:
36+
"""
37+
Return resolved vulnerabilities
38+
"""
39+
resolved_vulnerabilities = []
40+
for previous_vuln in self.previous_vulnerabilities:
41+
if self._is_resolved(previous_vuln):
42+
resolved_vulnerabilities.append(previous_vuln)
43+
return resolved_vulnerabilities
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from exasol.toolbox.util.dependencies.track_vulnerabilities import (
2+
ResolvedVulnerabilities,
3+
)
4+
5+
6+
class TestResolvedVulnerabilities:
7+
def test_vulnerability_present_for_previous_and_current(self, sample_vulnerability):
8+
vuln = sample_vulnerability.vulnerability
9+
resolved = ResolvedVulnerabilities(
10+
previous_vulnerabilities=[vuln], current_vulnerabilities=[vuln]
11+
)
12+
assert resolved._is_resolved(vuln) is False
13+
14+
def test_vulnerability_present_for_previous_and_current_with_different_id(
15+
self, sample_vulnerability
16+
):
17+
vuln2 = sample_vulnerability.vulnerability.__dict__.copy()
18+
vuln2["version"] = sample_vulnerability.version
19+
# flipping aliases & id to ensure can match across types
20+
vuln2["aliases"] = [sample_vulnerability.vulnerability_id]
21+
vuln2["id"] = sample_vulnerability.cve_id
22+
23+
resolved = ResolvedVulnerabilities(
24+
previous_vulnerabilities=[sample_vulnerability.vulnerability],
25+
current_vulnerabilities=[vuln2],
26+
)
27+
assert resolved._is_resolved(sample_vulnerability.vulnerability) is False
28+
29+
def test_vulnerability_in_previous_resolved_in_current(self, sample_vulnerability):
30+
vuln = sample_vulnerability.vulnerability
31+
resolved = ResolvedVulnerabilities(
32+
previous_vulnerabilities=[vuln], current_vulnerabilities=[]
33+
)
34+
assert resolved._is_resolved(vuln) is True
35+
36+
def test_no_vulnerabilities_for_previous_and_current(self):
37+
resolved = ResolvedVulnerabilities(
38+
previous_vulnerabilities=[], current_vulnerabilities=[]
39+
)
40+
assert resolved.resolutions == []
41+
42+
def test_vulnerability_in_current_but_not_present(self, sample_vulnerability):
43+
resolved = ResolvedVulnerabilities(
44+
previous_vulnerabilities=[],
45+
current_vulnerabilities=[sample_vulnerability.vulnerability],
46+
)
47+
# only care about "resolved" vulnerabilities, not new ones
48+
assert resolved.resolutions == []
49+
50+
def test_resolved_vulnerabilities(self, sample_vulnerability):
51+
resolved = ResolvedVulnerabilities(
52+
previous_vulnerabilities=[sample_vulnerability.vulnerability],
53+
current_vulnerabilities=[],
54+
)
55+
assert resolved.resolutions == [sample_vulnerability.vulnerability]

0 commit comments

Comments
 (0)