This commit is contained in:
Daniel Grunberger
2023-07-25 16:54:44 +03:00
parent 03b69bfacc
commit 6341947142
38 changed files with 673 additions and 449 deletions

View File

@@ -72,6 +72,8 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. e.g: --exclude-namespaces ns-a,ns-b. Notice, when running with `exclude-namespace` kubescape does not scan cluster-scoped objects.")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ChartPath, "chart", "c", "", "Path to a Helm chart. If not set will download Helm chart from ARMO management portal")
scanCmd.PersistentFlags().StringVarP(&scanInfo.FilePath, "file", "i", "", "Path to a YAML file or a directory containing YAML files. If not set will download YAML files from ARMO management portal")
scanCmd.PersistentFlags().Float32VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
scanCmd.PersistentFlags().Float32VarP(&scanInfo.ComplianceThreshold, "compliance-threshold", "", 0, "Compliance threshold is the percent below which the command fails and returns exit code 1")
@@ -91,6 +93,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Submit the scan results to Kubescape SaaS where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.OmitRawResources, "omit-raw-resources", "", false, "Omit raw resources from the output. By default the raw resources are included in the output")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.PrintAttackTree, "print-attack-tree", "", false, "Print attack tree")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.ScanImages, "scan-images", "", false, "Scan images")
scanCmd.PersistentFlags().MarkDeprecated("silent", "use '--logger' flag instead. Flag will be removed at 1.May.2022")
scanCmd.PersistentFlags().MarkDeprecated("fail-threshold", "use '--compliance-threshold' flag instead. Flag will be removed at 1.Dec.2023")

View File

@@ -3,6 +3,7 @@ package cautils
import (
"context"
"github.com/anchore/grype/grype/presenter/models"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
@@ -18,6 +19,11 @@ import (
type K8SResources map[string][]string
type KSResources map[string][]string
type ImageScanData struct {
PresenterConfig *models.PresenterConfig
Image string
}
type ScanTypes string
const (
@@ -48,6 +54,7 @@ type OPASessionObj struct {
Exceptions []armotypes.PostureExceptionPolicy // list of exceptions to apply on scan results
OmitRawResources bool // omit raw resources from output
ScanType ScanTypes // scan type
ResourceIDToImageMap map[string][]string
}
func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework, k8sResources K8SResources, scanInfo *ScanInfo) *OPASessionObj {

View File

@@ -8,7 +8,9 @@ import (
)
// NativeFrameworks identifies all pre-built, native frameworks.
var NativeFrameworks = []string{"security", "mitre", "nsa"}
var NativeFrameworks = []string{"clusterscan", "mitre", "nsa"}
// var NativeFrameworks = []string{"clusterscan"}
type (
// TenantResponse holds the credentials for a tenant.

View File

@@ -17,6 +17,7 @@ import (
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter"
"github.com/kubescape/kubescape/v2/pkg/imagescan"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"go.opentelemetry.io/otel"
@@ -206,6 +207,9 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
spanPrioritization.End()
}
if scanInfo.ScanImages && len(scanData.ResourceIDToImageMap) > 0 {
scanImages(scanInfo, scanData, ctx, resultsHandling)
}
// ========================= results handling =====================
resultsHandling.SetData(scanData)
@@ -216,6 +220,35 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
return resultsHandling, nil
}
func scanImages(scanInfo *cautils.ScanInfo, scanData *cautils.OPASessionObj, ctx context.Context, resultsHandling *resultshandling.ResultsHandler) {
logger.L().Ctx(ctx).Info("Scanning images")
dbCfg, _ := imagescan.NewDefaultDBConfig()
svc := imagescan.NewScanService(dbCfg)
for _, imgs := range scanData.ResourceIDToImageMap {
for _, img := range imgs {
scanSingleImage(ctx, img, svc, resultsHandling)
}
}
logger.L().Ctx(ctx).Success("Finished scanning images")
}
func scanSingleImage(ctx context.Context, img string, svc imagescan.Service, resultsHandling *resultshandling.ResultsHandler) {
logger.L().Ctx(ctx).Debug(fmt.Sprintf("Scanning image: %s", img))
scanResults, err := svc.Scan(ctx, img)
if err != nil {
logger.L().Ctx(ctx).Error(fmt.Sprintf("failed to scan image: %s", img), helpers.Error(err))
return
}
resultsHandling.ImageScanData = append(resultsHandling.ImageScanData, cautils.ImageScanData{
Image: img,
PresenterConfig: scanResults,
})
}
func isPrioritizationScanType(scanType cautils.ScanTypes) bool {
return scanType == cautils.ScanTypeCluster || scanType == cautils.ScanTypeRepo
}

View File

@@ -38,7 +38,7 @@ func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDeta
ScoreFactor: frameworks[i].Controls[j].BaseScore,
Description: frameworks[i].Controls[j].Description,
Remediation: frameworks[i].Controls[j].Remediation,
Categories: frameworks[i].Controls[j].Categories,
Category: frameworks[i].Controls[j].Category,
}
if frameworks[i].Controls[j].GetActionRequiredAttribute() == string(apis.SubStatusManualReview) {
c.Status = apis.StatusSkipped

View File

@@ -32,9 +32,15 @@ func NewFileResourceHandler(_ context.Context, inputPatterns []string, workloadI
}
}
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, _ *identifiers.PortalDesignator, progressListener opaprocessor.IJobProgressNotificationClient) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error) {
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, _ *identifiers.PortalDesignator, progressListener opaprocessor.IJobProgressNotificationClient, isImageScan bool) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, map[string][]string, error) {
//
// build resources map
// map resources based on framework required resources: map["/group/version/kind"][]<k8s workloads ids>
k8sResources := setK8sResourceMap(sessionObj.Policies)
allResources := map[string]workloadinterface.IMetadata{}
ksResources := cautils.KSResources{}
ksResources := &cautils.KSResources{}
resourceIDToImages := make(map[string][]string, 0)
if len(fileHandler.inputPatterns) == 0 {
return nil, nil, nil, nil, fmt.Errorf("missing input")
@@ -53,6 +59,16 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
logger.L().Debug("path ignored because contains only a non-kubernetes file", helpers.String("path", fileHandler.inputPatterns[path]))
}
if isImageScan {
for _, workload := range workloads {
wlObj := workloadinterface.NewWorkloadObj(workload.GetObject())
containers, _ := wlObj.GetContainers()
for _, container := range containers {
resourceIDToImages[workload.GetID()] = append(resourceIDToImages[workload.GetID()], container.Image)
}
}
}
for k, v := range workloadIDToSource {
sessionObj.ResourceSource[k] = v
}
@@ -124,6 +140,7 @@ func (fileHandler *FileResourceHandler) findWorkloadToScan(mappedResources map[s
}
return wls[0], nil
// return k8sResources, allResources, ksResources, resourceIDToImages, nil
}
func getResourcesFromPath(ctx context.Context, path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
@@ -238,6 +255,7 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
}
workloadSource := reporthandling.Source{
Path: path,
RelativePath: source,
FileType: reporthandling.SourceTypeHelmChart,
HelmChartName: helmChartName,

View File

@@ -19,8 +19,46 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient) error {
ctx, span := otel.Tracer("").Start(ctx, "resourcehandler.CollectResources")
// PolicyHandler -
type PolicyHandler struct {
resourceHandler resourcehandler.IResourceHandler
// we are listening on this chan in opaprocessor/processorhandler.go/ProcessRulesListener func
getters *cautils.Getters
}
// CreatePolicyHandler Create ws-handler obj
func NewPolicyHandler(resourceHandler resourcehandler.IResourceHandler) *PolicyHandler {
return &PolicyHandler{
resourceHandler: resourceHandler,
}
}
func (policyHandler *PolicyHandler) CollectResources(ctx context.Context, policyIdentifier []cautils.PolicyIdentifier, scanInfo *cautils.ScanInfo, progressListener opaprocessor.IJobProgressNotificationClient) (*cautils.OPASessionObj, error) {
opaSessionObj := cautils.NewOPASessionObj(ctx, nil, nil, scanInfo)
// validate notification
// TODO
policyHandler.getters = &scanInfo.Getters
// get policies
if err := policyHandler.getPolicies(ctx, policyIdentifier, opaSessionObj); err != nil {
return opaSessionObj, err
}
err := policyHandler.getResources(ctx, policyIdentifier, opaSessionObj, progressListener, scanInfo.ScanImages)
if err != nil {
return opaSessionObj, err
}
if (opaSessionObj.K8SResources == nil || len(*opaSessionObj.K8SResources) == 0) && (opaSessionObj.ArmoResource == nil || len(*opaSessionObj.ArmoResource) == 0) {
return opaSessionObj, fmt.Errorf("empty list of resources")
}
// update channel
return opaSessionObj, nil
}
func (policyHandler *PolicyHandler) getResources(ctx context.Context, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, isImageScan bool) error {
ctx, span := otel.Tracer("").Start(ctx, "policyHandler.getResources")
defer span.End()
opaSessionObj.Report.ClusterAPIServerInfo = rsrcHandler.GetClusterAPIServerInfo(ctx)
@@ -30,6 +68,7 @@ func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyI
}
resourcesMap, allResources, ksResources, excludedRulesMap, err := rsrcHandler.GetResources(ctx, opaSessionObj, &policyIdentifier[0].Designators, progressListener)
resourcesMap, allResources, ksResources, resourceIDToImages, err := policyHandler.resourceHandler.GetResources(ctx, opaSessionObj, &policyIdentifier[0].Designators, progressListener, isImageScan)
if err != nil {
return err
}
@@ -38,6 +77,11 @@ func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyI
opaSessionObj.AllResources = allResources
opaSessionObj.KubescapeResource = ksResources
opaSessionObj.ExcludedRules = excludedRulesMap
opaSessionObj.ArmoResource = ksResources
opaSessionObj.ResourceIDToImageMap = resourceIDToImages
return nil
}
if (opaSessionObj.K8SResources == nil || len(opaSessionObj.K8SResources) == 0) && (opaSessionObj.KubescapeResource == nil || len(opaSessionObj.KubescapeResource) == 0) {
return fmt.Errorf("empty list of resources")

View File

@@ -87,7 +87,29 @@ func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionO
k8sResourcesMap, allResources, err := k8sHandler.pullResources(queryableResources, namespace, labels)
if err != nil {
cautils.StopSpinner()
return k8sResourcesMap, allResources, ksResourceMap, excludedRulesMap, err
return k8sResourcesMap, allResources, ksResourceMap, nil, err
}
resourceIDToImages := make(map[string][]string, 0)
if isImageScan {
for _, workload := range allResources {
wlObj := workloadinterface.NewWorkloadObj(workload.GetObject())
containers, _ := wlObj.GetContainers()
for _, container := range containers {
resourceIDToImages[workload.GetID()] = append(resourceIDToImages[workload.GetID()], container.Image)
}
}
}
resourceIDToImages := make(map[string][]string, 0)
if isImageScan {
for _, workload := range allResources {
wlObj := workloadinterface.NewWorkloadObj(workload.GetObject())
containers, _ := wlObj.GetContainers()
for _, container := range containers {
resourceIDToImages[workload.GetID()] = append(resourceIDToImages[workload.GetID()], container.Image)
}
}
}
// add workload to k8s resources map (for single workload scan)
@@ -163,6 +185,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionO
}
return k8sResourcesMap, allResources, ksResourceMap, excludedRulesMap, nil
return k8sResourcesMap, allResources, ksResourceMap, resourceIDToImages, nil
}
func (k8sHandler *K8sResourceHandler) findWorkloadToScan(workloadIdentifier *cautils.WorkloadIdentifier) (workloadinterface.IWorkload, error) {
@@ -349,6 +372,8 @@ func (k8sHandler *K8sResourceHandler) pullResources(queryableResources Queryable
}
}
return k8sResources, allResources, errs
return errs
}
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string, fields string) ([]unstructured.Unstructured, error) {

View File

@@ -6,7 +6,6 @@ import (
"os"
"path/filepath"
"github.com/anchore/grype/grype/presenter/models"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -26,7 +25,7 @@ const (
type IPrinter interface {
PrintNextSteps()
ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig)
ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData)
SetWriter(ctx context.Context, outputFile string)
Score(score float32)
}

View File

@@ -19,6 +19,8 @@ const (
jsonOutputExt = ".json"
)
var _ printer.IPrinter = &JsonPrinter{}
type JsonPrinter struct {
writer *os.File
}
@@ -48,7 +50,7 @@ func (jsonPrinter *JsonPrinter) PrintNextSteps() {
}
func (jsonPrinter *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (jsonPrinter *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, _ []cautils.ImageScanData) {
report := cautils.ReportV2ToV1(opaSessionObj)
var postureReportStr []byte

View File

@@ -59,7 +59,7 @@ func (hp *HtmlPrinter) PrintNextSteps() {
}
func (hp *HtmlPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (hp *HtmlPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
tplFuncMap := template.FuncMap{
"sum": func(nums ...int) int {
total := 0

View File

@@ -72,14 +72,14 @@ func printImageAndConfigurationScanning(output io.Writer, imageScanData *models.
return enc.Encode(&docForJson)
}
func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
var err error
if opaSessionObj != nil && imageScanData != nil {
err = printImageAndConfigurationScanning(jp.writer, imageScanData, opaSessionObj)
err = printImageAndConfigurationScanning(jp.writer, imageScanData[0].PresenterConfig, opaSessionObj)
} else if opaSessionObj != nil {
err = printConfigurationsScanning(opaSessionObj, ctx, jp)
} else if imageScanData != nil {
err = jp.PrintImageScan(ctx, imageScanData)
err = jp.PrintImageScan(ctx, imageScanData[0].PresenterConfig)
} else {
err = fmt.Errorf("failed to print results, no data provided")
}

View File

@@ -120,7 +120,7 @@ func (jp *JunitPrinter) PrintNextSteps() {
}
func (jp *JunitPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (jp *JunitPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
junitResult := testsSuites(opaSessionObj)
postureReportStr, err := xml.Marshal(junitResult)
if err != nil {

View File

@@ -93,7 +93,7 @@ func (pp *PdfPrinter) PrintNextSteps() {
}
func (pp *PdfPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (pp *PdfPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
sortedControlIDs := getSortedControlsIDs(opaSessionObj.Report.SummaryDetails.Controls)
infoToPrintInfo := mapInfoToPrintInfo(opaSessionObj.Report.SummaryDetails.Controls)

View File

@@ -8,7 +8,6 @@ import (
"sort"
"strings"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/presenter/models"
"github.com/enescakir/emoji"
logger "github.com/kubescape/go-logger"
@@ -18,7 +17,6 @@ import (
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
@@ -72,74 +70,39 @@ func (pp *PrettyPrinter) SetMainPrinter() {
}
}
func (pp *PrettyPrinter) convertToImageScanSummary(presenterConfig *models.PresenterConfig) (*imageprinter.ImageScanSummary, error) {
doc, err := models.NewDocument(presenterConfig.Packages, presenterConfig.Context, presenterConfig.Matches, presenterConfig.IgnoredMatches, presenterConfig.MetadataProvider, nil, presenterConfig.DBStatus)
if err != nil {
return nil, err
// convertToImageScanSummary takes a list of image scan data and converts it to a single image scan summary
func (pp *PrettyPrinter) convertToImageScanSummary(imageScanData []cautils.ImageScanData) (*imageprinter.ImageScanSummary, error) {
imageScanSummary := imageprinter.ImageScanSummary{
CVEs: []imageprinter.CVE{},
PackageScores: map[string]*imageprinter.PackageScore{},
MapsSeverityToSummary: map[string]*imageprinter.SeveritySummary{},
}
cves := extractCVEs(doc)
for _, imageScan := range imageScanData {
imageScanSummary.Images = append(imageScanSummary.Images, imageScan.Image)
mapPackageNameToScore := extractPkgNameToScore(doc)
presenterConfig := imageScan.PresenterConfig
doc, err := models.NewDocument(presenterConfig.Packages, presenterConfig.Context, presenterConfig.Matches, presenterConfig.IgnoredMatches, presenterConfig.MetadataProvider, nil, presenterConfig.DBStatus)
if err != nil {
logger.L().Error(fmt.Sprintf("failed to create document for image: %v", imageScan.Image), helpers.Error(err))
continue
}
mapSeverityToSummary := extractSeverityToSummaryMap(cves)
cves := extractCVEs(doc)
imageScanSummary.CVEs = append(imageScanSummary.CVEs, cves...)
imageScanSummary := imageprinter.ImageScanSummary{
CVEs: cves,
MapsSeverityToSummary: mapSeverityToSummary,
PackageScores: mapPackageNameToScore,
mapPackageNameToScore := extractPkgNameToScore(doc)
insertPackageScoresIntoMap(mapPackageNameToScore, imageScanSummary)
mapSeverityToSummary := extractSeverityToSummaryMap(cves)
insertSeveritiesSummariesIntoMap(mapSeverityToSummary, imageScanSummary)
}
return &imageScanSummary, nil
}
func extractSeverityToSummaryMap(cves []imageprinter.CVE) map[string]*imageprinter.SeveritySummary {
mapSeverityToSummary := map[string]*imageprinter.SeveritySummary{}
for _, cve := range cves {
if _, ok := mapSeverityToSummary[cve.Severity]; !ok {
mapSeverityToSummary[cve.Severity] = &imageprinter.SeveritySummary{}
}
mapSeverityToSummary[cve.Severity].NumberOfCVEs = mapSeverityToSummary[cve.Severity].NumberOfCVEs + 1
if cve.FixedState == string(v5.FixedState) {
mapSeverityToSummary[cve.Severity].NumberOfFixableCVEs = mapSeverityToSummary[cve.Severity].NumberOfFixableCVEs + 1
}
}
return mapSeverityToSummary
}
func extractPkgNameToScore(doc models.Document) map[string]*imageprinter.Package {
mapPackageNameToScore := make(map[string]*imageprinter.Package, 0)
for _, cve := range doc.Matches {
if _, ok := mapPackageNameToScore[cve.Artifact.Name]; !ok {
mapPackageNameToScore[cve.Artifact.Name] = &imageprinter.Package{
Score: 0,
}
}
mapPackageNameToScore[cve.Artifact.Name].Score = mapPackageNameToScore[cve.Artifact.Name].Score + utils.ImageSeverityToInt(cve.Vulnerability.Severity)
mapPackageNameToScore[cve.Artifact.Name].Version = cve.Artifact.Version
}
return mapPackageNameToScore
}
func extractCVEs(doc models.Document) []imageprinter.CVE {
cves := []imageprinter.CVE{}
for _, match := range doc.Matches {
cve := imageprinter.CVE{
ID: match.Vulnerability.ID,
Severity: match.Vulnerability.Severity,
Package: match.Artifact.Name,
Version: match.Artifact.Version,
FixVersions: match.Vulnerability.Fix.Versions,
FixedState: match.Vulnerability.Fix.State,
}
cves = append(cves, cve)
}
return cves
}
func (pp *PrettyPrinter) PrintImageScan(ctx context.Context, presenterConfig *models.PresenterConfig) {
imageScanSummary, err := pp.convertToImageScanSummary(presenterConfig)
func (pp *PrettyPrinter) PrintImageScan(imageScanData []cautils.ImageScanData) {
imageScanSummary, err := pp.convertToImageScanSummary(imageScanData)
if err != nil {
logger.L().Error("failed to convert to image scan summary", helpers.Error(err))
return
@@ -147,7 +110,7 @@ func (pp *PrettyPrinter) PrintImageScan(ctx context.Context, presenterConfig *mo
pp.mainPrinter.PrintImageScanning(imageScanSummary)
}
func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
if opaSessionObj != nil {
fmt.Fprintf(pp.writer, "\n"+getSeparator("^")+"\n")
@@ -173,8 +136,8 @@ func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.O
pp.printAttackTracks(opaSessionObj)
}
if imageScanData != nil {
pp.PrintImageScan(context.Background(), imageScanData)
if len(imageScanData) > 0 {
pp.PrintImageScan(imageScanData)
}
}

View File

@@ -30,7 +30,7 @@ func (cp *ClusterPrinter) PrintImageScanning(summary *imageprinter.ImageScanSumm
func (cp *ClusterPrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
cp.categoriesTablePrinter.PrintCategoriesTable(cp.writer, summaryDetails, sortedControlIDs)
cp.categoriesTablePrinter.PrintCategoriesTables(cp.writer, summaryDetails, sortedControlIDs)
printComplianceScore(cp.writer, filterComplianceFrameworks(summaryDetails.ListFrameworks()))
@@ -44,6 +44,13 @@ func (cp *ClusterPrinter) PrintNextSteps() {
printNextSteps(cp.writer, cp.getNextSteps())
}
func (cp *ClusterPrinter) getNextSteps() []string {
return []string{
complianceScanRunText,
installHelmText,
}
}
func (cp *ClusterPrinter) printTopWorkloads(summaryDetails *reportsummary.SummaryDetails) {
cautils.InfoTextDisplay(cp.writer, getTopWorkloadsTitle(len(summaryDetails.TopWorkloadsByScore)))
@@ -54,16 +61,11 @@ func (cp *ClusterPrinter) printTopWorkloads(summaryDetails *reportsummary.Summar
cautils.SimpleDisplay(cp.writer, fmt.Sprintf("%d. namespace: %s, name: %s, kind: %s - '%s'\n", i+1, ns, name, kind, cp.getWorkloadScanCommand(ns, kind, name)))
}
cautils.SimpleDisplay(cp.writer, "Read more about the most risky workloads here: https://docs.io/most-risky-workloads\n")
cautils.InfoTextDisplay(cp.writer, "\n")
}
func (cp *ClusterPrinter) getWorkloadScanCommand(namespace, kind, name string) string {
return fmt.Sprintf("$ kubescape scan workload %s/%s/%s", namespace, kind, name)
}
func (cp *ClusterPrinter) getNextSteps() []string {
return []string{
complianceScanRunText,
installHelmText,
}
}

View File

@@ -6,7 +6,7 @@ import (
)
type MainPrinter interface {
PrintConfigurationsScanning(*reportsummary.SummaryDetails, [][]string)
PrintImageScanning(*imageprinter.ImageScanSummary)
PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControls [][]string)
PrintImageScanning(imageScanSummary *imageprinter.ImageScanSummary)
PrintNextSteps()
}

View File

@@ -3,7 +3,6 @@ package prettyprinter
import (
"fmt"
"os"
"strings"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/configurationprinter"
@@ -27,13 +26,15 @@ func NewRepoPrinter(writer *os.File, inputPatterns []string) *RepoPrinter {
var _ MainPrinter = &RepoPrinter{}
func (rp *RepoPrinter) PrintImageScanning(*imageprinter.ImageScanSummary) {
func (rp *RepoPrinter) PrintImageScanning(summary *imageprinter.ImageScanSummary) {
printImageScanningSummary(rp.writer, *summary, false)
printTopVulnerabilities(rp.writer, *summary)
}
func (rp *RepoPrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
rp.categoriesTablePrinter.PrintCategoriesTable(rp.writer, summaryDetails, sortedControlIDs)
rp.categoriesTablePrinter.PrintCategoriesTables(rp.writer, summaryDetails, sortedControlIDs)
if len(summaryDetails.TopWorkloadsByScore) > 0 {
if len(summaryDetails.TopWorkloadsByScore) > 1 {
rp.printTopWorkloads(summaryDetails)
}
@@ -52,10 +53,6 @@ func (rp *RepoPrinter) getNextSteps() []string {
}
}
func (rp *RepoPrinter) printNextSteps() {
printNextSteps(rp.writer, rp.getNextSteps())
}
func (rp *RepoPrinter) printTopWorkloads(summaryDetails *reportsummary.SummaryDetails) {
cautils.InfoTextDisplay(rp.writer, getTopWorkloadsTitle(len(summaryDetails.TopWorkloadsByScore)))
@@ -75,14 +72,10 @@ func (rp *RepoPrinter) getWorkloadScanCommand(ns, kind, name string, source repo
if ns == "" {
cmd = fmt.Sprintf("$ kubescape scan workload %s/%s", kind, name)
}
if source.FileType == "Helm" {
return fmt.Sprintf("%s --chart-path=%s", cmd, source.RelativePath)
if source.FileType == reporthandling.SourceTypeHelmChart {
return fmt.Sprintf("%s --chart-path=%s --file-path=%s", cmd, source.Path, source.RelativePath)
} else {
return fmt.Sprintf("%s --file-path=%s", cmd, source.RelativePath)
}
}
func (rp *RepoPrinter) generateTableNextSteps(controlSummary reportsummary.IControlSummary, inputPatterns []string) string {
return fmt.Sprintf("$ kubescape scan control %s %s", controlSummary.GetID(), strings.Join(inputPatterns, ","))
}

View File

@@ -1,31 +1,110 @@
package configurationprinter
import (
"github.com/kubescape/opa-utils/reporthandling/apis"
"fmt"
"io"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
const (
categoriesColumnSeverity = iota
categoriesColumnName = iota
categoriesColumnFailed = iota
categoriesColumnNextSteps = iota
docsPrefix = "https://hub.armosec.io/docs"
scanControlPrefix = "$ kubescape scan control"
controlNameHeader = "CONTROL NAME"
statusHeader = "STATUS"
docsHeader = "DOCS"
resourcesHeader = "RESOURCES"
runHeader = "RUN"
)
func setCategoryStatusRow(controlSummary reportsummary.IControlSummary, row []string) {
status := controlSummary.GetStatus().Status()
if status == apis.StatusSkipped {
status = "action required"
// initializes the table headers and column alignments based on the category type
func initCategoryTableData(categoryType CategoryType) ([]string, []int) {
if categoryType == TypeCounting {
return getCategoryCountingTypeHeaders(), getCountingTypeAlignments()
}
row[categoriesColumnFailed] = string(status)
return getCategoryStatusTypeHeaders(), getStatusTypeAlignments()
}
func getCommonCategoriesTableHeaders() []string {
headers := make([]string, 4)
headers[categoriesColumnSeverity] = "SEVERITY"
headers[categoriesColumnName] = "CONTROL NAME"
headers[categoriesColumnFailed] = "STATUS"
headers[categoriesColumnNextSteps] = "NEXT STEP"
func getCategoryStatusTypeHeaders() []string {
headers := make([]string, 3)
headers[0] = controlNameHeader
headers[1] = statusHeader
headers[2] = docsHeader
return headers
}
func getCategoryCountingTypeHeaders() []string {
headers := make([]string, 3)
headers[0] = controlNameHeader
headers[1] = resourcesHeader
headers[2] = runHeader
return headers
}
func getStatusTypeAlignments() []int {
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER}
}
func getCountingTypeAlignments() []int {
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT}
}
// returns a row for status type table based on the control summary
func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) []string {
// show only passed, failed and action required controls
status := controlSummary.GetStatus()
if !status.IsFailed() && !status.IsSkipped() && !status.IsPassed() {
return nil
}
rows := make([]string, 3)
rows[0] = controlSummary.GetName()
if len(controlSummary.GetName()) > 50 {
rows[0] = controlSummary.GetName()[:50] + "..."
} else {
rows[0] = controlSummary.GetName()
}
// skipped is shown as action required
if status.IsSkipped() {
rows[1] = fmt.Sprintf("%s %s", "action required", GetInfoColumn(controlSummary, infoToPrintInfo))
} else {
rows[1] = string(controlSummary.GetStatus().Status())
}
rows[2] = getDocsForControl(controlSummary)
return rows
}
func getCategoryTableWriter(writer io.Writer, headers []string, columnAligments []int) *tablewriter.Table {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetColumnAlignment(columnAligments)
table.SetAutoWrapText(false)
return table
}
func renderSingleCategory(writer io.Writer, categoryName string, table *tablewriter.Table, rows [][]string, infoToPrintInfo []utils.InfoStars) {
cautils.InfoTextDisplay(writer, "\n"+categoryName+"\n")
table.ClearRows()
table.AppendBulk(rows)
table.Render()
if len(infoToPrintInfo) > 0 {
utils.PrintInfo(writer, infoToPrintInfo)
}
cautils.SimpleDisplay(writer, "\n")
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
@@ -19,58 +20,63 @@ func (cp *ClusterPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *re
}
func (cp *ClusterPrinter) PrintCategoriesTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
headers := cp.getCategoriesTableHeaders()
columnAligments := cp.getCategoriesColumnsAlignments()
func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
table := getTableWriter(writer, headers, columnAligments)
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapClusterControlsToCategories)
mapCategoryToRows := cp.generateRows(summaryDetails, sortedControlIDs)
for _, id := range categoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
continue
}
renderCategoriesTable(mapCategoryToRows, writer, table)
infoToPrintInfo := utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries)
cp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, infoToPrintInfo)
}
}
func (cp *ClusterPrinter) generateRows(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) map[string][][]string {
mapCategoryToRows := make(map[string][][]string)
func (cp *ClusterPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
headers, columnAligments := initCategoryTableData(categoryType)
categoriesToControlSummariesMap := mapCategoryToControlSummaries(*summaryDetails, sortedControlIDs)
table := getCategoryTableWriter(writer, headers, columnAligments)
for category, ctrls := range categoriesToControlSummariesMap {
for i := range ctrls {
row := cp.generateCategoriesRow(ctrls[i])
if len(row) > 0 {
mapCategoryToRows[category] = append(mapCategoryToRows[category], row)
}
var rows [][]string
for _, ctrls := range controlSummaries {
var row []string
if categoryType == TypeCounting {
row = cp.generateCountingCategoryRow(ctrls)
} else {
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
}
if len(row) > 0 {
rows = append(rows, row)
}
}
return mapCategoryToRows
}
func (cp *ClusterPrinter) generateCategoriesRow(controlSummary reportsummary.IControlSummary) []string {
row := make([]string, 4)
row[categoriesColumnSeverity] = GetSeverityColumn(controlSummary)
if len(controlSummary.GetName()) > 50 {
row[categoriesColumnName] = controlSummary.GetName()[:50] + "..."
} else {
row[categoriesColumnName] = controlSummary.GetName()
if len(rows) == 0 {
return
}
setCategoryStatusRow(controlSummary, row)
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
row[categoriesColumnNextSteps] = cp.generateNextSteps(controlSummary)
}
func (cp *ClusterPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary) []string {
row := make([]string, 3)
row[0] = controlSummary.GetName()
row[1] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
row[2] = cp.generateTableNextSteps(controlSummary)
return row
}
func (cp *ClusterPrinter) getCategoriesTableHeaders() []string {
return getCommonCategoriesTableHeaders()
}
func (cp *ClusterPrinter) getCategoriesColumnsAlignments() []int {
return getCommonColumnsAlignments()
func (cp *ClusterPrinter) generateTableNextSteps(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("%s %s -v", scanControlPrefix, controlSummary.GetID())
}
func (cp *ClusterPrinter) generateNextSteps(controlSummary reportsummary.IControlSummary) string {

View File

@@ -66,7 +66,7 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
utils.PrintInfo(writer, infoToPrintInfo)
}
func (fp *FrameworkPrinter) PrintCategoriesTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (fp *FrameworkPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
}

View File

@@ -7,6 +7,6 @@ import (
)
type TablePrinter interface {
PrintCategoriesTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string)
PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string)
PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string)
}

View File

@@ -5,6 +5,7 @@ import (
"io"
"strings"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
@@ -25,58 +26,58 @@ func (rp *RepoPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *repor
}
func (rp *RepoPrinter) PrintCategoriesTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
headers := rp.getCategoriesTableHeaders()
columnAligments := rp.getCategoriesColumnsAlignments()
func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
table := getTableWriter(writer, headers, columnAligments)
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapClusterControlsToCategories)
mapCategoryToRows := rp.generateRows(summaryDetails, sortedControlIDs)
for _, id := range categoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
continue
}
renderCategoriesTable(mapCategoryToRows, writer, table)
infoToPrintInfo := utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries)
rp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, infoToPrintInfo)
}
}
func (rp *RepoPrinter) generateRows(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) map[string][][]string {
mapCategoryToRows := make(map[string][][]string)
func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
headers, columnAligments := initCategoryTableData(categoryType)
categoriesToControlSummariesMap := mapCategoryToControlSummaries(*summaryDetails, sortedControlIDs)
table := getCategoryTableWriter(writer, headers, columnAligments)
for category, ctrls := range categoriesToControlSummariesMap {
for i := range ctrls {
row := rp.generateCategoriesRow(ctrls[i], rp.inputPatterns)
if len(row) > 0 {
mapCategoryToRows[category] = append(mapCategoryToRows[category], row)
}
var rows [][]string
for _, ctrls := range controlSummaries {
var row []string
if categoryType == TypeCounting {
row = rp.generateCountingCategoryRow(ctrls, rp.inputPatterns)
} else {
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
}
if len(row) > 0 {
rows = append(rows, row)
}
}
return mapCategoryToRows
}
func (rp *RepoPrinter) generateCategoriesRow(controlSummary reportsummary.IControlSummary, inputPatterns []string) []string {
row := make([]string, 4)
row[categoriesColumnSeverity] = GetSeverityColumn(controlSummary)
if len(controlSummary.GetName()) > 50 {
row[categoriesColumnName] = controlSummary.GetName()[:50] + "..."
} else {
row[categoriesColumnName] = controlSummary.GetName()
if len(rows) == 0 {
return
}
setCategoryStatusRow(controlSummary, row)
row[categoriesColumnNextSteps] = rp.generateTableNextSteps(controlSummary, inputPatterns)
return row
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
}
func (rp *RepoPrinter) getCategoriesTableHeaders() []string {
return getCommonCategoriesTableHeaders()
}
func (rp *RepoPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary, inputPatterns []string) []string {
rows := make([]string, 3)
func (rp *RepoPrinter) getCategoriesColumnsAlignments() []int {
return getCommonColumnsAlignments()
rows[0] = controlSummary.GetName()
rows[1] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
rows[2] = rp.generateTableNextSteps(controlSummary, inputPatterns)
return rows
}
func (rp *RepoPrinter) getWorkloadScanCommand(ns, kind, name string, source reporthandling.Source) string {

View File

@@ -3,8 +3,10 @@ package configurationprinter
import (
"fmt"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
@@ -18,6 +20,14 @@ const (
_summaryRowLen = iota
)
func ControlCountersForSummary(counters reportsummary.ICounters) string {
return fmt.Sprintf("Controls: %d (Failed: %d, Passed: %d, Action Required: %d)", counters.All(), counters.Failed(), counters.Passed(), counters.Skipped())
}
func GetSeverityColumn(controlSummary reportsummary.IControlSummary) string {
return color.New(utils.GetColor(apis.ControlSeverityToInt(controlSummary.GetScoreFactor())), color.Bold).SprintFunc()(apis.ControlSeverityToString(controlSummary.GetScoreFactor()))
}
func GetControlTableHeaders() []string {
headers := make([]string, _summaryRowLen)
headers[summaryColumnName] = "CONTROL NAME"

View File

@@ -2,68 +2,70 @@ package configurationprinter
import (
"fmt"
"io"
"strings"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
func GetSeverityColumn(controlSummary reportsummary.IControlSummary) string {
return color.New(utils.GetColor(apis.ControlSeverityToInt(controlSummary.GetScoreFactor())), color.Bold).SprintFunc()(apis.ControlSeverityToString(controlSummary.GetScoreFactor()))
}
// returns map of category ID to category controls (name and controls)
// controls will be on the map only if the are in the mapClusterControlsToCategories map
func mapCategoryToSummary(controlSummaries []reportsummary.IControlSummary, mapDisplayCtrlIDToCategory map[string]string) map[string]CategoryControls {
type InfoStars struct {
Stars string
Info string
}
mapCategoriesToCtrlSummary := map[string][]reportsummary.IControlSummary{}
// helper map to get the category name
mapCategoryIDToName := make(map[string]string)
func ControlCountersForSummary(counters reportsummary.ICounters) string {
return fmt.Sprintf("Controls: %d (Failed: %d, Passed: %d, Action Required: %d)", counters.All(), counters.Failed(), counters.Passed(), counters.Skipped())
}
for i := range controlSummaries {
// check if we need to print this control
category, ok := mapDisplayCtrlIDToCategory[controlSummaries[i].GetID()]
if !ok {
continue
}
func getCommonColumnsAlignments() []int {
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT}
}
func mapCategoryToControlSummaries(summaryDetails reportsummary.SummaryDetails, sortedControlIDs [][]string) map[string][]reportsummary.IControlSummary {
categories := map[string][]reportsummary.IControlSummary{}
for i := len(sortedControlIDs) - 1; i >= 0; i-- {
for _, c := range sortedControlIDs[i] {
ctrl := summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, c)
if ctrl.GetStatus().Status() == apis.StatusPassed {
continue
// the category on the map can be either category or subcategory, so we need to check both
if controlSummaries[i].GetCategory().ID == category {
if _, ok := mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID]; !ok {
mapCategoryIDToName[controlSummaries[i].GetCategory().ID] = controlSummaries[i].GetCategory().Name // set category name
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID] = []reportsummary.IControlSummary{}
}
for j := range ctrl.GetCategories() {
categories[ctrl.GetCategories()[j]] = append(categories[ctrl.GetCategories()[j]], ctrl)
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID] = append(mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().ID], controlSummaries[i])
continue
}
if controlSummaries[i].GetCategory().SubCategory.ID == category {
if _, ok := mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID]; !ok {
mapCategoryIDToName[controlSummaries[i].GetCategory().SubCategory.ID] = controlSummaries[i].GetCategory().SubCategory.Name // set category name
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID] = []reportsummary.IControlSummary{}
}
mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID] = append(mapCategoriesToCtrlSummary[controlSummaries[i].GetCategory().SubCategory.ID], controlSummaries[i])
continue
}
}
return categories
mapCategoryToControls := buildCategoryToControlsMap(mapCategoriesToCtrlSummary, mapCategoryIDToName)
return mapCategoryToControls
}
func getTableWriter(writer io.Writer, headers []string, columnAligments []int) *tablewriter.Table {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetColumnAlignment(columnAligments)
return table
}
func renderCategoriesTable(mapCategoryToRows map[string][][]string, writer io.Writer, table *tablewriter.Table) {
for category, rows := range mapCategoryToRows {
cautils.InfoTextDisplay(writer, "\n"+category+"\n")
table.ClearRows()
table.AppendBulk(rows)
table.Render()
cautils.SimpleDisplay(writer, "\n")
// returns map of category ID to category controls (name and controls)
func buildCategoryToControlsMap(mapCategoriesToCtrlSummary map[string][]reportsummary.IControlSummary, mapCategoryIDToName map[string]string) map[string]CategoryControls {
mapCategoryToControls := make(map[string]CategoryControls)
for categoryID, ctrls := range mapCategoriesToCtrlSummary {
categoryName := mapCategoryIDToName[categoryID]
mapCategoryToControls[categoryID] = CategoryControls{
CategoryName: categoryName,
controlSummaries: ctrls,
}
}
return mapCategoryToControls
}
// returns doc link for control
func getDocsForControl(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("%s/%s", docsPrefix, strings.ToLower(controlSummary.GetID()))
}
// returns run command with verbose for control
func getRunCommandForControl(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("%s %s -v", scanControlPrefix, controlSummary.GetID())
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
@@ -20,59 +21,71 @@ func (wp *WorkloadPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *r
}
func (wp *WorkloadPrinter) PrintCategoriesTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
headers := wp.getCategoriesTableHeaders()
columnAligments := wp.getCategoriesColumnsAlignments()
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapClusterControlsToCategories)
table := getTableWriter(writer, headers, columnAligments)
for _, id := range categoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
continue
}
mapCategoryToRows := wp.generateRows(summaryDetails, sortedControlIDs)
infoToPrintInfo := utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries)
renderCategoriesTable(mapCategoryToRows, writer, table)
wp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, infoToPrintInfo)
}
}
func (wp *WorkloadPrinter) generateRows(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) map[string][][]string {
mapCategoryToRows := make(map[string][][]string)
func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
headers, columnAligments := initCategoryTableData(categoryType)
categoriesToControlSummariesMap := mapCategoryToControlSummaries(*summaryDetails, sortedControlIDs)
table := getCategoryTableWriter(writer, headers, columnAligments)
for category, ctrls := range categoriesToControlSummariesMap {
for i := range ctrls {
row := wp.generateCategoriesRow(ctrls[i])
if len(row) > 0 {
mapCategoryToRows[category] = append(mapCategoryToRows[category], row)
}
var rows [][]string
for _, ctrls := range controlSummaries {
var row []string
if categoryType == TypeCounting {
row = wp.generateCountingCategoryRow(ctrls)
} else {
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
}
if len(row) > 0 {
rows = append(rows, row)
}
}
return mapCategoryToRows
if len(rows) == 0 {
return
}
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
}
func (wp *WorkloadPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary) []string {
row := make([]string, 3)
row[0] = controlSummary.GetName()
row[1] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
row[2] = wp.generateTableNextSteps(controlSummary)
return row
}
func (wp *WorkloadPrinter) generateTableNextSteps(controlSummary reportsummary.IControlSummary) string {
return fmt.Sprintf("%s %s -v", scanControlPrefix, controlSummary.GetID())
}
func (wp *WorkloadPrinter) getCategoriesTableHeaders() []string {
return getCommonCategoriesTableHeaders()
return getCategoryCountingTypeHeaders()
}
func (wp *WorkloadPrinter) getCategoriesColumnsAlignments() []int {
return getCommonColumnsAlignments()
}
func (wp *WorkloadPrinter) generateCategoriesRow(controlSummary reportsummary.IControlSummary) []string {
row := make([]string, 4)
row[categoriesColumnSeverity] = GetSeverityColumn(controlSummary)
if len(controlSummary.GetName()) > 50 {
row[categoriesColumnName] = controlSummary.GetName()[:50] + "..."
} else {
row[categoriesColumnName] = controlSummary.GetName()
}
setCategoryStatusRow(controlSummary, row)
row[categoriesColumnNextSteps] = wp.generateNextSteps(controlSummary)
return row
return getCountingTypeAlignments()
}
func (wp *WorkloadPrinter) generateNextSteps(controlSummary reportsummary.IControlSummary) string {

View File

@@ -1,26 +0,0 @@
package imageprinter
type ImageScanSummary struct {
MapsSeverityToSummary map[string]*SeveritySummary
CVEs []CVE
PackageScores map[string]*Package
}
type SeveritySummary struct {
NumberOfCVEs int
NumberOfFixableCVEs int
}
type CVE struct {
Severity string
ID string
Package string
Version string
FixVersions []string
FixedState string
}
type Package struct {
Version string
Score int
}

View File

@@ -2,13 +2,6 @@ package imageprinter
import (
"io"
"sort"
"strings"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/fatih/color"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/olekukonko/tablewriter"
)
const (
@@ -37,53 +30,5 @@ func (tw *TableWriter) PrintImageScanningTable(writer io.Writer, summary ImageSc
headers := getImageScanningHeaders()
columnAlignments := getImageScanningColumnsAlignments()
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetColumnAlignment(columnAlignments)
table.AppendBulk(rows)
table.Render()
}
func generateRows(summary ImageScanSummary) [][]string {
rows := make([][]string, 0, len(summary.CVEs))
sort.Slice(summary.CVEs, func(i, j int) bool {
return utils.ImageSeverityToInt(summary.CVEs[i].Severity) > utils.ImageSeverityToInt(summary.CVEs[j].Severity)
})
for _, cve := range summary.CVEs {
rows = append(rows, generateImageScanRow(cve))
}
return rows
}
func generateImageScanRow(cve CVE) []string {
row := make([]string, 5)
row[imageColumnSeverity] = color.New(utils.GetColor(utils.ImageSeverityToInt(cve.Severity)), color.Bold).Sprint(cve.Severity)
row[imageColumnName] = cve.ID
row[imageColumnComponent] = cve.Package
row[imageColumnVersion] = cve.Version
if cve.FixedState == string(v5.FixedState) {
row[imageColumnFixedIn] = strings.Join(cve.FixVersions, "")
} else if cve.FixedState == string(v5.WontFixState) {
row[imageColumnFixedIn] = cve.FixedState
}
return row
}
func getImageScanningHeaders() []string {
headers := make([]string, 5)
headers[imageColumnSeverity] = "SEVERITY"
headers[imageColumnName] = "NAME"
headers[imageColumnComponent] = "COMPONENT"
headers[imageColumnVersion] = "VERSION"
headers[imageColumnFixedIn] = "FIXED IN"
return headers
}
func getImageScanningColumnsAlignments() []int {
return []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}
renderTable(writer, headers, columnAlignments, rows)
}

View File

@@ -16,6 +16,25 @@ type InfoStars struct {
Info string
}
func MapInfoToPrintInfoFromIface(ctrls []reportsummary.IControlSummary) []InfoStars {
infoToPrintInfo := []InfoStars{}
infoToPrintInfoMap := map[string]interface{}{}
starCount := "*"
for _, ctrl := range ctrls {
if ctrl.GetStatus().IsSkipped() && ctrl.GetStatus().Info() != "" {
if _, ok := infoToPrintInfoMap[ctrl.GetStatus().Info()]; !ok {
infoToPrintInfo = append(infoToPrintInfo, InfoStars{
Info: ctrl.GetStatus().Info(),
Stars: starCount,
})
starCount += "*"
infoToPrintInfoMap[ctrl.GetStatus().Info()] = nil
}
}
}
return infoToPrintInfo
}
func MapInfoToPrintInfo(controls reportsummary.ControlSummaries) []InfoStars {
infoToPrintInfo := []InfoStars{}
infoToPrintInfoMap := map[string]interface{}{}

View File

@@ -27,15 +27,6 @@ var (
complianceFrameworks = []string{"nsa", "mitre"}
)
func printComplianceScore(writer *os.File, frameworks []reportsummary.IFrameworkSummary) {
cautils.InfoTextDisplay(writer, "Compliance Score:\n")
for _, fw := range frameworks {
cautils.SimpleDisplay(writer, "* %s: %.2f%%\n", fw.GetName(), fw.GetComplianceScore())
}
cautils.InfoTextDisplay(writer, "\n")
}
func filterComplianceFrameworks(frameworks []reportsummary.IFrameworkSummary) []reportsummary.IFrameworkSummary {
complianceFws := []reportsummary.IFrameworkSummary{}
for _, fw := range frameworks {
@@ -57,16 +48,8 @@ func getWorkloadPrefixForCmd(namespace, kind, name string) string {
return fmt.Sprintf("namespace: %s, name: %s, kind: %s", namespace, name, kind)
}
func printNextSteps(writer *os.File, nextSteps []string) {
cautils.InfoTextDisplay(writer, "Follow-up steps:\n")
for _, ns := range nextSteps {
cautils.SimpleDisplay(writer, "- "+ns+"\n")
}
cautils.SimpleDisplay(writer, "\n")
}
func getTopWorkloadsTitle(topWLsLen int) string {
if topWLsLen > 2 {
if topWLsLen > 1 {
return "Your most risky workloads:\n"
}
if topWLsLen > 0 {
@@ -75,6 +58,80 @@ func getTopWorkloadsTitle(topWLsLen int) string {
return ""
}
// getSeverityToSummaryMap returns a map of severity to summary, if shouldMerge is true, it will merge Low, Negligible and Unknown to Other
func getSeverityToSummaryMap(summary imageprinter.ImageScanSummary, shouldMerge bool) map[string]*imageprinter.SeveritySummary {
tempMap := map[string]*imageprinter.SeveritySummary{}
for severity, severitySummary := range summary.MapsSeverityToSummary {
if shouldMerge {
if severity == "Low" || severity == "Negligible" || severity == "Unknown" {
severity = "Other"
}
}
if _, ok := tempMap[severity]; !ok {
tempMap[severity] = &imageprinter.SeveritySummary{}
}
tempMap[severity].NumberOfCVEs += severitySummary.NumberOfCVEs
tempMap[severity].NumberOfFixableCVEs += severitySummary.NumberOfFixableCVEs
}
return tempMap
}
// filterCVEsBySeverities returns a list of CVEs only with the severities that are in the severities list
func filterCVEsBySeverities(cves []imageprinter.CVE, severities []string) []imageprinter.CVE {
var filteredCVEs []imageprinter.CVE
for _, cve := range cves {
for _, severity := range severities {
if cve.Severity == severity {
filteredCVEs = append(filteredCVEs, cve)
}
}
}
return filteredCVEs
}
// sortTopVulnerablePackages sorts the top vulnerable packages by score. It return a map of packages to their score and version
func sortTopVulnerablePackages(pkgScores map[string]*imageprinter.PackageScore) map[string]*imageprinter.PackageScore {
var ss []string
for k := range pkgScores {
ss = append(ss, k)
}
sort.Slice(ss, func(i, j int) bool {
return pkgScores[ss[i]].Score > pkgScores[ss[j]].Score
})
var sortedMap = make(map[string]*imageprinter.PackageScore)
for i := 0; i < len(ss) && i < TopPackagesNumber; i++ {
sortedMap[ss[i]] = &imageprinter.PackageScore{
Score: pkgScores[ss[i]].Score,
Version: pkgScores[ss[i]].Version,
}
}
return sortedMap
}
// / PRINTERS ///
func printTopVulnerabilities(writer *os.File, summary imageprinter.ImageScanSummary) {
if len(summary.PackageScores) == 0 {
return
}
cautils.InfoTextDisplay(writer, "\nMost vulnerable components:\n")
topVulnerablePackages := sortTopVulnerablePackages(summary.PackageScores)
for k, v := range topVulnerablePackages {
cautils.SimpleDisplay(writer, " * %s (%s)\n", k, v.Version)
}
cautils.SimpleDisplay(writer, "\n")
return
}
func printImageScanningSummary(writer *os.File, summary imageprinter.ImageScanSummary, verboseMode bool) {
mapSeverityTSummary := getSeverityToSummaryMap(summary, !verboseMode)
@@ -98,71 +155,21 @@ func printImageScanningSummary(writer *os.File, summary imageprinter.ImageScanSu
}
}
func getSeverityToSummaryMap(summary imageprinter.ImageScanSummary, shouldMerge bool) map[string]*imageprinter.SeveritySummary {
tempMap := map[string]*imageprinter.SeveritySummary{}
for severity, severitySummary := range summary.MapsSeverityToSummary {
if shouldMerge && severity == "Low" || severity == "Negligible" || severity == "Unknown" {
severity = "Other"
}
if _, ok := tempMap[severity]; !ok {
tempMap[severity] = &imageprinter.SeveritySummary{}
}
tempMap[severity].NumberOfCVEs += severitySummary.NumberOfCVEs
tempMap[severity].NumberOfFixableCVEs += severitySummary.NumberOfFixableCVEs
func printNextSteps(writer *os.File, nextSteps []string) {
cautils.InfoTextDisplay(writer, "Follow-up steps:\n")
for _, ns := range nextSteps {
cautils.SimpleDisplay(writer, "- "+ns+"\n")
}
return tempMap
}
func filterCVEsBySeverities(cves []imageprinter.CVE, severities []string) []imageprinter.CVE {
var filteredCves []imageprinter.CVE
for _, cve := range cves {
for _, severity := range severities {
if cve.Severity == severity {
filteredCves = append(filteredCves, cve)
}
}
}
return filteredCves
}
func sortTopVulnerableWorkloads(pkgScores map[string]*imageprinter.Package) map[string]*imageprinter.Package {
var ss []string
for k := range pkgScores {
ss = append(ss, k)
}
sort.Slice(ss, func(i, j int) bool {
return pkgScores[ss[i]].Score > pkgScores[ss[j]].Score
})
var sortedMap = make(map[string]*imageprinter.Package)
for i := 0; i < len(ss) && i < TopPackagesNumber; i++ {
sortedMap[ss[i]] = &imageprinter.Package{
Score: pkgScores[ss[i]].Score,
Version: pkgScores[ss[i]].Version,
}
}
return sortedMap
}
func printTopVulnerabilities(writer *os.File, summary imageprinter.ImageScanSummary) {
if len(summary.PackageScores) == 0 {
return
}
cautils.InfoTextDisplay(writer, "\nMost vulnerable components:\n")
topVulnerableImages := sortTopVulnerableWorkloads(summary.PackageScores)
for k, v := range topVulnerableImages {
cautils.SimpleDisplay(writer, " * %s (%s)\n", k, v.Version)
}
cautils.SimpleDisplay(writer, "\n")
return
}
func printComplianceScore(writer *os.File, frameworks []reportsummary.IFrameworkSummary) {
cautils.InfoTextDisplay(writer, "Compliance Score:\n")
for _, fw := range frameworks {
cautils.SimpleDisplay(writer, "* %s: %.2f%%\n", fw.GetName(), fw.GetComplianceScore())
}
cautils.SimpleDisplay(writer, "View full compliance report by running: $ kubescape scan framework nsa,mitre\n")
cautils.InfoTextDisplay(writer, "\n")
}

View File

@@ -22,12 +22,12 @@ func NewWorkloadPrinter(writer *os.File) *WorkloadPrinter {
var _ MainPrinter = &WorkloadPrinter{}
func (wp *WorkloadPrinter) PrintImageScanning(*imageprinter.ImageScanSummary) {
func (wp *WorkloadPrinter) PrintImageScanning(summary *imageprinter.ImageScanSummary) {
}
func (wp *WorkloadPrinter) PrintNextSteps() {}
func (wp *WorkloadPrinter) PrintConfigurationsScanning(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
wp.categoriesTablePrinter.PrintCategoriesTable(wp.writer, summaryDetails, sortedControlIDs)
wp.categoriesTablePrinter.PrintCategoriesTables(wp.writer, summaryDetails, sortedControlIDs)
}

View File

@@ -55,7 +55,7 @@ func (pp *PrometheusPrinter) generatePrometheusFormat(
func (pp *PrometheusPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (pp *PrometheusPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (pp *PrometheusPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
metrics := pp.generatePrometheusFormat(opaSessionObj.AllResources, opaSessionObj.ResourcesResult, &opaSessionObj.Report.SummaryDetails)

View File

@@ -117,7 +117,7 @@ func (sp *SARIFPrinter) PrintNextSteps() {
}
func (sp *SARIFPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (sp *SARIFPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
report, err := sarif.New(sarif.Version210)
if err != nil {
panic(err)

View File

@@ -21,7 +21,7 @@ func (silentPrinter *SilentPrinter) PrintNextSteps() {
func (silentPrinter *SilentPrinter) PrintImageScan(context.Context, *models.PresenterConfig) {
}
func (silentPrinter *SilentPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData *models.PresenterConfig) {
func (silentPrinter *SilentPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj, imageScanData []cautils.ImageScanData) {
}
func (silentPrinter *SilentPrinter) SetWriter(ctx context.Context, outputFile string) {

View File

@@ -1,8 +1,12 @@
package printer
import (
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/anchore/grype/grype/presenter/models"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/imageprinter"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/results/v1/prioritization"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
@@ -82,3 +86,72 @@ func finalizeResources(results []resourcesresults.Result, allResources map[strin
}
return resources
}
func insertSeveritiesSummariesIntoMap(mapSeverityToSummary map[string]*imageprinter.SeveritySummary, imageScanSummary imageprinter.ImageScanSummary) {
for k, v := range mapSeverityToSummary {
severitySummary, ok := imageScanSummary.MapsSeverityToSummary[k]
if !ok {
imageScanSummary.MapsSeverityToSummary[k] = v
continue
}
severitySummary.NumberOfCVEs = severitySummary.NumberOfCVEs + v.NumberOfCVEs
severitySummary.NumberOfFixableCVEs = severitySummary.NumberOfFixableCVEs + v.NumberOfFixableCVEs
imageScanSummary.MapsSeverityToSummary[k] = severitySummary
}
}
func insertPackageScoresIntoMap(mapPackageNameToScore map[string]*imageprinter.PackageScore, imageScanSummary imageprinter.ImageScanSummary) {
for k, v := range mapPackageNameToScore {
pkgScore, ok := imageScanSummary.PackageScores[k]
if !ok {
imageScanSummary.PackageScores[k] = v
continue
}
pkgScore.Score = pkgScore.Score + v.Score
imageScanSummary.PackageScores[k] = pkgScore
}
}
func extractSeverityToSummaryMap(cves []imageprinter.CVE) map[string]*imageprinter.SeveritySummary {
mapSeverityToSummary := map[string]*imageprinter.SeveritySummary{}
for _, cve := range cves {
if _, ok := mapSeverityToSummary[cve.Severity]; !ok {
mapSeverityToSummary[cve.Severity] = &imageprinter.SeveritySummary{}
}
mapSeverityToSummary[cve.Severity].NumberOfCVEs = mapSeverityToSummary[cve.Severity].NumberOfCVEs + 1
if cve.FixedState == string(v5.FixedState) {
mapSeverityToSummary[cve.Severity].NumberOfFixableCVEs = mapSeverityToSummary[cve.Severity].NumberOfFixableCVEs + 1
}
}
return mapSeverityToSummary
}
func extractPkgNameToScore(doc models.Document) map[string]*imageprinter.PackageScore {
mapPackageNameToScore := make(map[string]*imageprinter.PackageScore, 0)
for _, cve := range doc.Matches {
if _, ok := mapPackageNameToScore[cve.Artifact.Name]; !ok {
mapPackageNameToScore[cve.Artifact.Name] = &imageprinter.PackageScore{
Score: 0,
}
}
mapPackageNameToScore[cve.Artifact.Name].Score = mapPackageNameToScore[cve.Artifact.Name].Score + utils.ImageSeverityToInt(cve.Vulnerability.Severity)
mapPackageNameToScore[cve.Artifact.Name].Version = cve.Artifact.Version
}
return mapPackageNameToScore
}
func extractCVEs(doc models.Document) []imageprinter.CVE {
cves := []imageprinter.CVE{}
for _, match := range doc.Matches {
cve := imageprinter.CVE{
ID: match.Vulnerability.ID,
Severity: match.Vulnerability.Severity,
Package: match.Artifact.Name,
Version: match.Artifact.Version,
FixVersions: match.Vulnerability.Fix.Versions,
FixedState: match.Vulnerability.Fix.State,
}
cves = append(cves, cve)
}
return cves
}

View File

@@ -21,7 +21,7 @@ type ResultsHandler struct {
UiPrinter printer.IPrinter
ScanData *cautils.OPASessionObj
PrinterObjs []printer.IPrinter
ImageScanData *models.PresenterConfig
ImageScanData []cautils.ImageScanData
}
func NewResultsHandler(reporterObj reporter.IReport, printerObjs []printer.IPrinter, uiPrinter printer.IPrinter, imageScanData *models.PresenterConfig) *ResultsHandler {
@@ -29,7 +29,7 @@ func NewResultsHandler(reporterObj reporter.IReport, printerObjs []printer.IPrin
ReporterObj: reporterObj,
PrinterObjs: printerObjs,
UiPrinter: uiPrinter,
ImageScanData: imageScanData,
ImageScanData: make([]cautils.ImageScanData, 0),
}
}

2
go.mod
View File

@@ -2,6 +2,8 @@ module github.com/kubescape/kubescape/v2
go 1.20
replace github.com/kubescape/opa-utils => github.com/kubescape/opa-utils v0.0.255-0.20230720064723-b84bab720db2
require (
cloud.google.com/go/containeranalysis v0.9.0
github.com/anchore/grype v0.64.2

2
go.sum
View File

@@ -1519,6 +1519,8 @@ github.com/kubescape/go-logger v0.0.11 h1:oucpq2S7+DT7O+UclG5IrmHado/tj6+IkYf9cz
github.com/kubescape/go-logger v0.0.11/go.mod h1:yGiKBJ2lhq/kxzY/MVYDREL9fLV3RGD6gv+UFjslaew=
github.com/kubescape/k8s-interface v0.0.116 h1:Sn76gsMLAArc5kbHZVoRMS6QlM4mOz9Dolpym9BOul8=
github.com/kubescape/k8s-interface v0.0.116/go.mod h1:ENpA9SkkS6E3PIT+AaMu/JGkuyE04aUamY+a7WLqsJQ=
github.com/kubescape/opa-utils v0.0.255-0.20230720064723-b84bab720db2 h1:hr6Gqb7MTb/pLkwpD9fnf/qw2l/khh2FXc2uND1xx9I=
github.com/kubescape/opa-utils v0.0.255-0.20230720064723-b84bab720db2/go.mod h1:SkNqbhUGipSYVE+oAUaHko6aggp8XVVbDChoNg48lao=
github.com/kubescape/rbac-utils v0.0.20 h1:1MMxsCsCZ3ntDi8f9ZYYcY+K7bv50bDW5ZvnGnhMhJw=
github.com/kubescape/rbac-utils v0.0.20/go.mod h1:t57AhSrjuNGQ+mpZWQM/hBzrCOeKBDHegFoVo4tbikQ=
github.com/kubescape/regolibrary v1.0.286-rc.0 h1:OzhtQEx1npAxTbgkbEpMLZvPWg6sh2CmCgQLs0j6pQ4=