@@ -8,8 +8,11 @@ use tower_lsp::{
88 lsp_types:: { Diagnostic , DiagnosticSeverity , MessageType , Position , Range } ,
99} ;
1010
11+ use crate :: infra:: parse_dockerfile;
12+
1113use super :: {
12- ImageBuilder , ImageScanner , InMemoryDocumentDatabase , LSPClient , lsp_server:: WithContext ,
14+ ImageBuilder , ImageScanResult , ImageScanner , InMemoryDocumentDatabase , LSPClient , VulnSeverity ,
15+ lsp_server:: WithContext ,
1316} ;
1417
1518pub struct CommandExecutor < C > {
@@ -126,10 +129,14 @@ where
126129 } ;
127130
128131 if scan_result. has_vulnerabilities ( ) {
129- let v = & scan_result. vulnerabilities ;
130132 diagnostic. message = format ! (
131133 "Vulnerabilities found for {}: {} Critical, {} High, {} Medium, {} Low, {} Negligible" ,
132- image_for_selected_line, v. critical, v. high, v. medium, v. low, v. negligible
134+ image_for_selected_line,
135+ scan_result. count_vulns_of_severity( VulnSeverity :: Critical ) ,
136+ scan_result. count_vulns_of_severity( VulnSeverity :: High ) ,
137+ scan_result. count_vulns_of_severity( VulnSeverity :: Medium ) ,
138+ scan_result. count_vulns_of_severity( VulnSeverity :: Low ) ,
139+ scan_result. count_vulns_of_severity( VulnSeverity :: Negligible ) ,
133140 ) ;
134141
135142 diagnostic. severity = Some ( if scan_result. is_compliant {
@@ -203,49 +210,129 @@ where
203210 )
204211 . await ;
205212
206- let diagnostic = {
207- let range_for_selected_line = Range :: new (
208- Position :: new ( line, 0 ) ,
209- Position :: new (
210- line,
211- document_text
212- . lines ( )
213- . nth ( line as usize )
214- . map ( |x| x. len ( ) as u32 )
215- . unwrap_or ( u32:: MAX ) ,
216- ) ,
217- ) ;
218-
219- let mut diagnostic = Diagnostic {
220- range : range_for_selected_line,
221- severity : Some ( DiagnosticSeverity :: HINT ) ,
222- message : "No vulnerabilities found." . to_owned ( ) ,
223- ..Default :: default ( )
224- } ;
225-
226- if scan_result. has_vulnerabilities ( ) {
227- let v = & scan_result. vulnerabilities ;
228- diagnostic. message = format ! (
229- "Vulnerabilities found for Dockerfile in {}: {} Critical, {} High, {} Medium, {} Low, {} Negligible" ,
230- uri_without_file_path, v. critical, v. high, v. medium, v. low, v. negligible
231- ) ;
232-
233- diagnostic. severity = Some ( if scan_result. is_compliant {
234- DiagnosticSeverity :: INFORMATION
235- } else {
236- DiagnosticSeverity :: ERROR
237- } ) ;
238- }
239-
240- diagnostic
241- } ;
213+ let diagnostic = diagnostic_for_image ( line, & document_text, & scan_result) ;
214+ let diagnostics_per_layer = diagnostics_for_layers ( & document_text, & scan_result) ?;
242215
243216 self . document_database
244217 . remove_diagnostics ( uri. to_str ( ) . unwrap ( ) )
245218 . await ;
246219 self . document_database
247220 . append_document_diagnostics ( uri. to_str ( ) . unwrap ( ) , & [ diagnostic] )
248221 . await ;
222+ self . document_database
223+ . append_document_diagnostics ( uri. to_str ( ) . unwrap ( ) , & diagnostics_per_layer)
224+ . await ;
249225 self . publish_all_diagnostics ( ) . await
250226 }
251227}
228+
229+ pub fn diagnostics_for_layers (
230+ document_text : & str ,
231+ scan_result : & ImageScanResult ,
232+ ) -> Result < Vec < Diagnostic > > {
233+ let instructions = parse_dockerfile ( document_text) ;
234+ let layers = & scan_result. layers ;
235+
236+ let mut instr_idx = instructions. len ( ) . checked_sub ( 1 ) ;
237+ let mut layer_idx = layers. len ( ) . checked_sub ( 1 ) ;
238+
239+ let mut diagnostics = Vec :: new ( ) ;
240+
241+ while let ( Some ( i) , Some ( l) ) = ( instr_idx, layer_idx) {
242+ let instr = & instructions[ i] ;
243+ let layer = & layers[ l] ;
244+
245+ if instr. keyword == "FROM" {
246+ break ;
247+ }
248+
249+ if layer. layer_text . contains ( & instr. arguments_str ) {
250+ instr_idx = instr_idx. and_then ( |x| x. checked_sub ( 1 ) ) ;
251+ layer_idx = layer_idx. and_then ( |x| x. checked_sub ( 1 ) ) ;
252+
253+ let mut msg = String :: new ( ) ;
254+ if layer. count_vulns_of_severity ( VulnSeverity :: Critical ) > 0 {
255+ msg += & format ! (
256+ "🟣 {} " ,
257+ layer. count_vulns_of_severity( VulnSeverity :: Critical )
258+ )
259+ }
260+ if layer. count_vulns_of_severity ( VulnSeverity :: High ) > 0 {
261+ msg += & format ! ( "🔴 {} " , layer. count_vulns_of_severity( VulnSeverity :: High ) )
262+ }
263+ if layer. count_vulns_of_severity ( VulnSeverity :: Medium ) > 0 {
264+ msg += & format ! (
265+ "🟠 {} " ,
266+ layer. count_vulns_of_severity( VulnSeverity :: Medium )
267+ )
268+ }
269+ if layer. count_vulns_of_severity ( VulnSeverity :: Low ) > 0 {
270+ msg += & format ! ( "🟡 {} " , layer. count_vulns_of_severity( VulnSeverity :: Low ) )
271+ }
272+ if layer. count_vulns_of_severity ( VulnSeverity :: Negligible ) > 0 {
273+ msg += & format ! (
274+ "⚪ {} " ,
275+ layer. count_vulns_of_severity( VulnSeverity :: Negligible )
276+ )
277+ }
278+
279+ let diagnostic = Diagnostic {
280+ range : instr. range ,
281+ severity : Some ( DiagnosticSeverity :: WARNING ) ,
282+ source : Some ( "Sysdig" . to_string ( ) ) ,
283+ message : msg,
284+ ..Default :: default ( )
285+ } ;
286+
287+ diagnostics. push ( diagnostic) ;
288+ } else {
289+ layer_idx = layer_idx. and_then ( |x| x. checked_sub ( 1 ) ) ;
290+ }
291+ }
292+
293+ Ok ( diagnostics)
294+ }
295+
296+ fn diagnostic_for_image (
297+ line : u32 ,
298+ document_text : & str ,
299+ scan_result : & ImageScanResult ,
300+ ) -> Diagnostic {
301+ let range_for_selected_line = Range :: new (
302+ Position :: new ( line, 0 ) ,
303+ Position :: new (
304+ line,
305+ document_text
306+ . lines ( )
307+ . nth ( line as usize )
308+ . map ( |x| x. len ( ) as u32 )
309+ . unwrap_or ( u32:: MAX ) ,
310+ ) ,
311+ ) ;
312+
313+ let mut diagnostic = Diagnostic {
314+ range : range_for_selected_line,
315+ severity : Some ( DiagnosticSeverity :: HINT ) ,
316+ message : "No vulnerabilities found." . to_owned ( ) ,
317+ ..Default :: default ( )
318+ } ;
319+
320+ if scan_result. has_vulnerabilities ( ) {
321+ diagnostic. message = format ! (
322+ "Vulnerabilities found: {} Critical, {} High, {} Medium, {} Low, {} Negligible" ,
323+ scan_result. count_vulns_of_severity( VulnSeverity :: Critical ) ,
324+ scan_result. count_vulns_of_severity( VulnSeverity :: High ) ,
325+ scan_result. count_vulns_of_severity( VulnSeverity :: Medium ) ,
326+ scan_result. count_vulns_of_severity( VulnSeverity :: Low ) ,
327+ scan_result. count_vulns_of_severity( VulnSeverity :: Negligible ) ,
328+ ) ;
329+
330+ diagnostic. severity = Some ( if scan_result. is_compliant {
331+ DiagnosticSeverity :: INFORMATION
332+ } else {
333+ DiagnosticSeverity :: ERROR
334+ } ) ;
335+ }
336+
337+ diagnostic
338+ }
0 commit comments