mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
@@ -89,6 +89,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
|
||||
}
|
||||
if len(args) > 1 {
|
||||
if len(args[1:]) == 0 || args[1] != "-" {
|
||||
scanInfo.ScanType = cautils.ScanTypeRepo
|
||||
scanInfo.InputPatterns = args[1:]
|
||||
logger.L().Debug("List of input files", helpers.Interface("patterns", scanInfo.InputPatterns))
|
||||
} else { // store stdin to file - do NOT move to separate function !!
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
apis "github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/prioritization"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
|
||||
)
|
||||
@@ -20,7 +21,9 @@ type KSResources map[string][]string
|
||||
type ScanTypes string
|
||||
|
||||
const (
|
||||
ScanTypeCluster ScanTypes = "cluster"
|
||||
TopWorkloadsNumber = 5
|
||||
ScanTypeCluster ScanTypes = "cluster"
|
||||
ScanTypeRepo ScanTypes = "repo"
|
||||
)
|
||||
|
||||
type OPASessionObj struct {
|
||||
@@ -62,6 +65,22 @@ func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework
|
||||
}
|
||||
}
|
||||
|
||||
func (sessionObj *OPASessionObj) SetTopWorkloads() {
|
||||
count := 0
|
||||
|
||||
for key := range sessionObj.ResourcesPrioritized {
|
||||
if count >= TopWorkloadsNumber {
|
||||
break
|
||||
}
|
||||
wlObj := reportsummary.TopWorkload{
|
||||
Workload: sessionObj.AllResources[key],
|
||||
ResourceSource: sessionObj.ResourceSource[key],
|
||||
}
|
||||
sessionObj.Report.SummaryDetails.TopWorkloadsByScore = append(sessionObj.Report.SummaryDetails.TopWorkloadsByScore, wlObj)
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
func (sessionObj *OPASessionObj) SetMapNamespaceToNumberOfResources(mapNamespaceToNumberOfResources map[string]int) {
|
||||
if sessionObj.Metadata.ContextMetadata.ClusterContextMetadata == nil {
|
||||
sessionObj.Metadata.ContextMetadata.ClusterContextMetadata = &reporthandlingv2.ClusterMetadata{}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
// NativeFrameworks identifies all pre-built, native frameworks.
|
||||
var NativeFrameworks = []string{"security"}
|
||||
var NativeFrameworks = []string{"security", "mitre", "nsa"}
|
||||
|
||||
type (
|
||||
// TenantResponse holds the credentials for a tenant.
|
||||
|
||||
@@ -286,7 +286,7 @@ func getAttackTracksGetter(ctx context.Context, attackTracks, accountID string,
|
||||
}
|
||||
|
||||
// getUIPrinter returns a printer that will be used to print to the program’s UI (terminal)
|
||||
func getUIPrinter(ctx context.Context, verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes, scanType cautils.ScanTypes) printer.IPrinter {
|
||||
func getUIPrinter(ctx context.Context, verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes, scanType cautils.ScanTypes, scanContext cautils.ScanningContext) printer.IPrinter {
|
||||
var p printer.IPrinter
|
||||
if helpers.ToLevel(logger.L().GetLevel()) >= helpers.WarningLevel {
|
||||
p = &printerv2.SilentPrinter{}
|
||||
|
||||
+10
-3
@@ -103,8 +103,7 @@ func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInt
|
||||
printerHandler.SetWriter(ctx, scanInfo.Output)
|
||||
outputPrinters = append(outputPrinters, printerHandler)
|
||||
}
|
||||
|
||||
uiPrinter := getUIPrinter(ctx, scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View), cautils.ScanTypes(scanInfo.ScanType))
|
||||
uiPrinter := getUIPrinter(ctx, scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View), scanInfo.ScanType, scanInfo.GetScanningContext())
|
||||
|
||||
// ================== return interface ======================================
|
||||
|
||||
@@ -178,13 +177,17 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
|
||||
}
|
||||
|
||||
// ======================== prioritization ===================
|
||||
if scanInfo.PrintAttackTree {
|
||||
scanInfo.ScanType = cautils.ScanTypeRepo
|
||||
if scanInfo.PrintAttackTree || isPrioritizationScanType(scanInfo.ScanType) {
|
||||
_, spanPrioritization := otel.Tracer("").Start(ctxOpa, "prioritization")
|
||||
if priotizationHandler, err := resourcesprioritization.NewResourcesPrioritizationHandler(ctxOpa, scanInfo.Getters.AttackTracksGetter, scanInfo.PrintAttackTree); err != nil {
|
||||
logger.L().Ctx(ctx).Warning("failed to get attack tracks, this may affect the scanning results", helpers.Error(err))
|
||||
} else if err := priotizationHandler.PrioritizeResources(scanData); err != nil {
|
||||
return resultsHandling, fmt.Errorf("%w", err)
|
||||
}
|
||||
if err == nil && isPrioritizationScanType(scanInfo.ScanType) {
|
||||
scanData.SetTopWorkloads()
|
||||
}
|
||||
spanPrioritization.End()
|
||||
}
|
||||
|
||||
@@ -197,3 +200,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
|
||||
|
||||
return resultsHandling, nil
|
||||
}
|
||||
|
||||
func isPrioritizationScanType(scanType cautils.ScanTypes) bool {
|
||||
return scanType == cautils.ScanTypeCluster || scanType == cautils.ScanTypeRepo
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
const (
|
||||
categoriesColumnSeverity = iota
|
||||
categoriesColumnName = iota
|
||||
categoriesColumnFailed = iota
|
||||
categoriesColumnNextSteps = iota
|
||||
)
|
||||
|
||||
var (
|
||||
mapScanTypeToOutput = map[cautils.ScanTypes]string{
|
||||
cautils.ScanTypeCluster: "Security Overview",
|
||||
}
|
||||
)
|
||||
|
||||
func 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()
|
||||
}
|
||||
|
||||
row[categoriesColumnFailed] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
|
||||
row[categoriesColumnNextSteps] = generateNextSteps(controlSummary)
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
func generateNextSteps(controlSummary reportsummary.IControlSummary) string {
|
||||
return fmt.Sprintf("kubescape scan control %s", controlSummary.GetID())
|
||||
}
|
||||
|
||||
func getCategoriesTableHeaders() []string {
|
||||
headers := make([]string, 4)
|
||||
headers[categoriesColumnSeverity] = "SEVERITY"
|
||||
headers[categoriesColumnName] = "CONTROL NAME"
|
||||
headers[categoriesColumnFailed] = "FAILED RESOURCES"
|
||||
headers[categoriesColumnNextSteps] = "NEXT STEPS"
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func getCategoriesColumnsAlignments() []int {
|
||||
alignments := make([]int, 4)
|
||||
alignments[categoriesColumnSeverity] = tablewriter.ALIGN_LEFT
|
||||
alignments[categoriesColumnName] = tablewriter.ALIGN_LEFT
|
||||
alignments[categoriesColumnFailed] = tablewriter.ALIGN_CENTER
|
||||
alignments[categoriesColumnNextSteps] = tablewriter.ALIGN_LEFT
|
||||
|
||||
return alignments
|
||||
}
|
||||
|
||||
func renderSingleCategory(writer *os.File, category string, ctrls []reportsummary.IControlSummary, categoriesTable *tablewriter.Table) {
|
||||
cautils.InfoTextDisplay(writer, "\n"+category+"\n")
|
||||
|
||||
var rows [][]string
|
||||
for i := range ctrls {
|
||||
row := generateCategoriesRow(ctrls[i])
|
||||
if len(row) > 0 {
|
||||
rows = append(rows, row)
|
||||
}
|
||||
}
|
||||
categoriesTable.ClearRows()
|
||||
categoriesTable.AppendBulk(rows)
|
||||
categoriesTable.Render()
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type clusterPrinter struct {
|
||||
}
|
||||
|
||||
var _ mainPrinter = &clusterPrinter{}
|
||||
|
||||
func NewClusterPrinter() *clusterPrinter {
|
||||
return &clusterPrinter{}
|
||||
}
|
||||
|
||||
func (cp *clusterPrinter) PrintMainTable(writer *os.File, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
|
||||
categoriesToControlSummariesMap := mapCategoryToControlSummaries(*summaryDetails)
|
||||
|
||||
categoriesTable := tablewriter.NewWriter(writer)
|
||||
categoriesTable.SetHeader(getCategoriesTableHeaders())
|
||||
categoriesTable.SetHeaderLine(true)
|
||||
categoriesTable.SetColumnAlignment(getCategoriesColumnsAlignments())
|
||||
|
||||
for category, ctrls := range categoriesToControlSummariesMap {
|
||||
renderSingleCategory(writer, category, ctrls, categoriesTable)
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
@@ -12,11 +12,10 @@ import (
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes"
|
||||
"github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,20 +32,17 @@ type PrettyPrinter struct {
|
||||
verboseMode bool
|
||||
printAttackTree bool
|
||||
scanType cautils.ScanTypes
|
||||
mainPrinter mainPrinter
|
||||
}
|
||||
|
||||
type mainPrinter interface {
|
||||
PrintMainTable(writer *os.File, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string)
|
||||
mainPrinter prettyprinter.MainPrinter
|
||||
}
|
||||
|
||||
func NewPrettyPrinter(verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes, scanType cautils.ScanTypes) *PrettyPrinter {
|
||||
|
||||
return &PrettyPrinter{
|
||||
verboseMode: verboseMode,
|
||||
formatVersion: formatVersion,
|
||||
viewType: viewType,
|
||||
printAttackTree: attackTree,
|
||||
scanType: scanType,
|
||||
mainPrinter: prettyprinter.NewMainPrinter(scanType),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,17 +60,7 @@ func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.O
|
||||
}
|
||||
}
|
||||
|
||||
if pp.scanType != "" {
|
||||
cautils.InfoTextDisplay(pp.writer, "\n"+mapScanTypeToOutput[pp.scanType]+":\n")
|
||||
}
|
||||
|
||||
pp.mainPrinter.PrintMainTable(pp.writer, &opaSessionObj.Report.SummaryDetails, sortedControlIDs)
|
||||
|
||||
if pp.scanType == cautils.ScanTypeCluster {
|
||||
pp.printCategoriesTables(&opaSessionObj.Report.SummaryDetails)
|
||||
} else {
|
||||
pp.printSummaryTable(&opaSessionObj.Report.SummaryDetails, sortedControlIDs)
|
||||
}
|
||||
pp.mainPrinter.Print(pp.writer, &opaSessionObj.Report.SummaryDetails, sortedControlIDs)
|
||||
|
||||
// When writing to Stdout, we aren’t really writing to an output file,
|
||||
// so no need to print that we are
|
||||
@@ -216,83 +202,6 @@ func generateRelatedObjectsStr(workload WorkloadSummary) string {
|
||||
}
|
||||
return relatedStr
|
||||
}
|
||||
func generateFooter(summaryDetails *reportsummary.SummaryDetails) []string {
|
||||
// Severity | Control name | failed resources | all resources | % success
|
||||
row := make([]string, _rowLen)
|
||||
row[columnName] = "Resource Summary"
|
||||
row[columnCounterFailed] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Failed())
|
||||
row[columnCounterAll] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().All())
|
||||
row[columnSeverity] = " "
|
||||
row[columnComplianceScore] = fmt.Sprintf("%.2f%s", summaryDetails.ComplianceScore, "%")
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
func (pp *PrettyPrinter) printCategoriesTables(summaryDetails *reportsummary.SummaryDetails) {
|
||||
|
||||
categoriesToControlSummariesMap := mapCategoryToControlSummaries(*summaryDetails)
|
||||
|
||||
categoriesTable := tablewriter.NewWriter(pp.writer)
|
||||
categoriesTable.SetHeader(getCategoriesTableHeaders())
|
||||
categoriesTable.SetHeaderLine(true)
|
||||
categoriesTable.SetColumnAlignment(getCategoriesColumnsAlignments())
|
||||
|
||||
for category, ctrls := range categoriesToControlSummariesMap {
|
||||
renderSingleCategory(pp, category, ctrls, categoriesTable)
|
||||
}
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func (pp *PrettyPrinter) printSummaryTable(summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
|
||||
|
||||
if summaryDetails.NumberOfControls().All() == 0 {
|
||||
fmt.Fprintf(pp.writer, "\nKubescape did not scan any of the resources, make sure you are scanning valid kubernetes manifests (Deployments, Pods, etc.)\n")
|
||||
return
|
||||
}
|
||||
cautils.InfoTextDisplay(pp.writer, "\n"+controlCountersForSummary(summaryDetails.NumberOfControls())+"\n")
|
||||
cautils.InfoTextDisplay(pp.writer, renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters())+"\n\n")
|
||||
|
||||
// cautils.InfoTextDisplay(prettyPrinter.writer, "\n"+"Severities: SOME OTHER"+"\n\n")
|
||||
|
||||
summaryTable := tablewriter.NewWriter(pp.writer)
|
||||
summaryTable.SetAutoWrapText(false)
|
||||
summaryTable.SetHeader(getControlTableHeaders())
|
||||
summaryTable.SetHeaderLine(true)
|
||||
summaryTable.SetColumnAlignment(getColumnsAlignments())
|
||||
|
||||
printAll := pp.verboseMode
|
||||
if summaryDetails.NumberOfResources().Failed() == 0 {
|
||||
// if there are no failed controls, print the resource table and detailed information
|
||||
printAll = true
|
||||
}
|
||||
|
||||
infoToPrintInfo := mapInfoToPrintInfo(summaryDetails.Controls)
|
||||
for i := len(sortedControlIDs) - 1; i >= 0; i-- {
|
||||
for _, c := range sortedControlIDs[i] {
|
||||
row := generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, c), infoToPrintInfo, printAll)
|
||||
if len(row) > 0 {
|
||||
summaryTable.Append(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
summaryTable.SetFooter(generateFooter(summaryDetails))
|
||||
|
||||
summaryTable.Render()
|
||||
|
||||
// When scanning controls the framework list will be empty
|
||||
cautils.InfoTextDisplay(pp.writer, frameworksScoresToString(summaryDetails.ListFrameworks()))
|
||||
|
||||
pp.printInfo(infoToPrintInfo)
|
||||
|
||||
}
|
||||
|
||||
func (pp *PrettyPrinter) printInfo(infoToPrintInfo []infoStars) {
|
||||
fmt.Println()
|
||||
for i := range infoToPrintInfo {
|
||||
cautils.InfoDisplay(pp.writer, fmt.Sprintf("%s %s\n", infoToPrintInfo[i].stars, infoToPrintInfo[i].info))
|
||||
}
|
||||
}
|
||||
|
||||
func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) string {
|
||||
if len(frameworks) == 1 {
|
||||
@@ -312,26 +221,6 @@ func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) stri
|
||||
return ""
|
||||
}
|
||||
|
||||
// renderSeverityCountersSummary renders the string that reports severity counters summary
|
||||
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) string {
|
||||
critical := counters.NumberOfCriticalSeverity()
|
||||
high := counters.NumberOfHighSeverity()
|
||||
medium := counters.NumberOfMediumSeverity()
|
||||
low := counters.NumberOfLowSeverity()
|
||||
|
||||
return fmt.Sprintf(
|
||||
"Failed Resources by Severity: Critical — %d, High — %d, Medium — %d, Low — %d",
|
||||
critical, high, medium, low,
|
||||
)
|
||||
}
|
||||
|
||||
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 controlCountersForResource(l *helpersv1.AllLists) string {
|
||||
return fmt.Sprintf("Controls: %d (Failed: %d, action required: %d)", l.Len(), l.Failed(), l.Skipped())
|
||||
}
|
||||
func getSeparator(sep string) string {
|
||||
s := ""
|
||||
for i := 0; i < 80; i++ {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package prettyprinter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
func NewClusterPrinter() *MainPrinterImpl {
|
||||
printer := &MainPrinterImpl{}
|
||||
|
||||
printer.CategoriesConfig = NewCategoriesConfiguration(getCommonCategoriesTableHeaders(), getCommonColumnsAlignments())
|
||||
|
||||
printer.NextSteps = getClusterNextSteps()
|
||||
|
||||
printer.TopXWorkloadsFunc = clusterTopXWorkloadsFunc
|
||||
|
||||
printer.SetCompliancePrint(true)
|
||||
|
||||
return printer
|
||||
}
|
||||
|
||||
func clusterTopXWorkloadsFunc(writer *os.File, topXWls []reportsummary.TopWorkload) {
|
||||
for i, wl := range topXWls {
|
||||
ns := wl.Workload.GetNamespace()
|
||||
name := wl.Workload.GetName()
|
||||
kind := wl.Workload.GetKind()
|
||||
cautils.SimpleDisplay(writer, fmt.Sprintf("%d. namespace: %s, name: %s, kind: %s - '%s'\n", i+1, ns, name, kind, getWorkloadScanCommand(ns, kind, name)))
|
||||
}
|
||||
|
||||
cautils.InfoTextDisplay(writer, "\n")
|
||||
}
|
||||
|
||||
func getWorkloadScanCommand(namespace, kind, name string) string {
|
||||
return fmt.Sprintf("$ kubescape scan workload %s/%s/%s", namespace, kind, name)
|
||||
}
|
||||
|
||||
func getClusterNextSteps() []string {
|
||||
return []string{
|
||||
"cluster scan run: '$ kubescape scan cluster'",
|
||||
"CICD setup: <link to docs>",
|
||||
"continues monitoring install helm: <docs to helm>",
|
||||
}
|
||||
}
|
||||
|
||||
func getCommonColumnsAlignments() []int {
|
||||
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT}
|
||||
}
|
||||
|
||||
func getCommonCategoriesTableHeaders() []string {
|
||||
headers := make([]string, 4)
|
||||
headers[categoriesColumnSeverity] = "SEVERITY"
|
||||
headers[categoriesColumnName] = "CONTROL NAME"
|
||||
headers[categoriesColumnFailed] = "STATUS"
|
||||
headers[categoriesColumnNextSteps] = "NEXT STEPS"
|
||||
|
||||
return headers
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package prettyprinter
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
)
|
||||
|
||||
type MainPrinter interface {
|
||||
Print(writer *os.File, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string)
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package prettyprinter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type MainPrinterImpl struct {
|
||||
VerboseMode bool
|
||||
SummaryPrint bool
|
||||
CompliancePrint bool
|
||||
NextSteps []string
|
||||
|
||||
CategoriesConfig *CategoriesConfiguration
|
||||
TopXWorkloadsFunc func(writer *os.File, TopXWorkloads []reportsummary.TopWorkload)
|
||||
}
|
||||
|
||||
var _ MainPrinter = &MainPrinterImpl{}
|
||||
|
||||
func (mpi *MainPrinterImpl) GetCompliancePrint() bool {
|
||||
return mpi.CompliancePrint
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) SetVerboseMode(value bool) {
|
||||
mpi.VerboseMode = value
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) GetVerboseMode() bool {
|
||||
return mpi.VerboseMode
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) SetSummaryPrint(value bool) {
|
||||
mpi.SummaryPrint = value
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) GetSummaryPrint() bool {
|
||||
return mpi.SummaryPrint
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) SetCompliancePrint(value bool) {
|
||||
mpi.CompliancePrint = value
|
||||
}
|
||||
|
||||
type CategoriesConfiguration struct {
|
||||
Headers []string
|
||||
ColumnsAlignments []int
|
||||
}
|
||||
|
||||
func NewCategoriesConfiguration(headers []string, columnsAlignments []int) *CategoriesConfiguration {
|
||||
return &CategoriesConfiguration{
|
||||
Headers: headers,
|
||||
ColumnsAlignments: columnsAlignments,
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *CategoriesConfiguration) GetCategoriesHeaders() []string {
|
||||
return cc.Headers
|
||||
}
|
||||
|
||||
func (cc *CategoriesConfiguration) GetCategoriesColumnsAlignments() []int {
|
||||
return cc.ColumnsAlignments
|
||||
}
|
||||
|
||||
func NewMainPrinter(scanType cautils.ScanTypes) MainPrinter {
|
||||
switch scanType {
|
||||
case cautils.ScanTypeCluster:
|
||||
return NewClusterPrinter()
|
||||
case cautils.ScanTypeRepo:
|
||||
return NewRepoPrinter()
|
||||
default:
|
||||
return NewSummaryPrinter()
|
||||
}
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) Print(writer *os.File, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
|
||||
if mpi.CategoriesConfig != nil {
|
||||
mpi.printCategories(writer, summaryDetails, sortedControlIDs)
|
||||
}
|
||||
|
||||
if mpi.GetSummaryPrint() {
|
||||
mpi.printSummary(writer, summaryDetails, sortedControlIDs)
|
||||
}
|
||||
|
||||
if mpi.TopXWorkloadsFunc != nil {
|
||||
// if number of workloads is less than 3, don't use the word "most"
|
||||
if len(summaryDetails.TopWorkloadsByScore) < 3 {
|
||||
cautils.InfoTextDisplay(writer, "Your risky workloads:\n")
|
||||
} else if len(summaryDetails.TopWorkloadsByScore) > 0 {
|
||||
cautils.InfoTextDisplay(writer, "Your most risky workloads:\n")
|
||||
}
|
||||
|
||||
mpi.TopXWorkloadsFunc(writer, summaryDetails.TopWorkloadsByScore)
|
||||
}
|
||||
|
||||
if mpi.GetCompliancePrint() {
|
||||
printComplianceScore(writer, filterComplianceFrameworks(summaryDetails.ListFrameworks()))
|
||||
}
|
||||
|
||||
if len(mpi.NextSteps) > 0 {
|
||||
mpi.printNextSteps(writer)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) printNextSteps(writer *os.File) {
|
||||
cautils.InfoTextDisplay(writer, "Follow-up Steps:\n")
|
||||
for _, ns := range mpi.NextSteps {
|
||||
cautils.InfoTextDisplay(writer, "- "+ns+"\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) printCategories(writer *os.File, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
|
||||
categoriesToControlSummariesMap := mapCategoryToControlSummaries(*summaryDetails, sortedControlIDs)
|
||||
|
||||
categoriesTable := tablewriter.NewWriter(writer)
|
||||
categoriesTable.SetHeader(mpi.CategoriesConfig.GetCategoriesHeaders())
|
||||
categoriesTable.SetHeaderLine(true)
|
||||
categoriesTable.SetColumnAlignment(mpi.CategoriesConfig.GetCategoriesColumnsAlignments())
|
||||
|
||||
for category, ctrls := range categoriesToControlSummariesMap {
|
||||
renderSingleCategory(writer, category, ctrls, categoriesTable)
|
||||
}
|
||||
}
|
||||
|
||||
func (mpi *MainPrinterImpl) printSummary(writer *os.File, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
|
||||
if summaryDetails.NumberOfControls().All() == 0 {
|
||||
fmt.Fprintf(writer, "\nKubescape did not scan any of the resources, make sure you are scanning valid kubernetes manifests (Deployments, Pods, etc.)\n")
|
||||
return
|
||||
}
|
||||
cautils.InfoTextDisplay(writer, "\n"+ControlCountersForSummary(summaryDetails.NumberOfControls())+"\n")
|
||||
cautils.InfoTextDisplay(writer, renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters())+"\n\n")
|
||||
|
||||
summaryTable := tablewriter.NewWriter(writer)
|
||||
summaryTable.SetAutoWrapText(false)
|
||||
summaryTable.SetHeader(getControlTableHeaders())
|
||||
summaryTable.SetHeaderLine(true)
|
||||
summaryTable.SetColumnAlignment(getColumnsAlignments())
|
||||
|
||||
printAll := mpi.GetVerboseMode()
|
||||
if summaryDetails.NumberOfResources().Failed() == 0 {
|
||||
// if there are no failed controls, print the resource table and detailed information
|
||||
printAll = true
|
||||
}
|
||||
|
||||
infoToPrintInfo := mapInfoToPrintInfo(summaryDetails.Controls)
|
||||
for i := len(sortedControlIDs) - 1; i >= 0; i-- {
|
||||
for _, c := range sortedControlIDs[i] {
|
||||
row := generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, c), infoToPrintInfo, printAll)
|
||||
if len(row) > 0 {
|
||||
summaryTable.Append(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
summaryTable.SetFooter(generateFooter(summaryDetails))
|
||||
|
||||
summaryTable.Render()
|
||||
|
||||
// When scanning controls the framework list will be empty
|
||||
cautils.InfoTextDisplay(writer, frameworksScoresToString(summaryDetails.ListFrameworks()))
|
||||
|
||||
printInfo(writer, infoToPrintInfo)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package prettyprinter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
)
|
||||
|
||||
func NewRepoPrinter() *MainPrinterImpl {
|
||||
printer := &MainPrinterImpl{}
|
||||
|
||||
printer.CategoriesConfig = NewCategoriesConfiguration(getCommonCategoriesTableHeaders(), getCommonColumnsAlignments())
|
||||
|
||||
printer.NextSteps = getRepoNextSteps()
|
||||
|
||||
printer.TopXWorkloadsFunc = repoTopXWorkloadsFunc
|
||||
|
||||
return printer
|
||||
}
|
||||
|
||||
func repoTopXWorkloadsFunc(writer *os.File, topXWls []reportsummary.TopWorkload) {
|
||||
for i, wl := range topXWls {
|
||||
ns := wl.Workload.GetNamespace()
|
||||
name := wl.Workload.GetName()
|
||||
kind := wl.Workload.GetKind()
|
||||
cmdPrefix := getWorkloadPrefixForCmd(ns, kind, name)
|
||||
cautils.SimpleDisplay(writer, fmt.Sprintf("%d. %s - '%s'\n", i+1, cmdPrefix, getRepoWorkloadScanCommand(ns, kind, name, wl.ResourceSource)))
|
||||
}
|
||||
|
||||
cautils.InfoTextDisplay(writer, "\n")
|
||||
}
|
||||
|
||||
func getRepoWorkloadScanCommand(ns, kind, name string, source reporthandling.Source) string {
|
||||
cmd := fmt.Sprintf("$ kubescape scan workload %s/%s/%s", ns, kind, name)
|
||||
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)
|
||||
|
||||
} else {
|
||||
return fmt.Sprintf("%s --file-path=%s", cmd, source.RelativePath)
|
||||
}
|
||||
}
|
||||
|
||||
func getRepoNextSteps() []string {
|
||||
return []string{
|
||||
"cluster scan run: '$ kubescape scan cluster'",
|
||||
"CICD setup: <link to docs>",
|
||||
"continue monitoring install helm: <docs to helm>",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package prettyprinter
|
||||
|
||||
const (
|
||||
summaryColumnSeverity = iota
|
||||
summaryColumnName = iota
|
||||
summaryColumnCounterFailed = iota
|
||||
summaryColumnCounterAll = iota
|
||||
summaryColumnComplianceScore = iota
|
||||
_summaryRowLen = iota
|
||||
)
|
||||
|
||||
func NewSummaryPrinter() *MainPrinterImpl {
|
||||
printer := &MainPrinterImpl{}
|
||||
printer.SetSummaryPrint(true)
|
||||
|
||||
return printer
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
package prettyprinter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"k8s.io/utils/strings/slices"
|
||||
)
|
||||
|
||||
const (
|
||||
categoriesColumnSeverity = iota
|
||||
categoriesColumnName = iota
|
||||
categoriesColumnFailed = iota
|
||||
categoriesColumnNextSteps = iota
|
||||
)
|
||||
|
||||
var (
|
||||
mapScanTypeToOutput = map[cautils.ScanTypes]string{
|
||||
cautils.ScanTypeCluster: "Security Overview",
|
||||
}
|
||||
complianceFrameworks = []string{"nsa", "mitre"}
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
for j := range ctrl.GetCategories() {
|
||||
categories[ctrl.GetCategories()[j]] = append(categories[ctrl.GetCategories()[j]], ctrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return categories
|
||||
}
|
||||
|
||||
func 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()
|
||||
}
|
||||
|
||||
row[categoriesColumnFailed] = fmt.Sprintf("%s", controlSummary.GetStatus().Status())
|
||||
row[categoriesColumnNextSteps] = generateNextSteps(controlSummary)
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
func generateNextSteps(controlSummary reportsummary.IControlSummary) string {
|
||||
return fmt.Sprintf("$ kubescape scan control %s", controlSummary.GetID())
|
||||
}
|
||||
|
||||
func getCategoriesTableHeaders() []string {
|
||||
headers := make([]string, 4)
|
||||
headers[categoriesColumnSeverity] = "SEVERITY"
|
||||
headers[categoriesColumnName] = "CONTROL NAME"
|
||||
headers[categoriesColumnFailed] = "FAILED RESOURCES"
|
||||
headers[categoriesColumnNextSteps] = "NEXT STEPS"
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func getCategoriesColumnsAlignments() []int {
|
||||
alignments := make([]int, 4)
|
||||
alignments[categoriesColumnSeverity] = tablewriter.ALIGN_LEFT
|
||||
alignments[categoriesColumnName] = tablewriter.ALIGN_LEFT
|
||||
alignments[categoriesColumnFailed] = tablewriter.ALIGN_CENTER
|
||||
alignments[categoriesColumnNextSteps] = tablewriter.ALIGN_LEFT
|
||||
|
||||
return alignments
|
||||
}
|
||||
|
||||
func renderSingleCategory(writer *os.File, category string, ctrls []reportsummary.IControlSummary, categoriesTable *tablewriter.Table) {
|
||||
cautils.InfoTextDisplay(writer, "\n"+category+"\n")
|
||||
|
||||
categoriesTable.ClearRows()
|
||||
for i := range ctrls {
|
||||
row := generateCategoriesRow(ctrls[i])
|
||||
if len(row) > 0 {
|
||||
categoriesTable.Append(row)
|
||||
}
|
||||
}
|
||||
categoriesTable.Render()
|
||||
}
|
||||
|
||||
func getSeverityColumn(controlSummary reportsummary.IControlSummary) string {
|
||||
return color.New(getColor(apis.ControlSeverityToInt(controlSummary.GetScoreFactor())), color.Bold).SprintFunc()(apis.ControlSeverityToString(controlSummary.GetScoreFactor()))
|
||||
}
|
||||
|
||||
func getColor(controlSeverity int) color.Attribute {
|
||||
switch controlSeverity {
|
||||
case apis.SeverityCritical:
|
||||
return color.FgRed
|
||||
case apis.SeverityHigh:
|
||||
return color.FgYellow
|
||||
case apis.SeverityMedium:
|
||||
return color.FgCyan
|
||||
case apis.SeverityLow:
|
||||
return color.FgWhite
|
||||
default:
|
||||
return color.FgWhite
|
||||
}
|
||||
}
|
||||
|
||||
type infoStars struct {
|
||||
stars string
|
||||
info string
|
||||
}
|
||||
|
||||
func mapInfoToPrintInfo(controls reportsummary.ControlSummaries) []infoStars {
|
||||
infoToPrintInfo := []infoStars{}
|
||||
infoToPrintInfoMap := map[string]interface{}{}
|
||||
starCount := "*"
|
||||
for _, control := range controls {
|
||||
if control.GetStatus().IsSkipped() && control.GetStatus().Info() != "" {
|
||||
if _, ok := infoToPrintInfoMap[control.GetStatus().Info()]; !ok {
|
||||
infoToPrintInfo = append(infoToPrintInfo, infoStars{
|
||||
info: control.GetStatus().Info(),
|
||||
stars: starCount,
|
||||
})
|
||||
starCount += "*"
|
||||
infoToPrintInfoMap[control.GetStatus().Info()] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return infoToPrintInfo
|
||||
}
|
||||
|
||||
func printInfo(writer *os.File, infoToPrintInfo []infoStars) {
|
||||
fmt.Println()
|
||||
for i := range infoToPrintInfo {
|
||||
cautils.InfoDisplay(writer, fmt.Sprintf("%s %s\n", infoToPrintInfo[i].stars, infoToPrintInfo[i].info))
|
||||
}
|
||||
}
|
||||
func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) string {
|
||||
if len(frameworks) == 1 {
|
||||
if frameworks[0].GetName() != "" {
|
||||
return fmt.Sprintf("FRAMEWORK %s\n", frameworks[0].GetName())
|
||||
// cautils.InfoTextDisplay(prettyPrinter.writer, ))
|
||||
}
|
||||
} else if len(frameworks) > 1 {
|
||||
p := "FRAMEWORKS: "
|
||||
i := 0
|
||||
for ; i < len(frameworks)-1; i++ {
|
||||
p += fmt.Sprintf("%s (compliance: %.2f), ", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
}
|
||||
p += fmt.Sprintf("%s (compliance: %.2f)\n", frameworks[i].GetName(), frameworks[i].GetComplianceScore())
|
||||
return p
|
||||
}
|
||||
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.InfoTextDisplay(writer, "\n")
|
||||
}
|
||||
|
||||
func filterComplianceFrameworks(frameworks []reportsummary.IFrameworkSummary) []reportsummary.IFrameworkSummary {
|
||||
complianceFws := []reportsummary.IFrameworkSummary{}
|
||||
for _, fw := range frameworks {
|
||||
if slices.Contains(complianceFrameworks, strings.ToLower(fw.GetName())) {
|
||||
complianceFws = append(complianceFws, fw)
|
||||
}
|
||||
}
|
||||
return complianceFws
|
||||
}
|
||||
|
||||
func getSeparator(sep string) string {
|
||||
s := ""
|
||||
for i := 0; i < 80; i++ {
|
||||
s += sep
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func generateFooter(summaryDetails *reportsummary.SummaryDetails) []string {
|
||||
// Severity | Control name | failed resources | all resources | % success
|
||||
row := make([]string, _summaryRowLen)
|
||||
row[summaryColumnName] = "Resource Summary"
|
||||
row[summaryColumnCounterFailed] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Failed())
|
||||
row[summaryColumnCounterAll] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().All())
|
||||
row[summaryColumnSeverity] = " "
|
||||
row[summaryColumnComplianceScore] = fmt.Sprintf("%.2f%s", summaryDetails.ComplianceScore, "%")
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
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 ControlCountersForResource(l *helpersv1.AllLists) string {
|
||||
return fmt.Sprintf("Controls: %d (Failed: %d, action required: %d)", l.Len(), l.Failed(), l.Skipped())
|
||||
}
|
||||
|
||||
// renderSeverityCountersSummary renders the string that reports severity counters summary
|
||||
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) string {
|
||||
critical := counters.NumberOfCriticalSeverity()
|
||||
high := counters.NumberOfHighSeverity()
|
||||
medium := counters.NumberOfMediumSeverity()
|
||||
low := counters.NumberOfLowSeverity()
|
||||
|
||||
return fmt.Sprintf(
|
||||
"Failed Resources by Severity: Critical — %d, High — %d, Medium — %d, Low — %d",
|
||||
critical, high, medium, low,
|
||||
)
|
||||
}
|
||||
|
||||
func getControlTableHeaders() []string {
|
||||
headers := make([]string, _summaryRowLen)
|
||||
headers[summaryColumnName] = "CONTROL NAME"
|
||||
headers[summaryColumnCounterFailed] = "FAILED RESOURCES"
|
||||
headers[summaryColumnCounterAll] = "ALL RESOURCES"
|
||||
headers[summaryColumnSeverity] = "SEVERITY"
|
||||
headers[summaryColumnComplianceScore] = "% COMPLIANCE-SCORE"
|
||||
return headers
|
||||
}
|
||||
|
||||
func getColumnsAlignments() []int {
|
||||
alignments := make([]int, _summaryRowLen)
|
||||
alignments[summaryColumnName] = tablewriter.ALIGN_LEFT
|
||||
alignments[summaryColumnCounterFailed] = tablewriter.ALIGN_CENTER
|
||||
alignments[summaryColumnCounterAll] = tablewriter.ALIGN_CENTER
|
||||
alignments[summaryColumnSeverity] = tablewriter.ALIGN_LEFT
|
||||
alignments[summaryColumnComplianceScore] = tablewriter.ALIGN_CENTER
|
||||
return alignments
|
||||
}
|
||||
|
||||
func generateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []infoStars, verbose bool) []string {
|
||||
row := make([]string, _summaryRowLen)
|
||||
|
||||
// ignore passed results
|
||||
if !verbose && (controlSummary.GetStatus().IsPassed()) {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
row[summaryColumnSeverity] = getSeverityColumn(controlSummary)
|
||||
if len(controlSummary.GetName()) > 50 {
|
||||
row[summaryColumnName] = controlSummary.GetName()[:50] + "..."
|
||||
} else {
|
||||
row[summaryColumnName] = controlSummary.GetName()
|
||||
}
|
||||
row[summaryColumnCounterFailed] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
|
||||
row[summaryColumnCounterAll] = fmt.Sprintf("%d", controlSummary.NumberOfResources().All())
|
||||
row[summaryColumnComplianceScore] = getComplianceScoreColumn(controlSummary, infoToPrintInfo)
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
func getComplianceScoreColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo []infoStars) string {
|
||||
if controlSummary.GetStatus().IsSkipped() {
|
||||
return fmt.Sprintf("%s %s", "Action Required", getInfoColumn(controlSummary, infoToPrintInfo))
|
||||
}
|
||||
return fmt.Sprintf("%d", cautils.Float32ToInt(controlSummary.GetComplianceScore())) + "%"
|
||||
}
|
||||
|
||||
func getInfoColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo []infoStars) string {
|
||||
for i := range infoToPrintInfo {
|
||||
if infoToPrintInfo[i].info == controlSummary.GetStatus().Info() {
|
||||
return infoToPrintInfo[i].stars
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getWorkloadPrefixForCmd(namespace, kind, name string) string {
|
||||
if namespace == "" {
|
||||
return fmt.Sprintf("name: %s, kind: %s", name, kind)
|
||||
}
|
||||
return fmt.Sprintf("namespace: %s, name: %s, kind: %s", namespace, name, kind)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2/prettyprinter"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
@@ -40,7 +41,7 @@ func (prettyPrinter *PrettyPrinter) resourceTable(opaSessionObj *cautils.OPASess
|
||||
if resource.GetNamespace() != "" {
|
||||
fmt.Fprintf(prettyPrinter.writer, "Namespace: %s\n", resource.GetNamespace())
|
||||
}
|
||||
fmt.Fprintf(prettyPrinter.writer, "\n"+controlCountersForResource(result.ListControlsIDs(nil))+"\n\n")
|
||||
fmt.Fprintf(prettyPrinter.writer, "\n"+prettyprinter.ControlCountersForResource(result.ListControlsIDs(nil))+"\n\n")
|
||||
|
||||
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
|
||||
summaryTable.SetAutoWrapText(true)
|
||||
|
||||
@@ -82,13 +82,3 @@ func finalizeResources(results []resourcesresults.Result, allResources map[strin
|
||||
}
|
||||
return resources
|
||||
}
|
||||
|
||||
func mapCategoryToControlSummaries(summaryDetails reportsummary.SummaryDetails) map[string][]reportsummary.IControlSummary {
|
||||
categories := map[string][]reportsummary.IControlSummary{}
|
||||
for i := range summaryDetails.Controls {
|
||||
for j := range summaryDetails.Controls[i].Categories {
|
||||
categories[summaryDetails.Controls[i].Categories[j]] = append(categories[summaryDetails.Controls[i].Categories[j]], summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, summaryDetails.Controls[i].ControlID))
|
||||
}
|
||||
}
|
||||
return categories
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user