|
1 | 1 | 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; |
4 | 5 | use crate::service::guac::GuacService; |
| 6 | + |
5 | 7 | use crate::service::v11y::V11yService; |
| 8 | +use crate::{endpoints, search}; |
6 | 9 | use actix_web::{web, HttpResponse}; |
7 | 10 | use actix_web_httpauth::extractors::bearer::BearerAuth; |
| 11 | +use bombastic_model::search::SearchHit; |
| 12 | +use cvss::Severity; |
| 13 | +use futures::future::join_all; |
8 | 14 | use spog_model::prelude::{Last10SbomVulnerabilitySummary, Last10SbomVulnerabilitySummaryVulnerabilities}; |
9 | 15 | 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; |
11 | 19 | use tracing::instrument; |
12 | 20 | use trustification_api::search::{SearchOptions, SearchResult}; |
13 | 21 | use trustification_auth::client::TokenProvider; |
@@ -121,68 +129,108 @@ pub async fn sboms_with_vulnerability_summary( |
121 | 129 | ) |
122 | 130 | .await?; |
123 | 131 |
|
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(); |
169 | 142 |
|
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"); |
179 | 147 |
|
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 | + }) |
188 | 236 | } |
0 commit comments