Skip to content

Commit

Permalink
Support redhat package fix state and versionless packages (#165)
Browse files Browse the repository at this point in the history
* Skip versionless packages in redhat feeds which was creating false positives

Signed-off-by: Prabhu Subramanian <[email protected]>

* Ignore redhat packages based on package fix state

Signed-off-by: Prabhu Subramanian <[email protected]>

* Fixes 166

Signed-off-by: Prabhu Subramanian <[email protected]>

---------

Signed-off-by: Prabhu Subramanian <[email protected]>
  • Loading branch information
prabhu authored Jul 15, 2024
1 parent 7b672d6 commit 030a2d0
Show file tree
Hide file tree
Showing 7 changed files with 945 additions and 35 deletions.
1 change: 1 addition & 0 deletions .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ jobs:
- name: CLI tests
run: |
python vdb/cli.py --search "pkg:maven/org.springframework/[email protected]"
python vdb/cli.py --search "pkg:maven/org.hibernate.orm/[email protected]"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "appthreat-vulnerability-db"
version = "6.0.10"
version = "6.0.11"
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 = "[email protected]"},
Expand Down
745 changes: 745 additions & 0 deletions test/data/CVE-2020-25638.json

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions test/data/CVE-2024-3801.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"id": "CVE-2024-3801",
"sourceIdentifier": "[email protected]",
"published": "2024-06-28T13:15:03.157",
"lastModified": "2024-07-03T14:36:52.797",
"vulnStatus": "Analyzed",
"descriptions": [
{
"lang": "en",
"value": "Sites managed in S@M CMS (Concept Intermedia) might be vulnerable to Reflected XSS via including scripts in one of GET header parameters. \nOnly a part of observed services is vulnerable, but since vendor has not investigated the root problem, it is hard to determine when the issue appears."
},
{
"lang": "es",
"value": "Los sitios administrados en S@M CMS (Concept Intermedia) pueden ser vulnerables a XSS Reflejado al incluir scripts en uno de los parámetros del encabezado GET. Sólo una parte de los servicios observados es vulnerable, pero como el proveedor no ha investigado la raíz del problema, es difícil determinar cuándo aparece el problema."
}
],
"metrics": {
"cvssMetricV31": [
{
"source": "[email protected]",
"type": "Primary",
"cvssData": {
"version": "3.1",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
"attackVector": "NETWORK",
"attackComplexity": "LOW",
"privilegesRequired": "NONE",
"userInteraction": "REQUIRED",
"scope": "CHANGED",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"availabilityImpact": "NONE",
"baseScore": 6.1,
"baseSeverity": "MEDIUM"
},
"exploitabilityScore": 2.8,
"impactScore": 2.7
}
]
},
"weaknesses": [
{
"source": "[email protected]",
"type": "Primary",
"description": [
{
"lang": "en",
"value": "CWE-79"
}
]
},
{
"source": "[email protected]",
"type": "Secondary",
"description": [
{
"lang": "en",
"value": "CWE-79"
}
]
}
],
"configurations": [
{
"nodes": [
{
"operator": "OR",
"negate": false,
"cpeMatch": [
{
"vulnerable": true,
"criteria": "cpe:2.3:a:conceptintermedia:s\\@m_cms:*:*:*:*:*:*:*:*",
"matchCriteriaId": "00A1AF5A-47FF-4080-A506-DB634A54CD6C",
"versionEndIncluding": "3.3"
}
]
}
]
}
],
"references": [
{
"url": "https://cert.pl/en/posts/2024/06/CVE-2024-3800",
"source": "[email protected]",
"tags": [
"Third Party Advisory"
]
},
{
"url": "https://cert.pl/posts/2024/06/CVE-2024-3800",
"source": "[email protected]",
"tags": [
"Third Party Advisory"
]
}
]
}
52 changes: 52 additions & 0 deletions test/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ def test_nvd_api_signal_json():
return json.loads(fp.read())


@pytest.fixture
def test_nvd_api_at_json():
test_cve_data = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2024-3801.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(
Expand Down Expand Up @@ -370,6 +379,15 @@ def test_aqua_redhat2_json():
return json.loads(fp.read())


@pytest.fixture
def test_aqua_redhat3_json():
test_cve_data = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "data", "CVE-2020-25638.json"
)
with open(test_cve_data, mode="r", encoding="utf-8") as fp:
return json.loads(fp.read())


@pytest.fixture
def test_aqua_arch_json():
test_cve_data = os.path.join(
Expand Down Expand Up @@ -731,6 +749,27 @@ def test_nvd_api_convert3(test_nvd_api_signal_json):
assert results_count == 1


def test_nvd_api_convert4(test_nvd_api_at_json):
db6.clear_all()
nvdlatest = NvdSource()
# signal ios
vulnerabilities = nvdlatest.convert(test_nvd_api_at_json)
assert len(vulnerabilities) == 1
assert len(vulnerabilities[0].details) == 1
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-2024-3801")))
assert results_count == 1
results_count = len(
list(
search.search_by_any("pkg:generic/conceptintermedia/s_m_cms")
)
)
assert results_count == 1


@pytest.mark.skip(reason="This downloads and tests with live data")
def test_nvd_download():
nvdlatest = NvdSource()
Expand Down Expand Up @@ -1044,6 +1083,7 @@ def test_aqua_convert(
test_aqua_ubuntu3_json,
test_aqua_redhat_json,
test_aqua_redhat2_json,
test_aqua_redhat3_json,
test_aqua_arch_json,
test_aqua_opensuse_json,
test_aqua_suse_json,
Expand Down Expand Up @@ -1183,6 +1223,18 @@ def test_aqua_convert(
results_count = len(list(search.search_by_any("CVE-2022-21824")))
assert results_count == 7

# redhat3
cve_data = aqualatest.convert(test_aqua_redhat3_json)
assert cve_data
assert len(cve_data) == 73
db6.clear_all()
aqualatest.store(cve_data)
cve_data_count, cve_index_count = db6.stats()
assert cve_data_count == 73
assert cve_index_count == 73
results_count = len(list(search.search_by_any("CVE-2020-25638")))
assert results_count == 73

# arch
cve_data = aqualatest.convert(test_aqua_arch_json)
assert cve_data
Expand Down
82 changes: 48 additions & 34 deletions vdb/lib/aqua.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,13 +134,14 @@ def alsa_to_vuln(cve_data):
cwe_id = ""
cve_references = cve_data.get("references", [])
references = []
for aref in cve_references:
references.append(
{"name": aref.get("title", "id"), "url": aref.get("href")}
)
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
if cve_references:
for aref in cve_references:
references.append(
{"name": aref.get("title", "id"), "url": aref.get("href")}
)
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
description = cve_data.get("description", "")
if not description and cve_data.get("title"):
description = f"""# {cve_data.get("summary")}
Expand Down Expand Up @@ -228,11 +229,12 @@ def alas_rlsa_to_vuln(cve_data, vendor):
cwe_id = ""
cve_references = cve_data.get("references", [])
references = []
for aref in cve_references:
references.append({"name": aref.get("id"), "url": aref.get("href")})
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
if cve_references:
for aref in cve_references:
references.append({"name": aref.get("id"), "url": aref.get("href")})
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
if not cve_id.startswith("CVE") and cve_data.get("cveids"):
cve_id = cve_data.get("cveids")[0]
description = cve_data.get("description", "")
Expand Down Expand Up @@ -314,11 +316,12 @@ def ubuntu_to_vuln(cve_data):
cwe_id = ""
cve_references = cve_data.get("References", [])
references = []
for aref in cve_references:
references.append({"name": aref, "url": aref})
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
if cve_references:
for aref in cve_references:
references.append({"name": aref, "url": aref})
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
description = cve_data.get("Description")
if "** DISPUTED **" in description or "** REJECT **" in description:
return ret_data
Expand Down Expand Up @@ -414,19 +417,28 @@ def redhat_to_vuln(cve_data):
cvss3 = cve_data.get("cvss3", {})
if cvss3.get("status") is not None and cvss3.get("status") != "verified":
return ret_data
ignore_cpes = {}
packages = cve_data.get("affected_release", [])
package_state = cve_data.get("package_state", [])
if package_state:
for ps in package_state:
fix_state = ps.get("fix_state")
cpe = ps.get("cpe")
if fix_state and fix_state.lower() != "affected":
ignore_cpes[cpe] = True
if not packages or not len(packages) > 0:
return ret_data
cve_id = cve_data.get("name")
cwe_id = cve_data.get("cwe")
cve_references = cve_data.get("references", "")
references = []
for aref in cve_references:
for bref in aref.split("\n"):
references.append({"name": bref, "url": bref})
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
if cve_references:
for aref in cve_references:
for bref in aref.split("\n"):
references.append({"name": bref, "url": bref})
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
description = "\n".join(cve_data.get("details", []))
assigner = "redhat"
vector_string = cvss3.get("cvss3_scoring_vector")
Expand All @@ -451,9 +463,11 @@ def redhat_to_vuln(cve_data):
done_pkgs = {}
for arelease in packages:
pkg_key = arelease.get("package")
if done_pkgs.get(pkg_key):
cpe = arelease.get("cpe", "")
# Ignore CPEs that are out of support or won't be fixed
if ignore_cpes.get(cpe) or done_pkgs.get(pkg_key):
continue
tmp_a = pkg_key.split(":" if ":" in pkg_key else "-")
tmp_a = pkg_key.split(":")
if len(tmp_a) < 2:
continue
pkg_name = tmp_a[0]
Expand All @@ -462,12 +476,11 @@ def redhat_to_vuln(cve_data):
if ":" in pkg_key:
version = pkg_key.split(":")[-1]
else:
version = pkg_key.replace(pkg_name + "-", "")
continue
# Remove epoch
if ":" in version:
version = version.split(":")[-1]
edition = "*"
cpe = arelease.get("cpe", "")
if cpe and cpe.startswith("cpe:"):
tmpc = cpe.split(":")
if len(tmpc) > 2:
Expand Down Expand Up @@ -622,13 +635,14 @@ def suse_to_vuln(self, cve_data):
cwe_id = ""
cve_references = avuln.get("References", [])
references = []
for aref in cve_references:
references.append(
{"name": aref.get("Description", "id"), "url": aref.get("URL")}
)
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
if cve_references:
for aref in cve_references:
references.append(
{"name": aref.get("Description", "id"), "url": aref.get("URL")}
)
references = orjson.dumps(references, option=orjson.OPT_NAIVE_UTC)
if isinstance(references, bytes):
references = references.decode("utf-8", "ignore")
assigner = "suse"
(
score,
Expand Down
1 change: 1 addition & 0 deletions vdb/lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,4 +1200,5 @@ def clean_cpe_uri(cpe_uri):
cpe_uri = cpe_uri.replace("_-_" , "-")
cpe_uri = cpe_uri.replace("_/_" , "/")
cpe_uri = cpe_uri.replace("__" , "_")
cpe_uri = cpe_uri.replace("@" , "_")
return cpe_uri

0 comments on commit 030a2d0

Please sign in to comment.