Skip to content

Commit a97ccd4

Browse files
authored
fix: VulnerabilityScoreSource.get_from_vector() for CVSS_V3_1 and CVSS_V4 (#824)
fixes #821 --------- Signed-off-by: Jan Kowalleck <[email protected]>
1 parent 3395fda commit a97ccd4

File tree

2 files changed

+122
-40
lines changed

2 files changed

+122
-40
lines changed

cyclonedx/model/vulnerability.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -611,14 +611,17 @@ def get_from_vector(vector: str) -> 'VulnerabilityScoreSource':
611611
Always returns an instance of `VulnerabilityScoreSource`. `VulnerabilityScoreSource.OTHER` is
612612
returned if the scheme is not obvious or known to us.
613613
"""
614+
if vector.startswith('CVSS:4.'):
615+
return VulnerabilityScoreSource.CVSS_V4
614616
if vector.startswith('CVSS:3.'):
617+
if vector.startswith('CVSS:3.1'):
618+
return VulnerabilityScoreSource.CVSS_V3_1
615619
return VulnerabilityScoreSource.CVSS_V3
616-
elif vector.startswith('CVSS:2.'):
620+
if vector.startswith('CVSS:2.'):
617621
return VulnerabilityScoreSource.CVSS_V2
618-
elif vector.startswith('OWASP'):
622+
if vector.startswith('OWASP'):
619623
return VulnerabilityScoreSource.OWASP
620-
else:
621-
return VulnerabilityScoreSource.OTHER
624+
return VulnerabilityScoreSource.OTHER
622625

623626
def get_localised_vector(self, vector: str) -> str:
624627
"""
@@ -630,15 +633,16 @@ def get_localised_vector(self, vector: str) -> str:
630633
Returns:
631634
The vector without any scheme prefix as a `str`.
632635
"""
633-
if self == VulnerabilityScoreSource.CVSS_V3 and vector.startswith('CVSS:3.'):
634-
return re.sub('^CVSS:3\\.\\d/?', '', vector)
635-
636-
if self == VulnerabilityScoreSource.CVSS_V2 and vector.startswith('CVSS:2.'):
637-
return re.sub('^CVSS:2\\.\\d/?', '', vector)
638-
639-
if self == VulnerabilityScoreSource.OWASP and vector.startswith('OWASP'):
640-
return re.sub('^OWASP/?', '', vector)
641-
636+
if self is VulnerabilityScoreSource.CVSS_V4 and vector.startswith('CVSS:4.'):
637+
return re.sub(r'^CVSS:4\.\d/?', '', vector)
638+
if (
639+
self in (VulnerabilityScoreSource.CVSS_V3_1, VulnerabilityScoreSource.CVSS_V3)
640+
) and vector.startswith('CVSS:3.'):
641+
return re.sub(r'^CVSS:3\.\d/?', '', vector)
642+
if self is VulnerabilityScoreSource.CVSS_V2 and vector.startswith('CVSS:2.'):
643+
return re.sub(r'^CVSS:2\.\d/?', '', vector)
644+
if self is VulnerabilityScoreSource.OWASP and vector.startswith('OWASP'):
645+
return re.sub(r'^OWASP/?', '', vector)
642646
return vector
643647

644648
def get_value_pre_1_4(self) -> str:
@@ -649,7 +653,7 @@ def get_value_pre_1_4(self) -> str:
649653
Returns:
650654
`str`
651655
"""
652-
if self == VulnerabilityScoreSource.OWASP:
656+
if self is VulnerabilityScoreSource.OWASP:
653657
return 'OWASP Risk'
654658
return self.value # type:ignore[no-any-return]
655659

tests/test_model_vulnerability.py

Lines changed: 104 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from tests import reorder
4242

4343

44-
class TestModelVulnerability(TestCase):
44+
class TestModelVulnerabilitySeverity(TestCase):
4545

4646
def test_v_severity_from_cvss_scores_single_critical(self) -> None:
4747
self.assertEqual(
@@ -85,87 +85,165 @@ def test_v_severity_from_cvss_scores_multiple_high(self) -> None:
8585
VulnerabilitySeverity.HIGH
8686
)
8787

88+
89+
class TestModelVulnerabilityScoreSource(TestCase):
90+
91+
def test_v_source_parse_other(self) -> None:
92+
self.assertEqual(
93+
VulnerabilityScoreSource.get_from_vector('loremIpsum'),
94+
VulnerabilityScoreSource.OTHER
95+
)
96+
97+
def test_v_source_parse_cvss4_0(self) -> None:
98+
self.assertEqual(
99+
VulnerabilityScoreSource.get_from_vector(
100+
'CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U'),
101+
VulnerabilityScoreSource.CVSS_V4
102+
)
103+
88104
def test_v_source_parse_cvss3_1(self) -> None:
89105
self.assertEqual(
90-
VulnerabilityScoreSource.get_from_vector('CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
106+
VulnerabilityScoreSource.get_from_vector(
107+
'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H'),
108+
VulnerabilityScoreSource.CVSS_V3_1
109+
)
110+
111+
def test_v_source_parse_cvss3_0(self) -> None:
112+
self.assertEqual(
113+
VulnerabilityScoreSource.get_from_vector(
114+
'CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
91115
VulnerabilityScoreSource.CVSS_V3
92116
)
93117

94-
def test_v_source_parse_cvss2_1(self) -> None:
118+
def test_v_source_parse_cvss2_0(self) -> None:
95119
self.assertEqual(
96-
VulnerabilityScoreSource.get_from_vector('CVSS:2.0/AV:N/AC:L/Au:N/C:N/I:N/A:C'),
120+
VulnerabilityScoreSource.get_from_vector(
121+
'CVSS:2.0/AV:N/AC:L/Au:N/C:N/I:N/A:C'),
97122
VulnerabilityScoreSource.CVSS_V2
98123
)
99124

100125
def test_v_source_parse_owasp_1(self) -> None:
101126
self.assertEqual(
102-
VulnerabilityScoreSource.get_from_vector('OWASP/K9:M1:O0:Z2/D1:X1:W1:L3/C2:I1:A1:T1/F1:R1:S2:P3/50'),
127+
VulnerabilityScoreSource.get_from_vector(
128+
'OWASP/K9:M1:O0:Z2/D1:X1:W1:L3/C2:I1:A1:T1/F1:R1:S2:P3/50'),
103129
VulnerabilityScoreSource.OWASP
104130
)
105131

106-
def test_v_source_get_localised_vector_cvss3_1(self) -> None:
132+
def test_v_source_get_localised_vector_cvss4_slash(self) -> None:
133+
self.assertEqual(
134+
VulnerabilityScoreSource.CVSS_V4.get_localised_vector(
135+
'CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'),
136+
'AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'
137+
)
138+
139+
def test_v_source_get_localised_vector_cvss4_noslash(self) -> None:
140+
self.assertEqual(
141+
VulnerabilityScoreSource.CVSS_V4.get_localised_vector(
142+
'CVSS:4.0AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'),
143+
'AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'
144+
)
145+
146+
def test_v_source_get_localised_vector_cvss4_none(self) -> None:
147+
self.assertEqual(
148+
VulnerabilityScoreSource.CVSS_V4.get_localised_vector(
149+
'AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'),
150+
'AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'
151+
)
152+
153+
def test_v_source_get_localised_vector_cvss3_1_slash(self) -> None:
154+
self.assertEqual(
155+
VulnerabilityScoreSource.CVSS_V3.get_localised_vector(
156+
'CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H'),
157+
'AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H'
158+
)
159+
160+
def test_v_source_get_localised_vector_cvss3_1_noslash(self) -> None:
161+
self.assertEqual(
162+
VulnerabilityScoreSource.CVSS_V3_1.get_localised_vector(
163+
'CVSS:3.0AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
164+
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
165+
)
166+
167+
def test_v_source_get_localised_vector_cvss3_1_none(self) -> None:
168+
self.assertEqual(
169+
VulnerabilityScoreSource.CVSS_V3_1.get_localised_vector(
170+
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
171+
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
172+
)
173+
174+
def test_v_source_get_localised_vector_cvss3_slash(self) -> None:
107175
self.assertEqual(
108176
VulnerabilityScoreSource.CVSS_V3.get_localised_vector(
109-
vector='CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
110-
),
177+
'CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
111178
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
112179
)
113180

114-
def test_v_source_get_localised_vector_cvss3_2(self) -> None:
181+
def test_v_source_get_localised_vector_cvss3_noslash(self) -> None:
115182
self.assertEqual(
116-
VulnerabilityScoreSource.CVSS_V3.get_localised_vector(vector='CVSS:3.0AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
183+
VulnerabilityScoreSource.CVSS_V3.get_localised_vector(
184+
'CVSS:3.0AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
117185
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
118186
)
119187

120-
def test_v_source_get_localised_vector_cvss3_3(self) -> None:
188+
def test_v_source_get_localised_vector_cvss3_none(self) -> None:
121189
self.assertEqual(
122-
VulnerabilityScoreSource.CVSS_V3.get_localised_vector(vector='AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
190+
VulnerabilityScoreSource.CVSS_V3.get_localised_vector(
191+
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
123192
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
124193
)
125194

126-
def test_v_source_get_localised_vector_cvss2_1(self) -> None:
195+
def test_v_source_get_localised_vector_cvss2_slash(self) -> None:
127196
self.assertEqual(
128197
VulnerabilityScoreSource.CVSS_V2.get_localised_vector(
129-
vector='CVSS:2.0/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
198+
'CVSS:2.0/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
130199
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
131200
)
132201

133-
def test_v_source_get_localised_vector_cvss2_2(self) -> None:
202+
def test_v_source_get_localised_vector_cvss2_noslash(self) -> None:
134203
self.assertEqual(
135-
VulnerabilityScoreSource.CVSS_V2.get_localised_vector(vector='CVSS:2.1AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
204+
VulnerabilityScoreSource.CVSS_V2.get_localised_vector(
205+
'CVSS:2.0AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
136206
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
137207
)
138208

139-
def test_v_source_get_localised_vector_cvss2_3(self) -> None:
209+
def test_v_source_get_localised_vector_cvss2_none(self) -> None:
140210
self.assertEqual(
141-
VulnerabilityScoreSource.CVSS_V2.get_localised_vector(vector='AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
211+
VulnerabilityScoreSource.CVSS_V2.get_localised_vector(
212+
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
142213
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
143214
)
144215

145-
def test_v_source_get_localised_vector_owasp_1(self) -> None:
216+
def test_v_source_get_localised_vector_owasp_slash(self) -> None:
146217
self.assertEqual(
147-
VulnerabilityScoreSource.OWASP.get_localised_vector(vector='OWASP/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
218+
VulnerabilityScoreSource.OWASP.get_localised_vector(
219+
'OWASP/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
148220
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
149221
)
150222

151-
def test_v_source_get_localised_vector_owasp_2(self) -> None:
223+
def test_v_source_get_localised_vector_owasp_noslash(self) -> None:
152224
self.assertEqual(
153-
VulnerabilityScoreSource.OWASP.get_localised_vector(vector='OWASPAV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
225+
VulnerabilityScoreSource.OWASP.get_localised_vector(
226+
'OWASPAV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
154227
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
155228
)
156229

157-
def test_v_source_get_localised_vector_owasp_3(self) -> None:
230+
def test_v_source_get_localised_vector_owasp_none(self) -> None:
158231
self.assertEqual(
159-
VulnerabilityScoreSource.OWASP.get_localised_vector(vector='AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
232+
VulnerabilityScoreSource.OWASP.get_localised_vector(
233+
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'),
160234
'AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:N/A:N'
161235
)
162236

163-
def test_v_source_get_localised_vector_other_2(self) -> None:
237+
def test_v_source_get_localised_vector_other(self) -> None:
164238
self.assertEqual(
165-
VulnerabilityScoreSource.OTHER.get_localised_vector(vector='SOMETHING_OR_OTHER'),
239+
VulnerabilityScoreSource.OTHER.get_localised_vector(
240+
'SOMETHING_OR_OTHER'),
166241
'SOMETHING_OR_OTHER'
167242
)
168243

244+
245+
class TestModelVulnerability(TestCase):
246+
169247
def test_empty_vulnerability(self) -> None:
170248
v = Vulnerability()
171249
self.assertIsNone(v.bom_ref.value)

0 commit comments

Comments
 (0)