From 2360b7956022159bb34f48227eebd84461f23916 Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Wed, 10 Jul 2024 11:54:50 +0100 Subject: [PATCH 1/5] Introduced a method to clean cpe uri Signed-off-by: Prabhu Subramanian --- pyproject.toml | 2 +- test/data/CVE-2021-27434.json | 138 ++++++++++++++++++++++++++++++++++ test/test_source.py | 24 +++++- test/test_utils.py | 15 ++++ vdb/lib/cve.py | 17 ++++- vdb/lib/db6.py | 4 +- vdb/lib/nvd.py | 8 +- vdb/lib/utils.py | 9 +++ 8 files changed, 202 insertions(+), 15 deletions(-) create mode 100644 test/data/CVE-2021-27434.json diff --git a/pyproject.toml b/pyproject.toml index 08ff5ae..1547ec1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "appthreat-vulnerability-db" -version = "6.0.8" +version = "6.0.9" description = "AppThreat's vulnerability database and package search library with a built-in sqlite based storage. OSV, CVE, GitHub, npm are the primary sources of vulnerabilities." authors = [ {name = "Team AppThreat", email = "cloud@appthreat.com"}, diff --git a/test/data/CVE-2021-27434.json b/test/data/CVE-2021-27434.json new file mode 100644 index 0000000..9af385d --- /dev/null +++ b/test/data/CVE-2021-27434.json @@ -0,0 +1,138 @@ +{ + "id": "CVE-2021-27434", + "sourceIdentifier": "ics-cert@hq.dhs.gov", + "published": "2021-05-20T14:15:07.767", + "lastModified": "2023-10-15T16:18:45.880", + "vulnStatus": "Analyzed", + "descriptions": [ + { + "lang": "en", + "value": "Products with Unified Automation .NET based OPC UA Client/Server SDK Bundle: Versions V3.0.7 and prior (.NET 4.5, 4.0, and 3.5 Framework versions only) are vulnerable to an uncontrolled recursion, which may allow an attacker to trigger a stack overflow." + }, + { + "lang": "es", + "value": "Productos con el programa Unified Automation .NET based OPC UA Client/Server SDK Bundle: Versiones V3.0.7 y anteriores (solo versiones de .NET 4.5, 4.0 y 3.5 Framework) son vulnerables a una recursividad no controlada, que puede permitir a un atacante desencadenar un desbordamiento de pila" + } + ], + "metrics": { + "cvssMetricV31": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "3.1", + "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", + "attackVector": "NETWORK", + "attackComplexity": "LOW", + "privilegesRequired": "NONE", + "userInteraction": "NONE", + "scope": "UNCHANGED", + "confidentialityImpact": "HIGH", + "integrityImpact": "NONE", + "availabilityImpact": "NONE", + "baseScore": 7.5, + "baseSeverity": "HIGH" + }, + "exploitabilityScore": 3.9, + "impactScore": 3.6 + } + ], + "cvssMetricV2": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "2.0", + "vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N", + "accessVector": "NETWORK", + "accessComplexity": "LOW", + "authentication": "NONE", + "confidentialityImpact": "PARTIAL", + "integrityImpact": "NONE", + "availabilityImpact": "NONE", + "baseScore": 5 + }, + "baseSeverity": "MEDIUM", + "exploitabilityScore": 10, + "impactScore": 2.9, + "acInsufInfo": false, + "obtainAllPrivilege": false, + "obtainUserPrivilege": false, + "obtainOtherPrivilege": false, + "userInteractionRequired": false + } + ] + }, + "weaknesses": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "description": [ + { + "lang": "en", + "value": "CWE-674" + } + ] + }, + { + "source": "ics-cert@hq.dhs.gov", + "type": "Secondary", + "description": [ + { + "lang": "en", + "value": "CWE-200" + } + ] + } + ], + "configurations": [ + { + "operator": "AND", + "nodes": [ + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": true, + "criteria": "cpe:2.3:a:unified-automation:.net_based_opc_ua_client\\/server_sdk:*:*:*:*:*:*:*:*", + "matchCriteriaId": "72FFADD0-F648-492C-9A45-1456EEDAAD06", + "versionEndIncluding": "3.0.7" + } + ] + }, + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": false, + "criteria": "cpe:2.3:a:microsoft:.net_framework:3.5:*:*:*:*:*:*:*", + "matchCriteriaId": "E039CE1F-B988-4741-AE2E-5B36E2AF9688" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:a:microsoft:.net_framework:4.0:*:*:*:*:*:*:*", + "matchCriteriaId": "792B417F-96A0-4E9D-9E79-5D7F982E2225" + }, + { + "vulnerable": false, + "criteria": "cpe:2.3:a:microsoft:.net_framework:4.5:*:*:*:*:*:*:*", + "matchCriteriaId": "61FAD9EE-FA7F-4B39-8A9B-AFFAEC8BF214" + } + ] + } + ] + } + ], + "references": [ + { + "url": "https://us-cert.cisa.gov/ics/advisories/icsa-21-133-04", + "source": "ics-cert@hq.dhs.gov", + "tags": [ + "Third Party Advisory", + "US Government Resource" + ] + } + ] +} diff --git a/test/test_source.py b/test/test_source.py index 483f387..7fe1073 100644 --- a/test/test_source.py +++ b/test/test_source.py @@ -12,12 +12,11 @@ from vdb.lib.nvd import NvdSource from vdb.lib.osv import OSVSource -# Temp directories were not working on macOS GitHub runners test_tmp_dir = os.getenv("TEST_VDB_HOME", tempfile.mkdtemp(prefix="vdb6-tests-")) os.makedirs(test_tmp_dir, exist_ok=True) -db6.get( - os.path.join(test_tmp_dir, "data.vdb6"), os.path.join(test_tmp_dir, "index.vdb6") -) + +# Use in-memory database for testing +db6.get(":memory:", ":memory:") @pytest.fixture @@ -83,6 +82,15 @@ def test_nvd_api_git_json(): return json.loads(fp.read()) +@pytest.fixture +def test_nvd_api_cpes_json(): + test_cve_data = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2021-27434.json" + ) + with open(test_cve_data, mode="r", encoding="utf-8") as fp: + return json.loads(fp.read()) + + @pytest.fixture def test_osv_rust_json(): test_cve_data = os.path.join( @@ -663,6 +671,14 @@ def test_nvd_api_convert( assert results_count == 1 +def test_nvd_api_convert2(test_nvd_api_cpes_json): + nvdlatest = NvdSource() + #cpes_json + vulnerabilities = nvdlatest.convert(test_nvd_api_cpes_json) + assert len(vulnerabilities) == 1 + assert len(vulnerabilities[0].details) == 2 + + @pytest.mark.skip(reason="This downloads and tests with live data") def test_nvd_download(): nvdlatest = NvdSource() diff --git a/test/test_utils.py b/test/test_utils.py index a7f484d..c70468f 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -1022,3 +1022,18 @@ def test_vers_compare(): assert not utils.vers_compare("1.13.0", "vers:rpm/<=1.12.8-24.el8_8.1") assert utils.vers_compare("5.5", "vers:deb/<5.6~rc2") assert not utils.vers_compare("5.7", "vers:deb/<5.6~rc2") + + +def test_clean_cpe_uri(): + test_tuples = ( + [None, None], + ["cpe:2.3:a:unified-automation:.net_based_opc_ua_client\\/server_sdk:*:*:*:*:*:*:*:*", "cpe:2.3:a:unified-automation:.net_based_opc_ua_client/server_sdk:*:*:*:*:*:*:*:*"], + ["cpe:2.3:a:cisco:dna_spaces\\:_connector:*:*:*:*:*:*:*:*", "cpe:2.3:a:cisco:dna_spaces:_connector:*:*:*:*:*:*:*:*"], + ["cpe:2.3:a:trendmicro:antivirus_\\+_security_2019:15.0:*:*:*:*:*:*:*", "cpe:2.3:a:trendmicro:antivirus_security_2019:15.0:*:*:*:*:*:*:*"], + ["cpe:2.3:a:eleopard:animate_it\\!:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:eleopard:animate_it:*:*:*:*:*:wordpress:*:*"], + ["cpe:2.3:a:icegram:email_subscribers_\\\u0026_newsletters:*:*:*:*:*:wordpress:*:*", "cpe:2.3:a:icegram:email_subscribers_newsletters:*:*:*:*:*:wordpress:*:*"], + ["cpe:2.3:a:display_post_meta,_term_meta,_comment_meta,_and_user_meta_project/display_post_meta,_term_meta,_comment_meta,_and_user_meta", "cpe:2.3:a:display_post_meta_term_meta_comment_meta_and_user_meta_project/display_post_meta_term_meta_comment_meta_and_user_meta"], + ["cpe:2.3:a:[gwa]_autoresponder_project/[gwa]_autoresponder", "cpe:2.3:a:gwa_autoresponder_project/gwa_autoresponder"] + ) + for tt in test_tuples: + assert utils.clean_cpe_uri(tt[0]) == tt[1] diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py index 2c57754..1b8695e 100644 --- a/vdb/lib/cve.py +++ b/vdb/lib/cve.py @@ -1,5 +1,6 @@ import base64 import os +import re import uuid import orjson @@ -164,6 +165,14 @@ def to_product_versions(vendor, adetail: VulnerabilityDetail) -> list[Versions]: return versions +def clean_vendor_product(s): + if not s: + return s + s = s.removeprefix("_").removesuffix("_") + s = re.sub(r"[&,()+]", "", s) + return s + + def to_cve_affected(avuln: Vulnerability) -> Affected | None: products = [] adetail: VulnerabilityDetail @@ -178,7 +187,7 @@ def to_cve_affected(avuln: Vulnerability) -> Affected | None: # Similar to purl namespace product = parts.group("package").removesuffix("\\").removesuffix("!") # Similar to purl name - package_name = parts.group("package") + package_name = product if "/" in product: tmp_a = product.split("/") # ubuntu/upstream/virtualbox should become @@ -235,9 +244,9 @@ def to_cve_affected(avuln: Vulnerability) -> Affected | None: vendor = k break p = Product( - vendor=vendor, - product=product, - packageName=package_name, + vendor=clean_vendor_product(vendor), + product=clean_vendor_product(product), + packageName=clean_vendor_product(package_name), cpes=[Cpe(cpe_uri)], defaultStatus=Status.unknown, versions=versions, diff --git a/vdb/lib/db6.py b/vdb/lib/db6.py index 468dd6b..aaaad6a 100644 --- a/vdb/lib/db6.py +++ b/vdb/lib/db6.py @@ -27,9 +27,9 @@ def get(db_file: str = config.VDB_BIN_FILE, index_file: str = config.VDB_BIN_IND apsw.Connection, apsw.Connection): """Gets the connection to the index and the data databases. Raises apsw.CantOpenError if the database is not available.""" global db_conn, index_conn, tables_created - if not db_file.startswith("file:"): + if not db_file.startswith("file:") and db_file != ":memory:": db_file = f"file:{DB_FILE_SEP}{os.path.abspath(db_file)}" - if not index_file.startswith("file:"): + if not index_file.startswith("file:") and index_file != ":memory:": index_file = f"file:{DB_FILE_SEP}{os.path.abspath(index_file)}" rw_flags = apsw.SQLITE_OPEN_URI | apsw.SQLITE_OPEN_NOFOLLOW | apsw.SQLITE_OPEN_CREATE | apsw.SQLITE_OPEN_READWRITE ro_flags = apsw.SQLITE_OPEN_URI | apsw.SQLITE_OPEN_NOFOLLOW | apsw.SQLITE_OPEN_READONLY diff --git a/vdb/lib/nvd.py b/vdb/lib/nvd.py index 9be9605..5d20e17 100644 --- a/vdb/lib/nvd.py +++ b/vdb/lib/nvd.py @@ -16,7 +16,7 @@ config, ) from vdb.lib.cve import CVESource -from vdb.lib.utils import url_to_purl +from vdb.lib.utils import clean_cpe_uri, url_to_purl logging.basicConfig( @@ -303,7 +303,7 @@ def convert_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None: if not cpe.get("cpe23Uri"): continue if cpe["vulnerable"]: - detail["cpe_uri"] = cpe.get("cpe23Uri") + detail["cpe_uri"] = clean_cpe_uri(cpe.get("cpe23Uri")) detail["mii"] = cpe.get("versionStartIncluding") detail["mie"] = cpe.get("versionStartExcluding") detail["mai"] = cpe.get("versionEndIncluding") @@ -313,7 +313,7 @@ def convert_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None: cpe_details_list.append(detail) else: # cpe is not vulnerable if node["operator"] == "OR": - fix_cpe_uri = cpe["cpe23Uri"] + fix_cpe_uri = clean_cpe_uri(cpe["cpe23Uri"]) # Add fix version details for det in cpe_details_list: if fix_cpe_uri: @@ -377,7 +377,7 @@ def convert_api_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None: ) version_end_including = cpe.get("versionEndIncluding", single_version) detail = { - "cpe_uri": cpe_uri, + "cpe_uri": clean_cpe_uri(cpe_uri), "mii": version_start_including, "mie": cpe.get("versionStartExcluding"), "mai": version_end_including, diff --git a/vdb/lib/utils.py b/vdb/lib/utils.py index 2b0efe4..10550fd 100644 --- a/vdb/lib/utils.py +++ b/vdb/lib/utils.py @@ -1185,3 +1185,12 @@ def url_to_purl(url: str) -> dict | None: if not purl_obj["version"] and version: purl_obj["version"] = version return purl_obj + + +def clean_cpe_uri(cpe_uri): + if not cpe_uri: + return cpe_uri + cpe_uri = re.sub(r"[\\!&,()+\[\]]" , "", cpe_uri) + cpe_uri = cpe_uri.replace("_-_" , "-") + cpe_uri = cpe_uri.replace("__" , "_") + return cpe_uri From 738b6d5a2279f9b95097a4d0b97b761238f14925 Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Wed, 10 Jul 2024 14:52:50 +0100 Subject: [PATCH 2/5] Clean purl prefix and vers Signed-off-by: Prabhu Subramanian --- test/test_utils.py | 12 ++++++++++++ vdb/lib/config.py | 4 ++-- vdb/lib/cve.py | 4 ++-- vdb/lib/utils.py | 3 +++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/test/test_utils.py b/test/test_utils.py index c70468f..e80088a 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -900,6 +900,18 @@ def test_purl_vers_convert(): ], "vers:generic/<001a3278b5572e52c0ecac0bd1157bf2599502b7", ), + ( + "cisco", + [ + { + "version": "6.3\(1\)", + "status": "affected", + "versionType": "cisco", + "lessThanOrEqual": "6.3\(1\)", + } + ], + "vers:cisco/6.3-1", + ), ] for tt in test_tuples: assert utils.to_purl_vers(tt[0], tt[1]) == tt[2] diff --git a/vdb/lib/config.py b/vdb/lib/config.py index 74b7a96..c318791 100644 --- a/vdb/lib/config.py +++ b/vdb/lib/config.py @@ -5,8 +5,8 @@ # NVD CVE json feed url NVD_URL = "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%(year)s.json.gz" -# NVD start year. 2018 is quicker. 2002 is quite detailed but slow -NVD_START_YEAR = os.getenv("NVD_START_YEAR", "2018") +# NVD start year. 2019 is quicker. 2002 is quite detailed but slow +NVD_START_YEAR = os.getenv("NVD_START_YEAR", "2019") try: NVD_START_YEAR = int(NVD_START_YEAR) except ValueError: diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py index 1b8695e..3b22019 100644 --- a/vdb/lib/cve.py +++ b/vdb/lib/cve.py @@ -488,8 +488,8 @@ def store5(self, data: list[CVE]): ) purl_prefix = f"""pkg:{purl_type}/""" if affected.product: - purl_prefix = f"{purl_prefix}{affected.product}/" - purl_prefix = f"{purl_prefix}{affected.packageName}" + purl_prefix = f"{purl_prefix}{affected.product.replace("@", "%40").removesuffix("_")}" + purl_prefix = f"{purl_prefix}/{affected.packageName.removeprefix("_")}" pkg_key = f"{cve_id}|{affected.vendor}|{affected.product}|{affected.packageName}|{source_hash}" index_pkg_key = f"{cve_id}|{affected.vendor}|{affected.product}|{affected.packageName}|{vers}" # Filter obvious duplicates diff --git a/vdb/lib/utils.py b/vdb/lib/utils.py index 10550fd..3141ce5 100644 --- a/vdb/lib/utils.py +++ b/vdb/lib/utils.py @@ -1103,6 +1103,8 @@ def to_purl_vers(vendor: str, versions: list) -> str: vers_list.append("*") continue elif version == less_than_or_equal: + if "(" in version: + version = version.replace("(", "-").replace(")", "").replace("\\", "") vers_list.append(version) continue else: @@ -1192,5 +1194,6 @@ def clean_cpe_uri(cpe_uri): return cpe_uri cpe_uri = re.sub(r"[\\!&,()+\[\]]" , "", cpe_uri) cpe_uri = cpe_uri.replace("_-_" , "-") + cpe_uri = cpe_uri.replace("_/_" , "/") cpe_uri = cpe_uri.replace("__" , "_") return cpe_uri From 75138e8eff3534b832e2cc73296dd442040edaed Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Wed, 10 Jul 2024 15:47:58 +0100 Subject: [PATCH 3/5] Fix tests Signed-off-by: Prabhu Subramanian --- test/test_utils.py | 4 ++-- vdb/lib/cve.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_utils.py b/test/test_utils.py index e80088a..bd10fb1 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -904,10 +904,10 @@ def test_purl_vers_convert(): "cisco", [ { - "version": "6.3\(1\)", + "version": r"6.3\(1\)", "status": "affected", "versionType": "cisco", - "lessThanOrEqual": "6.3\(1\)", + "lessThanOrEqual": r"6.3\(1\)", } ], "vers:cisco/6.3-1", diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py index 3b22019..258d6fe 100644 --- a/vdb/lib/cve.py +++ b/vdb/lib/cve.py @@ -488,8 +488,8 @@ def store5(self, data: list[CVE]): ) purl_prefix = f"""pkg:{purl_type}/""" if affected.product: - purl_prefix = f"{purl_prefix}{affected.product.replace("@", "%40").removesuffix("_")}" - purl_prefix = f"{purl_prefix}/{affected.packageName.removeprefix("_")}" + purl_prefix = f'{purl_prefix}{affected.product.replace("@", "%40").removesuffix("_")}/' + purl_prefix = f'{purl_prefix}{affected.packageName.removeprefix("_")}' pkg_key = f"{cve_id}|{affected.vendor}|{affected.product}|{affected.packageName}|{source_hash}" index_pkg_key = f"{cve_id}|{affected.vendor}|{affected.product}|{affected.packageName}|{vers}" # Filter obvious duplicates From 98631a4110749d1f5c0aa67a4b6af4823807424c Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Wed, 10 Jul 2024 16:41:46 +0100 Subject: [PATCH 4/5] Make use of target_sw to improve purl prefix for generic packages Signed-off-by: Prabhu Subramanian --- test/data/CVE-2018-9840.json | 123 +++++++++++++++++++++++++++++++++++ test/test_source.py | 43 ++++++++++++ vdb/lib/__init__.py | 7 +- vdb/lib/cve.py | 11 ++++ 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 test/data/CVE-2018-9840.json diff --git a/test/data/CVE-2018-9840.json b/test/data/CVE-2018-9840.json new file mode 100644 index 0000000..a671cb5 --- /dev/null +++ b/test/data/CVE-2018-9840.json @@ -0,0 +1,123 @@ +{ + "id": "CVE-2018-9840", + "sourceIdentifier": "cve@mitre.org", + "published": "2018-04-10T05:29:00.207", + "lastModified": "2019-10-03T00:03:26.223", + "vulnStatus": "Analyzed", + "descriptions": [ + { + "lang": "en", + "value": "The Open Whisper Signal app before 2.23.2 for iOS allows physically proximate attackers to bypass the screen locker feature via certain rapid sequences of actions that include app opening, clicking on cancel, and using the home button." + }, + { + "lang": "es", + "value": "La aplicación Open Whisper Signal, en versiones anteriores a la 2.23.2 para iOS, permite que atacantes cercanos físicamente omitan la característica de bloqueo de pantalla mediante determinadas secuencias rápidas de acciones que incluyen la apertura de apps, clics en cancelar y el uso del botón de inicio." + } + ], + "metrics": { + "cvssMetricV30": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "3.0", + "vectorString": "CVSS:3.0/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + "attackVector": "PHYSICAL", + "attackComplexity": "LOW", + "privilegesRequired": "NONE", + "userInteraction": "NONE", + "scope": "UNCHANGED", + "confidentialityImpact": "HIGH", + "integrityImpact": "HIGH", + "availabilityImpact": "HIGH", + "baseScore": 6.8, + "baseSeverity": "MEDIUM" + }, + "exploitabilityScore": 0.9, + "impactScore": 5.9 + } + ], + "cvssMetricV2": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "cvssData": { + "version": "2.0", + "vectorString": "AV:L/AC:L/Au:N/C:P/I:P/A:P", + "accessVector": "LOCAL", + "accessComplexity": "LOW", + "authentication": "NONE", + "confidentialityImpact": "PARTIAL", + "integrityImpact": "PARTIAL", + "availabilityImpact": "PARTIAL", + "baseScore": 4.6 + }, + "baseSeverity": "MEDIUM", + "exploitabilityScore": 3.9, + "impactScore": 6.4, + "acInsufInfo": false, + "obtainAllPrivilege": false, + "obtainUserPrivilege": false, + "obtainOtherPrivilege": false, + "userInteractionRequired": false + } + ] + }, + "weaknesses": [ + { + "source": "nvd@nist.gov", + "type": "Primary", + "description": [ + { + "lang": "en", + "value": "NVD-CWE-noinfo" + } + ] + } + ], + "configurations": [ + { + "nodes": [ + { + "operator": "OR", + "negate": false, + "cpeMatch": [ + { + "vulnerable": true, + "criteria": "cpe:2.3:a:signal:signal:*:*:*:*:*:iphone_os:*:*", + "matchCriteriaId": "E791C3AF-A1F9-4564-AAB1-FAE3D608F176", + "versionEndExcluding": "2.23.2" + } + ] + } + ] + } + ], + "references": [ + { + "url": "http://nint.en.do/Signal-Bypass-Screen-locker.php", + "source": "cve@mitre.org", + "tags": [ + "Broken Link", + "Third Party Advisory" + ] + }, + { + "url": "https://github.com/signalapp/Signal-iOS/commit/018a35df7b42b4941cb4dfc9f462b37c3fafd9e9", + "source": "cve@mitre.org", + "tags": [ + "Patch", + "Third Party Advisory" + ] + }, + { + "url": "https://github.com/signalapp/Signal-iOS/commits/release/2.23.2", + "source": "cve@mitre.org", + "tags": [ + "Issue Tracking", + "Patch", + "Third Party Advisory" + ] + } + ] +} \ No newline at end of file diff --git a/test/test_source.py b/test/test_source.py index 7fe1073..60c8719 100644 --- a/test/test_source.py +++ b/test/test_source.py @@ -91,6 +91,15 @@ def test_nvd_api_cpes_json(): return json.loads(fp.read()) +@pytest.fixture +def test_nvd_api_signal_json(): + test_cve_data = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2018-9840.json" + ) + with open(test_cve_data, mode="r", encoding="utf-8") as fp: + return json.loads(fp.read()) + + @pytest.fixture def test_osv_rust_json(): test_cve_data = os.path.join( @@ -677,6 +686,40 @@ def test_nvd_api_convert2(test_nvd_api_cpes_json): vulnerabilities = nvdlatest.convert(test_nvd_api_cpes_json) assert len(vulnerabilities) == 1 assert len(vulnerabilities[0].details) == 2 + db6.clear_all() + nvdlatest.store(vulnerabilities) + cve_data_count, cve_index_count = db6.stats() + assert cve_data_count == 1 + assert cve_index_count == 1 + results_count = len(list(search.search_by_any("CVE-2021-27434"))) + assert results_count == 1 + results_count = len( + list( + search.search_by_any(".net_based_opc_ua_client:server_sdk:*") + ) + ) + assert results_count == 1 + + +def test_nvd_api_convert3(test_nvd_api_signal_json): + db6.clear_all() + nvdlatest = NvdSource() + # signal ios + vulnerabilities = nvdlatest.convert(test_nvd_api_signal_json) + assert len(vulnerabilities) == 1 + assert len(vulnerabilities[0].details) == 2 + nvdlatest.store(vulnerabilities) + cve_data_count, cve_index_count = db6.stats() + assert cve_data_count == 2 + assert cve_index_count == 2 + results_count = len(list(search.search_by_any("CVE-2018-9840"))) + assert results_count == 2 + results_count = len( + list( + search.search_by_any("pkg:generic/ios/signal") + ) + ) + assert results_count == 1 @pytest.mark.skip(reason="This downloads and tests with live data") diff --git a/vdb/lib/__init__.py b/vdb/lib/__init__.py index d9a04ef..3aad79d 100644 --- a/vdb/lib/__init__.py +++ b/vdb/lib/__init__.py @@ -104,7 +104,7 @@ "composer": ["php", "laravel", "wordpress", "joomla"], "maven": ["jenkins", "java", "kotlin", "groovy", "clojars", "hackage"], "npm": ["javascript", "node.js", "nodejs", "npmjs"], - "nuget": [".net_framework", "csharp", ".net_core", "asp.net"], + "nuget": [".net_framework", "csharp", ".net_core", "asp.net", "c#"], "pypi": ["python"], "gem": ["ruby"], "rubygems": ["ruby", "gem"], @@ -127,7 +127,10 @@ "fedoraproject" ], "alpm": ["arch", "archlinux"], - "ebuild": ["gentoo", "portage"] + "ebuild": ["gentoo", "portage"], + "ios": ["iphoneos", "iphone_os"], + "vscode": ["vs_code", "visual_studio_code"], + "macos": ["mac_os_x", "osx"] } # CPE Regex diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py index 258d6fe..f20a29b 100644 --- a/vdb/lib/cve.py +++ b/vdb/lib/cve.py @@ -188,6 +188,14 @@ def to_cve_affected(avuln: Vulnerability) -> Affected | None: product = parts.group("package").removesuffix("\\").removesuffix("!") # Similar to purl name package_name = product + target_sw = parts.group("target_sw") + # NVD can represent the same target_sw with many different names + # Eg: npm, node.js, nodejs + if target_sw and len(target_sw) > 2: + for k, v in PKG_TYPES_MAP.items(): + if target_sw.lower() == k or target_sw.lower() in v: + target_sw = k + break if "/" in product: tmp_a = product.split("/") # ubuntu/upstream/virtualbox should become @@ -243,6 +251,9 @@ def to_cve_affected(avuln: Vulnerability) -> Affected | None: if vendor.lower() in v: vendor = k break + # We don't have a vendor, but valid target_sw + if not product and target_sw and len(target_sw) > 2: + product = target_sw p = Product( vendor=clean_vendor_product(vendor), product=clean_vendor_product(product), From a68f3611d8139bd825af599a94c4ee205ae56829 Mon Sep 17 00:00:00 2001 From: Prabhu Subramanian Date: Wed, 10 Jul 2024 17:40:29 +0100 Subject: [PATCH 5/5] Keep 2018 as the default year Signed-off-by: Prabhu Subramanian --- vdb/lib/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vdb/lib/config.py b/vdb/lib/config.py index c318791..74b7a96 100644 --- a/vdb/lib/config.py +++ b/vdb/lib/config.py @@ -5,8 +5,8 @@ # NVD CVE json feed url NVD_URL = "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%(year)s.json.gz" -# NVD start year. 2019 is quicker. 2002 is quite detailed but slow -NVD_START_YEAR = os.getenv("NVD_START_YEAR", "2019") +# NVD start year. 2018 is quicker. 2002 is quite detailed but slow +NVD_START_YEAR = os.getenv("NVD_START_YEAR", "2018") try: NVD_START_YEAR = int(NVD_START_YEAR) except ValueError: