From bcdee7d2f165e678e0f38679441181153031c5f9 Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Tue, 9 Feb 2021 17:42:35 +0530 Subject: [PATCH 1/2] Add apache kafka advisory importer Signed-off-by: Shivam Sandbhor --- vulnerabilities/importer_yielder.py | 7 ++ vulnerabilities/importers/__init__.py | 1 + vulnerabilities/importers/apache_kafka.py | 120 ++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 vulnerabilities/importers/apache_kafka.py diff --git a/vulnerabilities/importer_yielder.py b/vulnerabilities/importer_yielder.py index 02f88e3e7..12cd4ff04 100644 --- a/vulnerabilities/importer_yielder.py +++ b/vulnerabilities/importer_yielder.py @@ -241,6 +241,13 @@ "etags": {} }, }, + { + 'name': 'apache_kafka', + 'license': '', + 'last_run': None, + 'data_source': 'ApacheKafkaDataSource', + 'data_source_cfg': {}, + }, ] diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index 14e82a77f..0dd87fa06 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -45,3 +45,4 @@ from vulnerabilities.importers.ubuntu import UbuntuDataSource from vulnerabilities.importers.ubuntu_usn import UbuntuUSNDataSource from vulnerabilities.importers.apache_tomcat import ApacheTomcatDataSource +from vulnerabilities.importers.apache_kafka import ApacheKafkaDataSource diff --git a/vulnerabilities/importers/apache_kafka.py b/vulnerabilities/importers/apache_kafka.py new file mode 100644 index 000000000..458d0ad59 --- /dev/null +++ b/vulnerabilities/importers/apache_kafka.py @@ -0,0 +1,120 @@ +# Copyright (c) nexB Inc. and others. All rights reserved. +# http://nexb.com and https://github.com/nexB/vulnerablecode/ +# The VulnerableCode software is licensed under the Apache License version 2.0. +# Data generated with VulnerableCode require an acknowledgment. +# +# You may not use this software except in compliance with the License. +# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# When you publish or redistribute any data created with VulnerableCode or any VulnerableCode +# derivative work, you must accompany this data with the following acknowledgment: +# +# Generated with VulnerableCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES +# OR CONDITIONS OF ANY KIND, either express or implied. No content created from +# VulnerableCode should be considered or used as legal advice. Consult an Attorney +# for any legal advice. +# VulnerableCode is a free software tool from nexB Inc. and others. +# Visit https://github.com/nexB/vulnerablecode/ for support and download. + +import asyncio + +import requests +from bs4 import BeautifulSoup +from dephell_specifier import RangeSpecifier +from packageurl import PackageURL + +from vulnerabilities.data_source import Advisory, DataSource, Reference +from vulnerabilities.package_managers import GitHubTagsAPI + +GH_PAGE_URL = "https://raw.githubusercontent.com/apache/kafka-site/asf-site/cve-list.html" +ASF_PAGE_URL = "https://kafka.apache.org/cve-list" + + +class ApacheKafkaDataSource(DataSource): + @staticmethod + def fetch_advisory_page(): + page = requests.get(GH_PAGE_URL) + return page.content + + def set_api(self): + self.version_api = GitHubTagsAPI() + asyncio.run(self.version_api.load_api(["apache/kafka"])) + + def updated_advisories(self): + advisory_page = self.fetch_advisory_page() + self.set_api() + parsed_data = self.to_advisory(advisory_page) + return self.batch_advisories(parsed_data) + + def to_advisory(self, advisory_page): + advisories = [] + advisory_page = BeautifulSoup(advisory_page, features="lxml") + cve_section_beginnings = advisory_page.find_all("h2") + for cve_section_beginning in cve_section_beginnings: + cve_id = cve_section_beginning.text.split("\n")[0] + cve_description_paragraph = cve_section_beginning.find_next_sibling("p") + cve_data_table = cve_section_beginning.find_next_sibling("table") + cve_data_table_rows = cve_data_table.find_all("tr") + affected_versions_row, fixed_versions_row = ( + cve_data_table_rows[0], + cve_data_table_rows[1], + ) + affected_version_ranges = to_version_ranges( + affected_versions_row.find_all("td")[1].text + ) + fixed_version_ranges = to_version_ranges(fixed_versions_row.find_all("td")[1].text) + + fixed_packages = [ + PackageURL(type="apache", name="kafka", version=version) + for version in self.version_api.get("apache/kafka") + if any([version in version_range for version_range in fixed_version_ranges]) + ] + + affected_packages = [ + PackageURL(type="apache", name="kafka", version=version) + for version in self.version_api.get("apache/kafka") + if any([version in version_range for version_range in affected_version_ranges]) + ] + + advisories.append( + Advisory( + cve_id=cve_id, + summary=cve_description_paragraph.text, + impacted_package_urls=affected_packages, + resolved_package_urls=fixed_packages, + vuln_references=[ + Reference(url=ASF_PAGE_URL), + Reference( + url=f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve_id}", + reference_id=cve_id, + ), + ], + ) + ) + return advisories + + +def to_version_ranges(version_range_text): + version_ranges = [] + range_expressions = version_range_text.split(",") + for range_expression in range_expressions: + if "to" in range_expression: + # eg range_expression == "3.2.0 to 3.2.1" + lower_bound, upper_bound = range_expression.split("to") + lower_bound = f">={lower_bound}" + upper_bound = f"<={upper_bound}" + version_ranges.append(RangeSpecifier(f"{lower_bound},{upper_bound}")) + + elif "and later" in range_expression: + # eg range_expression == "2.1.1 and later" + range_expression = range_expression.replace("and later", "") + version_ranges.append(RangeSpecifier(f">={range_expression}")) + + else: + # eg range_expression == "3.0.0" + version_ranges.append(RangeSpecifier(range_expression)) + return version_ranges From 87175b958ba999c90c0ad06be3de3a21d6c89b00 Mon Sep 17 00:00:00 2001 From: Shivam Sandbhor Date: Tue, 9 Feb 2021 17:42:56 +0530 Subject: [PATCH 2/2] Add tests for apache kafka advisory importer and minor changes Signed-off-by: Shivam Sandbhor --- vulnerabilities/importers/apache_kafka.py | 10 +- vulnerabilities/tests/test_apache_kafka.py | 102 ++++++++++++++++++ .../test_data/apache_kafka/cve-list.html | 40 +++++++ 3 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 vulnerabilities/tests/test_apache_kafka.py create mode 100644 vulnerabilities/tests/test_data/apache_kafka/cve-list.html diff --git a/vulnerabilities/importers/apache_kafka.py b/vulnerabilities/importers/apache_kafka.py index 458d0ad59..60df7482c 100644 --- a/vulnerabilities/importers/apache_kafka.py +++ b/vulnerabilities/importers/apache_kafka.py @@ -27,7 +27,9 @@ from dephell_specifier import RangeSpecifier from packageurl import PackageURL -from vulnerabilities.data_source import Advisory, DataSource, Reference +from vulnerabilities.data_source import Advisory +from vulnerabilities.data_source import DataSource +from vulnerabilities.data_source import Reference from vulnerabilities.package_managers import GitHubTagsAPI GH_PAGE_URL = "https://raw.githubusercontent.com/apache/kafka-site/asf-site/cve-list.html" @@ -59,10 +61,8 @@ def to_advisory(self, advisory_page): cve_description_paragraph = cve_section_beginning.find_next_sibling("p") cve_data_table = cve_section_beginning.find_next_sibling("table") cve_data_table_rows = cve_data_table.find_all("tr") - affected_versions_row, fixed_versions_row = ( - cve_data_table_rows[0], - cve_data_table_rows[1], - ) + affected_versions_row = cve_data_table_rows[0] + fixed_versions_row = cve_data_table_rows[1] affected_version_ranges = to_version_ranges( affected_versions_row.find_all("td")[1].text ) diff --git a/vulnerabilities/tests/test_apache_kafka.py b/vulnerabilities/tests/test_apache_kafka.py new file mode 100644 index 000000000..32cec5659 --- /dev/null +++ b/vulnerabilities/tests/test_apache_kafka.py @@ -0,0 +1,102 @@ +# Copyright (c) nexB Inc. and others. All rights reserved. +# http://nexb.com and https://github.com/nexB/vulnerablecode/ +# The VulnerableCode software is licensed under the Apache License version 2.0. +# Data generated with VulnerableCode require an acknowledgment. +# +# You may not use this software except in compliance with the License. +# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# When you publish or redistribute any data created with VulnerableCode or any VulnerableCode +# derivative work, you must accompany this data with the following acknowledgment: +# +# Generated with VulnerableCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES +# OR CONDITIONS OF ANY KIND, either express or implied. No content created from +# VulnerableCode should be considered or used as legal advice. Consult an Attorney +# for any legal advice. +# VulnerableCode is a free software tool from nexB Inc. and others. +# Visit https://github.com/nexB/vulnerablecode/ for support and download. + +import os +from unittest import TestCase + +from dephell_specifier import RangeSpecifier +from packageurl import PackageURL + +from vulnerabilities.data_source import Advisory +from vulnerabilities.data_source import Reference +from vulnerabilities.package_managers import GitHubTagsAPI +from vulnerabilities.importers.apache_kafka import ApacheKafkaDataSource +from vulnerabilities.importers.apache_kafka import to_version_ranges + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA = os.path.join(BASE_DIR, "test_data", "apache_kafka", "cve-list.html") + + +class TestApacheKafkaDataSource(TestCase): + def test_to_version_ranges(self): + # Check single version + assert [RangeSpecifier("==3.2.2")] == to_version_ranges("3.2.2") + + # Check range with lower and upper bounds + assert [RangeSpecifier(">=3.2.2, <=3.2.3")] == to_version_ranges("3.2.2 to 3.2.3") + + # Check range with "and later" + assert [RangeSpecifier(">=3.2.2")] == to_version_ranges("3.2.2 and later") + + # Check combination of above cases + assert [ + RangeSpecifier(">=3.2.2"), + RangeSpecifier(">=3.2.2, <=3.2.3"), + RangeSpecifier("==3.2.2"), + ] == to_version_ranges("3.2.2 and later, 3.2.2 to 3.2.3, 3.2.2") + + def test_to_advisory(self): + data_source = ApacheKafkaDataSource(batch_size=1) + data_source.version_api = GitHubTagsAPI( + cache={"apache/kafka": ["2.1.2", "0.10.2.2"]} + ) + expected_data = [ + Advisory( + summary="In Apache Kafka versions between 0.11.0.0 and 2.1.0, it is possible to " + "manually\n craft a Produce request which bypasses transaction/idempotent ACL " + "validation.\n Only authenticated clients with Write permission on the " + "respective topics are\n able to exploit this vulnerability. Users should " + "upgrade to 2.1.1 or later\n where this vulnerability has been fixed.", + impacted_package_urls=[ + PackageURL( + type="apache", + namespace=None, + name="kafka", + version="0.10.2.2", + qualifiers={}, + subpath=None, + ) + ], + resolved_package_urls=[ + PackageURL( + type="apache", + namespace=None, + name="kafka", + version="2.1.2", + qualifiers={}, + subpath=None, + ) + ], + vuln_references=[ + Reference(url="https://kafka.apache.org/cve-list", reference_id=""), + Reference( + url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17196", + reference_id="CVE-2018-17196", + ), + ], + cve_id="CVE-2018-17196", + ) + ] + with open(TEST_DATA) as f: + found_data = data_source.to_advisory(f) + + assert found_data == expected_data diff --git a/vulnerabilities/tests/test_data/apache_kafka/cve-list.html b/vulnerabilities/tests/test_data/apache_kafka/cve-list.html new file mode 100644 index 000000000..22bc79eb1 --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_kafka/cve-list.html @@ -0,0 +1,40 @@ + + + +
+ +
+ +

Apache Kafka Security Vulnerabilities

+ + This page lists all security vulnerabilities fixed in released versions of Apache Kafka + +

CVE-2018-17196 + Authenticated clients with Write permission may bypass transaction/idempotent ACL validation

+

In Apache Kafka versions between 0.11.0.0 and 2.1.0, it is possible to manually + craft a Produce request which bypasses transaction/idempotent ACL validation. + Only authenticated clients with Write permission on the respective topics are + able to exploit this vulnerability. Users should upgrade to 2.1.1 or later + where this vulnerability has been fixed.

+ + + + + + + + + + + + + + + + + + + + +
Versions affected0.11.0.0 to 2.1.0, 0.10.2.2
Fixed versions2.1.1 and later
ImpactThis issue could result in privilege escalation.
Issue announced10 July 2019
+ \ No newline at end of file