use tower_lsp_server::ls_types::DiagnosticSeverity;
use super::types::{OsvAffected, VulnSeverity};
fn parse_severity_label(s: &str) -> Option<VulnSeverity> {
match s.to_ascii_uppercase().as_str() {
"CRITICAL" => Some(VulnSeverity::Critical),
"HIGH" => Some(VulnSeverity::High),
"MODERATE" | "MEDIUM" => Some(VulnSeverity::Medium),
"LOW" => Some(VulnSeverity::Low),
_ => None,
}
}
fn severity_from_json(value: &serde_json::Value) -> Option<VulnSeverity> {
value
.get("severity")?
.as_str()
.and_then(parse_severity_label)
}
pub(super) fn classify(
database_specific: Option<&serde_json::Value>,
relevant_affected: &[&OsvAffected],
) -> VulnSeverity {
if let Some(v) = database_specific.and_then(severity_from_json) {
return v;
}
for affected in relevant_affected {
if let Some(v) = affected
.ecosystem_specific
.as_ref()
.and_then(severity_from_json)
{
return v;
}
}
VulnSeverity::Unknown
}
#[must_use]
pub const fn to_diagnostic_severity(severity: VulnSeverity) -> DiagnosticSeverity {
match severity {
VulnSeverity::Critical | VulnSeverity::High | VulnSeverity::Unknown => {
DiagnosticSeverity::WARNING
}
VulnSeverity::Medium | VulnSeverity::Low => DiagnosticSeverity::INFORMATION,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::osv::types::OsvAffected;
#[test]
fn database_specific_severity_wins() {
let json = serde_json::json!({ "severity": "CRITICAL" });
assert_eq!(classify(Some(&json), &[]), VulnSeverity::Critical);
}
#[test]
fn database_specific_moderate_maps_to_medium() {
let json = serde_json::json!({ "severity": "MODERATE" });
assert_eq!(classify(Some(&json), &[]), VulnSeverity::Medium);
}
#[test]
fn falls_back_to_ecosystem_specific_severity() {
let affected = OsvAffected {
package: None,
ecosystem_specific: Some(serde_json::json!({ "severity": "LOW" })),
ranges: vec![],
};
assert_eq!(classify(None, &[&affected]), VulnSeverity::Low);
}
#[test]
fn cvss_vector_only_record_is_unknown() {
assert_eq!(classify(None, &[]), VulnSeverity::Unknown);
}
#[test]
fn no_severity_at_all_is_unknown() {
assert_eq!(classify(None, &[]), VulnSeverity::Unknown);
}
#[test]
fn to_diagnostic_severity_mapping() {
assert_eq!(
to_diagnostic_severity(VulnSeverity::Critical),
DiagnosticSeverity::WARNING
);
assert_eq!(
to_diagnostic_severity(VulnSeverity::High),
DiagnosticSeverity::WARNING
);
assert_eq!(
to_diagnostic_severity(VulnSeverity::Medium),
DiagnosticSeverity::INFORMATION
);
assert_eq!(
to_diagnostic_severity(VulnSeverity::Low),
DiagnosticSeverity::INFORMATION
);
assert_eq!(
to_diagnostic_severity(VulnSeverity::Unknown),
DiagnosticSeverity::WARNING
);
}
}