Skip to content

Commit

Permalink
feat(output): show package view for container scanning table result
Browse files Browse the repository at this point in the history
  • Loading branch information
hogo6002 committed Nov 19, 2024
1 parent 5e82cf7 commit 9e04caf
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 15 deletions.
96 changes: 88 additions & 8 deletions internal/output/__snapshots__/output_result_test.snap
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,13 @@
"IsContainerScanning": false,
"AllLayers": [],
"VulnTypeCount": {
"All": 4,
"All": 6,
"OS": 0,
"Project": 4,
"Project": 6,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 4,
"Uncalled": 0
},
"VulnCount": {
Expand Down Expand Up @@ -539,9 +543,13 @@
"IsContainerScanning": false,
"AllLayers": [],
"VulnTypeCount": {
"All": 4,
"All": 6,
"OS": 0,
"Project": 4,
"Project": 6,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 4,
"Uncalled": 0
},
"VulnCount": {
Expand Down Expand Up @@ -814,6 +822,10 @@
"Project": 0,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 0,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 0,
Expand Down Expand Up @@ -1120,6 +1132,10 @@
"Project": 3,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 3,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 3,
Expand Down Expand Up @@ -1404,9 +1420,13 @@
"IsContainerScanning": false,
"AllLayers": [],
"VulnTypeCount": {
"All": 4,
"All": 6,
"OS": 0,
"Project": 4,
"Project": 6,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 4,
"Uncalled": 0
},
"VulnCount": {
Expand Down Expand Up @@ -1694,9 +1714,13 @@
"IsContainerScanning": false,
"AllLayers": [],
"VulnTypeCount": {
"All": 4,
"All": 5,
"OS": 0,
"Project": 4,
"Project": 5,
"Uncalled": 1
},
"PackageTypeCount": {
"Called": 4,
"Uncalled": 1
},
"VulnCount": {
Expand Down Expand Up @@ -1816,6 +1840,10 @@
"Project": 0,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 0,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 0,
Expand Down Expand Up @@ -1848,6 +1876,10 @@
"Project": 0,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 0,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 0,
Expand Down Expand Up @@ -1913,6 +1945,10 @@
"Project": 0,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 0,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 0,
Expand Down Expand Up @@ -2009,6 +2045,10 @@
"Project": 0,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 0,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 0,
Expand Down Expand Up @@ -2129,6 +2169,10 @@
"Project": 1,
"Uncalled": 1
},
"PackageTypeCount": {
"Called": 1,
"Uncalled": 1
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 1,
Expand Down Expand Up @@ -2237,6 +2281,10 @@
"Project": 1,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 1,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 1,
Expand Down Expand Up @@ -2345,6 +2393,10 @@
"Project": 0,
"Uncalled": 1
},
"PackageTypeCount": {
"Called": 0,
"Uncalled": 1
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 0,
Expand Down Expand Up @@ -2453,6 +2505,10 @@
"Project": 1,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 1,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 1,
Expand Down Expand Up @@ -2561,6 +2617,10 @@
"Project": 1,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 1,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 1,
Expand Down Expand Up @@ -2673,6 +2733,10 @@
"Project": 0,
"Uncalled": 1
},
"PackageTypeCount": {
"Called": 0,
"Uncalled": 1
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 0,
Expand Down Expand Up @@ -2785,6 +2849,10 @@
"Project": 1,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 1,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 1,
Expand Down Expand Up @@ -2935,6 +3003,10 @@
"Project": 2,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 2,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 2,
Expand Down Expand Up @@ -3100,6 +3172,10 @@
"Project": 1,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 1,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 1,
Expand Down Expand Up @@ -3277,6 +3353,10 @@
"Project": 2,
"Uncalled": 0
},
"PackageTypeCount": {
"Called": 2,
"Uncalled": 0
},
"VulnCount": {
"CallAnalysisCount": {
"Called": 2,
Expand Down
22 changes: 19 additions & 3 deletions internal/output/output_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Result struct {
IsContainerScanning bool
AllLayers []LayerInfo
VulnTypeCount VulnTypeCount
PackageTypeCount CallAnalysisCount
VulnCount VulnCount
}

Expand Down Expand Up @@ -190,10 +191,12 @@ func buildResult(ecosystemMap map[string][]SourceResult, resultCount VulnCount)
isContainerScanning = true
}
vulnTypeCount := getVulnTypeCount(ecosystemResults)
packageTypeCount := getPackageTypeCount(ecosystemResults)

return Result{
Ecosystems: ecosystemResults,
VulnTypeCount: vulnTypeCount,
PackageTypeCount: packageTypeCount,
VulnCount: resultCount,
IsContainerScanning: isContainerScanning,
AllLayers: layers,
Expand Down Expand Up @@ -512,11 +515,11 @@ func getVulnTypeCount(result []EcosystemResult) VulnTypeCount {
for _, ecosystem := range result {
for _, source := range ecosystem.Sources {
if ecosystem.IsOS {
vulnCount.OS += source.PackageTypeCount.Called
vulnCount.OS += source.VulnCount.CallAnalysisCount.Called
} else {
vulnCount.Project += source.PackageTypeCount.Called
vulnCount.Project += source.VulnCount.CallAnalysisCount.Called
}
vulnCount.Uncalled += source.PackageTypeCount.Uncalled
vulnCount.Uncalled += source.VulnCount.CallAnalysisCount.Uncalled
}
}

Expand All @@ -525,6 +528,19 @@ func getVulnTypeCount(result []EcosystemResult) VulnTypeCount {
return vulnCount
}

func getPackageTypeCount(result []EcosystemResult) CallAnalysisCount {
var packageCount CallAnalysisCount

for _, ecosystem := range result {
for _, source := range ecosystem.Sources {
packageCount.Called += source.PackageTypeCount.Called
packageCount.Uncalled += source.PackageTypeCount.Uncalled
}
}

return packageCount
}

// calculateCount calculates the vulnerability counts based on the provided
// lists of called and uncalled vulnerabilities.
func calculateCount(calledVulnList, uncalledVulnList []VulnResult) VulnCount {
Expand Down
63 changes: 59 additions & 4 deletions internal/output/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,17 @@ func PrintTableResults(vulnResult *models.VulnerabilityResults, outputWriter io.
text.DisableColors()
}

outputResult := BuildResults(vulnResult)

// Render the vulnerabilities.
outputTable := newTable(outputWriter, terminalWidth)
outputTable = tableBuilder(outputTable, vulnResult)
if outputTable.Length() != 0 {
outputTable.Render()
if outputResult.IsContainerScanning {
printContainerScanningResult(outputResult, outputWriter, terminalWidth)
} else {
outputTable := newTable(outputWriter, terminalWidth)
outputTable = tableBuilder(outputTable, vulnResult)
if outputTable.Length() != 0 {
outputTable.Render()
}
}

// Render the licenses if any.
Expand Down Expand Up @@ -84,6 +90,55 @@ func tableBuilder(outputTable table.Writer, vulnResult *models.VulnerabilityResu
return outputTable
}

func printContainerScanningResult(result Result, outputWriter io.Writer, terminalWidth int) {
summary := fmt.Sprintf(
"Total %[1]d packages affected by %[2]d vulnerabilities (%[3]d Critical, %[4]d High, %[5]d Medium, %[6]d Low, %[7]d Unknown) from %[8]d ecosystems, %[9]d have fixes available",
result.PackageTypeCount.Called,
result.VulnTypeCount.All,
result.VulnCount.SeverityCount.Critical,
result.VulnCount.SeverityCount.High,
result.VulnCount.SeverityCount.Medium,
result.VulnCount.SeverityCount.Low,
result.VulnCount.SeverityCount.Unknown,
len(result.Ecosystems),
result.VulnCount.FixableCount.Fixed,
)
fmt.Fprintln(outputWriter, summary)

for _, ecosystem := range result.Ecosystems {
fmt.Fprintln(outputWriter, ecosystem.Name)

for _, source := range ecosystem.Sources {
outputTable := newTable(outputWriter, terminalWidth)
outputTable.SetTitle("Source:" + source.Name)
outputTable.AppendHeader(table.Row{"Package", "Installed Version", "Fix available", "Vuln count"})
for _, pkg := range source.Packages {
outputRow := table.Row{}
totalCount := pkg.VulnCount.CallAnalysisCount.Called
var fixAvailable string
if pkg.FixedVersion == UnfixedDescription {
fixAvailable = UnfixedDescription
} else {
if pkg.VulnCount.FixableCount.UnFixed > 0 {
fixAvailable = "Partial fixes Available"
} else {
fixAvailable = "Fix Available"
}
}
outputRow = append(outputRow, pkg.Name, pkg.InstalledVersion, fixAvailable, totalCount)
outputTable.AppendRow(outputRow)
}
outputTable.Render()
}
}

const promptMessage = "For the most comprehensive scan results, we recommend using the HTML output: " +
"`osv-scanner --format html --output results.html`. \n" +
"You can also view the full vulnerability list in your terminal with: " +
"`osv-scanner --format vertical`"
fmt.Fprintln(outputWriter, promptMessage)
}

type tbInnerResponse struct {
row table.Row
shouldMerge bool
Expand Down

0 comments on commit 9e04caf

Please sign in to comment.