Files
trivy/pkg/k8s/report.go
2022-05-15 19:01:58 +03:00

156 lines
3.6 KiB
Go

package k8s
import (
"fmt"
"io"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-kubernetes/pkg/artifacts"
"github.com/aquasecurity/trivy/pkg/types"
)
const (
allReport = "all"
summaryReport = "summary"
tableFormat = "table"
jsonFormat = "json"
)
type Option struct {
Format string
Report string
Output io.Writer
Severities []dbTypes.Severity
}
// Report represents a kubernetes scan report
type Report struct {
SchemaVersion int `json:",omitempty"`
ClusterName string
Vulnerabilities []Resource `json:",omitempty"`
Misconfigurations []Resource `json:",omitempty"`
}
// ConsolidatedReport represents a kubernetes scan report with consolidated findings
type ConsolidatedReport struct {
SchemaVersion int `json:",omitempty"`
ClusterName string
Findings []Resource `json:",omitempty"`
}
// Resource represents a kubernetes resource report
type Resource struct {
Namespace string `json:",omitempty"`
Kind string
Name string
// TODO(josedonizetti): should add metadata? per report? per Result?
// Metadata Metadata `json:",omitempty"`
Results types.Results `json:",omitempty"`
Error string `json:",omitempty"`
// original report
Report types.Report `json:"-"`
}
// Failed returns whether the k8s report includes any vulnerabilities or misconfigurations
func (r Report) Failed() bool {
for _, r := range r.Vulnerabilities {
if r.Results.Failed() {
return true
}
}
for _, r := range r.Misconfigurations {
if r.Results.Failed() {
return true
}
}
return false
}
func (r Report) consolidate() ConsolidatedReport {
consolidated := ConsolidatedReport{
SchemaVersion: r.SchemaVersion,
ClusterName: r.ClusterName,
}
for _, m := range r.Misconfigurations {
found := false
for _, v := range r.Vulnerabilities {
if v.Kind == m.Kind && v.Name == m.Name && v.Namespace == m.Namespace {
consolidated.Findings = append(consolidated.Findings, Resource{
Namespace: v.Namespace,
Kind: v.Kind,
Name: v.Name,
Results: append(v.Results, m.Results...),
Error: v.Error,
})
found = true
continue
}
}
if !found {
consolidated.Findings = append(consolidated.Findings, m)
}
}
return consolidated
}
// Writer defines the result write operation
type Writer interface {
Write(Report) error
}
// write writes the results in the give format
func write(report Report, option Option) error {
var writer Writer
switch option.Format {
case jsonFormat:
writer = &JSONWriter{Output: option.Output, Report: option.Report}
case tableFormat:
writer = &TableWriter{
Output: option.Output,
Report: option.Report,
Severities: option.Severities,
}
default:
return xerrors.Errorf("unknown format: %v", option.Format)
}
return writer.Write(report)
}
func createResource(artifact *artifacts.Artifact, report types.Report, err error) Resource {
results := make([]types.Result, 0, len(report.Results))
// fix target name
for _, result := range report.Results {
// if resource is a kubernetes file fix the target name,
// to avoid showing the temp file that was removed.
if result.Type == ftypes.Kubernetes {
result.Target = fmt.Sprintf("%s/%s", artifact.Kind, artifact.Name)
}
results = append(results, result)
}
r := Resource{
Namespace: artifact.Namespace,
Kind: artifact.Kind,
Name: artifact.Name,
Results: results,
Report: report,
}
// if there was any error during the scan
if err != nil {
r.Error = err.Error()
}
return r
}