deps_core/osv/
severity.rs1use tower_lsp_server::ls_types::DiagnosticSeverity;
8
9use super::types::{OsvAffected, VulnSeverity};
10
11fn parse_severity_label(s: &str) -> Option<VulnSeverity> {
14 match s.to_ascii_uppercase().as_str() {
15 "CRITICAL" => Some(VulnSeverity::Critical),
16 "HIGH" => Some(VulnSeverity::High),
17 "MODERATE" | "MEDIUM" => Some(VulnSeverity::Medium),
18 "LOW" => Some(VulnSeverity::Low),
19 _ => None,
20 }
21}
22
23fn severity_from_json(value: &serde_json::Value) -> Option<VulnSeverity> {
24 value
25 .get("severity")?
26 .as_str()
27 .and_then(parse_severity_label)
28}
29
30pub(super) fn classify(
42 database_specific: Option<&serde_json::Value>,
43 relevant_affected: &[&OsvAffected],
44) -> VulnSeverity {
45 if let Some(v) = database_specific.and_then(severity_from_json) {
46 return v;
47 }
48
49 for affected in relevant_affected {
50 if let Some(v) = affected
51 .ecosystem_specific
52 .as_ref()
53 .and_then(severity_from_json)
54 {
55 return v;
56 }
57 }
58
59 VulnSeverity::Unknown
60}
61
62#[must_use]
84pub const fn to_diagnostic_severity(severity: VulnSeverity) -> DiagnosticSeverity {
85 match severity {
86 VulnSeverity::Critical | VulnSeverity::High | VulnSeverity::Unknown => {
87 DiagnosticSeverity::WARNING
88 }
89 VulnSeverity::Medium | VulnSeverity::Low => DiagnosticSeverity::INFORMATION,
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::osv::types::OsvAffected;
97
98 #[test]
99 fn database_specific_severity_wins() {
100 let json = serde_json::json!({ "severity": "CRITICAL" });
101 assert_eq!(classify(Some(&json), &[]), VulnSeverity::Critical);
102 }
103
104 #[test]
105 fn database_specific_moderate_maps_to_medium() {
106 let json = serde_json::json!({ "severity": "MODERATE" });
107 assert_eq!(classify(Some(&json), &[]), VulnSeverity::Medium);
108 }
109
110 #[test]
111 fn falls_back_to_ecosystem_specific_severity() {
112 let affected = OsvAffected {
113 package: None,
114 ecosystem_specific: Some(serde_json::json!({ "severity": "LOW" })),
115 ranges: vec![],
116 };
117 assert_eq!(classify(None, &[&affected]), VulnSeverity::Low);
118 }
119
120 #[test]
121 fn cvss_vector_only_record_is_unknown() {
122 assert_eq!(classify(None, &[]), VulnSeverity::Unknown);
123 }
124
125 #[test]
126 fn no_severity_at_all_is_unknown() {
127 assert_eq!(classify(None, &[]), VulnSeverity::Unknown);
128 }
129
130 #[test]
131 fn to_diagnostic_severity_mapping() {
132 assert_eq!(
133 to_diagnostic_severity(VulnSeverity::Critical),
134 DiagnosticSeverity::WARNING
135 );
136 assert_eq!(
137 to_diagnostic_severity(VulnSeverity::High),
138 DiagnosticSeverity::WARNING
139 );
140 assert_eq!(
141 to_diagnostic_severity(VulnSeverity::Medium),
142 DiagnosticSeverity::INFORMATION
143 );
144 assert_eq!(
145 to_diagnostic_severity(VulnSeverity::Low),
146 DiagnosticSeverity::INFORMATION
147 );
148 assert_eq!(
149 to_diagnostic_severity(VulnSeverity::Unknown),
150 DiagnosticSeverity::WARNING
151 );
152 }
153}