Skip to content

Commit 257ca02

Browse files
committed
TC-1970 Dashboard: enhance vulnerabilities retrieval (trustification#2091)
Signed-off-by: mrizzi <[email protected]>
1 parent e8add1c commit 257ca02

File tree

6 files changed

+182
-129
lines changed

6 files changed

+182
-129
lines changed

spog/api/src/endpoints/sbom/search.rs

Lines changed: 113 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
use crate::app_state::AppState;
2-
use crate::endpoints::sbom::process_get_vulnerabilities;
3-
use crate::search;
2+
3+
use crate::error::Error;
4+
use crate::search::QueryParams;
45
use crate::service::guac::GuacService;
6+
57
use crate::service::v11y::V11yService;
8+
use crate::{endpoints, search};
69
use actix_web::{web, HttpResponse};
710
use actix_web_httpauth::extractors::bearer::BearerAuth;
11+
use bombastic_model::search::SearchHit;
12+
use cvss::Severity;
13+
use futures::future::join_all;
814
use spog_model::prelude::{Last10SbomVulnerabilitySummary, Last10SbomVulnerabilitySummaryVulnerabilities};
915
use spog_model::search::SbomSummary;
10-
use spog_model::vuln::SbomReport;
16+
use std::sync::atomic::AtomicUsize;
17+
use std::sync::atomic::Ordering::Relaxed;
18+
use std::sync::Arc;
1119
use tracing::instrument;
1220
use trustification_api::search::{SearchOptions, SearchResult};
1321
use trustification_auth::client::TokenProvider;
@@ -121,68 +129,108 @@ pub async fn sboms_with_vulnerability_summary(
121129
)
122130
.await?;
123131

124-
let mut summary: Vec<Last10SbomVulnerabilitySummary> = vec![];
125-
for item in ten_latest_sboms.result {
126-
let item = item.document;
127-
let vulnerabilities =
128-
process_get_vulnerabilities(&state, &v11y, &guac, &access_token, &item.id, Some(0), Some(100000))
129-
.await?
130-
.as_ref()
131-
.and_then(|sbom_report: &SbomReport| sbom_report.summary.first())
132-
.map_or(
133-
Last10SbomVulnerabilitySummaryVulnerabilities {
134-
none: 0,
135-
low: 0,
136-
medium: 0,
137-
high: 0,
138-
critical: 0,
139-
},
140-
|(_mitre, vulnerability_summary)| {
141-
let none = vulnerability_summary
142-
.iter()
143-
.find(|item| item.severity == Some(cvss::Severity::None))
144-
.map_or_else(
145-
|| {
146-
vulnerability_summary
147-
.iter()
148-
.find(|item| item.severity.is_none())
149-
.map_or(0, |entry| entry.count)
150-
},
151-
|entry| entry.count,
152-
);
153-
let low = vulnerability_summary
154-
.iter()
155-
.find(|item| item.severity == Some(cvss::Severity::Low))
156-
.map_or(0, |entry| entry.count);
157-
let medium = vulnerability_summary
158-
.iter()
159-
.find(|item| item.severity == Some(cvss::Severity::Medium))
160-
.map_or(0, |entry| entry.count);
161-
let high = vulnerability_summary
162-
.iter()
163-
.find(|item| item.severity == Some(cvss::Severity::High))
164-
.map_or(0, |entry| entry.count);
165-
let critical = vulnerability_summary
166-
.iter()
167-
.find(|item| item.severity == Some(cvss::Severity::Critical))
168-
.map_or(0, |entry| entry.count);
132+
// Create a vector of async tasks
133+
let tasks: Vec<_> = ten_latest_sboms
134+
.result
135+
.into_iter()
136+
.map(|sbom| {
137+
let v11y = Arc::clone(&v11y);
138+
let guac = Arc::clone(&guac);
139+
tokio::task::spawn_local(async move { sbom_vulnerabilities_retrieval(guac, v11y, sbom).await })
140+
})
141+
.collect();
169142

170-
Last10SbomVulnerabilitySummaryVulnerabilities {
171-
none,
172-
low,
173-
medium,
174-
high,
175-
critical,
176-
}
177-
},
178-
);
143+
// Await all tasks and collect the results
144+
log::debug!("start waiting results");
145+
let results = join_all(tasks).await;
146+
log::debug!("stop waiting results");
179147

180-
let sbom_vulnerabilities = Last10SbomVulnerabilitySummary {
181-
sbom_id: item.id,
182-
sbom_name: item.name,
183-
vulnerabilities,
184-
};
185-
summary.push(sbom_vulnerabilities);
186-
}
187-
Ok(HttpResponse::Ok().json(summary))
148+
// Collecting the results in the correct order
149+
let mut output: Vec<Last10SbomVulnerabilitySummary> = vec![];
150+
results.into_iter().for_each(|result| match result {
151+
Ok(inner_result) => match inner_result {
152+
Ok(summary) => output.push(summary),
153+
Err(err) => log::warn!("Retrieving vulnerabilities for an SBOM failed due to: {:?}", err),
154+
},
155+
Err(err) => {
156+
log::warn!("Retrieving vulnerabilities for an SBOM failed due to: {:?}", err);
157+
}
158+
});
159+
160+
Ok(HttpResponse::Ok().json(output))
161+
}
162+
163+
#[instrument(skip(v11y, guac, sbom), err)]
164+
async fn sbom_vulnerabilities_retrieval(
165+
guac: Arc<GuacService>,
166+
v11y: Arc<V11yService>,
167+
sbom: SearchHit,
168+
) -> Result<Last10SbomVulnerabilitySummary, Error> {
169+
// find vulnerabilities
170+
let cve_to_purl = guac
171+
.find_vulnerability_by_uid(
172+
sbom.document.uid.clone().unwrap_or("".to_string()).as_str(),
173+
Some(0),
174+
Some(100000),
175+
)
176+
.await?;
177+
log::info!("{:?} {} vulnerabilities found", sbom.document.uid, cve_to_purl.len());
178+
179+
// fetch CVE details
180+
let cves = cve_to_purl.keys().cloned().collect::<Vec<String>>();
181+
let none = &AtomicUsize::new(0);
182+
let low = &AtomicUsize::new(0);
183+
let medium = &AtomicUsize::new(0);
184+
let high = &AtomicUsize::new(0);
185+
let critical = &AtomicUsize::new(0);
186+
// query 25 vulnerabilities at time
187+
let futures = cves.chunks(25).map(|chunk| {
188+
let v11y = Arc::clone(&v11y);
189+
async move {
190+
let q = format!("id:\"{}\"", chunk.join("\" OR id:\""));
191+
log::debug!("querying for {}", q);
192+
let query: QueryParams = QueryParams {
193+
q,
194+
offset: 0,
195+
limit: chunk.len(),
196+
};
197+
198+
match v11y.search(query).await {
199+
Ok(SearchResult { result, total }) => {
200+
if let Some(1..) = total {
201+
result.iter().for_each(|cve| {
202+
let score = Option::from(cve.document.cvss3x_score.unwrap_or(0f64) as f32);
203+
log::debug!("{} score is {:?}", cve.document.id, score);
204+
match endpoints::sbom::vuln::into_severity(cve.document.cvss3x_score.unwrap_or(0f64) as f32)
205+
{
206+
Severity::None => none.fetch_add(1, Relaxed),
207+
Severity::Low => low.fetch_add(1, Relaxed),
208+
Severity::Medium => medium.fetch_add(1, Relaxed),
209+
Severity::High => high.fetch_add(1, Relaxed),
210+
Severity::Critical => critical.fetch_add(1, Relaxed),
211+
};
212+
})
213+
};
214+
}
215+
Err(e) => {
216+
log::error!("Vulnerabilities search failed due to {:?}", e);
217+
}
218+
}
219+
}
220+
});
221+
log::debug!("{:?} Start waiting for futures", sbom.document.uid);
222+
join_all(futures).await;
223+
log::debug!("{:?} Stop waiting for futures", sbom.document.uid);
224+
225+
Ok(Last10SbomVulnerabilitySummary {
226+
sbom_id: sbom.document.id,
227+
sbom_name: sbom.document.name,
228+
vulnerabilities: Last10SbomVulnerabilitySummaryVulnerabilities {
229+
none: none.load(Relaxed),
230+
low: low.load(Relaxed),
231+
medium: medium.load(Relaxed),
232+
high: high.load(Relaxed),
233+
critical: critical.load(Relaxed),
234+
},
235+
})
188236
}

spog/api/src/endpoints/sbom/vuln/analyze.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub async fn analyze_spdx(
4747
sbom_id: &str,
4848
offset: Option<i64>,
4949
limit: Option<i64>,
50+
retrieve_remediation: Option<bool>,
5051
) -> Result<AnalyzeOutcome, Error> {
5152
// find vulnerabilities
5253

@@ -75,11 +76,17 @@ pub async fn analyze_spdx(
7576

7677
// get all relevant VEX documents
7778

78-
let vex = collect_vex(state, token, cve_to_purl.keys()).await?;
79+
let vex = match retrieve_remediation {
80+
Some(false) => {
81+
log::debug!("No VEX retrieval since no remediation retrieval is required");
82+
HashMap::<String, Vec<Rc<Csaf>>>::new()
83+
}
84+
_ => collect_vex(state, token, cve_to_purl.keys()).await?,
85+
};
7986

8087
// fill in the remediations
8188

82-
let cve_to_purl = info_span!("scrape_remediations").in_scope(|| {
89+
let cve_to_purl = info_span!("scrape_remediations", retrieve_remediation).in_scope(|| {
8390
let mut count = 0;
8491

8592
let result = cve_to_purl

0 commit comments

Comments
 (0)