1use crate::{
4 Error, PREFIX,
5 v4::{
6 MetricType,
7 metric::{
8 base::{
9 AttackComplexity, AttackRequirements, AttackVector,
10 AvailabilityImpactToTheSubsequentSystem, AvailabilityImpactToTheVulnerableSystem,
11 ConfidentialityImpactToTheSubsequentSystem,
12 ConfidentialityImpactToTheVulnerableSystem, IntegrityImpactToTheSubsequentSystem,
13 IntegrityImpactToTheVulnerableSystem, PrivilegesRequired, UserInteraction,
14 },
15 environmental::{
16 AvailabilityRequirements, ConfidentialityRequirements, IntegrityRequirements,
17 ModifiedAttackComplexity, ModifiedAttackRequirements, ModifiedAttackVector,
18 ModifiedAvailabilityImpactToTheSubsequentSystem,
19 ModifiedAvailabilityImpactToTheVulnerableSystem,
20 ModifiedConfidentialityImpactToTheSubsequentSystem,
21 ModifiedConfidentialityImpactToTheVulnerableSystem,
22 ModifiedIntegrityImpactToTheSubsequentSystem,
23 ModifiedIntegrityImpactToTheVulnerableSystem, ModifiedPrivilegesRequired,
24 ModifiedUserInteraction,
25 },
26 supplemental::{
27 Automatable, ProviderUrgency, Recovery, Safety, ValueDensity,
28 VulnerabilityResponseEffort,
29 },
30 threat::ExploitMaturity,
31 },
32 },
33};
34use alloc::{borrow::ToOwned, string::String, vec::Vec};
35use core::{fmt, str::FromStr};
36#[cfg(feature = "serde")]
37use {
38 alloc::string::ToString,
39 serde::{Deserialize, Serialize, de, ser},
40};
41
42#[cfg(feature = "std")]
43use crate::v4::Score;
44use crate::v4::score::Nomenclature;
45
46#[derive(Clone, Debug, Default, Eq, PartialEq)]
48pub struct Vector {
49 pub minor_version: usize,
51
52 pub(crate) ac: Option<AttackComplexity>,
54 pub(crate) at: Option<AttackRequirements>,
56 pub(crate) av: Option<AttackVector>,
58 pub(crate) pr: Option<PrivilegesRequired>,
60 pub(crate) sa: Option<AvailabilityImpactToTheSubsequentSystem>,
62 pub(crate) sc: Option<ConfidentialityImpactToTheSubsequentSystem>,
64 pub(crate) si: Option<IntegrityImpactToTheSubsequentSystem>,
66 pub(crate) ui: Option<UserInteraction>,
68 pub(crate) va: Option<AvailabilityImpactToTheVulnerableSystem>,
70 pub(crate) vc: Option<ConfidentialityImpactToTheVulnerableSystem>,
72 pub(crate) vi: Option<IntegrityImpactToTheVulnerableSystem>,
74 pub(crate) e: Option<ExploitMaturity>,
76 pub(crate) ar: Option<AvailabilityRequirements>,
78 pub(crate) cr: Option<ConfidentialityRequirements>,
80 pub(crate) ir: Option<IntegrityRequirements>,
82 pub(crate) mac: Option<ModifiedAttackComplexity>,
84 pub(crate) mat: Option<ModifiedAttackRequirements>,
86 pub(crate) mav: Option<ModifiedAttackVector>,
88 pub(crate) mpr: Option<ModifiedPrivilegesRequired>,
90 pub(crate) msa: Option<ModifiedAvailabilityImpactToTheSubsequentSystem>,
92 pub(crate) msc: Option<ModifiedConfidentialityImpactToTheSubsequentSystem>,
94 pub(crate) msi: Option<ModifiedIntegrityImpactToTheSubsequentSystem>,
96 pub(crate) mui: Option<ModifiedUserInteraction>,
98 pub(crate) mva: Option<ModifiedAvailabilityImpactToTheVulnerableSystem>,
100 pub(crate) mvc: Option<ModifiedConfidentialityImpactToTheVulnerableSystem>,
102 pub(crate) mvi: Option<ModifiedIntegrityImpactToTheVulnerableSystem>,
104 pub(crate) au: Option<Automatable>,
106 pub(crate) r: Option<Recovery>,
108 pub(crate) re: Option<VulnerabilityResponseEffort>,
110 pub(crate) s: Option<Safety>,
112 pub(crate) u: Option<ProviderUrgency>,
114 pub(crate) v: Option<ValueDensity>,
116}
117
118impl Vector {
119 #[cfg(feature = "std")]
121 pub fn score(&self) -> Score {
122 self.into()
123 }
124
125 pub fn nomenclature(&self) -> Nomenclature {
129 Nomenclature::from(self)
130 }
131
132 pub fn metrics(&self) -> impl Iterator<Item = (MetricType, &dyn fmt::Debug)> {
134 [
135 (
136 MetricType::AC,
137 self.ac.as_ref().map(|m| m as &dyn fmt::Debug),
138 ),
139 (
140 MetricType::AT,
141 self.at.as_ref().map(|m| m as &dyn fmt::Debug),
142 ),
143 (
144 MetricType::AV,
145 self.av.as_ref().map(|m| m as &dyn fmt::Debug),
146 ),
147 (
148 MetricType::PR,
149 self.pr.as_ref().map(|m| m as &dyn fmt::Debug),
150 ),
151 (
152 MetricType::SA,
153 self.sa.as_ref().map(|m| m as &dyn fmt::Debug),
154 ),
155 (
156 MetricType::SC,
157 self.sc.as_ref().map(|m| m as &dyn fmt::Debug),
158 ),
159 (
160 MetricType::SI,
161 self.si.as_ref().map(|m| m as &dyn fmt::Debug),
162 ),
163 (
164 MetricType::UI,
165 self.ui.as_ref().map(|m| m as &dyn fmt::Debug),
166 ),
167 (
168 MetricType::VA,
169 self.va.as_ref().map(|m| m as &dyn fmt::Debug),
170 ),
171 (
172 MetricType::VC,
173 self.vc.as_ref().map(|m| m as &dyn fmt::Debug),
174 ),
175 (
176 MetricType::VI,
177 self.vi.as_ref().map(|m| m as &dyn fmt::Debug),
178 ),
179 (MetricType::E, self.e.as_ref().map(|m| m as &dyn fmt::Debug)),
180 (
181 MetricType::AR,
182 self.ar.as_ref().map(|m| m as &dyn fmt::Debug),
183 ),
184 (
185 MetricType::CR,
186 self.cr.as_ref().map(|m| m as &dyn fmt::Debug),
187 ),
188 (
189 MetricType::IR,
190 self.ir.as_ref().map(|m| m as &dyn fmt::Debug),
191 ),
192 (
193 MetricType::MAC,
194 self.mac.as_ref().map(|m| m as &dyn fmt::Debug),
195 ),
196 (
197 MetricType::MAT,
198 self.mat.as_ref().map(|m| m as &dyn fmt::Debug),
199 ),
200 (
201 MetricType::MAV,
202 self.mav.as_ref().map(|m| m as &dyn fmt::Debug),
203 ),
204 (
205 MetricType::MPR,
206 self.mpr.as_ref().map(|m| m as &dyn fmt::Debug),
207 ),
208 (
209 MetricType::MSA,
210 self.msa.as_ref().map(|m| m as &dyn fmt::Debug),
211 ),
212 (
213 MetricType::MSC,
214 self.msc.as_ref().map(|m| m as &dyn fmt::Debug),
215 ),
216 (
217 MetricType::MSI,
218 self.msi.as_ref().map(|m| m as &dyn fmt::Debug),
219 ),
220 (
221 MetricType::MUI,
222 self.mui.as_ref().map(|m| m as &dyn fmt::Debug),
223 ),
224 (
225 MetricType::MVA,
226 self.mva.as_ref().map(|m| m as &dyn fmt::Debug),
227 ),
228 (
229 MetricType::MVC,
230 self.mvc.as_ref().map(|m| m as &dyn fmt::Debug),
231 ),
232 (
233 MetricType::MVI,
234 self.mvi.as_ref().map(|m| m as &dyn fmt::Debug),
235 ),
236 (
237 MetricType::AU,
238 self.au.as_ref().map(|m| m as &dyn fmt::Debug),
239 ),
240 (MetricType::R, self.r.as_ref().map(|m| m as &dyn fmt::Debug)),
241 (
242 MetricType::RE,
243 self.re.as_ref().map(|m| m as &dyn fmt::Debug),
244 ),
245 (MetricType::S, self.s.as_ref().map(|m| m as &dyn fmt::Debug)),
246 (MetricType::U, self.u.as_ref().map(|m| m as &dyn fmt::Debug)),
247 (MetricType::V, self.v.as_ref().map(|m| m as &dyn fmt::Debug)),
248 ]
249 .into_iter()
250 .filter_map(|(ty, m)| m.map(|m| (ty, m)))
251 }
252
253 fn check_mandatory_metrics(&self) -> Result<(), Error> {
257 fn ensure_present<T>(metric: Option<T>, metric_type: MetricType) -> Result<(), Error> {
258 if metric.is_none() {
259 return Err(Error::MissingMandatoryMetricV4 { metric_type });
260 }
261 Ok(())
262 }
263
264 ensure_present(self.ac.as_ref(), MetricType::AC)?;
265 ensure_present(self.at.as_ref(), MetricType::AT)?;
266 ensure_present(self.av.as_ref(), MetricType::AV)?;
267 ensure_present(self.pr.as_ref(), MetricType::PR)?;
268 ensure_present(self.sa.as_ref(), MetricType::SA)?;
269 ensure_present(self.sc.as_ref(), MetricType::SC)?;
270 ensure_present(self.si.as_ref(), MetricType::SI)?;
271 ensure_present(self.ui.as_ref(), MetricType::UI)?;
272 ensure_present(self.va.as_ref(), MetricType::VA)?;
273 ensure_present(self.vc.as_ref(), MetricType::VC)?;
274 ensure_present(self.vi.as_ref(), MetricType::VI)?;
275 Ok(())
276 }
277}
278
279macro_rules! write_metrics {
280 ($f:expr, $($metric:expr),+) => {
281 $(
282 if let Some(metric) = $metric {
283 write!($f, "/{}", metric)?;
284 }
285 )+
286 };
287}
288
289impl fmt::Display for Vector {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 write!(f, "{}:4.{}", PREFIX, self.minor_version)?;
292 write_metrics!(
293 f, self.av, self.ac, self.at, self.pr, self.ui, self.vc, self.vi, self.va, self.sc,
294 self.si, self.sa, self.e, self.cr, self.ir, self.ar, self.mav, self.mac, self.mat,
295 self.mpr, self.mui, self.mvc, self.mvi, self.mva, self.msc, self.msi, self.msa, self.s,
296 self.au, self.r, self.v, self.re, self.u
297 );
298 Ok(())
299 }
300}
301
302impl FromStr for Vector {
303 type Err = Error;
304
305 fn from_str(s: &str) -> crate::Result<Self> {
306 let component_vec = s
307 .split('/')
308 .map(|component| {
309 let mut parts = component.split(':');
310
311 let id = parts.next().ok_or_else(|| Error::InvalidComponent {
312 component: component.to_owned(),
313 })?;
314
315 let value = parts.next().ok_or_else(|| Error::InvalidComponent {
316 component: component.to_owned(),
317 })?;
318
319 if parts.next().is_some() {
320 return Err(Error::InvalidComponent {
321 component: component.to_owned(),
322 });
323 }
324
325 Ok((id, value))
326 })
327 .collect::<crate::Result<Vec<_>>>()?;
328
329 let mut components = component_vec.iter();
330 let &(id, version_string) = components.next().ok_or(Error::InvalidPrefix {
331 prefix: s.to_owned(),
332 })?;
333
334 if id != PREFIX {
335 return Err(Error::InvalidPrefix {
336 prefix: id.to_owned(),
337 });
338 }
339
340 let mut metrics = Self {
341 minor_version: match version_string {
342 "4.0" => 0,
343 _ => {
344 return Err(Error::UnsupportedVersion {
345 version: version_string.to_owned(),
346 });
347 }
348 },
349 ..Default::default()
350 };
351
352 for &component in components {
353 let id = component.0.to_ascii_uppercase();
354 let value = component.1.to_ascii_uppercase();
355
356 fn get_value<T: FromStr<Err = Error>>(
357 metric_type: MetricType,
358 current_val: Option<T>,
359 new_val: String,
360 ) -> Result<Option<T>, Error> {
361 let parsed: T = new_val.parse()?;
362 if current_val.is_some() {
363 return Err(Error::DuplicateMetricV4 { metric_type });
364 }
365 Ok(Some(parsed))
366 }
367
368 match id.parse::<MetricType>()? {
369 MetricType::AV => metrics.av = get_value(MetricType::AV, metrics.av, value)?,
370 MetricType::AC => metrics.ac = get_value(MetricType::AC, metrics.ac, value)?,
371 MetricType::PR => metrics.pr = get_value(MetricType::PR, metrics.pr, value)?,
372 MetricType::UI => metrics.ui = get_value(MetricType::UI, metrics.ui, value)?,
373 MetricType::S => metrics.s = get_value(MetricType::S, metrics.s, value)?,
374 MetricType::AT => metrics.at = get_value(MetricType::AT, metrics.at, value)?,
375 MetricType::SA => metrics.sa = get_value(MetricType::SA, metrics.sa, value)?,
376 MetricType::SC => metrics.sc = get_value(MetricType::SC, metrics.sc, value)?,
377 MetricType::SI => metrics.si = get_value(MetricType::SI, metrics.si, value)?,
378 MetricType::VA => metrics.va = get_value(MetricType::VA, metrics.va, value)?,
379 MetricType::VC => metrics.vc = get_value(MetricType::VC, metrics.vc, value)?,
380 MetricType::VI => metrics.vi = get_value(MetricType::VI, metrics.vi, value)?,
381 MetricType::E => metrics.e = get_value(MetricType::E, metrics.e, value)?,
382 MetricType::AR => metrics.ar = get_value(MetricType::AR, metrics.ar, value)?,
383 MetricType::CR => metrics.cr = get_value(MetricType::CR, metrics.cr, value)?,
384 MetricType::IR => metrics.ir = get_value(MetricType::IR, metrics.ir, value)?,
385 MetricType::MAC => metrics.mac = get_value(MetricType::MAC, metrics.mac, value)?,
386 MetricType::MAT => metrics.mat = get_value(MetricType::MAT, metrics.mat, value)?,
387 MetricType::MAV => metrics.mav = get_value(MetricType::MAV, metrics.mav, value)?,
388 MetricType::MPR => metrics.mpr = get_value(MetricType::MPR, metrics.mpr, value)?,
389 MetricType::MSA => metrics.msa = get_value(MetricType::MSA, metrics.msa, value)?,
390 MetricType::MSC => metrics.msc = get_value(MetricType::MSC, metrics.msc, value)?,
391 MetricType::MSI => metrics.msi = get_value(MetricType::MSI, metrics.msi, value)?,
392 MetricType::MUI => metrics.mui = get_value(MetricType::MUI, metrics.mui, value)?,
393 MetricType::MVA => metrics.mva = get_value(MetricType::MVA, metrics.mva, value)?,
394 MetricType::MVC => metrics.mvc = get_value(MetricType::MVC, metrics.mvc, value)?,
395 MetricType::MVI => metrics.mvi = get_value(MetricType::MVI, metrics.mvi, value)?,
396 MetricType::AU => metrics.au = get_value(MetricType::AU, metrics.au, value)?,
397 MetricType::R => metrics.r = get_value(MetricType::R, metrics.r, value)?,
398 MetricType::RE => metrics.re = get_value(MetricType::RE, metrics.re, value)?,
399 MetricType::U => metrics.u = get_value(MetricType::U, metrics.u, value)?,
400 MetricType::V => metrics.v = get_value(MetricType::V, metrics.v, value)?,
401 }
402 }
403
404 metrics.check_mandatory_metrics()?;
405
406 Ok(metrics)
407 }
408}
409
410#[cfg(feature = "serde")]
411#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
412impl<'de> Deserialize<'de> for Vector {
413 fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
414 String::deserialize(deserializer)?
415 .parse()
416 .map_err(de::Error::custom)
417 }
418}
419
420#[cfg(feature = "serde")]
421#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
422impl Serialize for Vector {
423 fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
424 self.to_string().serialize(serializer)
425 }
426}
427
428#[cfg(test)]
429#[cfg(feature = "std")]
430mod tests {
431 use super::*;
432 use alloc::{borrow::ToOwned, string::ToString};
433
434 #[test]
435 fn fails_to_parse_invalid_cvss4() {
436 assert_eq!(
438 Vector::from_str("CVSS:5.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N"),
439 Err(Error::UnsupportedVersion {
440 version: "5.0".to_string(),
441 })
442 );
443 assert_eq!(
445 Vector::from_str("CSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N"),
446 Err(Error::InvalidPrefix {
447 prefix: "CSS".to_owned(),
448 })
449 );
450 assert_eq!(
452 Vector::from_str("CVSS:4.0/AV:F/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N"),
453 Err(Error::InvalidMetricV4 {
454 metric_type: MetricType::AV,
455 value: "F".to_owned()
456 })
457 );
458 assert_eq!(
460 Vector::from_str("CVSS:4.0/AV:N/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N"),
461 Err(Error::MissingMandatoryMetricV4 {
462 metric_type: MetricType::AC
463 })
464 );
465 }
466
467 #[test]
468 fn parse_base_cvss4() {
469 assert!(
470 Vector::from_str("CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N")
471 .is_ok()
472 );
473 }
474
475 #[test]
476 fn parse_full_cvss4() {
477 let vector_s = "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:U/CR:L/IR:X/AR:L/MAV:A/MAC:H/MAT:N/MPR:N/MUI:P/MVC:X/MVI:N/MVA:H/MSC:N/MSI:L/MSA:S/S:N/AU:N/R:I/V:C/RE:H/U:Green";
478 let v = Vector::from_str(vector_s).unwrap();
479 assert_eq!(vector_s, v.to_string());
480 }
481}