1use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Advisory {
15 pub id: String,
17 pub summary: Option<String>,
19 pub details: Option<String>,
21 #[serde(default)]
23 pub affected: Vec<Affected>,
24 #[serde(default)]
26 pub references: Vec<Reference>,
27 pub published: Option<DateTime<Utc>>,
29 pub modified: Option<DateTime<Utc>>,
31 pub aliases: Option<Vec<String>>,
33 pub database_specific: Option<serde_json::Value>,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub enrichment: Option<Enrichment>,
38}
39
40#[derive(Debug, Clone, Default, Serialize, Deserialize)]
46pub struct Enrichment {
47 #[serde(skip_serializing_if = "Option::is_none")]
50 pub epss_score: Option<f64>,
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub epss_percentile: Option<f64>,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub epss_date: Option<DateTime<Utc>>,
57 #[serde(default)]
59 pub is_kev: bool,
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub kev_due_date: Option<DateTime<Utc>>,
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub kev_date_added: Option<DateTime<Utc>>,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub kev_ransomware: Option<bool>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub cvss_v3_score: Option<f64>,
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub cvss_v3_severity: Option<Severity>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct Affected {
79 pub package: Package,
80 #[serde(default)]
82 pub ranges: Vec<Range>,
83 #[serde(default)]
85 pub versions: Vec<String>,
86 pub ecosystem_specific: Option<serde_json::Value>,
87 pub database_specific: Option<serde_json::Value>,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct Package {
92 pub ecosystem: String,
93 pub name: String,
94 pub purl: Option<String>,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct Range {
99 #[serde(rename = "type")]
100 pub range_type: RangeType,
101 pub events: Vec<Event>,
102 pub repo: Option<String>,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
107#[serde(rename_all = "snake_case")]
108pub enum RangeTranslationStatus {
109 Exact,
110 Lossy,
111 Unsupported,
112 Invalid,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct RangeTranslation {
118 pub source: String,
119 pub raw: Option<String>,
120 pub status: RangeTranslationStatus,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub reason: Option<String>,
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126#[serde(rename_all = "UPPERCASE")]
127pub enum RangeType {
128 Semver,
129 Ecosystem,
130 Git,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134#[serde(rename_all = "snake_case")]
135pub enum Event {
136 Introduced(String),
137 Fixed(String),
138 LastAffected(String),
139 Limit(String),
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct Reference {
144 #[serde(rename = "type")]
145 pub reference_type: ReferenceType,
146 pub url: String,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize, Default)]
152#[serde(rename_all = "UPPERCASE")]
153pub enum ReferenceType {
154 Advisory,
155 Article,
156 Detection,
157 Discussion,
158 Evidence,
159 Fix,
160 Git,
161 Introduced,
162 Package,
163 Report,
164 Web,
165 #[default]
167 #[serde(other)]
168 Other,
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
173#[serde(rename_all = "UPPERCASE")]
174pub enum Severity {
175 None,
177 Low,
179 Medium,
181 High,
183 Critical,
185}
186
187impl Severity {
188 pub fn from_cvss_score(score: f64) -> Self {
190 match score {
191 s if s >= 9.0 => Self::Critical,
192 s if s >= 7.0 => Self::High,
193 s if s >= 4.0 => Self::Medium,
194 s if s > 0.0 => Self::Low,
195 _ => Self::None,
196 }
197 }
198
199 pub fn min_score(&self) -> f64 {
201 match self {
202 Self::None => 0.0,
203 Self::Low => 0.1,
204 Self::Medium => 4.0,
205 Self::High => 7.0,
206 Self::Critical => 9.0,
207 }
208 }
209}