feat: Enhance summary report to show vulnerability risk and sample

This commit is contained in:
abhisek 2024-02-03 12:49:37 +05:30
parent c0f9848b77
commit 79d7cf9599
No known key found for this signature in database
GPG Key ID: CB92A4990C02A88F

View File

@ -22,13 +22,17 @@ import (
const ( const (
summaryListPrependText = " ** " summaryListPrependText = " ** "
summaryWeightCriticalVuln = 5 // Opinionated weights for scoring
summaryWeightHighVuln = 3 summaryWeightCriticalVuln = 10
summaryWeightMediumVuln = 1 summaryWeightHighVuln = 8
summaryWeightLowVuln = 0 summaryWeightMediumVuln = 2
summaryWeightUnpopular = 2 summaryWeightLowVuln = 1
summaryWeightUnpopular = 1
summaryWeightMajorDrift = 2 summaryWeightMajorDrift = 2
// Opinionated thresholds for identifying repo popularity by stars
minStarsForPopularity = 10
tagVuln = "vulnerability" tagVuln = "vulnerability"
tagUnpopular = "low popularity" tagUnpopular = "low popularity"
tagDrift = "drift" tagDrift = "drift"
@ -48,8 +52,14 @@ type summaryReporterRemediationData struct {
tags []string tags []string
} }
type summaryReporterVulnerabilityData struct {
pkg *models.Package
vulnerabilities map[insightapi.PackageVulnerabilitySeveritiesRisk][]string
}
type SummaryReporterConfig struct { type SummaryReporterConfig struct {
MaxAdvice int MaxAdvice int
GroupByDirectDependency bool
} }
type summaryReporter struct { type summaryReporter struct {
@ -74,7 +84,14 @@ type summaryReporter struct {
// Map of pkgId and associated meta for building remediation advice // Map of pkgId and associated meta for building remediation advice
remediationScores map[string]*summaryReporterRemediationData remediationScores map[string]*summaryReporterRemediationData
violations map[string]*summaryReporterInputViolationData
// Map of pkgId and associated meta for rendering vulnerability risk
vulnerabilityInfo map[string]*summaryReporterVulnerabilityData
// Map of pkgId and violation information
violations map[string]*summaryReporterInputViolationData
// List of lockfile poisoning detection signals
lockfilePoisoning []string lockfilePoisoning []string
} }
@ -86,6 +103,7 @@ func NewSummaryReporter(config SummaryReporterConfig) (Reporter, error) {
return &summaryReporter{ return &summaryReporter{
config: config, config: config,
remediationScores: make(map[string]*summaryReporterRemediationData), remediationScores: make(map[string]*summaryReporterRemediationData),
vulnerabilityInfo: make(map[string]*summaryReporterVulnerabilityData),
violations: make(map[string]*summaryReporterInputViolationData), violations: make(map[string]*summaryReporterInputViolationData),
}, nil }, nil
} }
@ -182,7 +200,7 @@ func (r *summaryReporter) processForPopularity(pkg *models.Package) {
starsCount := utils.SafelyGetValue(p.Stars) starsCount := utils.SafelyGetValue(p.Stars)
projectType := utils.SafelyGetValue(p.Type) projectType := utils.SafelyGetValue(p.Type)
if (strings.EqualFold(projectType, "github")) && (starsCount < 10) { if (strings.EqualFold(projectType, "github")) && (starsCount < minStarsForPopularity) {
r.summary.metrics.unpopular += 1 r.summary.metrics.unpopular += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightUnpopular, tagUnpopular) r.addPkgForRemediationAdvice(pkg, summaryWeightUnpopular, tagUnpopular)
} }
@ -217,6 +235,8 @@ func (r *summaryReporter) processForVulns(pkg *models.Package) {
continue continue
} }
r.addPkgForVulnerabilityRisk(pkg, risk, utils.SafelyGetValue(vuln.Id))
switch risk { switch risk {
case insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL: case insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL:
r.summary.vulns.critical += 1 r.summary.vulns.critical += 1
@ -255,6 +275,23 @@ func (r *summaryReporter) addPkgForRemediationAdvice(pkg *models.Package,
} }
} }
func (r *summaryReporter) addPkgForVulnerabilityRisk(pkg *models.Package,
risk insightapi.PackageVulnerabilitySeveritiesRisk, vuln string) {
if _, ok := r.vulnerabilityInfo[pkg.Id()]; !ok {
r.vulnerabilityInfo[pkg.Id()] = &summaryReporterVulnerabilityData{
pkg: pkg,
vulnerabilities: make(map[insightapi.PackageVulnerabilitySeveritiesRisk][]string),
}
}
if _, ok := r.vulnerabilityInfo[pkg.Id()].vulnerabilities[risk]; !ok {
r.vulnerabilityInfo[pkg.Id()].vulnerabilities[risk] = []string{}
}
r.vulnerabilityInfo[pkg.Id()].vulnerabilities[risk] =
append(r.vulnerabilityInfo[pkg.Id()].vulnerabilities[risk], vuln)
}
func (r *summaryReporter) Finish() error { func (r *summaryReporter) Finish() error {
fmt.Println(summaryListPrependText, text.BgBlue.Sprint(" Summary of Findings ")) fmt.Println(summaryListPrependText, text.BgBlue.Sprint(" Summary of Findings "))
@ -313,8 +350,6 @@ func (r *summaryReporter) sortedRemediations() []*summaryReporterRemediationData
} }
func (r *summaryReporter) renderRemediationAdvice() { func (r *summaryReporter) renderRemediationAdvice() {
sortedPackages := r.sortedRemediations()
fmt.Println(text.Bold.Sprint("Consider upgrading the following libraries for maximum impact:")) fmt.Println(text.Bold.Sprint("Consider upgrading the following libraries for maximum impact:"))
fmt.Println() fmt.Println()
@ -322,9 +357,11 @@ func (r *summaryReporter) renderRemediationAdvice() {
tbl.SetOutputMirror(os.Stdout) tbl.SetOutputMirror(os.Stdout)
tbl.SetStyle(table.StyleLight) tbl.SetStyle(table.StyleLight)
tbl.AppendHeader(table.Row{"Ecosystem", "Package", "Update To", "Impact"}) tbl.AppendHeader(table.Row{"Ecosystem", "Package", "Update To", "Impact Score", "Vuln Risk"})
sortedPackages := r.sortedRemediations()
maxAdvice := r.config.MaxAdvice maxAdvice := r.config.MaxAdvice
for idx, sp := range sortedPackages { for idx, sp := range sortedPackages {
if idx >= maxAdvice { if idx >= maxAdvice {
break break
@ -335,6 +372,7 @@ func (r *summaryReporter) renderRemediationAdvice() {
r.packageNameForRemediationAdvice(sp.pkg), r.packageNameForRemediationAdvice(sp.pkg),
r.packageUpdateVersionForRemediationAdvice(sp.pkg), r.packageUpdateVersionForRemediationAdvice(sp.pkg),
sp.score, sp.score,
r.packageVulnerabilityRiskText(sp.pkg),
}) })
tagText := "" tagText := ""
@ -344,12 +382,13 @@ func (r *summaryReporter) renderRemediationAdvice() {
tbl.AppendRow(table.Row{ tbl.AppendRow(table.Row{
"", tagText, "", "", "", tagText, "", "",
r.packageVulnerabilitySampleText(sp.pkg),
}) })
pathToRoot := text.Faint.Sprint(r.pathToPackageRoot(sp.pkg)) pathToRoot := text.Faint.Sprint(r.pathToPackageRoot(sp.pkg))
if pathToRoot != "" { if pathToRoot != "" {
tbl.AppendRow(table.Row{ tbl.AppendRow(table.Row{
"", pathToRoot, "", "", "", pathToRoot, "", "", "",
}) })
} }
@ -369,6 +408,71 @@ func (r *summaryReporter) renderRemediationAdvice() {
} }
} }
func (r *summaryReporter) packageVulnerabilityRiskText(pkg *models.Package) string {
if _, ok := r.vulnerabilityInfo[pkg.Id()]; !ok {
return text.BgGreen.Sprint(" None ")
}
vulnData := r.vulnerabilityInfo[pkg.Id()]
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL]) > 0 {
return text.BgHiRed.Sprint(" Critical ")
}
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskHIGH]) > 0 {
return text.BgRed.Sprint(" High ")
}
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskMEDIUM]) > 0 {
return text.BgYellow.Sprint(" Medium ")
}
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskLOW]) > 0 {
return text.BgBlue.Sprint(" Low ")
}
return text.BgWhite.Sprint(" Unknown ")
}
func (r *summaryReporter) packageVulnerabilitySampleText(pkg *models.Package) string {
if _, ok := r.vulnerabilityInfo[pkg.Id()]; !ok {
return ""
}
vulnData := r.vulnerabilityInfo[pkg.Id()]
textTemplateFunc := func(s string, c int) string {
text := s
if c > 1 {
text += fmt.Sprintf(" + %d", c-1)
}
return text
}
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL]) > 0 {
return textTemplateFunc(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL][0],
len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL]))
}
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskHIGH]) > 0 {
return textTemplateFunc(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskHIGH][0],
len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskHIGH]))
}
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskMEDIUM]) > 0 {
return textTemplateFunc(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskMEDIUM][0],
len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskMEDIUM]))
}
if len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskLOW]) > 0 {
return textTemplateFunc(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskLOW][0],
len(vulnData.vulnerabilities[insightapi.PackageVulnerabilitySeveritiesRiskLOW]))
}
return ""
}
func (r *summaryReporter) packageNameForRemediationAdvice(pkg *models.Package) string { func (r *summaryReporter) packageNameForRemediationAdvice(pkg *models.Package) string {
return fmt.Sprintf("%s@%s", pkg.PackageDetails.Name, return fmt.Sprintf("%s@%s", pkg.PackageDetails.Name,
pkg.PackageDetails.Version) pkg.PackageDetails.Version)