Skip to content

Commit 887b94a

Browse files
committed
Modify Apache Kafka importer to support package-first mode #1922
* Update Apache Kafka importer to filter and process advisories relevant to the purl passed in the constructor * Update Apache Kafka importer tests to include testing the package-first mode Signed-off-by: Michael Ehab Mikhail <[email protected]>
1 parent a05b65e commit 887b94a

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

vulnerabilities/importers/apache_kafka.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from bs4 import BeautifulSoup
1616
from dateutil.parser import parse
1717
from packageurl import PackageURL
18+
from univers.version_range import VersionRange
19+
from univers.versions import SemverVersion
1820

1921
from vulnerabilities.importer import AdvisoryData
2022
from vulnerabilities.importer import AffectedPackage
@@ -97,16 +99,65 @@ class ApacheKafkaImporter(Importer):
9799
license_url = "https://www.apache.org/licenses/"
98100
importer_name = "Apache Kafka Importer"
99101

102+
def __init__(self, purl=None, *args, **kwargs):
103+
super().__init__(*args, **kwargs)
104+
self.purl = purl
105+
if self.purl:
106+
if self.purl.type != "apache" or self.purl.name != "kafka":
107+
print(
108+
f"Warning: This importer handles Apache Kafka packages. Current PURL: {self.purl!s}"
109+
)
110+
100111
@staticmethod
101112
def fetch_advisory_page(self):
102113
page = requests.get(self.GH_PAGE_URL)
103114
return page.content
104115

105116
def advisory_data(self):
106-
advisory_page = self.fetch_advisory_page(self)
117+
advisory_page = self.fetch_advisory_page()
118+
119+
all_advisories = self.to_advisory(advisory_page)
120+
121+
if self.purl:
122+
return self._filter_advisories_for_purl(all_advisories)
123+
124+
return all_advisories
125+
126+
def _filter_advisories_for_purl(self, advisories):
127+
if not self.purl:
128+
return advisories
129+
130+
filtered_advisories = []
131+
for advisory in advisories:
132+
if self._advisory_affects_purl(advisory):
133+
filtered_advisories.append(advisory)
134+
135+
return filtered_advisories
136+
137+
def _advisory_affects_purl(self, advisory):
138+
if not self.purl:
139+
return True
140+
141+
if self.purl.type != "apache" or self.purl.name != "kafka":
142+
return False
143+
144+
if not self.purl.version:
145+
return True
146+
147+
for affected_package in advisory.affected_packages:
148+
149+
if affected_package.affected_version_range:
150+
try:
151+
purl_version = SemverVersion(self.purl.version)
152+
if purl_version in VersionRange.from_string(
153+
affected_package.affected_version_range
154+
):
155+
return True
156+
except Exception as e:
157+
logger.error(f"Error checking version {self.purl.version}: {e}")
158+
return True
107159

108-
parsed_data = self.to_advisory(advisory_page)
109-
return parsed_data
160+
return False
110161

111162
def to_advisory(self, advisory_page):
112163
advisories = []

vulnerabilities/tests/test_apache_kafka.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from unittest.mock import patch
1313

1414
import pytest
15+
from packageurl import PackageURL
1516

1617
from vulnerabilities.importer import AdvisoryData
1718
from vulnerabilities.importers.apache_kafka import ApacheKafkaImporter
@@ -79,3 +80,51 @@ def to_advisory_changed_fixed_versions():
7980
with open(os.path.join(TEST_DATA, "cve-list-changed-fixed-versions.html")) as f:
8081
raw_data = f.read()
8182
advisories = ApacheKafkaImporter().to_advisory(raw_data)
83+
84+
85+
@pytest.fixture
86+
def mock_apache_kafka_advisory_page(monkeypatch):
87+
test_file = os.path.join(TEST_DATA, "cve-list-2022-12-06.html")
88+
with open(test_file, "rb") as f:
89+
html_content = f.read()
90+
91+
def mock_fetch_advisory_page(self):
92+
return html_content
93+
94+
monkeypatch.setattr(ApacheKafkaImporter, "fetch_advisory_page", mock_fetch_advisory_page)
95+
96+
97+
def test_package_first_mode_all_advisories(monkeypatch, mock_apache_kafka_advisory_page):
98+
purl = PackageURL(type="apache", name="kafka")
99+
importer = ApacheKafkaImporter(purl=purl)
100+
advisories = list(importer.advisory_data())
101+
102+
assert len(advisories) == 6
103+
104+
105+
def test_package_first_mode_version_affected(monkeypatch, mock_apache_kafka_advisory_page):
106+
purl = PackageURL(type="apache", name="kafka", version="2.8.0")
107+
importer = ApacheKafkaImporter(purl=purl)
108+
advisories = list(importer.advisory_data())
109+
110+
assert any(
111+
any(
112+
ap.affected_version_range and "2.8.0" in ap.affected_version_range
113+
for ap in adv.affected_packages
114+
)
115+
for adv in advisories
116+
)
117+
118+
119+
def test_package_first_mode_version_not_affected(monkeypatch, mock_apache_kafka_advisory_page):
120+
purl = PackageURL(type="apache", name="kafka", version="3.3.0")
121+
importer = ApacheKafkaImporter(purl=purl)
122+
advisories = list(importer.advisory_data())
123+
assert advisories == []
124+
125+
126+
def test_package_first_mode_wrong_package(monkeypatch, mock_apache_kafka_advisory_page):
127+
purl = PackageURL(type="apache", name="notkafka")
128+
importer = ApacheKafkaImporter(purl=purl)
129+
advisories = list(importer.advisory_data())
130+
assert advisories == []

0 commit comments

Comments
 (0)