1use serde::Deserialize;
45use std::fmt::{self, Display, Formatter};
46use strum::{Display, EnumDiscriminants, EnumString};
47
48pub mod v2_0;
49pub mod v3;
50pub mod v4_0;
51pub mod version;
52
53#[derive(Debug, Deserialize, EnumDiscriminants)]
55#[serde(tag = "version")]
56#[strum_discriminants(name(Version))]
57#[strum_discriminants(vis(pub))]
58#[strum_discriminants(derive(Display, EnumString))]
59pub enum Cvss {
60 #[serde(rename = "2.0")]
61 V2(v2_0::CvssV2),
62 #[serde(rename = "3.0")]
63 V3_0(v3::CvssV3),
64 #[serde(rename = "3.1")]
65 V3_1(v3::CvssV3),
66 #[serde(rename = "4.0")]
67 V4(v4_0::CvssV4),
68}
69
70impl Display for Cvss {
71 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
72 write!(f, "{}", self.vector_string())
73 }
74}
75
76impl Cvss {
77 pub fn version(&self) -> Version {
79 self.into()
80 }
81
82 pub fn vector_string(&self) -> &str {
84 match self {
85 Cvss::V2(c) => c.vector_string(),
86 Cvss::V3_0(c) => c.vector_string(),
87 Cvss::V3_1(c) => c.vector_string(),
88 Cvss::V4(c) => c.vector_string(),
89 }
90 }
91
92 pub fn base_score(&self) -> f64 {
94 match self {
95 Cvss::V2(c) => c.base_score(),
96 Cvss::V3_0(c) => c.base_score(),
97 Cvss::V3_1(c) => c.base_score(),
98 Cvss::V4(c) => c.base_score(),
99 }
100 }
101
102 pub fn base_severity(&self) -> Option<Severity> {
104 match self {
105 Cvss::V2(c) => c.base_severity(),
106 Cvss::V3_0(c) => c.base_severity(),
107 Cvss::V3_1(c) => c.base_severity(),
108 Cvss::V4(c) => c.base_severity(),
109 }
110 }
111}
112
113#[derive(Clone, Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
115#[serde(rename_all = "UPPERCASE")]
116pub enum Severity {
117 None,
118 Low,
119 Medium,
120 High,
121 Critical,
122}
123
124#[derive(Clone, Debug, PartialEq)]
126pub enum ParseError {
127 InvalidPrefix { found: String },
129 InvalidVersion { version: String },
131 InvalidComponent { component: String },
133 UnknownMetric { metric: String },
135 InvalidMetricValue { metric: String, value: String },
137 MissingRequiredMetric { metric: String },
139 DuplicateMetric { metric: String },
141}
142
143impl Display for ParseError {
144 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
145 match self {
146 ParseError::InvalidPrefix { found } => {
147 write!(
148 f,
149 "invalid vector prefix: expected 'CVSS', found '{}'",
150 found
151 )
152 }
153 ParseError::InvalidVersion { version } => {
154 write!(f, "invalid or unsupported CVSS version: '{}'", version)
155 }
156 ParseError::InvalidComponent { component } => {
157 write!(
158 f,
159 "invalid component format: '{}' (expected 'KEY:VALUE')",
160 component
161 )
162 }
163 ParseError::UnknownMetric { metric } => {
164 write!(f, "unknown metric abbreviation: '{}'", metric)
165 }
166 ParseError::InvalidMetricValue { metric, value } => {
167 write!(f, "invalid value '{}' for metric '{}'", value, metric)
168 }
169 ParseError::MissingRequiredMetric { metric } => {
170 write!(f, "missing required metric: '{}'", metric)
171 }
172 ParseError::DuplicateMetric { metric } => {
173 write!(f, "duplicate metric: '{}'", metric)
174 }
175 }
176 }
177}
178
179impl std::error::Error for ParseError {}