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  }