From ef6dd16242f01a7550a168a9c5ee32cea9457af4 Mon Sep 17 00:00:00 2001 From: newklei Date: Sun, 8 Mar 2026 01:02:01 -0500 Subject: [PATCH 1/5] Add ZDI security advisory importer Implements ZDI importer pipeline (issue 1471) using the Zero Day Initiative RSS feeds at zerodayinitiative.com/rss/published/YEAR/. Fetches year-specific feeds from 2007 through the current year, parses advisory ID from the link URL, extracts CVE aliases from the description, and records the publication date. Deduplicates advisories across feeds using a seen-IDs set. Includes 7 unit tests covering normal parsing, missing CVE, missing link, invalid XML, and alias deduplication. Signed-off-by: newklei --- vulnerabilities/importers/__init__.py | 2 + .../pipelines/v2_importers/zdi_importer.py | 170 ++++++++++++++++++ .../zdi/expected_zdi_advisory_output1.json | 20 +++ .../tests/test_data/zdi/zdi_rss_mock.xml | 22 +++ vulnerabilities/tests/test_zdi_importer.py | 86 +++++++++ 5 files changed, 300 insertions(+) create mode 100644 vulnerabilities/pipelines/v2_importers/zdi_importer.py create mode 100644 vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json create mode 100644 vulnerabilities/tests/test_data/zdi/zdi_rss_mock.xml create mode 100644 vulnerabilities/tests/test_zdi_importer.py diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index 594021092..ac86c5eec 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -58,6 +58,7 @@ from vulnerabilities.pipelines.v2_importers import gentoo_importer as gentoo_importer_v2 from vulnerabilities.pipelines.v2_importers import github_osv_importer as github_osv_importer_v2 from vulnerabilities.pipelines.v2_importers import gitlab_importer as gitlab_importer_v2 +from vulnerabilities.pipelines.v2_importers import zdi_importer as zdi_importer_v2 from vulnerabilities.pipelines.v2_importers import istio_importer as istio_importer_v2 from vulnerabilities.pipelines.v2_importers import mattermost_importer as mattermost_importer_v2 from vulnerabilities.pipelines.v2_importers import mozilla_importer as mozilla_importer_v2 @@ -110,6 +111,7 @@ ruby_importer_v2.RubyImporterPipeline, epss_importer_v2.EPSSImporterPipeline, gentoo_importer_v2.GentooImporterPipeline, + zdi_importer_v2.ZDIImporterPipeline, nginx_importer_v2.NginxImporterPipeline, debian_importer_v2.DebianImporterPipeline, mattermost_importer_v2.MattermostImporterPipeline, diff --git a/vulnerabilities/pipelines/v2_importers/zdi_importer.py b/vulnerabilities/pipelines/v2_importers/zdi_importer.py new file mode 100644 index 000000000..360b7290e --- /dev/null +++ b/vulnerabilities/pipelines/v2_importers/zdi_importer.py @@ -0,0 +1,170 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import logging +import re +from datetime import datetime +from datetime import timezone +from typing import Iterable +from xml.etree import ElementTree + +from vulnerabilities.importer import AdvisoryDataV2 +from vulnerabilities.importer import ReferenceV2 +from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2 +from vulnerabilities.utils import fetch_response + +logger = logging.getLogger(__name__) + +ZDI_RSS_YEAR_URL = "https://www.zerodayinitiative.com/rss/published/{year}/" +ZDI_START_YEAR = 2007 +ZDI_ID_RE = re.compile(r"ZDI-\d+-\d+") +CVE_RE = re.compile(r"CVE-\d{4}-\d{4,7}") +PUBDATE_FORMAT = "%a, %d %b %Y %H:%M:%S %z" + + +class ZDIImporterPipeline(VulnerableCodeBaseImporterPipelineV2): + """Collect ZDI security advisories from the Zero Day Initiative RSS feeds.""" + + pipeline_id = "zdi_importer" + spdx_license_expression = "LicenseRef-scancode-proprietary-license" + license_url = "https://www.zerodayinitiative.com" + repo_url = "https://www.zerodayinitiative.com" + precedence = 200 + + @classmethod + def steps(cls): + return (cls.collect_and_store_advisories,) + + def advisories_count(self) -> int: + return 0 + + def collect_advisories(self) -> Iterable[AdvisoryDataV2]: + current_year = datetime.now(tz=timezone.utc).year + urls = [ + ZDI_RSS_YEAR_URL.format(year=year) + for year in range(ZDI_START_YEAR, current_year + 1) + ] + + seen_ids = set() + for url in urls: + self.log(f"Fetching ZDI RSS feed: {url}") + try: + response = fetch_response(url) + items = parse_rss_feed(response.text) + except Exception as e: + logger.error("Failed to fetch %s: %s", url, e) + continue + + for item in items: + advisory = parse_advisory_data(item) + if advisory and advisory.advisory_id not in seen_ids: + seen_ids.add(advisory.advisory_id) + yield advisory + + +def parse_rss_feed(xml_text: str) -> list: + """ + Parse ZDI RSS feed XML text and return a list of raw item dicts. + + Each dict has keys: ``title``, ``link``, ``description``, ``pub_date``. + Returns an empty list if the XML is malformed or has no ```` element. + + >>> xml = ( + ... '' + ... 'ZDI-25-001: Test' + ... 'http://www.zerodayinitiative.com/advisories/ZDI-25-001/' + ... 'CVE-2025-12345' + ... 'Mon, 06 Jan 2025 00:00:00 -0600' + ... '' + ... ) + >>> items = parse_rss_feed(xml) + >>> len(items) + 1 + >>> items[0]['title'] + 'ZDI-25-001: Test' + """ + try: + root = ElementTree.fromstring(xml_text) + except ElementTree.ParseError as e: + logger.error("Failed to parse RSS XML: %s", e) + return [] + + channel = root.find("channel") + if channel is None: + logger.error("RSS feed has no element") + return [] + + items = [] + for item_el in channel.findall("item"): + items.append( + { + "title": (item_el.findtext("title") or "").strip(), + "link": (item_el.findtext("link") or "").strip(), + "description": (item_el.findtext("description") or "").strip(), + "pub_date": (item_el.findtext("pubDate") or "").strip(), + } + ) + return items + + +def parse_advisory_data(item: dict): + """ + Parse a single ZDI RSS item dict into an AdvisoryDataV2 object. + + Returns ``None`` if a ZDI advisory ID cannot be extracted from the link URL. + The RSS feed does not carry structured package data, so ``affected_packages`` + is always empty. + + >>> item = { + ... "title": "ZDI-25-001: Example Remote Code Execution", + ... "link": "http://www.zerodayinitiative.com/advisories/ZDI-25-001/", + ... "description": "CVSS rating of 8.8. CVE-2025-12345.", + ... "pub_date": "Mon, 06 Jan 2025 00:00:00 -0600", + ... } + >>> result = parse_advisory_data(item) + >>> result.advisory_id + 'ZDI-25-001' + >>> result.aliases + ['CVE-2025-12345'] + """ + link = item.get("link") or "" + title = item.get("title") or "" + description = item.get("description") or "" + pub_date_str = item.get("pub_date") or "" + + match = ZDI_ID_RE.search(link) + if not match: + logger.error("Could not extract ZDI advisory ID from link: %r", link) + return None + + advisory_id = match.group(0) + aliases = list(dict.fromkeys(CVE_RE.findall(description))) + + date_published = None + if pub_date_str: + try: + date_published = datetime.strptime(pub_date_str, PUBDATE_FORMAT) + except ValueError: + logger.warning( + "Could not parse date %r for advisory %s", pub_date_str, advisory_id + ) + + references = [] + if link: + references.append(ReferenceV2(url=link)) + + return AdvisoryDataV2( + advisory_id=advisory_id, + aliases=aliases, + summary=title, + affected_packages=[], + references=references, + date_published=date_published, + url=link, + ) diff --git a/vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json b/vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json new file mode 100644 index 000000000..70324f1a8 --- /dev/null +++ b/vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json @@ -0,0 +1,20 @@ +{ + "advisory_id": "ZDI-25-001", + "aliases": [ + "CVE-2025-12345" + ], + "summary": "ZDI-25-001: Example Vendor Product Remote Code Execution Vulnerability", + "affected_packages": [], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "http://www.zerodayinitiative.com/advisories/ZDI-25-001/" + } + ], + "patches": [], + "severities": [], + "date_published": "2025-01-06T00:00:00-06:00", + "weaknesses": [], + "url": "http://www.zerodayinitiative.com/advisories/ZDI-25-001/" +} diff --git a/vulnerabilities/tests/test_data/zdi/zdi_rss_mock.xml b/vulnerabilities/tests/test_data/zdi/zdi_rss_mock.xml new file mode 100644 index 000000000..18642075a --- /dev/null +++ b/vulnerabilities/tests/test_data/zdi/zdi_rss_mock.xml @@ -0,0 +1,22 @@ + + + + Zero Day Initiative - Published Advisories + http://www.zerodayinitiative.com + Published ZDI Advisories + + <![CDATA[ZDI-25-001: Example Vendor Product Remote Code Execution Vulnerability]]> + ZDI-CAN-12345 + http://www.zerodayinitiative.com/advisories/ZDI-25-001/ + + Mon, 06 Jan 2025 00:00:00 -0600 + + + <![CDATA[ZDI-25-002: Another Vendor Product Information Disclosure Vulnerability]]> + ZDI-CAN-67890 + http://www.zerodayinitiative.com/advisories/ZDI-25-002/ + + Tue, 07 Jan 2025 00:00:00 -0600 + + + diff --git a/vulnerabilities/tests/test_zdi_importer.py b/vulnerabilities/tests/test_zdi_importer.py new file mode 100644 index 000000000..e2373e858 --- /dev/null +++ b/vulnerabilities/tests/test_zdi_importer.py @@ -0,0 +1,86 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/aboutcode-org/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# + +import os +from unittest import TestCase + +from vulnerabilities.pipelines.v2_importers.zdi_importer import parse_advisory_data +from vulnerabilities.pipelines.v2_importers.zdi_importer import parse_rss_feed +from vulnerabilities.tests import util_tests + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA = os.path.join(BASE_DIR, "test_data/zdi") + + +def _load_rss(filename="zdi_rss_mock.xml"): + with open(os.path.join(TEST_DATA, filename), encoding="utf-8") as f: + return f.read() + + +class TestZDIImporter(TestCase): + def test_parse_rss_feed_returns_correct_item_count(self): + """parse_rss_feed returns one dict per in the RSS feed.""" + items = parse_rss_feed(_load_rss()) + self.assertEqual(len(items), 2) + + def test_parse_rss_feed_item_fields(self): + """Each parsed item dict contains the expected keys and values.""" + items = parse_rss_feed(_load_rss()) + first = items[0] + self.assertEqual(first["title"], "ZDI-25-001: Example Vendor Product Remote Code Execution Vulnerability") + self.assertEqual(first["link"], "http://www.zerodayinitiative.com/advisories/ZDI-25-001/") + self.assertIn("CVE-2025-12345", first["description"]) + self.assertEqual(first["pub_date"], "Mon, 06 Jan 2025 00:00:00 -0600") + + def test_parse_advisory_with_cve(self): + """Advisory with CVE alias and pubDate is parsed into a correct AdvisoryDataV2.""" + items = parse_rss_feed(_load_rss()) + result = parse_advisory_data(items[0]) + self.assertIsNotNone(result) + result_dict = result.to_dict() + expected_file = os.path.join(TEST_DATA, "expected_zdi_advisory_output1.json") + util_tests.check_results_against_json(result_dict, expected_file) + + def test_parse_advisory_no_cve_has_empty_aliases(self): + """Advisory whose description contains no CVE IDs has an empty aliases list.""" + items = parse_rss_feed(_load_rss()) + result = parse_advisory_data(items[1]) + self.assertIsNotNone(result) + self.assertEqual(result.advisory_id, "ZDI-25-002") + self.assertEqual(result.aliases, []) + + def test_parse_advisory_missing_link_returns_none(self): + """Advisory with an empty link (no ZDI ID) must return None.""" + item = { + "title": "ZDI-25-999: Test Advisory", + "link": "", + "description": "Some description. CVE-2025-99999.", + "pub_date": "Mon, 06 Jan 2025 00:00:00 -0600", + } + result = parse_advisory_data(item) + self.assertIsNone(result) + + def test_parse_rss_feed_invalid_xml_returns_empty(self): + """Malformed XML input returns an empty list without raising.""" + result = parse_rss_feed("not valid xml <>>>") + self.assertEqual(result, []) + + def test_parse_advisory_zdi_id_not_in_aliases(self): + """The ZDI advisory ID must be advisory_id only, not duplicated in aliases.""" + item = { + "title": "ZDI-25-100: Some Vulnerability", + "link": "http://www.zerodayinitiative.com/advisories/ZDI-25-100/", + "description": "CVSS 7.0. CVE-2025-11111.", + "pub_date": "Wed, 08 Jan 2025 00:00:00 -0600", + } + result = parse_advisory_data(item) + self.assertIsNotNone(result) + self.assertEqual(result.advisory_id, "ZDI-25-100") + self.assertNotIn("ZDI-25-100", result.aliases) + self.assertIn("CVE-2025-11111", result.aliases) From 49660d44c8cf341cf8ee8af82c0a4c0a0048be52 Mon Sep 17 00:00:00 2001 From: newklei <105632119+NucleiAv@users.noreply.github.com> Date: Sun, 8 Mar 2026 01:58:06 -0500 Subject: [PATCH 2/5] Update zdi_importer.py Signed-off-by: newklei --- .../pipelines/v2_importers/zdi_importer.py | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/vulnerabilities/pipelines/v2_importers/zdi_importer.py b/vulnerabilities/pipelines/v2_importers/zdi_importer.py index 360b7290e..819e281a4 100644 --- a/vulnerabilities/pipelines/v2_importers/zdi_importer.py +++ b/vulnerabilities/pipelines/v2_importers/zdi_importer.py @@ -71,23 +71,8 @@ def collect_advisories(self) -> Iterable[AdvisoryDataV2]: def parse_rss_feed(xml_text: str) -> list: """ Parse ZDI RSS feed XML text and return a list of raw item dicts. - Each dict has keys: ``title``, ``link``, ``description``, ``pub_date``. Returns an empty list if the XML is malformed or has no ```` element. - - >>> xml = ( - ... '' - ... 'ZDI-25-001: Test' - ... 'http://www.zerodayinitiative.com/advisories/ZDI-25-001/' - ... 'CVE-2025-12345' - ... 'Mon, 06 Jan 2025 00:00:00 -0600' - ... '' - ... ) - >>> items = parse_rss_feed(xml) - >>> len(items) - 1 - >>> items[0]['title'] - 'ZDI-25-001: Test' """ try: root = ElementTree.fromstring(xml_text) @@ -116,22 +101,9 @@ def parse_rss_feed(xml_text: str) -> list: def parse_advisory_data(item: dict): """ Parse a single ZDI RSS item dict into an AdvisoryDataV2 object. - Returns ``None`` if a ZDI advisory ID cannot be extracted from the link URL. The RSS feed does not carry structured package data, so ``affected_packages`` is always empty. - - >>> item = { - ... "title": "ZDI-25-001: Example Remote Code Execution", - ... "link": "http://www.zerodayinitiative.com/advisories/ZDI-25-001/", - ... "description": "CVSS rating of 8.8. CVE-2025-12345.", - ... "pub_date": "Mon, 06 Jan 2025 00:00:00 -0600", - ... } - >>> result = parse_advisory_data(item) - >>> result.advisory_id - 'ZDI-25-001' - >>> result.aliases - ['CVE-2025-12345'] """ link = item.get("link") or "" title = item.get("title") or "" From 36fc660e12a32c1508988af394ef55bac5a7b70c Mon Sep 17 00:00:00 2001 From: newklei Date: Sun, 8 Mar 2026 11:57:38 -0400 Subject: [PATCH 3/5] style: apply black formatting to zdi_importer and test_zdi_importer Signed-off-by: newklei --- vulnerabilities/pipelines/v2_importers/zdi_importer.py | 7 ++----- vulnerabilities/tests/test_zdi_importer.py | 4 +++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/vulnerabilities/pipelines/v2_importers/zdi_importer.py b/vulnerabilities/pipelines/v2_importers/zdi_importer.py index 819e281a4..b055f0efa 100644 --- a/vulnerabilities/pipelines/v2_importers/zdi_importer.py +++ b/vulnerabilities/pipelines/v2_importers/zdi_importer.py @@ -47,8 +47,7 @@ def advisories_count(self) -> int: def collect_advisories(self) -> Iterable[AdvisoryDataV2]: current_year = datetime.now(tz=timezone.utc).year urls = [ - ZDI_RSS_YEAR_URL.format(year=year) - for year in range(ZDI_START_YEAR, current_year + 1) + ZDI_RSS_YEAR_URL.format(year=year) for year in range(ZDI_START_YEAR, current_year + 1) ] seen_ids = set() @@ -123,9 +122,7 @@ def parse_advisory_data(item: dict): try: date_published = datetime.strptime(pub_date_str, PUBDATE_FORMAT) except ValueError: - logger.warning( - "Could not parse date %r for advisory %s", pub_date_str, advisory_id - ) + logger.warning("Could not parse date %r for advisory %s", pub_date_str, advisory_id) references = [] if link: diff --git a/vulnerabilities/tests/test_zdi_importer.py b/vulnerabilities/tests/test_zdi_importer.py index e2373e858..13402403c 100644 --- a/vulnerabilities/tests/test_zdi_importer.py +++ b/vulnerabilities/tests/test_zdi_importer.py @@ -33,7 +33,9 @@ def test_parse_rss_feed_item_fields(self): """Each parsed item dict contains the expected keys and values.""" items = parse_rss_feed(_load_rss()) first = items[0] - self.assertEqual(first["title"], "ZDI-25-001: Example Vendor Product Remote Code Execution Vulnerability") + self.assertEqual( + first["title"], "ZDI-25-001: Example Vendor Product Remote Code Execution Vulnerability" + ) self.assertEqual(first["link"], "http://www.zerodayinitiative.com/advisories/ZDI-25-001/") self.assertIn("CVE-2025-12345", first["description"]) self.assertEqual(first["pub_date"], "Mon, 06 Jan 2025 00:00:00 -0600") From 62687379aae6827cc6aa68466f5be5cc4c86045f Mon Sep 17 00:00:00 2001 From: newklei Date: Sun, 8 Mar 2026 15:40:17 -0400 Subject: [PATCH 4/5] style: sort imports in importers __init__.py Signed-off-by: newklei --- vulnerabilities/importers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index ac86c5eec..a222e144d 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -58,7 +58,6 @@ from vulnerabilities.pipelines.v2_importers import gentoo_importer as gentoo_importer_v2 from vulnerabilities.pipelines.v2_importers import github_osv_importer as github_osv_importer_v2 from vulnerabilities.pipelines.v2_importers import gitlab_importer as gitlab_importer_v2 -from vulnerabilities.pipelines.v2_importers import zdi_importer as zdi_importer_v2 from vulnerabilities.pipelines.v2_importers import istio_importer as istio_importer_v2 from vulnerabilities.pipelines.v2_importers import mattermost_importer as mattermost_importer_v2 from vulnerabilities.pipelines.v2_importers import mozilla_importer as mozilla_importer_v2 @@ -83,6 +82,7 @@ from vulnerabilities.pipelines.v2_importers import ubuntu_osv_importer as ubuntu_osv_importer_v2 from vulnerabilities.pipelines.v2_importers import vulnrichment_importer as vulnrichment_importer_v2 from vulnerabilities.pipelines.v2_importers import xen_importer as xen_importer_v2 +from vulnerabilities.pipelines.v2_importers import zdi_importer as zdi_importer_v2 from vulnerabilities.utils import create_registry IMPORTERS_REGISTRY = create_registry( From 8517dc59d5b595fcf24d5b9789e0629eb523908e Mon Sep 17 00:00:00 2001 From: newklei Date: Sun, 8 Mar 2026 15:47:48 -0400 Subject: [PATCH 5/5] fix: address reviewer feedback on ZDI importer - Remove repo_url field (not needed by base class) - Remove seen_ids deduplication set - Replace strptime/PUBDATE_FORMAT with dateparser.parse() - Use find_all_cve from utils.py instead of local CVE_RE - Add CVSS score parsing from RSS description field - Update expected test fixture to include CVSS severity Signed-off-by: newklei --- .../pipelines/v2_importers/zdi_importer.py | 26 ++++++++++++------- .../zdi/expected_zdi_advisory_output1.json | 8 +++++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/vulnerabilities/pipelines/v2_importers/zdi_importer.py b/vulnerabilities/pipelines/v2_importers/zdi_importer.py index b055f0efa..158764597 100644 --- a/vulnerabilities/pipelines/v2_importers/zdi_importer.py +++ b/vulnerabilities/pipelines/v2_importers/zdi_importer.py @@ -14,18 +14,22 @@ from typing import Iterable from xml.etree import ElementTree +import dateparser + from vulnerabilities.importer import AdvisoryDataV2 from vulnerabilities.importer import ReferenceV2 +from vulnerabilities.importer import VulnerabilitySeverity from vulnerabilities.pipelines import VulnerableCodeBaseImporterPipelineV2 +from vulnerabilities.severity_systems import GENERIC from vulnerabilities.utils import fetch_response +from vulnerabilities.utils import find_all_cve logger = logging.getLogger(__name__) ZDI_RSS_YEAR_URL = "https://www.zerodayinitiative.com/rss/published/{year}/" ZDI_START_YEAR = 2007 ZDI_ID_RE = re.compile(r"ZDI-\d+-\d+") -CVE_RE = re.compile(r"CVE-\d{4}-\d{4,7}") -PUBDATE_FORMAT = "%a, %d %b %Y %H:%M:%S %z" +CVSS_RE = re.compile(r"CVSS rating of (\d+\.?\d*)") class ZDIImporterPipeline(VulnerableCodeBaseImporterPipelineV2): @@ -34,7 +38,6 @@ class ZDIImporterPipeline(VulnerableCodeBaseImporterPipelineV2): pipeline_id = "zdi_importer" spdx_license_expression = "LicenseRef-scancode-proprietary-license" license_url = "https://www.zerodayinitiative.com" - repo_url = "https://www.zerodayinitiative.com" precedence = 200 @classmethod @@ -50,7 +53,6 @@ def collect_advisories(self) -> Iterable[AdvisoryDataV2]: ZDI_RSS_YEAR_URL.format(year=year) for year in range(ZDI_START_YEAR, current_year + 1) ] - seen_ids = set() for url in urls: self.log(f"Fetching ZDI RSS feed: {url}") try: @@ -62,8 +64,7 @@ def collect_advisories(self) -> Iterable[AdvisoryDataV2]: for item in items: advisory = parse_advisory_data(item) - if advisory and advisory.advisory_id not in seen_ids: - seen_ids.add(advisory.advisory_id) + if advisory: yield advisory @@ -115,15 +116,19 @@ def parse_advisory_data(item: dict): return None advisory_id = match.group(0) - aliases = list(dict.fromkeys(CVE_RE.findall(description))) + aliases = list(dict.fromkeys(find_all_cve(description))) date_published = None if pub_date_str: - try: - date_published = datetime.strptime(pub_date_str, PUBDATE_FORMAT) - except ValueError: + date_published = dateparser.parse(pub_date_str) + if date_published is None: logger.warning("Could not parse date %r for advisory %s", pub_date_str, advisory_id) + severities = [] + cvss_match = CVSS_RE.search(description) + if cvss_match: + severities.append(VulnerabilitySeverity(system=GENERIC, value=cvss_match.group(1))) + references = [] if link: references.append(ReferenceV2(url=link)) @@ -135,5 +140,6 @@ def parse_advisory_data(item: dict): affected_packages=[], references=references, date_published=date_published, + severities=severities, url=link, ) diff --git a/vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json b/vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json index 70324f1a8..6a8b0c294 100644 --- a/vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json +++ b/vulnerabilities/tests/test_data/zdi/expected_zdi_advisory_output1.json @@ -13,7 +13,13 @@ } ], "patches": [], - "severities": [], + "severities": [ + { + "system": "generic_textual", + "value": "8.8", + "scoring_elements": "" + } + ], "date_published": "2025-01-06T00:00:00-06:00", "weaknesses": [], "url": "http://www.zerodayinitiative.com/advisories/ZDI-25-001/"