Skip to content

Commit 321f0c3

Browse files
committed
Refactor codebase and tests to treat Advisory class mutable
Signed-off-by: Shivam Sandbhor <shivam.sandbhor@gmail.com>
1 parent 4193543 commit 321f0c3

19 files changed

Lines changed: 145 additions & 107 deletions

vulnerabilities/data_source.py

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,10 @@ class Advisory:
7878
"""
7979

8080
summary: str
81-
impacted_package_urls: Iterable[PackageURL]
81+
vulnerability_id: Optional[str] = None
82+
impacted_package_urls: Iterable[PackageURL] = dataclasses.field(default_factory=list)
8283
resolved_package_urls: Iterable[PackageURL] = dataclasses.field(default_factory=list)
8384
vuln_references: List[Reference] = dataclasses.field(default_factory=list)
84-
vulnerability_id: Optional[str] = None
85-
86-
def __hash__(self):
87-
s = "{}{}{}{}".format(
88-
self.summary,
89-
''.join(sorted([str(p) for p in self.impacted_package_urls])),
90-
''.join(sorted([str(p) for p in self.resolved_package_urls])),
91-
self.vulnerability_id,
92-
)
93-
return hash(s)
9485

9586

9687
class InvalidConfigurationError(Exception):
@@ -209,7 +200,7 @@ def batch_advisories(self, advisories: List[Advisory]) -> Set[Advisory]:
209200

210201
while advisories:
211202
b, advisories = advisories[: self.batch_size], advisories[self.batch_size:]
212-
yield set(b)
203+
yield b
213204

214205

215206
@dataclasses.dataclass

vulnerabilities/importers/alpine_linux.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def _load_advisories(
187187
impacted_package_urls=[],
188188
resolved_package_urls=resolved_purls,
189189
vuln_references=references,
190-
vulnerability_id=vuln_ids[0] if vuln_ids[0] != "CVE-????-?????" else None,
190+
vulnerability_id=vuln_ids[0] if vuln_ids[0] != "CVE-????-?????" else "",
191191
)
192192
)
193193

vulnerabilities/importers/rust.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ def _load_advisories(self, files) -> Set[Advisory]:
7575

7676
while files:
7777
batch, files = files[: self.batch_size], files[self.batch_size:]
78-
advisories = set()
78+
advisories = []
7979
for path in batch:
8080
advisory = self._load_advisory(path)
8181
if advisory:
82-
advisories.add(advisory)
82+
advisories.append(advisory)
8383
yield advisories
8484

8585
def collect_packages(self, paths):

vulnerabilities/tests/test_alpine.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
from vulnerabilities.data_source import Advisory, Reference
3232
from vulnerabilities.importers.alpine_linux import AlpineDataSource
33+
from vulnerabilities.tests.utils import advisories_are_equal
3334

3435

3536
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -141,7 +142,7 @@ def test__process_link(self):
141142
url="https://xenbits.xen.org/xsa/advisory-295.html", reference_id="XSA-295"
142143
)
143144
],
144-
vulnerability_id=None,
145+
vulnerability_id="",
145146
),
146147
]
147148
mock_requests = MagicMock()
@@ -151,4 +152,4 @@ def test__process_link(self):
151152
mock_content.content = f
152153
with patch("vulnerabilities.importers.alpine_linux.requests", new=mock_requests):
153154
found_advisories = self.data_source._process_link("does not matter")
154-
assert expected_advisories == found_advisories
155+
assert advisories_are_equal(expected_advisories, found_advisories)

vulnerabilities/tests/test_apache_kafka.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from vulnerabilities.package_managers import GitHubTagsAPI
3232
from vulnerabilities.importers.apache_kafka import ApacheKafkaDataSource
3333
from vulnerabilities.importers.apache_kafka import to_version_ranges
34+
from vulnerabilities.tests.utils import advisories_are_equal
3435

3536
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
3637
TEST_DATA = os.path.join(BASE_DIR, "test_data", "apache_kafka", "cve-list.html")
@@ -59,7 +60,7 @@ def test_to_advisory(self):
5960
data_source.version_api = GitHubTagsAPI(
6061
cache={"apache/kafka": ["2.1.2", "0.10.2.2"]}
6162
)
62-
expected_data = [
63+
expected_advisories = [
6364
Advisory(
6465
summary="In Apache Kafka versions between 0.11.0.0 and 2.1.0, it is possible to "
6566
"manually\n craft a Produce request which bypasses transaction/idempotent ACL "
@@ -97,6 +98,6 @@ def test_to_advisory(self):
9798
)
9899
]
99100
with open(TEST_DATA) as f:
100-
found_data = data_source.to_advisory(f)
101+
found_advisories = data_source.to_advisory(f)
101102

102-
assert found_data == expected_data
103+
assert advisories_are_equal(found_advisories, expected_advisories)

vulnerabilities/tests/test_apache_tomcat.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from vulnerabilities.data_source import Advisory
3131
from vulnerabilities.data_source import Reference
3232
from vulnerabilities.importers.apache_tomcat import ApacheTomcatDataSource
33+
from vulnerabilities.tests.utils import advisories_are_equal
3334

3435
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
3536
TEST_DATA = os.path.join(BASE_DIR, "test_data", "apache_tomcat", "security-9.html")
@@ -47,8 +48,7 @@ def setUpClass(cls):
4748
cls.data_src.version_api = mock_api
4849

4950
def test_to_advisories(self):
50-
expected_advisories = sorted(
51-
[
51+
expected_advisories = [
5252
Advisory(
5353
summary="",
5454
impacted_package_urls=[
@@ -209,19 +209,8 @@ def test_to_advisories(self):
209209
],
210210
vulnerability_id="CVE-2016-0714",
211211
),
212-
],
213-
key=lambda x: x.vulnerability_id,
214-
)
212+
]
215213

216214
with open(TEST_DATA) as f:
217215
found_advisories = self.data_src.to_advisories(f)
218-
219-
found_advisories.sort(key=lambda x: x.vulnerability_id)
220-
221-
for i in range(len(found_advisories)):
222-
found_advisories[i].vuln_references.sort(key=lambda x: x.url)
223-
224-
for i in range(len(expected_advisories)):
225-
expected_advisories[i].vuln_references.sort(key=lambda x: x.url)
226-
227-
assert expected_advisories == found_advisories
216+
assert advisories_are_equal(expected_advisories, found_advisories)

vulnerabilities/tests/test_debian_oval.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from vulnerabilities.oval_parser import OvalParser
1313
from vulnerabilities.importers.debian_oval import DebianOvalDataSource
1414
from vulnerabilities.data_source import Advisory
15+
from vulnerabilities.tests.utils import advisories_are_equal
1516

1617

1718
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -43,7 +44,7 @@ def setUpClass(cls):
4344
'2.3.9'})
4445
@patch('vulnerabilities.importers.debian_oval.DebianVersionAPI.load_api', new=mock)
4546
def test_get_data_from_xml_doc(self, mock_write):
46-
expected_data = {
47+
expected_advisories = [
4748
Advisory(
4849
summary='denial of service',
4950
impacted_package_urls={
@@ -97,18 +98,17 @@ def test_get_data_from_xml_doc(self, mock_write):
9798
qualifiers=OrderedDict([('distro', 'wheezy')]),
9899
subpath=None)},
99100
vulnerability_id='CVE-2001-1593')
100-
101-
}
101+
]
102102

103103
xml_doc = ET.parse(os.path.join(TEST_DATA, "debian_oval_data.xml"))
104104
# Dirty quick patch to mock batch_advisories
105105
with patch('vulnerabilities.importers.debian_oval.DebianOvalDataSource.batch_advisories',
106106
new=return_adv):
107-
data = {i for i in self.debian_oval_data_src.get_data_from_xml_doc(
107+
found_advisories = [i for i in self.debian_oval_data_src.get_data_from_xml_doc(
108108
xml_doc,
109109
{
110110
"type": "deb",
111111
"qualifiers": {"distro": "wheezy"}
112112
})
113-
}
114-
assert expected_data == data
113+
]
114+
assert advisories_are_equal(expected_advisories, found_advisories)

vulnerabilities/tests/test_elixir_security.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from vulnerabilities.data_source import Reference
3131
from vulnerabilities.importers.elixir_security import ElixirSecurityDataSource
3232
from vulnerabilities.package_managers import HexVersionAPI
33+
from vulnerabilities.tests.utils import advisories_are_equal
3334

3435
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
3536

@@ -62,7 +63,7 @@ def setUpClass(cls):
6263
def test_process_file(self):
6364

6465
path = os.path.join(BASE_DIR, "test_data/elixir_security/test_file.yml")
65-
expected_data = Advisory(
66+
expected_advisory = Advisory(
6667
summary=('The Coherence library has "Mass Assignment"-like vulnerabilities.\n'),
6768
impacted_package_urls={
6869
PackageURL(
@@ -132,6 +133,6 @@ def test_process_file(self):
132133
vulnerability_id="CVE-2018-20301",
133134
)
134135

135-
found_data = self.data_src.process_file(path)
136+
found_advisory = self.data_src.process_file(path)
136137

137-
assert expected_data == found_data
138+
assert advisories_are_equal([expected_advisory], [found_advisory])

vulnerabilities/tests/test_gentoo.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from vulnerabilities.importers.gentoo import GentooDataSource
3232
from vulnerabilities.data_source import Advisory
3333
from vulnerabilities.data_source import Reference
34+
from vulnerabilities.tests.utils import advisories_are_equal
3435

3536
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
3637
TEST_DATA = os.path.join(BASE_DIR, "test_data/gentoo/glsa-201709-09.xml")
@@ -87,7 +88,7 @@ def test_cves_from_reference(self):
8788

8889
def test_process_file(self):
8990

90-
exp_data = [
91+
expected_advisories = [
9192
Advisory(
9293
summary=(
9394
'A command injection vulnerability in '
@@ -115,5 +116,5 @@ def test_process_file(self):
115116
reference_id='GLSA-201709-09')],
116117
vulnerability_id='CVE-2017-9800')]
117118

118-
found_data = self.data_src.process_file(TEST_DATA)
119-
assert exp_data == found_data
119+
found_advisories = self.data_src.process_file(TEST_DATA)
120+
assert advisories_are_equal(expected_advisories, found_advisories)

vulnerabilities/tests/test_github.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from vulnerabilities.severity_systems import ScoringSystem
4343
from vulnerabilities.importers.github import GitHubTokenError
4444
from vulnerabilities.importers.github import query
45+
from vulnerabilities.tests.utils import advisories_are_equal
4546

4647
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
4748
TEST_DATA = os.path.join(BASE_DIR, "test_data")
@@ -163,7 +164,7 @@ def test_process_response(self):
163164
resp = json.load(f)
164165
self.data_src.advisories = resp
165166

166-
expected_result = [
167+
expected_advisories = [
167168
Advisory(
168169
summary="Denial of Service in Tomcat",
169170
impacted_package_urls=set(),
@@ -371,10 +372,8 @@ def test_process_response(self):
371372
mock_version_api = MagicMock()
372373
mock_version_api.package_type = "maven"
373374
mock_version_api.get = lambda x: {"1.2.0", "9.0.2"}
374-
with patch(
375-
"vulnerabilities.importers.github.MavenVersionAPI", return_value=mock_version_api
376-
): # nopep8
375+
with patch("vulnerabilities.importers.github.MavenVersionAPI", return_value=mock_version_api): # nopep8
377376
with patch("vulnerabilities.importers.github.GitHubAPIDataSource.set_api"):
378-
found_result = self.data_src.process_response()
377+
found_advisories = self.data_src.process_response()
379378

380-
assert expected_result == found_result
379+
assert advisories_are_equal(expected_advisories, found_advisories)

0 commit comments

Comments
 (0)