mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 15:50:15 -08:00
fix: present control status instead of compliance percentage in compliance report (#3181)
Signed-off-by: chenk <hen.keinan@gmail.com>
This commit is contained in:
@@ -49,9 +49,8 @@ $ trivy k8s cluster --compliance=nsa --report summary
|
||||
|
||||

|
||||
|
||||
***Note*** : The `compliance` column represent the calculation of all tests pass vs. fail for all resources per control check in percentage format.
|
||||
***Note*** : The `Issues` column represent the total number of failed checks for this control.
|
||||
|
||||
Example: if I have two resources in cluster and one resource scan result show pass while the other one show fail for `1.0 Non-root Containers` then it compliance will show 50%
|
||||
|
||||
An additional report is supported to get all of the detail the output contains, use `--report all`
|
||||
```
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 318 KiB |
@@ -57,8 +57,7 @@ type ControlCheckSummary struct {
|
||||
ID string
|
||||
Name string
|
||||
Severity string
|
||||
TotalPass float32
|
||||
TotalFail float32
|
||||
TotalFail *int `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Writer defines the result write operation
|
||||
|
||||
@@ -3,15 +3,16 @@ package report
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/aquasecurity/table"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/compliance/spec"
|
||||
pkgReport "github.com/aquasecurity/trivy/pkg/report/table"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func BuildSummary(cr *ComplianceReport) *SummaryReport {
|
||||
@@ -22,23 +23,8 @@ func BuildSummary(cr *ComplianceReport) *SummaryReport {
|
||||
Name: control.Name,
|
||||
Severity: control.Severity,
|
||||
}
|
||||
if len(control.Results) == 0 { // this validation is mainly for vuln type
|
||||
if control.DefaultStatus == spec.PassStatus {
|
||||
ccm.TotalPass = 1
|
||||
}
|
||||
ccma = append(ccma, ccm)
|
||||
continue
|
||||
}
|
||||
for _, check := range control.Results {
|
||||
for _, m := range check.Misconfigurations {
|
||||
if m.Status == types.StatusPassed {
|
||||
ccm.TotalPass++
|
||||
continue
|
||||
}
|
||||
ccm.TotalFail++
|
||||
}
|
||||
// Detected vulnerabilities are always failure.
|
||||
ccm.TotalFail += float32(len(check.Vulnerabilities))
|
||||
if !strings.Contains(control.Name, "Manual") {
|
||||
ccm.TotalFail = pointer.Int(len(control.Results))
|
||||
}
|
||||
ccma = append(ccma, ccm)
|
||||
}
|
||||
@@ -101,16 +87,17 @@ func (s SummaryWriter) Write(report *ComplianceReport) error {
|
||||
}
|
||||
|
||||
func (s SummaryWriter) generateSummary(summaryControls ControlCheckSummary) []string {
|
||||
percentage := calculatePercentage(summaryControls.TotalFail, summaryControls.TotalPass)
|
||||
return []string{summaryControls.ID, summaryControls.Severity, summaryControls.Name, percentage}
|
||||
}
|
||||
|
||||
func calculatePercentage(totalFail float32, totalPass float32) string {
|
||||
if totalPass == 0 && totalFail == 0 {
|
||||
return fmt.Sprintf("%.2f", 0.00) + "%"
|
||||
var numOfIssues string
|
||||
var status string
|
||||
if summaryControls.TotalFail != nil {
|
||||
if *summaryControls.TotalFail == 0 {
|
||||
status = "PASS"
|
||||
} else {
|
||||
status = "FAIL"
|
||||
}
|
||||
numOfIssues = strconv.Itoa(*summaryControls.TotalFail)
|
||||
}
|
||||
relPass := totalPass / (totalFail + totalPass)
|
||||
return fmt.Sprintf("%.2f", relPass*100.0) + "%"
|
||||
return []string{summaryControls.ID, summaryControls.Severity, summaryControls.Name, status, numOfIssues}
|
||||
}
|
||||
|
||||
func getRequiredSeverities(requiredSevs []dbTypes.Severity) ([]string, []string) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/compliance/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestBuildSummary(t *testing.T) {
|
||||
@@ -58,15 +59,13 @@ func TestBuildSummary(t *testing.T) {
|
||||
ID: "1.0",
|
||||
Name: "Non-root containers",
|
||||
Severity: "MEDIUM",
|
||||
TotalPass: 0,
|
||||
TotalFail: 1,
|
||||
TotalFail: pointer.Int(1),
|
||||
},
|
||||
{
|
||||
ID: "1.1",
|
||||
Name: "Immutable container file systems",
|
||||
Severity: "LOW",
|
||||
TotalPass: 0,
|
||||
TotalFail: 1,
|
||||
TotalFail: pointer.Int(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -127,22 +126,19 @@ func TestBuildSummary(t *testing.T) {
|
||||
ID: "1.0",
|
||||
Name: "Non-root containers",
|
||||
Severity: "MEDIUM",
|
||||
TotalPass: 0,
|
||||
TotalFail: 1,
|
||||
TotalFail: pointer.Int(1),
|
||||
},
|
||||
{
|
||||
ID: "1.1",
|
||||
Name: "Immutable container file systems",
|
||||
Severity: "LOW",
|
||||
TotalPass: 0,
|
||||
TotalFail: 1,
|
||||
TotalFail: pointer.Int(1),
|
||||
},
|
||||
{
|
||||
ID: "1.2",
|
||||
Name: "tzdata - new upstream version",
|
||||
Severity: "LOW",
|
||||
TotalPass: 0,
|
||||
TotalFail: 2,
|
||||
TotalFail: pointer.Int(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -22,11 +22,12 @@ const (
|
||||
ControlIDColumn = "ID"
|
||||
SeverityColumn = "Severity"
|
||||
ControlNameColumn = "Control Name"
|
||||
ComplianceColumn = "Compliance"
|
||||
StatusColumn = "Status"
|
||||
IssuesColumn = "Issues"
|
||||
)
|
||||
|
||||
func (tw TableWriter) columns() []string {
|
||||
return []string{ControlIDColumn, SeverityColumn, ControlNameColumn, ComplianceColumn}
|
||||
return []string{ControlIDColumn, SeverityColumn, ControlNameColumn, StatusColumn, IssuesColumn}
|
||||
}
|
||||
|
||||
func (tw TableWriter) Write(report *ComplianceReport) error {
|
||||
|
||||
4
pkg/compliance/report/testdata/summary.json
vendored
4
pkg/compliance/report/testdata/summary.json
vendored
@@ -6,15 +6,13 @@
|
||||
"ID": "1.0",
|
||||
"Name": "Non-root containers",
|
||||
"Severity": "MEDIUM",
|
||||
"TotalPass": 0,
|
||||
"TotalFail": 1
|
||||
},
|
||||
{
|
||||
"ID": "1.1",
|
||||
"Name": "Immutable container file systems",
|
||||
"Severity": "LOW",
|
||||
"TotalPass": 0,
|
||||
"TotalFail": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
12
pkg/compliance/report/testdata/table_summary.txt
vendored
12
pkg/compliance/report/testdata/table_summary.txt
vendored
@@ -1,10 +1,10 @@
|
||||
|
||||
Summary Report for compliance: NSA
|
||||
┌─────┬──────────┬──────────────────────────────────┬────────────┐
|
||||
│ ID │ Severity │ Control Name │ Compliance │
|
||||
├─────┼──────────┼──────────────────────────────────┼────────────┤
|
||||
│ 1.0 │ MEDIUM │ Non-root containers │ 0.00% │
|
||||
│ 1.1 │ LOW │ Immutable container file systems │ 0.00% │
|
||||
└─────┴──────────┴──────────────────────────────────┴────────────┘
|
||||
┌─────┬──────────┬──────────────────────────────────┬────────┬────────┐
|
||||
│ ID │ Severity │ Control Name │ Status │ Issues │
|
||||
├─────┼──────────┼──────────────────────────────────┼────────┼────────┤
|
||||
│ 1.0 │ MEDIUM │ Non-root containers │ FAIL │ 1 │
|
||||
│ 1.1 │ LOW │ Immutable container file systems │ FAIL │ 1 │
|
||||
└─────┴──────────┴──────────────────────────────────┴────────┴────────┘
|
||||
|
||||
|
||||
|
||||
@@ -130,11 +130,9 @@ func (s *Scanner) scanMisconfigs(ctx context.Context, artifact *artifacts.Artifa
|
||||
}
|
||||
func (s *Scanner) filter(ctx context.Context, r types.Report, artifact *artifacts.Artifact) (report.Resource, error) {
|
||||
var err error
|
||||
if len(s.opts.ReportOptions.Compliance) == 0 {
|
||||
r, err = s.runner.Filter(ctx, s.opts, r)
|
||||
if err != nil {
|
||||
return report.Resource{}, xerrors.Errorf("filter error: %w", err)
|
||||
}
|
||||
r, err = s.runner.Filter(ctx, s.opts, r)
|
||||
if err != nil {
|
||||
return report.Resource{}, xerrors.Errorf("filter error: %w", err)
|
||||
}
|
||||
return report.CreateResource(artifact, r, nil), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user