Skip to content

Commit 2ca2aae

Browse files
committed
Fix nvd importer to collect severities correctly
Add a test Signed-off-by: ziad hany <ziadhany2016@gmail.com>
1 parent 541ce04 commit 2ca2aae

File tree

4 files changed

+267
-86
lines changed

4 files changed

+267
-86
lines changed

vulnerabilities/pipelines/nvd_importer.py

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -200,43 +200,32 @@ def severities(self):
200200
Return a list of VulnerabilitySeverity for this CVE.
201201
"""
202202
severities = []
203-
impact = self.cve_item.get("impact") or {}
204-
base_metric_v4 = impact.get("baseMetricV4") or {}
205-
if base_metric_v4:
206-
cvss_v4 = base_metric_v4.get("cvssV4") or {}
207-
vs = VulnerabilitySeverity(
208-
system=severity_systems.CVSSV4,
209-
value=str(cvss_v4.get("baseScore") or ""),
210-
scoring_elements=str(cvss_v4.get("vectorString") or ""),
211-
)
212-
severities.append(vs)
213-
214-
base_metric_v3 = impact.get("baseMetricV3") or {}
215-
if base_metric_v3:
216-
cvss_v3 = get_item(base_metric_v3, "cvssV3")
217-
version = cvss_v3.get("version")
218-
system = None
219-
if version == "3.1":
220-
system = severity_systems.CVSSV31
221-
else:
222-
system = severity_systems.CVSSV3
223-
vs = VulnerabilitySeverity(
224-
system=system,
225-
value=str(cvss_v3.get("baseScore") or ""),
226-
scoring_elements=str(cvss_v3.get("vectorString") or ""),
227-
)
228-
severities.append(vs)
229-
230-
base_metric_v2 = impact.get("baseMetricV2") or {}
231-
if base_metric_v2:
232-
cvss_v2 = base_metric_v2.get("cvssV2") or {}
233-
vs = VulnerabilitySeverity(
234-
system=severity_systems.CVSSV2,
235-
value=str(cvss_v2.get("baseScore") or ""),
236-
scoring_elements=str(cvss_v2.get("vectorString") or ""),
237-
)
238-
severities.append(vs)
203+
metrics = get_item(self.cve_item, "cve", "metrics") or {}
204+
url = f"https://nvd.nist.gov/vuln/detail/{self.cve_id}"
205+
metric_configs = [
206+
("cvssMetricV40", severity_systems.CVSSV4),
207+
("cvssMetricV31", severity_systems.CVSSV31),
208+
("cvssMetricV30", severity_systems.CVSSV3),
209+
("cvssMetricV2", severity_systems.CVSSV2),
210+
]
239211

212+
for key, default_system in metric_configs:
213+
items = metrics.get(key) or []
214+
215+
for item in items:
216+
cvss_data = item.get("cvssData") or {}
217+
system = default_system
218+
if key == "cvssMetricV31" and cvss_data.get("version") != "3.1":
219+
system = severity_systems.CVSSV3
220+
221+
severities.append(
222+
VulnerabilitySeverity(
223+
system=system,
224+
value=str(cvss_data.get("baseScore") or ""),
225+
scoring_elements=str(cvss_data.get("vectorString") or ""),
226+
url=url,
227+
)
228+
)
240229
return severities
241230

242231
@property

vulnerabilities/pipelines/v2_importers/nvd_importer.py

Lines changed: 25 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -202,46 +202,32 @@ def severities(self):
202202
Return a list of VulnerabilitySeverity for this CVE.
203203
"""
204204
severities = []
205-
impact = self.cve_item.get("impact") or {}
206-
base_metric_v4 = impact.get("baseMetricV4") or {}
207-
if base_metric_v4:
208-
cvss_v4 = base_metric_v4.get("cvssV4") or {}
209-
vs = VulnerabilitySeverity(
210-
system=severity_systems.CVSSV4,
211-
value=str(cvss_v4.get("baseScore") or ""),
212-
scoring_elements=str(cvss_v4.get("vectorString") or ""),
213-
url=f"https://nvd.nist.gov/vuln/detail/{self.cve_id}",
214-
)
215-
severities.append(vs)
216-
217-
base_metric_v3 = impact.get("baseMetricV3") or {}
218-
if base_metric_v3:
219-
cvss_v3 = get_item(base_metric_v3, "cvssV3")
220-
version = cvss_v3.get("version")
221-
system = None
222-
if version == "3.1":
223-
system = severity_systems.CVSSV31
224-
else:
225-
system = severity_systems.CVSSV3
226-
vs = VulnerabilitySeverity(
227-
system=system,
228-
value=str(cvss_v3.get("baseScore") or ""),
229-
scoring_elements=str(cvss_v3.get("vectorString") or ""),
230-
url=f"https://nvd.nist.gov/vuln/detail/{self.cve_id}",
231-
)
232-
severities.append(vs)
233-
234-
base_metric_v2 = impact.get("baseMetricV2") or {}
235-
if base_metric_v2:
236-
cvss_v2 = base_metric_v2.get("cvssV2") or {}
237-
vs = VulnerabilitySeverity(
238-
system=severity_systems.CVSSV2,
239-
value=str(cvss_v2.get("baseScore") or ""),
240-
scoring_elements=str(cvss_v2.get("vectorString") or ""),
241-
url=f"https://nvd.nist.gov/vuln/detail/{self.cve_id}",
242-
)
243-
severities.append(vs)
205+
metrics = get_item(self.cve_item, "cve", "metrics") or {}
206+
url = f"https://nvd.nist.gov/vuln/detail/{self.cve_id}"
207+
metric_configs = [
208+
("cvssMetricV40", severity_systems.CVSSV4),
209+
("cvssMetricV31", severity_systems.CVSSV31),
210+
("cvssMetricV30", severity_systems.CVSSV3),
211+
("cvssMetricV2", severity_systems.CVSSV2),
212+
]
244213

214+
for key, default_system in metric_configs:
215+
items = metrics.get(key) or []
216+
217+
for item in items:
218+
cvss_data = item.get("cvssData") or {}
219+
system = default_system
220+
if key == "cvssMetricV31" and cvss_data.get("version") != "3.1":
221+
system = severity_systems.CVSSV3
222+
223+
severities.append(
224+
VulnerabilitySeverity(
225+
system=system,
226+
value=str(cvss_data.get("baseScore") or ""),
227+
scoring_elements=str(cvss_data.get("vectorString") or ""),
228+
url=url,
229+
)
230+
)
245231
return severities
246232

247233
@property

vulnerabilities/tests/pipelines/test_nvd_importer_pipeline.py

Lines changed: 191 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
import json
1111
from pathlib import Path
1212

13+
from vulnerabilities.importer import VulnerabilitySeverity
1314
from vulnerabilities.pipelines import nvd_importer
15+
from vulnerabilities.severity_systems import Cvssv2ScoringSystem
16+
from vulnerabilities.severity_systems import Cvssv3ScoringSystem
17+
from vulnerabilities.severity_systems import Cvssv4ScoringSystem
1418
from vulnerabilities.tests.util_tests import VULNERABLECODE_REGEN_TEST_FIXTURES as REGEN
1519

1620
TEST_DATA = Path(__file__).parent.parent / "test_data" / "nvd"
@@ -95,28 +99,139 @@ def get_test_cve_item():
9599
},
96100
],
97101
"metrics": {
102+
"cvssMetricV40": [
103+
{
104+
"source": "cna@vuldb.com",
105+
"type": "Secondary",
106+
"cvssData": {
107+
"version": "4.0",
108+
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
109+
"baseScore": 5.3,
110+
"baseSeverity": "MEDIUM",
111+
"attackVector": "NETWORK",
112+
"attackComplexity": "LOW",
113+
"attackRequirements": "NONE",
114+
"privilegesRequired": "LOW",
115+
"userInteraction": "NONE",
116+
"vulnConfidentialityImpact": "LOW",
117+
"vulnIntegrityImpact": "LOW",
118+
"vulnAvailabilityImpact": "LOW",
119+
"subConfidentialityImpact": "NONE",
120+
"subIntegrityImpact": "NONE",
121+
"subAvailabilityImpact": "NONE",
122+
"exploitMaturity": "NOT_DEFINED",
123+
"confidentialityRequirement": "NOT_DEFINED",
124+
"integrityRequirement": "NOT_DEFINED",
125+
"availabilityRequirement": "NOT_DEFINED",
126+
"modifiedAttackVector": "NOT_DEFINED",
127+
"modifiedAttackComplexity": "NOT_DEFINED",
128+
"modifiedAttackRequirements": "NOT_DEFINED",
129+
"modifiedPrivilegesRequired": "NOT_DEFINED",
130+
"modifiedUserInteraction": "NOT_DEFINED",
131+
"modifiedVulnConfidentialityImpact": "NOT_DEFINED",
132+
"modifiedVulnIntegrityImpact": "NOT_DEFINED",
133+
"modifiedVulnAvailabilityImpact": "NOT_DEFINED",
134+
"modifiedSubConfidentialityImpact": "NOT_DEFINED",
135+
"modifiedSubIntegrityImpact": "NOT_DEFINED",
136+
"modifiedSubAvailabilityImpact": "NOT_DEFINED",
137+
"Safety": "NOT_DEFINED",
138+
"Automatable": "NOT_DEFINED",
139+
"Recovery": "NOT_DEFINED",
140+
"valueDensity": "NOT_DEFINED",
141+
"vulnerabilityResponseEffort": "NOT_DEFINED",
142+
"providerUrgency": "NOT_DEFINED",
143+
},
144+
}
145+
],
98146
"cvssMetricV31": [
99147
{
100-
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
148+
"source": "cna@vuldb.com",
101149
"type": "Secondary",
102150
"cvssData": {
103151
"version": "3.1",
104-
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
105-
"baseScore": 9.8,
106-
"baseSeverity": "CRITICAL",
152+
"vectorString": "CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
153+
"baseScore": 3.5,
154+
"baseSeverity": "LOW",
155+
"attackVector": "ADJACENT_NETWORK",
156+
"attackComplexity": "LOW",
157+
"privilegesRequired": "LOW",
158+
"userInteraction": "NONE",
159+
"scope": "UNCHANGED",
160+
"confidentialityImpact": "NONE",
161+
"integrityImpact": "NONE",
162+
"availabilityImpact": "LOW",
163+
},
164+
"exploitabilityScore": 2.1,
165+
"impactScore": 1.4,
166+
},
167+
{
168+
"source": "nvd@nist.gov",
169+
"type": "Primary",
170+
"cvssData": {
171+
"version": "3.1",
172+
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
173+
"baseScore": 7.5,
174+
"baseSeverity": "HIGH",
175+
"attackVector": "NETWORK",
176+
"attackComplexity": "LOW",
177+
"privilegesRequired": "NONE",
178+
"userInteraction": "NONE",
179+
"scope": "UNCHANGED",
180+
"confidentialityImpact": "NONE",
181+
"integrityImpact": "NONE",
182+
"availabilityImpact": "HIGH",
183+
},
184+
"exploitabilityScore": 3.9,
185+
"impactScore": 3.6,
186+
},
187+
],
188+
"cvssMetricV30": [
189+
{
190+
"source": "nvd@nist.gov",
191+
"type": "Primary",
192+
"cvssData": {
193+
"version": "3.0",
194+
"vectorString": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
195+
"baseScore": 7.5,
196+
"baseSeverity": "HIGH",
107197
"attackVector": "NETWORK",
108198
"attackComplexity": "LOW",
109199
"privilegesRequired": "NONE",
110200
"userInteraction": "NONE",
111201
"scope": "UNCHANGED",
112-
"confidentialityImpact": "HIGH",
113-
"integrityImpact": "HIGH",
202+
"confidentialityImpact": "NONE",
203+
"integrityImpact": "NONE",
114204
"availabilityImpact": "HIGH",
115205
},
116206
"exploitabilityScore": 3.9,
117-
"impactScore": 5.9,
207+
"impactScore": 3.6,
118208
}
119-
]
209+
],
210+
"cvssMetricV2": [
211+
{
212+
"source": "cna@vuldb.com",
213+
"type": "Secondary",
214+
"cvssData": {
215+
"version": "2.0",
216+
"vectorString": "AV:A/AC:L/Au:S/C:N/I:N/A:P",
217+
"baseScore": 2.7,
218+
"accessVector": "ADJACENT_NETWORK",
219+
"accessComplexity": "LOW",
220+
"authentication": "SINGLE",
221+
"confidentialityImpact": "NONE",
222+
"integrityImpact": "NONE",
223+
"availabilityImpact": "PARTIAL",
224+
},
225+
"baseSeverity": "LOW",
226+
"exploitabilityScore": 5.1,
227+
"impactScore": 2.9,
228+
"acInsufInfo": False,
229+
"obtainAllPrivilege": False,
230+
"obtainUserPrivilege": False,
231+
"obtainOtherPrivilege": False,
232+
"userInteractionRequired": False,
233+
}
234+
],
120235
},
121236
"weaknesses": [
122237
{
@@ -381,6 +496,74 @@ def get_test_cve_item():
381496
}
382497

383498

499+
def test_CveItem_severities():
500+
expected_severities = [
501+
VulnerabilitySeverity(
502+
system=Cvssv4ScoringSystem(
503+
identifier="cvssv4",
504+
name="CVSSv4 Base Score",
505+
url="https://www.first.org/cvss/v4-0/",
506+
notes="CVSSv4 base score and " "vector",
507+
),
508+
value="5.3",
509+
scoring_elements="CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
510+
published_at=None,
511+
url="https://nvd.nist.gov/vuln/detail/CVE-2025-45988",
512+
),
513+
VulnerabilitySeverity(
514+
system=Cvssv3ScoringSystem(
515+
identifier="cvssv3.1",
516+
name="CVSSv3.1 Base Score",
517+
url="https://www.first.org/cvss/v3-1/",
518+
notes="CVSSv3.1 base score and vector",
519+
),
520+
value="3.5",
521+
scoring_elements="CVSS:3.1/AV:A/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
522+
published_at=None,
523+
url="https://nvd.nist.gov/vuln/detail/CVE-2025-45988",
524+
),
525+
VulnerabilitySeverity(
526+
system=Cvssv3ScoringSystem(
527+
identifier="cvssv3.1",
528+
name="CVSSv3.1 Base Score",
529+
url="https://www.first.org/cvss/v3-1/",
530+
notes="CVSSv3.1 base score and vector",
531+
),
532+
value="7.5",
533+
scoring_elements="CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
534+
published_at=None,
535+
url="https://nvd.nist.gov/vuln/detail/CVE-2025-45988",
536+
),
537+
VulnerabilitySeverity(
538+
system=Cvssv3ScoringSystem(
539+
identifier="cvssv3",
540+
name="CVSSv3 Base Score",
541+
url="https://www.first.org/cvss/v3-0/",
542+
notes="CVSSv3 base score and " "vector",
543+
),
544+
value="7.5",
545+
scoring_elements="CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
546+
published_at=None,
547+
url="https://nvd.nist.gov/vuln/detail/CVE-2025-45988",
548+
),
549+
VulnerabilitySeverity(
550+
system=Cvssv2ScoringSystem(
551+
identifier="cvssv2",
552+
name="CVSSv2 Base Score",
553+
url="https://www.first.org/cvss/v2/",
554+
notes="CVSSv2 base score and vector",
555+
),
556+
value="2.7",
557+
scoring_elements="AV:A/AC:L/Au:S/C:N/I:N/A:P",
558+
published_at=None,
559+
url="https://nvd.nist.gov/vuln/detail/CVE-2025-45988",
560+
),
561+
]
562+
563+
found_severities = nvd_importer.CveItem(cve_item=get_test_cve_item()).severities
564+
assert found_severities == expected_severities
565+
566+
384567
def test_CveItem_cpes():
385568
expected_cpes = [
386569
"cpe:2.3:o:b-link:bl-wr9000_firmware:2.4.9:*:*:*:*:*:*:*",

0 commit comments

Comments
 (0)