Skip to content

Commit def0a87

Browse files
committed
import Vulnrichment affected_packages and weaknesses
Signed-off-by: ziadhany <[email protected]>
1 parent 95d66f3 commit def0a87

File tree

9 files changed

+396
-154
lines changed

9 files changed

+396
-154
lines changed

vulnerabilities/importers/vulnrichment.py

Lines changed: 119 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import logging
3+
import re
34
from datetime import datetime
45
from pathlib import Path
56
from typing import Iterable
@@ -8,7 +9,24 @@
89

910
import dateparser
1011
from packageurl import PackageURL
12+
from univers.version_constraint import VersionConstraint
13+
from univers.version_range import RANGE_CLASS_BY_SCHEMES
14+
from univers.version_range import VersionRange
15+
from univers.versions import AlpineLinuxVersion
16+
from univers.versions import ArchLinuxVersion
17+
from univers.versions import ComposerVersion
18+
from univers.versions import DebianVersion
19+
from univers.versions import GenericVersion
20+
from univers.versions import GentooVersion
21+
from univers.versions import GolangVersion
1122
from univers.versions import InvalidVersion
23+
from univers.versions import LegacyOpensslVersion
24+
from univers.versions import MavenVersion
25+
from univers.versions import NginxVersion
26+
from univers.versions import NugetVersion
27+
from univers.versions import OpensslVersion
28+
from univers.versions import PypiVersion
29+
from univers.versions import RpmVersion
1230
from univers.versions import SemverVersion
1331
from univers.versions import Version
1432

@@ -23,6 +41,16 @@
2341
from vulnerabilities.utils import get_advisory_url
2442
from vulnerabilities.utils import get_cwe_id
2543

44+
logger = logging.getLogger(__name__)
45+
46+
VULNRICH_VERSION_CLASS_SCHEMES = {
47+
"semver": SemverVersion,
48+
"python": PypiVersion,
49+
"custom": GenericVersion,
50+
"rpm": RpmVersion,
51+
"maven": MavenVersion,
52+
}
53+
2654

2755
class VulnrichImporter(Importer):
2856
spdx_license_expression = "CC0-1.0"
@@ -44,7 +72,7 @@ def advisory_data(self) -> Iterable[AdvisoryData]:
4472
advisory_url = get_advisory_url(
4573
file=file_path,
4674
base_path=base_path,
47-
url="https://github.com/rubysec/ruby-advisory-db/blob/master/",
75+
url="https://github.com/cisagov/vulnrichment/blob/develop/",
4876
)
4977
yield parse_cve_advisory(raw_data, advisory_url)
5078
finally:
@@ -53,12 +81,12 @@ def advisory_data(self) -> Iterable[AdvisoryData]:
5381

5482

5583
def parse_cve_advisory(raw_data, advisory_url):
56-
""""""
57-
84+
""" """
5885
# Extract CVE Metadata
5986
cve_metadata = raw_data.get("cveMetadata", {})
6087
cve_id = cve_metadata.get("cveId")
6188
state = cve_metadata.get("state")
89+
6290
date_published = cve_metadata.get("datePublished")
6391
date_published = dateparser.parse(date_published)
6492

@@ -68,25 +96,75 @@ def parse_cve_advisory(raw_data, advisory_url):
6896
adp_data = containers.get("adp", {})
6997

7098
# Extract affected products
71-
# affected_products = cna_data.get("affected", [])
72-
# products = []
73-
# for product in affected_products:
74-
# product_info = {
75-
# "default_status": product.get("defaultStatus"),
76-
# "platforms": product.get("platforms", []),
77-
# "product": product.get("product"),
78-
# "vendor": product.get("vendor"),
79-
# "versions": product.get("versions", [])
80-
# }
81-
# products.append(product_info)
99+
affected_packages = []
100+
for affected_product in cna_data.get("affected", []):
101+
if type(affected_product) != dict:
102+
continue
103+
cpes = affected_product.get("cpes") # TODO Add references cpes
104+
105+
vendor = affected_product.get("vendor") or ""
106+
collection_url = affected_product.get("collectionURL") or ""
107+
product = affected_product.get("product") or ""
108+
package_name = affected_product.get("packageName") or ""
109+
110+
platforms = affected_product.get("platforms", [])
111+
default_status = affected_product.get("defaultStatus")
112+
113+
affected_packages = []
114+
# purl (vendor, collection_url, product, package_name, platforms)
115+
purl = PackageURL(
116+
type=vendor,
117+
name=product,
118+
namespace=package_name,
119+
)
120+
121+
versions = affected_product.get("versions", [])
122+
for version_data in versions:
123+
# version ≤ V ≤ (lessThanOrEqual/lessThan)
124+
# right_version ≤ V ≤ left_version
125+
version_constraints = []
126+
r_version = version_data.get("version")
127+
version_type = version_data.get("versionType")
128+
version_class = VULNRICH_VERSION_CLASS_SCHEMES.get(version_type)
129+
if not version_class:
130+
logger.error(f"Invalid version_class type: {version_type}")
131+
continue
132+
133+
l_version, l_comparator = None, ""
134+
if "lessThan" in version_data:
135+
l_version = version_data.get("lessThan")
136+
l_comparator = "<"
137+
elif "lessThanOrEqual" in version_data:
138+
l_version = version_data.get("lessThanOrEqual")
139+
l_comparator = "<="
140+
try:
141+
if l_version and l_comparator:
142+
version_constraints.append(
143+
VersionConstraint(comparator=l_comparator, version=version_class(l_version))
144+
)
145+
if r_version:
146+
version_constraints.append(
147+
VersionConstraint(comparator=">", version=version_class(r_version))
148+
)
149+
except InvalidVersion:
150+
logger.error(f"InvalidVersion: {l_version}-{r_version}")
151+
continue
152+
153+
affected_packages.append(
154+
AffectedPackage(
155+
purl,
156+
affected_version_range=VersionRange(constraints=version_constraints),
157+
)
158+
)
159+
status = version_data.get("status")
82160

83161
# Extract descriptions
84-
description = ""
162+
summary = ""
85163
description_list = cna_data.get("descriptions", [])
86164
for description_dict in description_list:
87-
if description_dict.get("lang") != "en":
165+
if not description_dict.get("lang") in ["en", "en-US"]:
88166
continue
89-
description = description_dict.get("value")
167+
summary = description_dict.get("value")
90168

91169
# Extract metrics
92170
severities = []
@@ -98,7 +176,7 @@ def parse_cve_advisory(raw_data, advisory_url):
98176
"cvssV2_0": SCORING_SYSTEMS["cvssv2"],
99177
"other": {
100178
"ssvc": SCORING_SYSTEMS["ssvc"],
101-
},
179+
}, # ignore kev
102180
}
103181

104182
for metric in metrics:
@@ -127,35 +205,34 @@ def parse_cve_advisory(raw_data, advisory_url):
127205
severities.append(severity)
128206

129207
# Extract references
208+
# TODO ADD reference type
130209
references = [
131210
Reference(url=ref.get("url"), severities=severities)
132211
for ref in cna_data.get("references", [])
133212
]
134213

135-
# Extract problem types
136214
weaknesses = []
137-
# problem_types = cna_data.get("problemTypes", [])
138-
# for problem in problem_types:
139-
# descriptions = problem.get("descriptions", [])
140-
# for description in descriptions:
141-
# weaknesses.append(
142-
# description.get("cweId")
143-
# "description": description.get("description"),
144-
# "lang": description.get("lang"),
145-
# "type": description.get("type")
146-
# )
147-
#
148-
# # cwe_id = description.get("cweId")
149-
# # cwe_id = get_cwe_id(cwe_id)
150-
# # weaknesses.append(cwe_id)
215+
for problem_type in cna_data.get("problemTypes", []):
216+
descriptions = problem_type.get("descriptions", [])
217+
for description in descriptions:
218+
cwe_id = description.get("cweId")
219+
if cwe_id:
220+
weaknesses.append(get_cwe_id(cwe_id))
221+
222+
description_text = description.get("description")
223+
if description_text:
224+
pattern = r"CWE-(\d{3})"
225+
match = re.search(pattern, description_text)
226+
if match:
227+
weaknesses.append(match.group(1))
151228

152229
return AdvisoryData(
153230
aliases=[cve_id],
154-
summary=description,
155-
# affected_packages=affected_products,
231+
summary=summary,
232+
affected_packages=affected_packages,
156233
references=references,
157-
date_published=date_published,
158-
# weaknesses=weaknesses,
234+
# date_published=dateparser.parse(self.cve_item.get("publishedDate")),
235+
weaknesses=weaknesses,
159236
url=advisory_url,
160237
)
161238

@@ -218,6 +295,7 @@ def ssvc_calculator(ssvc_data):
218295

219296
# "Decision": {"D": {"Track": "T", "Track*": "R", "Attend": "A", "Act": "C"}},
220297
decision_values = {"Track": "T", "Track*": "R", "Attend": "A", "Act": "C"}
298+
221299
decision_lookup = {
222300
("none", "no", "partial", "low"): "Track",
223301
("none", "no", "partial", "medium"): "Track",
@@ -262,8 +340,8 @@ def ssvc_calculator(ssvc_data):
262340
if decision:
263341
ssvc_vector += f"D:{decision_values.get(decision)}/"
264342

265-
timestamp_formatted = datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%fZ").strftime(
266-
"%Y-%m-%dT%H:%M:%SZ"
267-
)
268-
ssvc_vector += f"{timestamp_formatted}/"
343+
if timestamp:
344+
timestamp_formatted = dateparser.parse(timestamp).strftime("%Y-%m-%dT%H:%M:%SZ")
345+
346+
ssvc_vector += f"{timestamp_formatted}/"
269347
return ssvc_vector, decision

vulnerabilities/severity_systems.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,14 @@ def get(self, scoring_elements: str) -> dict:
182182
"Low",
183183
]
184184

185-
SSVC = ScoringSystem(
185+
186+
@dataclasses.dataclass(order=True)
187+
class SSVCScoringSystem(ScoringSystem):
188+
def get(self, scoring_elements: str):
189+
return {"version": "ssvc", "vectorString": scoring_elements}
190+
191+
192+
SSVC = SSVCScoringSystem(
186193
identifier="ssvc",
187194
name="Stakeholder-Specific Vulnerability Categorization",
188195
url="https://www.cisa.gov/stakeholder-specific-vulnerability-categorization-ssvc",

vulnerabilities/templates/vulnerability_details.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@
380380
<td>{{ severity_vector.availabilityImpact|cvss_printer:"high,low,none" }}</td>
381381
</tr>
382382
</table>
383-
{% elif severity_vector.version == '4' or severity_vector.version == '4'%}
383+
{% elif severity_vector.version == '4' %}
384384
Vector: {{ severity_vector.vectorString }}
385385
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth gray-header-border">
386386
<tr>
@@ -414,11 +414,15 @@
414414
<td>{{ severity_vector.subsequentSystemImpactAvailability|cvss_printer:"high,low,none" }}</td>
415415
</tr>
416416
</table>
417+
{% elif severity_vector.version == 'ssvc' %}
418+
<hr/>
419+
Vector: {{ severity_vector.vectorString }}
420+
<hr/>
417421
{% endif %}
418422
{% empty %}
419423
<tr>
420424
<td>
421-
There are no known CVSS vectors.
425+
There are no known vectors.
422426
</td>
423427
</tr>
424428
{% endfor %}

vulnerabilities/tests/test_data/vulnrichment/vulnrichment-data1-expected.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"aliases": [
33
"CVE-2024-3018"
44
],
5-
"summary": "",
5+
"summary": "The Essential Addons for Elementor plugin for WordPress is vulnerable to PHP Object Injection in all versions up to, and including, 5.9.13 via deserialization of untrusted input from the 'error_resetpassword' attribute of the \"Login | Register Form\" widget (disabled by default). This makes it possible for authenticated attackers, with author-level access and above, to inject a PHP Object. If a POP chain is present via an additional plugin or theme installed on the target system, it could allow the attacker to delete arbitrary files, retrieve sensitive data, or execute code.",
66
"affected_packages": [],
77
"references": [
88
{
@@ -38,7 +38,9 @@
3838
]
3939
}
4040
],
41-
"date_published": "2024-03-30T11:17:25.675000+00:00",
42-
"weaknesses": [],
41+
"date_published": null,
42+
"weaknesses": [
43+
"502"
44+
],
4345
"url": "http://test.com"
4446
}

vulnerabilities/tests/test_data/vulnrichment/vulnrichment-data2-expected.json

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,21 @@
22
"aliases": [
33
"CVE-2022-26915"
44
],
5-
"summary": "",
6-
"affected_packages": [],
5+
"summary": "Windows Secure Channel Denial of Service Vulnerability",
6+
"affected_packages": [
7+
{
8+
"package": {
9+
"type": "microsoft",
10+
"namespace": "",
11+
"name": "Windows Server 2012 R2 (Server Core installation)",
12+
"version": "",
13+
"qualifiers": "",
14+
"subpath": ""
15+
},
16+
"affected_version_range": "vers:None/>6.3.0|<6.3.9600.20337",
17+
"fixed_version": null
18+
}
19+
],
720
"references": [
821
{
922
"reference_id": "",
@@ -22,7 +35,7 @@
2235
]
2336
}
2437
],
25-
"date_published": "2022-04-15T19:05:52",
38+
"date_published": null,
2639
"weaknesses": [],
2740
"url": "http://test.com"
2841
}

0 commit comments

Comments
 (0)