#![doc = include_str!("../README.md")]
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct OpenVex {
#[serde(flatten)]
pub metadata: Metadata,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub statements: Vec<Statement>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Metadata {
#[serde(rename = "@context")]
pub context: String,
#[serde(rename = "@id")]
pub id: String,
pub author: String,
pub role: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub timestamp: Option<DateTime<Utc>>,
pub version: String,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub tooling: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub supplier: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Statement {
#[serde(skip_serializing_if = "Option::is_none", default)]
pub vulnerability: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub vuln_description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub timestamp: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub products: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub subcomponents: Vec<String>,
pub status: Status,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub status_notes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub justification: Option<Justification>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub impact_statement: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub action_statement: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub action_statement_timestamp: Option<DateTime<Utc>>,
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
pub enum Status {
#[serde(rename = "not_affected")]
NotAffected,
#[serde(rename = "affected")]
Affected,
#[serde(rename = "fixed")]
Fixed,
#[serde(rename = "under_investigation")]
UnderInvestigation,
}
#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq)]
pub enum Justification {
#[serde(rename = "component_not_present")]
ComponentNotPresent,
#[serde(rename = "vulnerable_code_not_present")]
VulnerableCodeNotPresent,
#[serde(rename = "vulnerable_code_not_in_execute_path")]
VulnerableCodeNotInExecutePath,
#[serde(rename = "vulnerable_code_cannot_be_controlled_by_adversary")]
VulnerableCodeCannotBeControlledByAdversary,
#[serde(rename = "inline_mitigations_already_exist")]
InlineMitigationsAlreadyExist,
}
#[cfg(test)]
mod tests {
use crate::{OpenVex, Status};
#[test]
fn test_deserialize() {
let input = include_str!("minimal.json");
let vex = serde_json::from_str::<OpenVex>(input).unwrap();
assert_eq!(vex.statements.len(), 1);
assert_eq!(vex.statements[0].status, Status::Fixed);
assert_eq!(
vex.statements[0].vulnerability.as_ref().unwrap(),
"CVE-2023-12345"
);
}
}