github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/cloud/report/service.go (about) 1 package report 2 3 import ( 4 "fmt" 5 "io" 6 "sort" 7 "strconv" 8 "time" 9 10 "github.com/aquasecurity/table" 11 "github.com/aquasecurity/tml" 12 pkgReport "github.com/devseccon/trivy/pkg/report/table" 13 "github.com/devseccon/trivy/pkg/types" 14 ) 15 16 func writeServiceTable(report *Report, results types.Results, output io.Writer) error { 17 18 t := table.New(output) 19 20 t.SetHeaders("Service", "Misconfigurations", "Last Scanned") 21 t.AddHeaders("Service", "Critical", "High", "Medium", "Low", "Unknown", "Last Scanned") 22 t.SetRowLines(false) 23 t.SetHeaderVerticalAlignment(table.AlignBottom) 24 t.SetHeaderAlignment(table.AlignLeft, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignLeft) 25 t.SetAlignment(table.AlignLeft, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignLeft) 26 t.SetAutoMergeHeaders(true) 27 t.SetHeaderColSpans(0, 1, 5, 1) 28 29 // map service -> severity -> count 30 grouped := make(map[string]map[string]int) 31 // set zero counts for all services 32 for _, service := range report.ServicesInScope { 33 grouped[service] = make(map[string]int) 34 } 35 for _, result := range results { 36 for _, misconfiguration := range result.Misconfigurations { 37 service := misconfiguration.CauseMetadata.Service 38 if _, ok := grouped[service]; !ok { 39 grouped[service] = make(map[string]int) 40 } 41 grouped[service][misconfiguration.Severity]++ 42 } 43 } 44 45 var sortable []sortableRow 46 for service, severityCounts := range grouped { 47 sortable = append(sortable, sortableRow{ 48 name: service, 49 counts: severityCounts, 50 }) 51 } 52 sort.Slice(sortable, func(i, j int) bool { return sortable[i].name < sortable[j].name }) 53 for _, row := range sortable { 54 var lastScanned string 55 scanAgo := time.Since(report.Results[row.name].CreationTime).Truncate(time.Minute) 56 switch { 57 case scanAgo.Hours() >= 48: 58 lastScanned = fmt.Sprintf("%d days ago", int(scanAgo.Hours()/24)) 59 case scanAgo.Hours() > 1: 60 lastScanned = fmt.Sprintf("%d hours ago", int(scanAgo.Hours())) 61 case scanAgo.Minutes() > 1: 62 lastScanned = fmt.Sprintf("%d minutes ago", int(scanAgo.Minutes())) 63 default: 64 lastScanned = "just now" 65 } 66 67 t.AddRow( 68 row.name, 69 pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["CRITICAL"]), "CRITICAL"), 70 pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["HIGH"]), "HIGH"), 71 pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["MEDIUM"]), "MEDIUM"), 72 pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["LOW"]), "LOW"), 73 pkgReport.ColorizeSeverity(strconv.Itoa(row.counts["UNKNOWN"]), "UNKNOWN"), 74 lastScanned, 75 ) 76 } 77 78 // render scan title 79 _ = tml.Fprintf(output, "\n<bold>Scan Overview for %s Account %s</bold>\n", report.Provider, report.AccountID) 80 81 // render table 82 t.Render() 83 84 return nil 85 }