diff --git a/cve/src/api/mod.rs b/cve/src/api/mod.rs index 7796a19..13722f5 100644 --- a/cve/src/api/mod.rs +++ b/cve/src/api/mod.rs @@ -15,6 +15,7 @@ pub struct CVE { pub vuln_status: VulnStatus, pub descriptions: Vec, pub metrics: ImpactMetrics, + #[serde(default)] pub weaknesses: Vec, #[serde(default)] pub configurations: Vec, @@ -27,6 +28,7 @@ pub enum VulnStatus { Analyzed, #[serde(rename = "Undergoing Analysis")] UndergoingAnalysis, + Rejected, } #[cfg(test)] diff --git a/helper/Cargo.toml b/helper/Cargo.toml index 7db06fe..795e681 100644 --- a/helper/Cargo.toml +++ b/helper/Cargo.toml @@ -22,6 +22,7 @@ serde_yaml = "0.9" flate2 = "1.0" zip = "0.6" cached = "0.44.0" +tokio = { version = "1.34.0", features = ["full"] } [dev-dependencies] serde = { version = "1", features = ["derive"] } quick-xml = { version = "0.29.0", features = ["serde", "encoding_rs", "serialize"] } @@ -29,6 +30,4 @@ serde_yaml = "0.9" serde_json = "1.0" flate2 = "1.0" zip = "0.6" -[[bin]] -name = "cpe_to_db" -path = "src/bin/cpe_to_db.rs" \ No newline at end of file +tokio = { version = "1.34.0", features = ["full"] } \ No newline at end of file diff --git a/helper/examples/cpe-api-example.rs b/helper/examples/cpe-api-example.rs new file mode 100644 index 0000000..bac3402 --- /dev/null +++ b/helper/examples/cpe-api-example.rs @@ -0,0 +1,30 @@ +use nvd_api::v2::products::{CpeMatchParameters, CpeParameters}; +// https://cwe.mitre.org/data/downloads.html +// curl -s -k https://cwe.mitre.org/data/downloads.html |grep -Eo '(/[^"]*\.xml.zip)'|xargs -I % wget -c https://cwe.mitre.org% +#[tokio::main] +async fn main() { + let api = nvd_api::NVDApi::new(None, "2.0").unwrap(); + let cpe = api + .cpe(CpeParameters{ + cpe_name_id: None, + cpe_match_string: None, + keyword: None, + last_mod: None, + match_criteria_id: None, + limit_offset: None, + }) + .await + .unwrap(); + println!("{:?}", cpe.format); + let cpe_match = api + .cpe_match(CpeMatchParameters{ + cve_id: None, + last_mod: None, + match_criteria_id: None, + keyword: None, + limit_offset: None, + }) + .await + .unwrap(); + println!("{:?}", cpe_match.format); +} diff --git a/helper/examples/cve-api-example.rs b/helper/examples/cve-api-example.rs new file mode 100644 index 0000000..ea6848d --- /dev/null +++ b/helper/examples/cve-api-example.rs @@ -0,0 +1,42 @@ +use nvd_api::v2::vulnerabilities::{CveHistoryParameters, CveParameters}; +// https://cwe.mitre.org/data/downloads.html +// curl -s -k https://cwe.mitre.org/data/downloads.html |grep -Eo '(/[^"]*\.xml.zip)'|xargs -I % wget -c https://cwe.mitre.org% +#[tokio::main] +async fn main() { + let api = nvd_api::NVDApi::new(None, "2.0").unwrap(); + let cve = api + .cve(CveParameters { + cpe_name: None, + cve_id: None, + cvss_v2_metrics: None, + cvss_v2_severity: None, + cvss_v3_metrics: None, + cvss_v3_severity: None, + cwe_id: None, + has_cert_alerts: None, + has_cert_notes: None, + has_kev: None, + has_oval: None, + is_vulnerable: None, + keyword: None, + last_mod: None, + no_rejected: None, + pub_date: None, + limit_offset: None, + source_identifier: None, + virtual_match: None, + }) + .await + .unwrap(); + println!("{:?}", cve.format); + let cve_history = api + .cve_history(CveHistoryParameters{ + cve_id: None, + change_date: None, + event_name: None, + limit_offset: None, + }) + .await + .unwrap(); + println!("{:?}", cve_history.format); +} diff --git a/nvd-api/src/lib.rs b/nvd-api/src/lib.rs index a2c2c7a..363eef1 100644 --- a/nvd-api/src/lib.rs +++ b/nvd-api/src/lib.rs @@ -1,5 +1,5 @@ use crate::error::Error; -use crate::pagination::Object; +use crate::pagination::{ListResponse}; use reqwest::{ClientBuilder, RequestBuilder}; mod error; @@ -37,7 +37,7 @@ impl NVDApi { } impl NVDApi { - pub async fn request(&self, request: RequestBuilder) -> Result { + pub async fn request(&self, request: RequestBuilder) -> Result { let request = request.build()?; let json = self .client @@ -47,10 +47,8 @@ impl NVDApi { .text() .await .map_err(|source| Error::ResponseIo { source })?; + // println!("{}", json); let result = serde_json::from_str(&json).map_err(|source| Error::JsonParse { source })?; - match result { - Object::Error { error } => Err(Error::Api { error }), - response => Ok(response), - } + Ok(result) } } diff --git a/nvd-api/src/pagination.rs b/nvd-api/src/pagination.rs index 446391d..13cd869 100644 --- a/nvd-api/src/pagination.rs +++ b/nvd-api/src/pagination.rs @@ -1,25 +1,8 @@ use crate::error::ErrorResponse; use crate::v2::products::{MatchStrings, Products}; -use crate::v2::vulnerabilities::{Change, Vulnerabilities}; +use crate::v2::vulnerabilities::{CveChanges, Vulnerabilities}; use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)] -#[serde(transparent)] -pub struct PagingCursor(String); - -#[derive(Serialize, Debug, Eq, PartialEq, Default, Clone)] -pub struct Paging { - #[serde(skip_serializing_if = "Option::is_none")] - pub start_cursor: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub page_size: Option, -} - -pub trait Pageable { - fn start_from(self, starting_point: Option) -> Self; -} - #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] #[serde(rename_all(deserialize = "camelCase"))] pub struct ListResponse { @@ -37,7 +20,7 @@ pub struct ListResponse { #[serde(rename_all = "camelCase")] pub enum Object { Vulnerabilities(Vec), - CveChanges(Vec), + CveChanges(Vec), Products(Vec), MatchStrings(Vec), Error { diff --git a/nvd-api/src/v2/api.rs b/nvd-api/src/v2/api.rs index db0b594..e91f39a 100644 --- a/nvd-api/src/v2/api.rs +++ b/nvd-api/src/v2/api.rs @@ -1,22 +1,22 @@ -use crate::pagination::Object; +use crate::pagination::ListResponse; use crate::v2::products::{CpeMatchParameters, CpeParameters}; -use crate::v2::vulnerabilities::CveParameters; +use crate::v2::vulnerabilities::{CveHistoryParameters, CveParameters}; use crate::{Error, NVDApi}; impl NVDApi { - pub async fn cve(&self, query: CveParameters) -> Result { + pub async fn cve(&self, query: CveParameters) -> Result { let u = format!("{}/{}/{}", self.base_path, "cves", self.version); self.request(self.client.get(u).query(&query)).await } - pub async fn cve_history(&self, query: CveParameters) -> Result { + pub async fn cve_history(&self, query: CveHistoryParameters) -> Result { let u = format!("{}/{}/{}", self.base_path, "cvehistory", self.version); self.request(self.client.get(u).query(&query)).await } - pub async fn cpe(&self, query: CpeParameters) -> Result { + pub async fn cpe(&self, query: CpeParameters) -> Result { let u = format!("{}/{}/{}", self.base_path, "cpes", self.version); self.request(self.client.get(u).query(&query)).await } - pub async fn cpe_match(&self, query: CpeMatchParameters) -> Result { + pub async fn cpe_match(&self, query: CpeMatchParameters) -> Result { let u = format!("{}/{}/{}", self.base_path, "cpematch", self.version); self.request(self.client.get(u).query(&query)).await } diff --git a/nvd-api/src/v2/products.rs b/nvd-api/src/v2/products.rs index c20fbaf..94993b8 100644 --- a/nvd-api/src/v2/products.rs +++ b/nvd-api/src/v2/products.rs @@ -60,6 +60,7 @@ pub struct Titles { #[serde(rename_all = "camelCase")] pub struct Refs { pub r#ref: String, + #[serde(default)] pub r#type: RefType, } #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq)] @@ -72,6 +73,13 @@ pub enum RefType { Vendor, Version, } + +impl Default for RefType { + fn default() -> Self { + Self::ChangeLog + } +} + #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Eq)] #[serde(rename_all = "camelCase")] pub struct DeprecatedBy {