diff --git a/pyproject.toml b/pyproject.toml index 9409ea1..b1cb7a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "appthreat-vulnerability-db" -version = "6.0.6" +version = "6.0.7" 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/GHSA-vc2p-r46x-m3vx.json b/test/data/GHSA-vc2p-r46x-m3vx.json new file mode 100644 index 0000000..9104034 --- /dev/null +++ b/test/data/GHSA-vc2p-r46x-m3vx.json @@ -0,0 +1,145 @@ +{ + "id": "GHSA-vc2p-r46x-m3vx", + "summary": "Argument injection in lettre", + "details": "### Impact\n\nAffected versions of lettre allowed argument injection to the sendmail command. It was possible, using forged to addresses, to pass arbitrary arguments to the sendmail executable.\n\nDepending on the implementation (original sendmail, postfix, exim, etc.) it could be possible in some cases to write email data into abritrary files (using sendmail's logging features).\n\n*NOTE*: This vulnerability only affects the sendmail transport. Others, including smtp, are not affected.\n\n### Fix\n\nThe flaw is corrected by modifying the executed command to stop parsing arguments before passing the destination addresses.\n\n### References\n\n* [RUSTSEC-2020-0069](https://rustsec.org/advisories/RUSTSEC-2020-0069.html)\n* [CVE-2020-28247](https://nvd.nist.gov/vuln/detail/CVE-2020-28247)", + "aliases": [ + "CVE-2020-28247", + "RUSTSEC-2020-0069" + ], + "modified": "2023-11-08T04:03:24.160094Z", + "published": "2021-08-25T20:56:48Z", + "database_specific": { + "nvd_published_at": null, + "cwe_ids": [ + "CWE-77" + ], + "severity": "MODERATE", + "github_reviewed": true, + "github_reviewed_at": "2021-08-18T20:59:57Z" + }, + "references": [ + { + "type": "WEB", + "url": "https://github.com/lettre/lettre/security/advisories/GHSA-vc2p-r46x-m3vx" + }, + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-28247" + }, + { + "type": "WEB", + "url": "https://github.com/RustSec/advisory-db/pull/478/files" + }, + { + "type": "WEB", + "url": "https://github.com/lettre/lettre/pull/508/commits/bbe7cc5381c5380b54fb8bbb4f77a3725917ff0b" + }, + { + "type": "PACKAGE", + "url": "https://github.com/lettre/lettre" + }, + { + "type": "WEB", + "url": "https://rustsec.org/advisories/RUSTSEC-2020-0069.html" + } + ], + "affected": [ + { + "package": { + "name": "lettre", + "ecosystem": "crates.io", + "purl": "pkg:cargo/lettre" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0.9.0" + }, + { + "fixed": "0.9.5" + } + ] + } + ], + "ecosystem_specific": { + "affected_functions": [ + "lettre::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send_raw" + ] + }, + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2021/08/GHSA-vc2p-r46x-m3vx/GHSA-vc2p-r46x-m3vx.json" + } + }, + { + "package": { + "name": "lettre", + "ecosystem": "crates.io", + "purl": "pkg:cargo/lettre" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0.8.0" + }, + { + "fixed": "0.8.4" + } + ] + } + ], + "ecosystem_specific": { + "affected_functions": [ + "lettre::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send_raw" + ] + }, + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2021/08/GHSA-vc2p-r46x-m3vx/GHSA-vc2p-r46x-m3vx.json" + } + }, + { + "package": { + "name": "lettre", + "ecosystem": "crates.io", + "purl": "pkg:cargo/lettre" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0.7.0" + }, + { + "fixed": "0.7.1" + } + ] + } + ], + "ecosystem_specific": { + "affected_functions": [ + "lettre::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send_raw" + ] + }, + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2021/08/GHSA-vc2p-r46x-m3vx/GHSA-vc2p-r46x-m3vx.json" + } + } + ], + "schema_version": "1.6.0", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N" + } + ] +} \ No newline at end of file diff --git a/test/data/GHSA-xrjj-mj9h-534m.json b/test/data/GHSA-xrjj-mj9h-534m.json new file mode 100644 index 0000000..9982bef --- /dev/null +++ b/test/data/GHSA-xrjj-mj9h-534m.json @@ -0,0 +1,183 @@ +{ + "id": "GHSA-xrjj-mj9h-534m", + "summary": "golang.org/x/net/http2 vulnerable to possible excessive memory growth", + "details": "An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests. HTTP/2 server connections contain a cache of HTTP header keys sent by the client. While the total number of entries in this cache is capped, an attacker sending very large keys can cause the server to allocate approximately 64 MiB per open connection.", + "aliases": [ + "BIT-golang-2022-41717", + "CVE-2022-41717", + "GO-2022-1144" + ], + "modified": "2024-05-20T21:41:40Z", + "published": "2022-12-08T21:30:19Z", + "database_specific": { + "nvd_published_at": "2022-12-08T20:15:00Z", + "cwe_ids": [ + "CWE-770" + ], + "severity": "MODERATE", + "github_reviewed": true, + "github_reviewed_at": "2023-01-18T00:05:16Z" + }, + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41717" + }, + { + "type": "WEB", + "url": "https://security.gentoo.org/glsa/202311-09" + }, + { + "type": "WEB", + "url": "https://pkg.go.dev/vuln/GO-2022-1144" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ZSVEMQV5ROY5YW5QE3I57HT3ITWG5GCV" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/WPEIZ7AMEJCZXU3FEJZMVRNHQZXX5P3I" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/T7N5GV4CHH6WAGX3GFMDD3COEOVCZ4RI" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/REMHVVIBDNKSRKNOTV7EQSB7CYQWOUOU" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/QBKBAZBIOXZV5QCFHZNSVXULR32XJCYD" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/Q52IQI754YAE4XPR4QBRWPIVZWYGZ4FS" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/PW3XC47AUW5J5M2ULJX7WCCL3B2ETLMT" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/PUM4DIVOLJCBK5ZDP4LJOL24GXT3YSIR" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/NQGNAXK3YBPMUP3J4TECIRDHFGW37522" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KEOTKBUPZXHE3F352JBYNTSNRXYLWD6P" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/CSVIS6MTMFVBA7JPMRAUNKUOYEVSJYSB" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/CHHITS4PUOZAKFIUBQAQZC7JWXMOYE4B" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ANIOPUXWIHVRA6CEWXCGOMX3YYS6KFHG" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5RSKA2II6QTD4YUKUNDVJQSRYSFC4VFR" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/56B2FFESRYYP6IY2AZ3UWXLWKZ5IYZN4" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/4SBIUECMLNC572P23DDOKJNKPJVX26SP" + }, + { + "type": "WEB", + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/4BUK2ZIAGCULOOYDNH25JPU6JBES5NF2" + }, + { + "type": "WEB", + "url": "https://groups.google.com/g/golang-announce/c/L_3rmdT0BMU/m/yZDrXjIiBQAJ" + }, + { + "type": "WEB", + "url": "https://go.dev/issue/56350" + }, + { + "type": "WEB", + "url": "https://go.dev/cl/455717" + }, + { + "type": "WEB", + "url": "https://go.dev/cl/455635" + }, + { + "type": "PACKAGE", + "url": "https://cs.opensource.google/go/x/net" + } + ], + "affected": [ + { + "package": { + "name": "golang.org/x/net/http2", + "ecosystem": "Go", + "purl": "pkg:golang/golang.org/x/net/http2" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.4.0" + } + ] + } + ], + "ecosystem_specific": { + "affected_functions": [ + "Server.ServeConn" + ] + }, + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/12/GHSA-xrjj-mj9h-534m/GHSA-xrjj-mj9h-534m.json" + } + }, + { + "package": { + "name": "golang.org/x/net", + "ecosystem": "Go", + "purl": "pkg:golang/golang.org/x/net" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.4.0" + } + ] + } + ], + "database_specific": { + "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2022/12/GHSA-xrjj-mj9h-534m/GHSA-xrjj-mj9h-534m.json" + } + } + ], + "schema_version": "1.6.0", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L" + } + ] +} \ No newline at end of file diff --git a/test/data/GO-2022-0251.json b/test/data/GO-2022-0251.json new file mode 100644 index 0000000..9bb455d --- /dev/null +++ b/test/data/GO-2022-0251.json @@ -0,0 +1,65 @@ +{ + "id": "GO-2022-0251", + "summary": "Panic on NUL character in ROA in github.com/cloudflare/cfrpki", + "details": "OctoRPKI crashes when a repository returns an invalid ROA that is only an encoded NUL character (\\0).", + "aliases": [ + "CVE-2021-3910", + "GHSA-5mxh-2qfv-4g7j" + ], + "modified": "2024-05-20T16:03:47Z", + "published": "2022-07-15T23:07:28Z", + "database_specific": { + "review_status": "REVIEWED", + "url": "https://pkg.go.dev/vuln/GO-2022-0251" + }, + "references": [ + { + "type": "FIX", + "url": "https://github.com/cloudflare/cfrpki/commit/76f0f7a98da001fa04e5bc0407c6702f91096bfa" + } + ], + "affected": [ + { + "package": { + "name": "github.com/cloudflare/cfrpki", + "ecosystem": "Go", + "purl": "pkg:golang/github.com/cloudflare/cfrpki" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.4.0" + } + ] + } + ], + "ecosystem_specific": { + "imports": [ + { + "path": "github.com/cloudflare/cfrpki/validator/lib", + "symbols": [ + "BER2DER", + "DecodeManifest", + "DecoderConfig.DecodeManifest", + "readObject" + ] + } + ] + }, + "database_specific": { + "source": "https://vuln.go.dev/ID/GO-2022-0251.json" + } + } + ], + "schema_version": "1.6.0", + "credits": [ + { + "name": "Koen van Hove" + } + ] +} \ No newline at end of file diff --git a/test/data/RUSTSEC-2024-0343.json b/test/data/RUSTSEC-2024-0343.json new file mode 100644 index 0000000..ff69494 --- /dev/null +++ b/test/data/RUSTSEC-2024-0343.json @@ -0,0 +1,69 @@ +{ + "id": "RUSTSEC-2024-0343", + "summary": "Reduced entropy due to inadequate character set usage", + "details": "## Description\n\nAffected versions of the nano-id crate incorrectly generated IDs using a reduced character set in the `nano_id::base62` and `nano_id::base58` functions. Specifically, the `base62` function used a character set of 32 symbols instead of the intended 62 symbols, and the `base58` function used a character set of 16 symbols instead of the intended 58 symbols. Additionally, the `nano_id::gen` macro is also affected when a custom character set that is not a power of 2 in size is specified.\n\nIt should be noted that `nano_id::base64` is not affected by this vulnerability.\n\n## Impact\n\nThis can result in a significant reduction in entropy, making the generated IDs predictable and vulnerable to brute-force attacks when the IDs are used in security-sensitive contexts such as session tokens or unique identifiers.\n\n## Patches\n\nThe flaws were corrected in commit [a9022772b2f1ce38929b5b81eccc670ac9d3ab23](https://github.com/viz-rs/nano-id/commit/a9022772b2f1ce38929b5b81eccc670ac9d3ab23) by updating the the `nano_id::gen` macro to use all specified characters correctly.\n\n## PoC\n\n```rust\nuse std::collections::BTreeSet;\n\nfn main() {\n test_base58();\n test_base62();\n}\n\nfn test_base58() {\n let mut produced_symbols = BTreeSet::new();\n\n for _ in 0..100_000 {\nid = \"RUSTSEC-2024-0343\"\n for c in id.chars() {\n produced_symbols.insert(c);\n }\n }\n\n println!(\n \"{} symbols generated from nano_id::base58\",\n produced_symbols.len()\n );\n}\n\nfn test_base62() {\n let mut produced_symbols = BTreeSet::new();\n\n for _ in 0..100_000 {\nid = \"RUSTSEC-2024-0343\"\n for c in id.chars() {\n produced_symbols.insert(c);\n }\n }\n\n println!(\n \"{} symbols generated from nano_id::base62\",\n produced_symbols.len()\n );\n}\n```", + "modified": "2024-06-03T07:19:16Z", + "published": "2024-06-03T12:00:00Z", + "database_specific": { + "license": "CC0-1.0" + }, + "references": [ + { + "type": "PACKAGE", + "url": "https://crates.io/crates/nano-id" + }, + { + "type": "ADVISORY", + "url": "https://rustsec.org/advisories/RUSTSEC-2024-0343.html" + } + ], + "affected": [ + { + "package": { + "name": "nano-id", + "ecosystem": "crates.io", + "purl": "pkg:cargo/nano-id" + }, + "ranges": [ + { + "type": "SEMVER", + "events": [ + { + "introduced": "0.0.0-0" + }, + { + "fixed": "0.4.0" + } + ] + } + ], + "ecosystem_specific": { + "affected_functions": null, + "affects": { + "os": [], + "functions": [ + "nano_id::base58", + "nano_id::base62", + "nano_id::gen" + ], + "arch": [] + } + }, + "database_specific": { + "cvss": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L", + "informational": null, + "source": "https://github.com/rustsec/advisory-db/blob/osv/crates/RUSTSEC-2024-0343.json", + "categories": [ + "crypto-failure" + ] + } + } + ], + "schema_version": "1.6.0", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L" + } + ] +} \ No newline at end of file diff --git a/test/test_source.py b/test/test_source.py index bfcd364..483f387 100644 --- a/test/test_source.py +++ b/test/test_source.py @@ -15,7 +15,9 @@ # 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")) +db6.get( + os.path.join(test_tmp_dir, "data.vdb6"), os.path.join(test_tmp_dir, "index.vdb6") +) @pytest.fixture @@ -23,7 +25,7 @@ def test_cve_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "cve_data.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -32,7 +34,7 @@ def test_cve_wconfig_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "cve_wconfig_data.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -41,7 +43,7 @@ def test_nvd_api_json1(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2024-0057.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -50,7 +52,7 @@ def test_nvd_api_json2(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2024-21312.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -59,7 +61,7 @@ def test_nvd_api_json3(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2024-23771.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -68,7 +70,7 @@ def test_nvd_api_json4(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2015-3192.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -77,7 +79,7 @@ def test_nvd_api_git_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2023-52426.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -86,7 +88,25 @@ def test_osv_rust_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv_rust_data.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: + return json.loads(fp.read()) + + +@pytest.fixture +def test_osv_rust2_json(): + test_cve_data = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data", "RUSTSEC-2024-0343.json" + ) + with open(test_cve_data, mode="r", encoding="utf-8") as fp: + return json.loads(fp.read()) + + +@pytest.fixture +def test_osv_rust3_json(): + test_cve_data = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data", "GHSA-vc2p-r46x-m3vx.json" + ) + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -95,7 +115,7 @@ def test_osv_mvn_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv_mvn_data.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -104,7 +124,7 @@ def test_osv_mixed_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv_mixed_data.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -113,7 +133,25 @@ def test_osv_go_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "GO-2022-0646.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: + return json.loads(fp.read()) + + +@pytest.fixture +def test_osv_go2_json(): + test_cve_data = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data", "GO-2022-0251.json" + ) + with open(test_cve_data, mode="r", encoding="utf-8") as fp: + return json.loads(fp.read()) + + +@pytest.fixture +def test_osv_go3_json(): + test_cve_data = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "data", "GHSA-xrjj-mj9h-534m.json" + ) + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -122,7 +160,7 @@ def test_osv_pypi_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv-pypi.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -131,7 +169,7 @@ def test_osv_pypi2_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv-pypi2.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -140,7 +178,7 @@ def test_osv_pypi3_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv-pypi-ujson-bug.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -149,7 +187,7 @@ def test_osv_swift_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv_swift_data.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -158,7 +196,7 @@ def test_osv_swift2_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv_swift_data2.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -167,7 +205,7 @@ def test_osv_mevents_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv_multi_events.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -176,7 +214,7 @@ def test_osv_git_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv-git.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -185,7 +223,7 @@ def test_osv_npm_star_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "osv-npm-star-bug.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -194,7 +232,7 @@ def test_osv_mal_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "MAL-2024-1396.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -203,7 +241,7 @@ def test_osv_mal2_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "MAL-2024-1333.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -212,7 +250,7 @@ def test_aqua_alsa_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "ALSA-2022-8580.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -221,7 +259,7 @@ def test_aqua_alas_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "ALAS2022-2022-207.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -230,7 +268,7 @@ def test_aqua_rlsa_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "RLSA-2022-7730.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -239,7 +277,7 @@ def test_aqua_ubuntu_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-45406.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -248,7 +286,7 @@ def test_aqua_ubuntu2_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-3715.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -257,7 +295,7 @@ def test_aqua_ubuntu1_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-6083.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -266,7 +304,7 @@ def test_aqua_ubuntu3_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-3219.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -275,7 +313,7 @@ def test_aqua_ubuntu4_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-32081.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -284,7 +322,7 @@ def test_aqua_ubuntu5_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-3821.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -293,7 +331,7 @@ def test_aqua_redhat_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-45418.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -302,7 +340,7 @@ def test_aqua_redhat2_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-21824.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -311,7 +349,7 @@ def test_aqua_arch_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "AVG-999.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -322,7 +360,7 @@ def test_aqua_opensuse_json(): "data", "openSUSE-SU-2022-2801-1.json", ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -331,7 +369,7 @@ def test_aqua_suse_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "SUSE-SU-2022-4192-1.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -340,7 +378,7 @@ def test_aqua_suse_json1(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "SUSE-SU-2015-0439-1.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -349,7 +387,7 @@ def test_aqua_photon_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2021-3618.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -358,7 +396,7 @@ def test_aqua_debian_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2019-18625.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -367,7 +405,7 @@ def test_aqua_debian2_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2023-21500.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -376,7 +414,7 @@ def test_aqua_debian3_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-3567.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -385,7 +423,7 @@ def test_aqua_debian4_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "DLA-981-1.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -394,7 +432,7 @@ def test_aqua_debian5_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2021-22890.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -403,7 +441,7 @@ def test_aqua_debian6_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2022-32091.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -412,7 +450,7 @@ def test_aqua_debian7_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2020-35448.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -421,7 +459,7 @@ def test_aqua_cg_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "redis.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -430,7 +468,7 @@ def test_aqua_wolfi_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "protobuf-c.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -439,7 +477,7 @@ def test_aqua_alpine_unfixed_json(): test_cve_data = os.path.join( os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2024-25450.json" ) - with open(test_cve_data, "r") as fp: + with open(test_cve_data, mode="r", encoding="utf-8") as fp: return json.loads(fp.read()) @@ -542,11 +580,11 @@ def test_nvd_api_convert( results_count = len(list(search.search_by_any("CVE-2020-8022"))) assert results_count == 0 results_count = len(list(search.search_by_any("CVE-2024-0057"))) - assert results_count == 10 + assert results_count == 20 results_count = len( list(search.search_by_any("cpe:2.3:a:microsoft:.net:*:*:*:*:*:*:*:*")) ) - assert results_count == 1 + assert results_count == 3 # json2 vulnerabilities = nvdlatest.convert(test_nvd_api_json2) @@ -685,9 +723,13 @@ def test_osv_download_all(): def test_osv_convert( test_osv_rust_json, + test_osv_rust2_json, + test_osv_rust3_json, test_osv_mvn_json, test_osv_mixed_json, test_osv_go_json, + test_osv_go2_json, + test_osv_go3_json, test_osv_pypi_json, test_osv_pypi2_json, test_osv_pypi3_json, @@ -711,12 +753,18 @@ def test_osv_convert( results_count = len(list(search.search_by_any("CVE-2020-8022"))) assert results_count == 0 results_count = len(list(search.search_by_any("CVE-2019-0647"))) - assert results_count == 2 - results_count = len(list(search.search_by_any("pkg:maven/org.springframework/spring-web"))) + assert results_count == 5 + results_count = len( + list(search.search_by_any("pkg:maven/org.springframework/spring-web")) + ) assert results_count == 0 - results_count = len(list(search.search_by_any("pkg:apk/alpine/mariadb?arch=source"))) + results_count = len( + list(search.search_by_any("pkg:apk/alpine/mariadb?arch=source")) + ) assert results_count == 0 - results_count = len(list(search.search_by_any("pkg:apk/alpine/alpine-3.2/mariadb?arch=source"))) + results_count = len( + list(search.search_by_any("pkg:apk/alpine/alpine-3.2/mariadb?arch=source")) + ) assert results_count == 1 # multi events @@ -732,10 +780,14 @@ def test_osv_convert( results_count = len(list(search.search_by_any("CVE-2020-8022"))) assert results_count == 0 results_count = len(list(search.search_by_any("CVE-2019-3192"))) - assert results_count == 1 - results_count = len(list(search.search_by_any("pkg:apk/alpine/mariadb?arch=source"))) + assert results_count == 4 + results_count = len( + list(search.search_by_any("pkg:apk/alpine/mariadb?arch=source")) + ) assert results_count == 0 - results_count = len(list(search.search_by_any("pkg:maven/org.springframework/spring-web"))) + results_count = len( + list(search.search_by_any("pkg:maven/org.springframework/spring-web")) + ) assert results_count == 4 # swift @@ -767,17 +819,45 @@ def test_osv_convert( # rust cve_data = osvlatest.convert(test_osv_rust_json) assert cve_data - db6.clear_all() osvlatest.store(cve_data) 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("pkg:apk/alpine/mariadb?arch=source"))) + results_count = len( + list(search.search_by_any("pkg:apk/alpine/mariadb?arch=source")) + ) assert results_count == 0 results_count = len(list(search.search_by_any("pkg:cargo/rusqlite"))) assert results_count == 1 + cve_data = osvlatest.convert(test_osv_rust2_json) + assert cve_data + assert cve_data[0].affects == { + "affected_functions": ["nano_id::base58", "nano_id::base62", "nano_id::gen"], + "affected_modules": ["nano_id"], + } + cve_data = osvlatest.convert(test_osv_rust3_json) + assert cve_data + assert cve_data[0].affects == { + "affected_functions": [ + "lettre::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send", + "lettre::transport::sendmail::SendmailTransport::send_raw", + ], + "affected_modules": [ + "lettre::sendmail::SendmailTransport", + "lettre::transport::sendmail::SendmailTransport", + ], + } + db6.clear_all() + osvlatest.store(cve_data) + cve_data_count, cve_index_count = db6.stats() + assert cve_data_count == 3 + assert cve_index_count == 3 + results_count = len(list(search.search_by_any("pkg:cargo/lettre"))) + assert results_count == 3 + # maven cve_data = osvlatest.convert(test_osv_mvn_json) assert cve_data @@ -802,13 +882,12 @@ def test_osv_convert( results_count = len(list(search.search_by_any("CVE-2020-8022"))) assert results_count == 0 results_count = len(list(search.search_by_any("CVE-2021-23440"))) - assert results_count == 2 + assert results_count == 3 # go cve_data = osvlatest.convert(test_osv_go_json) assert cve_data assert len(cve_data) == 1 - db6.clear_all() osvlatest.store(cve_data) cve_data_count, cve_index_count = db6.stats() @@ -818,6 +897,25 @@ def test_osv_convert( assert results_count == 0 results_count = len(list(search.search_by_any("CVE-2020-8911"))) assert results_count == 1 + cve_data = osvlatest.convert(test_osv_go2_json) + assert cve_data + assert len(cve_data) == 1 + assert cve_data[0].affects == { + "affected_functions": [ + "BER2DER", + "DecodeManifest", + "DecoderConfig.DecodeManifest", + "readObject", + ], + "affected_modules": ["github.com/cloudflare/cfrpki/validator/lib"], + } + cve_data = osvlatest.convert(test_osv_go3_json) + assert cve_data + assert len(cve_data) == 2 + assert cve_data[0].affects == { + "affected_functions": ["Server.ServeConn"], + "affected_modules": [], + } # pypi cve_data = osvlatest.convert(test_osv_pypi_json) @@ -933,7 +1031,7 @@ def test_aqua_convert( results_count = len(list(search.search_by_any("CVE-2020-8022"))) assert results_count == 0 results_count = len(list(search.search_by_any("CVE-2022-45406"))) - assert results_count == 26 + assert results_count == 27 # ubuntu1 cve_data = aqualatest.convert(test_aqua_ubuntu1_json) diff --git a/vdb/cli.py b/vdb/cli.py index 807587e..3ad460f 100644 --- a/vdb/cli.py +++ b/vdb/cli.py @@ -129,12 +129,11 @@ def add_table_row(table: Table, res: dict, added_row_keys: dict): return source_data: CVE = res.get("source_data") descriptions = [] - if ( - source_data.root.containers.cna - and source_data.root.containers.cna.descriptions - and source_data.root.containers.cna.descriptions.root - ): - for adesc in source_data.root.containers.cna.descriptions.root: + cna_container = source_data.root.containers.cna + affected_functions = set() + affected_modules = set() + if cna_container and cna_container.descriptions and cna_container.descriptions.root: + for adesc in cna_container.descriptions.root: description = ( "\n".join( [ @@ -147,10 +146,24 @@ def add_table_row(table: Table, res: dict, added_row_keys: dict): ) description = description.replace("\\n", "\n").replace("\\t", " ") descriptions.append(description) + if cna_container.affected and cna_container.affected.root: + for each_affected in cna_container.affected.root: + if each_affected.programRoutines: + affected_functions |= {r.name for r in each_affected.programRoutines} + if each_affected.modules: + affected_modules |= {m.root for m in each_affected.modules} + affected_functions = list(affected_functions) + affected_modules = list(affected_modules) + affects = "" + if affected_functions: + affects = f"## Functions\n- {'\n- '.join(affected_functions)}" + if affected_modules: + affects = f"{affects}\n## Modules\n- {'\n- '.join(affected_modules)}" table.add_row( res.get("cve_id"), res.get("matched_by"), Markdown("\n".join(descriptions), justify="left", hyperlinks=True), + Markdown(affects, justify="left"), ) added_row_keys[row_key] = True @@ -158,9 +171,10 @@ def add_table_row(table: Table, res: dict, added_row_keys: dict): def print_results(results): added_row_keys = {} table = Table(title="VDB Results", show_lines=True) - table.add_column("CVE", justify="left") + table.add_column("CVE", justify="left", max_width=20) table.add_column("Locator") table.add_column("Description") + table.add_column("Affected Symbols", max_width=50) if isinstance(results, types.GeneratorType): with Live( table, console=console, refresh_per_second=4, vertical_overflow="visible" diff --git a/vdb/lib/__init__.py b/vdb/lib/__init__.py index ececd3b..d9a04ef 100644 --- a/vdb/lib/__init__.py +++ b/vdb/lib/__init__.py @@ -507,6 +507,7 @@ def __init__( cvss_v3: CvssV3, source_update_time: str, source_orig_time: str, + affects: dict = None, ): self.id = vid self.assigner = assigner @@ -519,6 +520,7 @@ def __init__( self.cvss_v3 = cvss_v3 self.source_update_time: datetime = convert_time(source_update_time) self.source_orig_time: datetime = convert_time(source_orig_time) + self.affects = affects def __repr__(self): return orjson.dumps( @@ -536,6 +538,7 @@ def __repr__(self): "%Y-%m-%dT%H:%M:%S" ), "source_orig_time": self.source_orig_time.strftime("%Y-%m-%dT%H:%M:%S"), + "affects": self.affects }, option=orjson.OPT_NAIVE_UTC, ).decode("utf-8", "ignore") diff --git a/vdb/lib/aqua.py b/vdb/lib/aqua.py index 076f2ea..1777a09 100644 --- a/vdb/lib/aqua.py +++ b/vdb/lib/aqua.py @@ -210,8 +210,9 @@ def alsa_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) - vuln.description = compress_str(description) - ret_data.append(vuln) + if vuln: + vuln.description = compress_str(description) + ret_data.append(vuln) except Exception: pass return ret_data @@ -293,6 +294,8 @@ def alas_rlsa_to_vuln(cve_data, vendor): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append(vuln) done_pkgs[pkg_key] = True @@ -396,6 +399,8 @@ def ubuntu_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append(vuln) except Exception: @@ -507,6 +512,8 @@ def redhat_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append(vuln) done_pkgs[pkg_key] = True @@ -573,6 +580,8 @@ def arch_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append(vuln) except Exception: @@ -671,6 +680,8 @@ def suse_to_vuln(self, cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append(vuln) done_pkgs[pkg_key] = True @@ -741,8 +752,9 @@ def photon_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) - vuln.description = compress_str(description) - ret_data.append(vuln) + if vuln: + vuln.description = compress_str(description) + ret_data.append(vuln) except Exception: pass return ret_data @@ -859,6 +871,8 @@ def debian_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append(vuln) except Exception: @@ -924,6 +938,8 @@ def wolfi_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = f"""URL Prefix: {cve_data.get("urlprefix")}. Affected arch: {", ".join(cve_data.get("archs"))}""" ret_data.append(vuln) except Exception: @@ -978,6 +994,8 @@ def alpine_to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = f"{cve_id} is unfixed in {edition}. Visit https://security.alpinelinux.org/vuln/{cve_id} for more details." ret_data.append(vuln) except Exception: diff --git a/vdb/lib/cve.py b/vdb/lib/cve.py index cbb8476..2c57754 100644 --- a/vdb/lib/cve.py +++ b/vdb/lib/cve.py @@ -33,9 +33,11 @@ Metrics, Metrics1, Metrics2, + Module, OrgId, ProblemType, ProblemTypes, + ProgramRoutine, Product, ProviderMetadata, Reference, @@ -240,6 +242,18 @@ def to_cve_affected(avuln: Vulnerability) -> Affected | None: defaultStatus=Status.unknown, versions=versions, ) + # Support for tracking Vulnerability.affects + if avuln.affects: + if avuln.affects.get("affected_functions"): + p.programRoutines = [ + ProgramRoutine(name=f) + for f in avuln.affects.get("affected_functions") + if f + ] + if avuln.affects.get("affected_modules"): + p.modules = [ + Module(m) for m in avuln.affects.get("affected_modules") + ] products.append(p) return Affected(products) if products else None diff --git a/vdb/lib/gha.py b/vdb/lib/gha.py index df3e695..5da0b60 100755 --- a/vdb/lib/gha.py +++ b/vdb/lib/gha.py @@ -280,6 +280,8 @@ def convert(self, cve_data): try: tdata_json = orjson.loads(tdata) vuln = NvdSource.convert_vuln(tdata_json) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append(vuln) except Exception as e: diff --git a/vdb/lib/npm.py b/vdb/lib/npm.py index d08ae61..3290b82 100644 --- a/vdb/lib/npm.py +++ b/vdb/lib/npm.py @@ -277,6 +277,8 @@ def to_vuln(self, v, ret_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) ret_data.append([vuln, f"""{v["id"]}|{product}|{used_version}"""]) except Exception as e: diff --git a/vdb/lib/nvd.py b/vdb/lib/nvd.py index 2623e95..9be9605 100644 --- a/vdb/lib/nvd.py +++ b/vdb/lib/nvd.py @@ -302,8 +302,8 @@ def convert_vuln_detail(vuln: dict) -> list[VulnerabilityDetail] | None: detail = {} if not cpe.get("cpe23Uri"): continue - if cpe["vulnerable"] and cpe.get("cpe23Uri"): - detail["cpe_uri"] = cpe["cpe23Uri"] + if cpe["vulnerable"]: + detail["cpe_uri"] = cpe.get("cpe23Uri") detail["mii"] = cpe.get("versionStartIncluding") detail["mie"] = cpe.get("versionStartExcluding") detail["mai"] = cpe.get("versionEndIncluding") diff --git a/vdb/lib/osv.py b/vdb/lib/osv.py index f7c6b60..113f762 100644 --- a/vdb/lib/osv.py +++ b/vdb/lib/osv.py @@ -20,6 +20,7 @@ parse_purl, ) + # Size of the stream to read and write to the file DOWNLOAD_CHUNK_SIZE = 4096 @@ -33,6 +34,18 @@ } +def functions_to_modules(affected_functions): + affected_modules = set() + for af in affected_functions: + if "::" in af: + tmp_parts = af.split("::") + affected_modules.add("::".join(tmp_parts[:-1])) + elif "/" in af: + tmp_parts = af.split("/") + affected_modules.add("/".join(tmp_parts[:-1])) + return affected_modules + + class OSVSource(NvdSource): """OSV CVE source""" @@ -139,6 +152,9 @@ def to_vuln(cve_data): # Issue 58 cve_database_specific = cve_data.get("database_specific") cve_ecosystem_specific = cve_data.get("ecosystem_specific") + # Support for collecting affected functions + affected_functions = set() + affected_modules = set() if cve_database_specific: if cve_database_specific.get("severity"): severity = cve_database_specific.get("severity") @@ -153,6 +169,15 @@ def to_vuln(cve_data): ecosystem_specific = pkg_data.get("ecosystem_specific") if ecosystem_specific.get("severity"): severity = ecosystem_specific.get("severity") + if ecosystem_specific.get("affected_functions"): + affected_functions = affected_functions.union(ecosystem_specific.get("affected_functions")) + if ecosystem_specific.get("affects", {}).get("functions"): + affected_functions = affected_functions.union(ecosystem_specific.get("affects").get("functions")) + for aimp in ecosystem_specific.get("imports", []): + if aimp.get("path"): + affected_modules.add(aimp.get("path")) + if aimp.get("symbols"): + affected_functions = affected_functions.union(aimp.get("symbols")) if pkg_data.get("database_specific"): database_specific = pkg_data.get("database_specific") if database_specific.get("cwes"): @@ -282,8 +307,14 @@ def to_vuln(cve_data): possible_fix_version = ev.get("fixed") if needs_version_backup and len(versions_list): try: - min_ver = min(versions_list, key=lambda x: Version.parse(x, optional_minor_and_patch=True)) - max_ver = max(versions_list, key=lambda x: Version.parse(x, optional_minor_and_patch=True)) + min_ver = min( + versions_list, + key=lambda x: Version.parse(x, optional_minor_and_patch=True), + ) + max_ver = max( + versions_list, + key=lambda x: Version.parse(x, optional_minor_and_patch=True), + ) except Exception: min_ver = versions_list[0] max_ver = versions_list[-1] @@ -317,7 +348,19 @@ def to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) + if affected_functions: + vuln.affects = { + "affected_functions": sorted(list(affected_functions)), + "affected_modules": sorted( + list( + affected_modules + | functions_to_modules(affected_functions) + ) + ), + } ret_data.append(vuln) except Exception: pass @@ -417,7 +460,23 @@ def to_vuln(cve_data): ) try: vuln = NvdSource.convert_vuln(orjson.loads(tdata)) + if vuln is None: + continue vuln.description = compress_str(description) + if affected_functions: + vuln.affects = { + "affected_functions": sorted( + list(affected_functions) + ), + "affected_modules": sorted( + list( + affected_modules + | functions_to_modules( + affected_functions + ) + ) + ), + } ret_data.append(vuln) except Exception: pass diff --git a/vdb/lib/utils.py b/vdb/lib/utils.py index 24edfb2..2b0efe4 100644 --- a/vdb/lib/utils.py +++ b/vdb/lib/utils.py @@ -453,7 +453,7 @@ def trim_epoch( def vers_compare(compare_ver: str | int | float, vers: str) -> bool: """Purl vers based version comparison""" min_version, max_version, min_excluding, max_excluding = None, None, None, None - if vers == "*" or compare_ver is None: + if vers == "*" or compare_ver is None or compare_ver == "*": return True if vers.startswith("vers:"): vers_parts = vers.split("/")[-1].split("|")