github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/cloud/report/resource.go (about) 1 package report 2 3 import ( 4 "fmt" 5 "io" 6 "sort" 7 "strconv" 8 9 "golang.org/x/term" 10 11 "github.com/aquasecurity/table" 12 "github.com/aquasecurity/tml" 13 pkgReport "github.com/devseccon/trivy/pkg/report/table" 14 "github.com/devseccon/trivy/pkg/types" 15 ) 16 17 type sortableRow struct { 18 name string 19 counts map[string]int 20 } 21 22 func writeResourceTable(report *Report, results types.Results, output io.Writer, service string) error { 23 24 termWidth, _, err := term.GetSize(0) 25 if err != nil { 26 termWidth = 80 27 } 28 maxWidth := termWidth - 48 29 if maxWidth < 20 { 30 maxWidth = 20 31 } 32 33 t := table.New(output) 34 t.SetColumnMaxWidth(maxWidth) 35 t.SetHeaders("Resource", "Misconfigurations") 36 t.AddHeaders("Resource", "Critical", "High", "Medium", "Low", "Unknown") 37 t.SetHeaderVerticalAlignment(table.AlignBottom) 38 t.SetHeaderAlignment(table.AlignLeft, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter, table.AlignCenter) 39 t.SetAlignment(table.AlignLeft, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight) 40 t.SetRowLines(false) 41 t.SetAutoMergeHeaders(true) 42 t.SetHeaderColSpans(0, 1, 5) 43 44 // map resource -> severity -> count 45 grouped := make(map[string]map[string]int) 46 for _, result := range results { 47 for _, misconfiguration := range result.Misconfigurations { 48 if misconfiguration.CauseMetadata.Service != service { 49 continue 50 } 51 if _, ok := grouped[misconfiguration.CauseMetadata.Resource]; !ok { 52 grouped[misconfiguration.CauseMetadata.Resource] = make(map[string]int) 53 } 54 grouped[misconfiguration.CauseMetadata.Resource][misconfiguration.Severity]++ 55 } 56 } 57 58 var sortable []sortableRow 59 for resource, severityCounts := range grouped { 60 sortable = append(sortable, sortableRow{ 61 name: resource, 62 counts: severityCounts, 63 }) 64 } 65 sort.Slice(sortable, func(i, j int) bool { return sortable[i].name < sortable[j].name }) 66 for _, row := range sortable { 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 ) 75 } 76 77 // render scan title 78 _ = tml.Fprintf(output, "\n<bold>Resource Summary for Service '%s' (%s Account %s)</bold>\n", service, report.Provider, report.AccountID) 79 80 // render table 81 if len(sortable) > 0 { 82 t.Render() 83 } else { 84 _, _ = fmt.Fprint(output, "\nNo problems detected.\n") 85 } 86 87 return nil 88 }