Skip to main content

libcvss/v2/
environmental.rs

1//! Environmental vector implementation.
2
3use crate::parsing;
4use crate::parsing::{Field, OptionalParser};
5use crate::v2::base::BaseVector;
6use crate::v2::temporal::TemporalVector;
7use serde::{Deserialize, Serialize};
8use std::fmt::{Error, Formatter};
9use std::str::Split;
10
11/// A CVSS V2.0 environmental vector.
12///
13/// Per the CVSS specification, this structure contains a Collateral Damage Potential field, a Target Distribution field, a Confidentiality Requirement field, an Integrity Requirement field, and an Availability Requirement field, all optionals.
14#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
15pub struct EnvironmentalVector {
16    /// Optional Collateral Damage Potential field as defined by the CVSS specification.
17    pub collateral_damage_potential: Option<CollateralDamagePotential>,
18    /// Optional Target Distribution field as defined by the CVSS specification.
19    pub target_distribution: Option<TargetDistribution>,
20    /// Optional Confidentiality Requirement field as defined by the CVSS specification.
21    pub confidentiality_requirement: Option<ConfidentialityRequirement>,
22    /// Optional Integrity Requirement field as defined by the CVSS specification.
23    pub integrity_requirement: Option<IntegrityRequirement>,
24    /// Optional Availability Requirement field as defined by the CVSS specification.
25    pub availability_requirement: Option<AvailabilityRequirement>,
26}
27
28/// The Collateral Damage Potential field as defined by the CVSS specification.
29///
30/// "Not Defined" is considered the same as a missing value. This is done by setting [`EnvironmentalVector.collateral_damage_potential`] to [`None`].
31///
32/// [`EnvironmentalVector.collateral_damage_potential`]: struct.EnvironmentalVector.html#structfield.collateral_damage_potential
33/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
34#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
35pub enum CollateralDamagePotential {
36    /// None ("CDP:N") value for the Collateral Damage Potential field.
37    None,
38    /// Low ("CDP:L") value for the Collateral Damage Potential field.
39    Low,
40    /// Low Medium ("CDP:LM") value for the Collateral Damage Potential field.
41    LowMedium,
42    /// Medium High ("CDP:MH") value for the Collateral Damage Potential field.
43    MediumHigh,
44    /// High ("CDP:H") value for the Collateral Damage Potential field.
45    High,
46    // "Not Defined" is considered the same as a missing value. This is done by setting EnvironmentalVector.collateral_damage_potential to None.
47}
48
49/// The Target Distribution field as defined by the CVSS specification.
50///
51/// "Not Defined" is considered the same as a missing value. This is done by setting [`EnvironmentalVector.target_distribution`] to [`None`].
52///
53/// [`EnvironmentalVector.target_distribution`]: struct.EnvironmentalVector.html#structfield.target_distribution
54/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
55#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
56pub enum TargetDistribution {
57    /// None ("TD:N") value for the Target Distribution field.
58    None,
59    /// Low ("TD:L") value for the Target Distribution field.
60    Low,
61    /// Medium ("TD:M") value for the Target Distribution field.
62    Medium,
63    /// High ("TD:H") value for the Target Distribution field.
64    High,
65    // "Not Defined" is considered the same as a missing value. This is done by setting EnvironmentalVector.target_distribution to None.
66}
67
68/// The Confidentiality Requirement field as defined by the CVSS specification.
69///
70/// "Not Defined" is considered the same as a missing value. This is done by setting [`EnvironmentalVector.confidentiality_requirement`] to [`None`].
71///
72/// [`EnvironmentalVector.confidentiality_requirement`]: struct.EnvironmentalVector.html#structfield.confidentiality_requirement
73/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None
74#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
75pub enum ConfidentialityRequirement {
76    /// Low ("CR:L") value for the Confidentiality Requirement field.
77    Low,
78    /// Medium ("CR:M") value for the Confidentiality Requirement field.
79    Medium,
80    /// High ("CR:H") value for the Confidentiality Requirement field.
81    High,
82    // "Not Defined" is considered the same as a missing value. This is done by setting EnvironmentalVector.confidentiality_requirement to None.
83}
84
85/// The Integrity Requirement field as defined by the CVSS specification.
86///
87/// "Not Defined" is considered the same as a missing value. This is done by setting [`EnvironmentalVector.integrity_requirement`] to [`None`].
88///
89/// [`EnvironmentalVector.integrity_requirement`]: struct.EnvironmentalVector.html#structfield.integrity_requirement
90/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.
91#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
92pub enum IntegrityRequirement {
93    /// Low ("IR:L") value for the Integrity Requirement field.
94    Low,
95    /// Medium ("IR:M") value for the Integrity Requirement field.
96    Medium,
97    /// High ("IR:H") value for the Integrity Requirement field.
98    High,
99    // "Not Defined" is considered the same as a missing value. This is done by setting EnvironmentalVector.integrity_requirement to None.
100}
101
102/// The Availability Requirement field as defined by the CVSS specification.
103///
104/// "Not Defined" is considered the same as a missing value. This is done by setting [`EnvironmentalVector.availability_requirement`] to [`None`].
105///
106/// [`EnvironmentalVector.availability_requirement`]: struct.EnvironmentalVector.html#structfield.availability_requirement
107/// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.
108#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
109pub enum AvailabilityRequirement {
110    /// Low ("AR:L") value for the Availability Requirement field.
111    Low,
112    /// Medium ("AR:M") value for the Availability Requirement field.
113    Medium,
114    /// High ("AR:H") value for the Availability Requirement field.
115    High,
116    // "Not Defined" is considered the same as a missing value. This is done by setting EnvironmentalVector.availability_requirement to None.
117}
118
119impl EnvironmentalVector {
120    /// Provides the severity score for the CVSS environmental vector, given a mandatory base vector and an optional environmental vector.
121    ///
122    /// Calling this method is identical to calling [`CVSS2Vector.score()`].
123    ///
124    /// [`CVSS2Vector.score()`]: ../struct.CVSS2Vector.html#method.score
125    pub fn score(&self, base_vector: BaseVector, temporal_vector: Option<TemporalVector>) -> f64 {
126        let confidentiality_requirement = match self.confidentiality_requirement {
127            None => 1.0,
128            Some(requirement) => requirement.numerical_value(),
129        };
130
131        let integrity_requirement = match self.integrity_requirement {
132            None => 1.0,
133            Some(requirement) => requirement.numerical_value(),
134        };
135
136        let availability_requirement = match self.availability_requirement {
137            None => 1.0,
138            Some(requirement) => requirement.numerical_value(),
139        };
140
141        let adjusted_impact = f64::min(
142            10.0,
143            10.41
144                * (1.0
145                    - (1.0
146                        - base_vector.confidentiality_impact.numerical_value()
147                            * confidentiality_requirement)
148                        * (1.0
149                            - base_vector.integrity_impact.numerical_value()
150                                * integrity_requirement)
151                        * (1.0
152                            - base_vector.availability_impact.numerical_value()
153                                * availability_requirement)),
154        );
155
156        let adjusted_base = base_vector.score_with_adjusted_impact(adjusted_impact);
157
158        let adjusted_temporal = match temporal_vector {
159            None => adjusted_base,
160            Some(vector) => vector.score(adjusted_base),
161        };
162
163        let collateral_damage_potential = match self.collateral_damage_potential {
164            None => 0.0,
165            Some(cdp) => cdp.numerical_value(),
166        };
167
168        let target_distribution = match self.target_distribution {
169            None => 1.0,
170            Some(td) => td.numerical_value(),
171        };
172
173        (10.0
174            * (adjusted_temporal + (10.0 - adjusted_temporal) * collateral_damage_potential)
175            * target_distribution)
176            .round()
177            / 10.0
178    }
179}
180
181impl CollateralDamagePotential {
182    /// Provides the numerical value associated with the metric, as specified by the CVSS specification.
183    ///
184    /// In normal usage you should not need to call this yourself.
185    pub fn numerical_value(&self) -> f64 {
186        match *self {
187            CollateralDamagePotential::None => 0.0,
188            CollateralDamagePotential::Low => 0.1,
189            CollateralDamagePotential::LowMedium => 0.3,
190            CollateralDamagePotential::MediumHigh => 0.4,
191            CollateralDamagePotential::High => 0.5,
192        }
193    }
194}
195
196impl TargetDistribution {
197    /// Provides the numerical value associated with the metric, as specified by the CVSS specification.
198    ///
199    /// In normal usage you should not need to call this yourself.
200    pub fn numerical_value(&self) -> f64 {
201        match *self {
202            TargetDistribution::None => 0.0,
203            TargetDistribution::Low => 0.25,
204            TargetDistribution::Medium => 0.75,
205            TargetDistribution::High => 1.0,
206        }
207    }
208}
209
210impl ConfidentialityRequirement {
211    /// Provides the numerical value associated with the metric, as specified by the CVSS specification.
212    ///
213    /// In normal usage you should not need to call this yourself.
214    pub fn numerical_value(&self) -> f64 {
215        match *self {
216            ConfidentialityRequirement::Low => 0.5,
217            ConfidentialityRequirement::Medium => 1.0,
218            ConfidentialityRequirement::High => 1.51,
219        }
220    }
221}
222
223impl IntegrityRequirement {
224    /// Provides the numerical value associated with the metric, as specified by the CVSS specification.
225    ///
226    /// In normal usage you should not need to call this yourself.
227    pub fn numerical_value(&self) -> f64 {
228        match *self {
229            IntegrityRequirement::Low => 0.5,
230            IntegrityRequirement::Medium => 1.0,
231            IntegrityRequirement::High => 1.51,
232        }
233    }
234}
235
236impl AvailabilityRequirement {
237    /// Provides the numerical value associated with the metric, as specified by the CVSS specification.
238    ///
239    /// In normal usage you should not need to call this yourself.
240    pub fn numerical_value(&self) -> f64 {
241        match *self {
242            AvailabilityRequirement::Low => 0.5,
243            AvailabilityRequirement::Medium => 1.0,
244            AvailabilityRequirement::High => 1.51,
245        }
246    }
247}
248
249impl OptionalParser for EnvironmentalVector {
250    fn parse_optional(split: &Box<Split<char>>) -> Option<Self>
251    where
252        Self: Sized,
253    {
254        let res = EnvironmentalVector {
255            collateral_damage_potential: CollateralDamagePotential::parse_optional(split),
256            target_distribution: TargetDistribution::parse_optional(split),
257            confidentiality_requirement: ConfidentialityRequirement::parse_optional(split),
258            integrity_requirement: IntegrityRequirement::parse_optional(split),
259            availability_requirement: AvailabilityRequirement::parse_optional(split),
260        };
261
262        if res.collateral_damage_potential.is_none()
263            && res.target_distribution.is_none()
264            && res.confidentiality_requirement.is_none()
265            && res.integrity_requirement.is_none()
266            && res.availability_requirement.is_none()
267        {
268            None
269        } else {
270            Some(res)
271        }
272    }
273}
274
275impl Field for CollateralDamagePotential {
276    fn abbreviated_form(&self) -> &'static str {
277        match *self {
278            CollateralDamagePotential::None => "CDP:N",
279            CollateralDamagePotential::Low => "CDP:L",
280            CollateralDamagePotential::LowMedium => "CDP:LM",
281            CollateralDamagePotential::MediumHigh => "CDP:MH",
282            CollateralDamagePotential::High => "CDP:H",
283        }
284    }
285
286    fn error_message() -> &'static str {
287        // We do not define any error message for optional fields
288        unreachable!()
289    }
290
291    fn parse(input: &str) -> Option<CollateralDamagePotential> {
292        if input == CollateralDamagePotential::None.abbreviated_form() {
293            Some(CollateralDamagePotential::None)
294        } else if input == CollateralDamagePotential::Low.abbreviated_form() {
295            Some(CollateralDamagePotential::Low)
296        } else if input == CollateralDamagePotential::LowMedium.abbreviated_form() {
297            Some(CollateralDamagePotential::LowMedium)
298        } else if input == CollateralDamagePotential::MediumHigh.abbreviated_form() {
299            Some(CollateralDamagePotential::MediumHigh)
300        } else if input == CollateralDamagePotential::High.abbreviated_form() {
301            Some(CollateralDamagePotential::High)
302        } else {
303            None
304        }
305    }
306}
307
308impl Field for TargetDistribution {
309    fn abbreviated_form(&self) -> &'static str {
310        match *self {
311            TargetDistribution::None => "TD:N",
312            TargetDistribution::Low => "TD:L",
313            TargetDistribution::Medium => "TD:M",
314            TargetDistribution::High => "TD:H",
315        }
316    }
317
318    fn error_message() -> &'static str {
319        // We do not define any error message for optional fields
320        unreachable!()
321    }
322
323    fn parse(input: &str) -> Option<TargetDistribution> {
324        if input == TargetDistribution::None.abbreviated_form() {
325            Some(TargetDistribution::None)
326        } else if input == TargetDistribution::Low.abbreviated_form() {
327            Some(TargetDistribution::Low)
328        } else if input == TargetDistribution::Medium.abbreviated_form() {
329            Some(TargetDistribution::Medium)
330        } else if input == TargetDistribution::High.abbreviated_form() {
331            Some(TargetDistribution::High)
332        } else {
333            None
334        }
335    }
336}
337
338impl Field for ConfidentialityRequirement {
339    fn abbreviated_form(&self) -> &'static str {
340        match *self {
341            ConfidentialityRequirement::Low => "CR:L",
342            ConfidentialityRequirement::Medium => "CR:M",
343            ConfidentialityRequirement::High => "CR:H",
344        }
345    }
346
347    fn error_message() -> &'static str {
348        // We do not define any error message for optional fields
349        unreachable!()
350    }
351
352    fn parse(input: &str) -> Option<ConfidentialityRequirement> {
353        if input == ConfidentialityRequirement::Low.abbreviated_form() {
354            Some(ConfidentialityRequirement::Low)
355        } else if input == ConfidentialityRequirement::Medium.abbreviated_form() {
356            Some(ConfidentialityRequirement::Medium)
357        } else if input == ConfidentialityRequirement::High.abbreviated_form() {
358            Some(ConfidentialityRequirement::High)
359        } else {
360            None
361        }
362    }
363}
364
365impl Field for IntegrityRequirement {
366    fn abbreviated_form(&self) -> &'static str {
367        match *self {
368            IntegrityRequirement::Low => "IR:L",
369            IntegrityRequirement::Medium => "IR:M",
370            IntegrityRequirement::High => "IR:H",
371        }
372    }
373
374    fn error_message() -> &'static str {
375        // We do not define any error message for optional fields
376        unreachable!()
377    }
378
379    fn parse(input: &str) -> Option<IntegrityRequirement> {
380        if input == IntegrityRequirement::Low.abbreviated_form() {
381            Some(IntegrityRequirement::Low)
382        } else if input == IntegrityRequirement::Medium.abbreviated_form() {
383            Some(IntegrityRequirement::Medium)
384        } else if input == IntegrityRequirement::High.abbreviated_form() {
385            Some(IntegrityRequirement::High)
386        } else {
387            None
388        }
389    }
390}
391
392impl Field for AvailabilityRequirement {
393    fn abbreviated_form(&self) -> &'static str {
394        match *self {
395            AvailabilityRequirement::Low => "AR:L",
396            AvailabilityRequirement::Medium => "AR:M",
397            AvailabilityRequirement::High => "AR:H",
398        }
399    }
400
401    fn error_message() -> &'static str {
402        // We do not define any error message for optional fields
403        unreachable!()
404    }
405
406    fn parse(input: &str) -> Option<AvailabilityRequirement> {
407        if input == AvailabilityRequirement::Low.abbreviated_form() {
408            Some(AvailabilityRequirement::Low)
409        } else if input == AvailabilityRequirement::Medium.abbreviated_form() {
410            Some(AvailabilityRequirement::Medium)
411        } else if input == AvailabilityRequirement::High.abbreviated_form() {
412            Some(AvailabilityRequirement::High)
413        } else {
414            None
415        }
416    }
417}
418
419impl OptionalParser for CollateralDamagePotential {
420    fn parse_optional(split: &Box<Split<char>>) -> Option<Self>
421    where
422        Self: Sized,
423    {
424        parsing::parse_optional_default_field(split)
425    }
426}
427
428impl OptionalParser for TargetDistribution {
429    fn parse_optional(split: &Box<Split<char>>) -> Option<Self>
430    where
431        Self: Sized,
432    {
433        parsing::parse_optional_default_field(split)
434    }
435}
436
437impl OptionalParser for ConfidentialityRequirement {
438    fn parse_optional(split: &Box<Split<char>>) -> Option<Self>
439    where
440        Self: Sized,
441    {
442        parsing::parse_optional_default_field(split)
443    }
444}
445
446impl OptionalParser for IntegrityRequirement {
447    fn parse_optional(split: &Box<Split<char>>) -> Option<Self>
448    where
449        Self: Sized,
450    {
451        parsing::parse_optional_default_field(split)
452    }
453}
454
455impl OptionalParser for AvailabilityRequirement {
456    fn parse_optional(split: &Box<Split<char>>) -> Option<Self>
457    where
458        Self: Sized,
459    {
460        parsing::parse_optional_default_field(split)
461    }
462}
463
464impl std::fmt::Display for CollateralDamagePotential {
465    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
466        write!(f, "{}", self.abbreviated_form())
467    }
468}
469
470impl std::fmt::Display for TargetDistribution {
471    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
472        write!(f, "{}", self.abbreviated_form())
473    }
474}
475
476impl std::fmt::Display for ConfidentialityRequirement {
477    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
478        write!(f, "{}", self.abbreviated_form())
479    }
480}
481
482impl std::fmt::Display for IntegrityRequirement {
483    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
484        write!(f, "{}", self.abbreviated_form())
485    }
486}
487
488impl std::fmt::Display for AvailabilityRequirement {
489    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
490        write!(f, "{}", self.abbreviated_form())
491    }
492}
493
494impl std::fmt::Display for EnvironmentalVector {
495    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
496        // Fancy way to write a/b/c which degrade to a/b, b/c, a etc. when any field is missing (as they are all optional)
497        let mut fields = Vec::new();
498
499        match self.collateral_damage_potential {
500            None => (),
501            Some(field) => fields.push(field.abbreviated_form()),
502        };
503
504        match self.target_distribution {
505            None => (),
506            Some(field) => fields.push(field.abbreviated_form()),
507        };
508
509        match self.confidentiality_requirement {
510            None => (),
511            Some(field) => fields.push(field.abbreviated_form()),
512        };
513
514        match self.integrity_requirement {
515            None => (),
516            Some(field) => fields.push(field.abbreviated_form()),
517        };
518
519        match self.availability_requirement {
520            None => (),
521            Some(field) => fields.push(field.abbreviated_form()),
522        };
523
524        write!(f, "{}", fields.join("/"))
525    }
526}
527
528#[cfg(test)]
529pub mod tests {
530    use super::*;
531    use crate::v2::base::{
532        AccessComplexity, AccessVector, Authentication, AvailabilityImpact, BaseVector,
533        ConfidentialityImpact, IntegrityImpact,
534    };
535    use crate::v2::temporal::{Exploitability, RemediationLevel, ReportConfidence, TemporalVector};
536
537    #[test]
538    fn test_formatting() {
539        // Collateral Damage Potential
540        assert_eq!("CDP:N", format!("{}", CollateralDamagePotential::None));
541        assert_eq!("CDP:L", format!("{}", CollateralDamagePotential::Low));
542        assert_eq!(
543            "CDP:LM",
544            format!("{}", CollateralDamagePotential::LowMedium)
545        );
546        assert_eq!(
547            "CDP:MH",
548            format!("{}", CollateralDamagePotential::MediumHigh)
549        );
550        assert_eq!("CDP:H", format!("{}", CollateralDamagePotential::High));
551
552        // Target distribution
553        assert_eq!("TD:N", format!("{}", TargetDistribution::None));
554        assert_eq!("TD:L", format!("{}", TargetDistribution::Low));
555        assert_eq!("TD:M", format!("{}", TargetDistribution::Medium));
556        assert_eq!("TD:H", format!("{}", TargetDistribution::High));
557
558        // Confidentiality Requirement
559        assert_eq!("CR:L", format!("{}", ConfidentialityRequirement::Low));
560        assert_eq!("CR:M", format!("{}", ConfidentialityRequirement::Medium));
561        assert_eq!("CR:H", format!("{}", ConfidentialityRequirement::High));
562
563        // Integrity Requirement
564        assert_eq!("IR:L", format!("{}", IntegrityRequirement::Low));
565        assert_eq!("IR:M", format!("{}", IntegrityRequirement::Medium));
566        assert_eq!("IR:H", format!("{}", IntegrityRequirement::High));
567
568        // Availability Requirement
569        assert_eq!("AR:L", format!("{}", AvailabilityRequirement::Low));
570        assert_eq!("AR:M", format!("{}", AvailabilityRequirement::Medium));
571        assert_eq!("AR:H", format!("{}", AvailabilityRequirement::High));
572
573        // Environmental vector
574        assert_eq!(
575            "CDP:H/TD:H/CR:M/IR:M/AR:H",
576            format!("{}", provide_environmental_vector1())
577        );
578
579        // Missing fields
580        let mut missing_fields = provide_environmental_vector1();
581        missing_fields.collateral_damage_potential = None;
582        missing_fields.target_distribution = None;
583        assert_eq!("CR:M/IR:M/AR:H", format!("{}", missing_fields));
584    }
585
586    #[test]
587    fn test_parsing() {
588        // Collateral Damage Potential
589        assert_eq!(
590            Some(CollateralDamagePotential::None),
591            CollateralDamagePotential::parse("CDP:N")
592        );
593        assert_eq!(
594            Some(CollateralDamagePotential::Low),
595            CollateralDamagePotential::parse("CDP:L")
596        );
597        assert_eq!(
598            Some(CollateralDamagePotential::LowMedium),
599            CollateralDamagePotential::parse("CDP:LM")
600        );
601        assert_eq!(
602            Some(CollateralDamagePotential::MediumHigh),
603            CollateralDamagePotential::parse("CDP:MH")
604        );
605        assert_eq!(
606            Some(CollateralDamagePotential::High),
607            CollateralDamagePotential::parse("CDP:H")
608        );
609        // Target distribution
610        assert_eq!(
611            Some(TargetDistribution::None),
612            TargetDistribution::parse("TD:N")
613        );
614        assert_eq!(
615            Some(TargetDistribution::Low),
616            TargetDistribution::parse("TD:L")
617        );
618        assert_eq!(
619            Some(TargetDistribution::Medium),
620            TargetDistribution::parse("TD:M")
621        );
622        assert_eq!(
623            Some(TargetDistribution::High),
624            TargetDistribution::parse("TD:H")
625        );
626
627        // Confidentiality Requirement
628        assert_eq!(
629            Some(ConfidentialityRequirement::Low),
630            ConfidentialityRequirement::parse("CR:L")
631        );
632        assert_eq!(
633            Some(ConfidentialityRequirement::Medium),
634            ConfidentialityRequirement::parse("CR:M")
635        );
636        assert_eq!(
637            Some(ConfidentialityRequirement::High),
638            ConfidentialityRequirement::parse("CR:H")
639        );
640
641        // Integrity Requirement
642        assert_eq!(
643            Some(IntegrityRequirement::Low),
644            IntegrityRequirement::parse("IR:L")
645        );
646        assert_eq!(
647            Some(IntegrityRequirement::Medium),
648            IntegrityRequirement::parse("IR:M")
649        );
650        assert_eq!(
651            Some(IntegrityRequirement::High),
652            IntegrityRequirement::parse("IR:H")
653        );
654
655        // Availability Requirement
656        assert_eq!(
657            Some(AvailabilityRequirement::Low),
658            AvailabilityRequirement::parse("AR:L")
659        );
660        assert_eq!(
661            Some(AvailabilityRequirement::Medium),
662            AvailabilityRequirement::parse("AR:M")
663        );
664        assert_eq!(
665            Some(AvailabilityRequirement::High),
666            AvailabilityRequirement::parse("AR:H")
667        );
668
669        // Environmental vector
670        // Happy path
671        assert_eq!(
672            Some(provide_environmental_vector1()),
673            EnvironmentalVector::parse_optional(&Box::new("CDP:H/TD:H/CR:M/IR:M/AR:H".split('/')))
674        );
675        // Wrong order
676        assert_eq!(
677            Some(provide_environmental_vector1()),
678            EnvironmentalVector::parse_optional(&Box::new("TD:H/CR:M/CDP:H/IR:M/AR:H".split('/')))
679        );
680        // Missing fields
681        let mut missing_fields = provide_environmental_vector1();
682        missing_fields.collateral_damage_potential = None;
683        missing_fields.target_distribution = None;
684        assert_eq!(
685            Some(missing_fields),
686            EnvironmentalVector::parse_optional(&Box::new("CR:M/IR:M/AR:H".split('/')))
687        );
688        // Junk
689        assert_eq!(
690            None,
691            EnvironmentalVector::parse_optional(&mut Box::new("fsjfskhf".split('/')))
692        );
693        assert_eq!(
694            None,
695            EnvironmentalVector::parse_optional(&mut Box::new("fs//jf/skhf".split('/')))
696        );
697    }
698
699    #[test]
700    fn test_scoring() {
701        let environmental_vector = provide_environmental_vector1();
702
703        assert_eq!(
704            9.2,
705            environmental_vector.score(provide_base_vector1(), Some(provide_temporal_vector1()))
706        );
707    }
708
709    // CVE-2002-0392 see CVSS v2 specification section 3.3.1
710    // Using the "High" variant
711    fn provide_environmental_vector1() -> EnvironmentalVector {
712        EnvironmentalVector {
713            collateral_damage_potential: Some(CollateralDamagePotential::High),
714            target_distribution: Some(TargetDistribution::High),
715            confidentiality_requirement: Some(ConfidentialityRequirement::Medium),
716            integrity_requirement: Some(IntegrityRequirement::Medium),
717            availability_requirement: Some(AvailabilityRequirement::High),
718        }
719    }
720
721    // CVE-2002-0392 see CVSS v2 specification section 3.3.1
722    fn provide_temporal_vector1() -> TemporalVector {
723        TemporalVector {
724            exploitability: Some(Exploitability::Functional),
725            remediation_level: Some(RemediationLevel::OfficialFix),
726            report_confidence: Some(ReportConfidence::Confirmed),
727        }
728    }
729
730    // CVE-2002-0392 see CVSS v2 specification section 3.3.1
731    fn provide_base_vector1() -> BaseVector {
732        BaseVector {
733            access_vector: AccessVector::Network,
734            access_complexity: AccessComplexity::Low,
735            authentication: Authentication::None,
736            confidentiality_impact: ConfidentialityImpact::None,
737            integrity_impact: IntegrityImpact::None,
738            availability_impact: AvailabilityImpact::Complete,
739        }
740    }
741}