172 lines
4.4 KiB
Rust
172 lines
4.4 KiB
Rust
//! Vulnerability findings from scans of the OS and container images.
|
|
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum Severity {
|
|
Unknown,
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Critical,
|
|
}
|
|
|
|
impl Severity {
|
|
pub const ALL: [Severity; 5] = [
|
|
Severity::Critical,
|
|
Severity::High,
|
|
Severity::Medium,
|
|
Severity::Low,
|
|
Severity::Unknown,
|
|
];
|
|
|
|
pub fn as_str(self) -> &'static str {
|
|
match self {
|
|
Severity::Critical => "critical",
|
|
Severity::High => "high",
|
|
Severity::Medium => "medium",
|
|
Severity::Low => "low",
|
|
Severity::Unknown => "unknown",
|
|
}
|
|
}
|
|
|
|
pub fn parse(s: &str) -> Severity {
|
|
match s.to_ascii_lowercase().as_str() {
|
|
"critical" => Severity::Critical,
|
|
"high" => Severity::High,
|
|
"medium" => Severity::Medium,
|
|
"low" => Severity::Low,
|
|
_ => Severity::Unknown,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum TargetKind {
|
|
Os,
|
|
Image,
|
|
}
|
|
|
|
impl TargetKind {
|
|
pub fn as_str(self) -> &'static str {
|
|
match self {
|
|
TargetKind::Os => "os",
|
|
TargetKind::Image => "image",
|
|
}
|
|
}
|
|
pub fn parse(s: &str) -> Option<TargetKind> {
|
|
match s {
|
|
"os" => Some(TargetKind::Os),
|
|
"image" => Some(TargetKind::Image),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "lowercase")]
|
|
pub enum FindingStatus {
|
|
Open,
|
|
Acknowledged,
|
|
Fixed,
|
|
}
|
|
|
|
impl FindingStatus {
|
|
pub fn as_str(self) -> &'static str {
|
|
match self {
|
|
FindingStatus::Open => "open",
|
|
FindingStatus::Acknowledged => "acknowledged",
|
|
FindingStatus::Fixed => "fixed",
|
|
}
|
|
}
|
|
pub fn parse(s: &str) -> Option<FindingStatus> {
|
|
match s {
|
|
"open" => Some(FindingStatus::Open),
|
|
"acknowledged" => Some(FindingStatus::Acknowledged),
|
|
"fixed" => Some(FindingStatus::Fixed),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// One vulnerability as reported by the scanner, without lifecycle data.
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct RawFinding {
|
|
pub cve_id: String,
|
|
pub severity: Severity,
|
|
pub package: String,
|
|
pub installed_version: String,
|
|
pub fixed_version: Option<String>,
|
|
pub title: String,
|
|
pub url: String,
|
|
}
|
|
|
|
impl RawFinding {
|
|
/// Identity of a finding within a target.
|
|
pub fn key(&self) -> String {
|
|
format!(
|
|
"{}|{}|{}",
|
|
self.cve_id, self.package, self.installed_version
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct Finding {
|
|
pub id: Uuid,
|
|
pub target_kind: TargetKind,
|
|
/// "os" for the host, otherwise the image reference.
|
|
pub target: String,
|
|
#[serde(flatten)]
|
|
pub raw: RawFinding,
|
|
pub status: FindingStatus,
|
|
pub first_seen: DateTime<Utc>,
|
|
pub last_seen: DateTime<Utc>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
pub struct FindingFilter {
|
|
pub min_severity: Option<Severity>,
|
|
pub target: Option<String>,
|
|
pub status: Option<FindingStatus>,
|
|
/// Include fixed findings (default: only open + acknowledged).
|
|
pub include_fixed: bool,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize)]
|
|
pub struct SeverityCounts {
|
|
pub critical: usize,
|
|
pub high: usize,
|
|
pub medium: usize,
|
|
pub low: usize,
|
|
pub unknown: usize,
|
|
}
|
|
|
|
impl SeverityCounts {
|
|
pub fn add(&mut self, s: Severity) {
|
|
match s {
|
|
Severity::Critical => self.critical += 1,
|
|
Severity::High => self.high += 1,
|
|
Severity::Medium => self.medium += 1,
|
|
Severity::Low => self.low += 1,
|
|
Severity::Unknown => self.unknown += 1,
|
|
}
|
|
}
|
|
pub fn total(&self) -> usize {
|
|
self.critical + self.high + self.medium + self.low + self.unknown
|
|
}
|
|
}
|
|
|
|
/// Outcome of one scan run.
|
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
pub struct ScanReport {
|
|
pub targets: Vec<String>,
|
|
pub failed_targets: Vec<String>,
|
|
pub total_open: usize,
|
|
pub new_findings: Vec<Finding>,
|
|
pub fixed: usize,
|
|
}
|