replace olekukonko/tablewriter with jedib0t/go-pretty

Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
This commit is contained in:
Matthias Bertschy
2025-08-28 18:43:08 +02:00
parent 07eda20b88
commit ca66ccb33d
25 changed files with 392 additions and 482 deletions

View File

@@ -7,14 +7,13 @@ import (
"sort"
"strings"
"github.com/jwalton/gchalk"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/core/cautils"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
v2 "github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/maruel/natural"
"github.com/olekukonko/tablewriter"
)
var listFunc = map[string]func(context.Context, *metav1.ListPolicies) ([]string, error){
@@ -100,30 +99,19 @@ func prettyPrintListFormat(ctx context.Context, targetPolicy string, policies []
return
}
policyTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
policyTable := table.NewWriter()
policyTable.SetOutputMirror(printer.GetWriter(ctx, ""))
policyTable.SetAutoWrapText(true)
header := fmt.Sprintf("Supported %s", targetPolicy)
policyTable.SetHeader([]string{header})
policyTable.SetHeaderLine(true)
policyTable.SetRowLine(true)
policyTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
policyTable.SetAutoFormatHeaders(false)
policyTable.SetAlignment(tablewriter.ALIGN_CENTER)
policyTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
data := v2.Matrix{}
policyTable.AppendHeader(table.Row{header})
policyTable.Style().Options.SeparateHeader = true
policyTable.Style().Options.SeparateRows = true
policyTable.Style().Format.HeaderAlign = text.AlignLeft
policyTable.Style().Format.Header = text.FormatDefault
policyTable.Style().Format.RowAlign = text.AlignCenter
policyTable.Style().Box = table.StyleBoxRounded
controlRows := generatePolicyRows(policies)
var headerColors []tablewriter.Colors
for range controlRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
policyTable.SetHeaderColor(headerColors...)
data = append(data, controlRows...)
policyTable.AppendBulk(data)
policyTable.AppendRows(generatePolicyRows(policies))
policyTable.Render()
}
@@ -134,40 +122,32 @@ func jsonListFormat(_ context.Context, _ string, policies []string) {
}
func prettyPrintControls(ctx context.Context, policies []string) {
controlsTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
controlsTable := table.NewWriter()
controlsTable.SetOutputMirror(printer.GetWriter(ctx, ""))
controlsTable.SetAutoWrapText(false)
controlsTable.SetHeaderLine(true)
controlsTable.SetRowLine(true)
controlsTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
controlsTable.SetAutoFormatHeaders(false)
controlsTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
controlsTable.Style().Options.SeparateHeader = true
controlsTable.Style().Options.SeparateRows = true
controlsTable.Style().Format.HeaderAlign = text.AlignLeft
controlsTable.Style().Format.Header = text.FormatDefault
controlsTable.Style().Box = table.StyleBoxRounded
controlsTable.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}})
controlRows := generateControlRows(policies)
short := utils.CheckShortTerminalWidth(controlRows, []string{"Control ID", "Control name", "Docs", "Frameworks"})
short := utils.CheckShortTerminalWidth(controlRows, table.Row{"Control ID", "Control name", "Docs", "Frameworks"})
if short {
controlsTable.SetAutoWrapText(false)
controlsTable.SetHeader([]string{"Controls"})
controlsTable.AppendHeader(table.Row{"Controls"})
controlRows = shortFormatControlRows(controlRows)
} else {
controlsTable.SetHeader([]string{"Control ID", "Control name", "Docs", "Frameworks"})
controlsTable.AppendHeader(table.Row{"Control ID", "Control name", "Docs", "Frameworks"})
}
var headerColors []tablewriter.Colors
for range controlRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
controlsTable.SetHeaderColor(headerColors...)
data := v2.Matrix{}
data = append(data, controlRows...)
controlsTable.AppendBulk(data)
controlsTable.AppendRows(controlRows)
controlsTable.Render()
}
func generateControlRows(policies []string) [][]string {
rows := [][]string{}
func generateControlRows(policies []string) []table.Row {
rows := make([]table.Row, 0, len(policies))
for _, control := range policies {
@@ -188,7 +168,7 @@ func generateControlRows(policies []string) [][]string {
docs := cautils.GetControlLink(id)
currentRow := []string{id, control, docs, strings.Replace(framework, " ", "\n", -1)}
currentRow := table.Row{id, control, docs, strings.Replace(framework, " ", "\n", -1)}
rows = append(rows, currentRow)
}
@@ -196,20 +176,19 @@ func generateControlRows(policies []string) [][]string {
return rows
}
func generatePolicyRows(policies []string) [][]string {
rows := [][]string{}
func generatePolicyRows(policies []string) []table.Row {
rows := make([]table.Row, 0, len(policies))
for _, policy := range policies {
currentRow := []string{policy}
rows = append(rows, currentRow)
rows = append(rows, table.Row{policy})
}
return rows
}
func shortFormatControlRows(controlRows [][]string) [][]string {
rows := [][]string{}
func shortFormatControlRows(controlRows []table.Row) []table.Row {
rows := make([]table.Row, 0, len(controlRows))
for _, controlRow := range controlRows {
rows = append(rows, []string{fmt.Sprintf("Control ID"+strings.Repeat(" ", 3)+": %+v\nControl Name"+strings.Repeat(" ", 1)+": %+v\nDocs"+strings.Repeat(" ", 9)+": %+v\nFrameworks"+strings.Repeat(" ", 3)+": %+v", controlRow[0], controlRow[1], controlRow[2], strings.Replace(controlRow[3], "\n", " ", -1))})
rows = append(rows, table.Row{fmt.Sprintf("Control ID"+strings.Repeat(" ", 3)+": %+v\nControl Name"+strings.Repeat(" ", 1)+": %+v\nDocs"+strings.Repeat(" ", 9)+": %+v\nFrameworks"+strings.Repeat(" ", 3)+": %+v", controlRow[0], controlRow[1], controlRow[2], strings.Replace(controlRow[3].(string), "\n", " ", -1))})
}
return rows
}

View File

@@ -9,6 +9,7 @@ import (
"sort"
"testing"
"github.com/jedib0t/go-pretty/v6/table"
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
"github.com/stretchr/testify/assert"
)
@@ -105,7 +106,7 @@ func TestGeneratePolicyRows_NonEmptyPolicyList(t *testing.T) {
result := generatePolicyRows(policies)
// Assert
assert.Equal(t, [][]string{{"policy1"}, {"policy2"}, {"policy3"}}, result)
assert.Equal(t, []table.Row{{"policy1"}, {"policy2"}, {"policy3"}}, result)
}
// Returns an empty 2D slice for an empty list of policies.
@@ -122,12 +123,12 @@ func TestGeneratePolicyRows_EmptyPolicyList(t *testing.T) {
// The function returns a list of rows, each containing a formatted string with control ID, control name, docs, and frameworks.
func TestShortFormatControlRows_ReturnsListOfRowsWithFormattedString(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"ID1", "Control 1", "Docs 1", "Framework 1"},
{"ID2", "Control 2", "Docs 2", "Framework 2"},
}
want := [][]string{
want := []table.Row{
{"Control ID : ID1\nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
{"Control ID : ID2\nControl Name : Control 2\nDocs : Docs 2\nFrameworks : Framework 2"},
}
@@ -139,12 +140,12 @@ func TestShortFormatControlRows_ReturnsListOfRowsWithFormattedString(t *testing.
// The function formats the control rows correctly, replacing newlines in the frameworks column with line breaks.
func TestShortFormatControlRows_FormatsControlRowsCorrectly(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"ID1", "Control 1", "Docs 1", "Framework\n1"},
{"ID2", "Control 2", "Docs 2", "Framework\n2"},
}
want := [][]string{
want := []table.Row{
{"Control ID : ID1\nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
{"Control ID : ID2\nControl Name : Control 2\nDocs : Docs 2\nFrameworks : Framework 2"},
}
@@ -156,11 +157,11 @@ func TestShortFormatControlRows_FormatsControlRowsCorrectly(t *testing.T) {
// The function handles a control row with an empty control ID.
func TestShortFormatControlRows_HandlesControlRowWithEmptyControlID(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"", "Control 1", "Docs 1", "Framework 1"},
}
want := [][]string{
want := []table.Row{
{"Control ID : \nControl Name : Control 1\nDocs : Docs 1\nFrameworks : Framework 1"},
}
@@ -171,11 +172,11 @@ func TestShortFormatControlRows_HandlesControlRowWithEmptyControlID(t *testing.T
// The function handles a control row with an empty control name.
func TestShortFormatControlRows_HandlesControlRowWithEmptyControlName(t *testing.T) {
controlRows := [][]string{
controlRows := []table.Row{
{"ID1", "", "Docs 1", "Framework 1"},
}
want := [][]string{
want := []table.Row{
{"Control ID : ID1\nControl Name : \nDocs : Docs 1\nFrameworks : Framework 1"},
}
@@ -192,7 +193,7 @@ func TestGenerateControlRowsWithAllFields(t *testing.T) {
"3|Control 3|Framework 3",
}
want := [][]string{
want := []table.Row{
{"1", "Control 1", "https://hub.armosec.io/docs/1", "Framework\n1"},
{"2", "Control 2", "https://hub.armosec.io/docs/2", "Framework\n2"},
{"3", "Control 3", "https://hub.armosec.io/docs/3", "Framework\n3"},
@@ -215,7 +216,7 @@ func TestGenerateControlRowsHandlesPoliciesWithEmptyStringOrNoPipesOrOnePipeMiss
"5|Control 5||Extra 5",
}
expectedRows := [][]string{
expectedRows := []table.Row{
{"", "", "https://hub.armosec.io/docs/", ""},
{"1", "", "https://hub.armosec.io/docs/1", ""},
{"2", "Control 2", "https://hub.armosec.io/docs/2", "Framework\n2"},
@@ -252,18 +253,18 @@ func TestGenerateTableWithCorrectHeadersAndRows(t *testing.T) {
os.Stdout = rescueStdout
// got := buf.String()
want := `────────────┬──────────────┬───────────────────────────────┬────────────
want := `────────────┬──────────────┬───────────────────────────────┬────────────
│ Control ID │ Control name │ Docs │ Frameworks │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 1 │ Control 1 │ https://hub.armosec.io/docs/1 │ Framework │
│ │ │ │ 1
│ │ │ │ 1
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 2 │ Control 2 │ https://hub.armosec.io/docs/2 │ Framework │
│ │ │ │ 2
│ │ │ │ 2
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 3 │ Control 3 │ https://hub.armosec.io/docs/3 │ Framework │
│ │ │ │ 3
────────────┴──────────────┴───────────────────────────────┴────────────
│ │ │ │ 3
────────────┴──────────────┴───────────────────────────────┴────────────
`
assert.Equal(t, want, string(got))
@@ -294,7 +295,7 @@ func TestGenerateTableWithMalformedPoliciesAndPrettyPrintHeadersAndRows(t *testi
os.Stdout = rescueStdout
want := `────────────┬──────────────┬───────────────────────────────┬────────────
want := `────────────┬──────────────┬───────────────────────────────┬────────────
│ Control ID │ Control name │ Docs │ Frameworks │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ │ │ https://hub.armosec.io/docs/ │ │
@@ -302,18 +303,18 @@ func TestGenerateTableWithMalformedPoliciesAndPrettyPrintHeadersAndRows(t *testi
│ 1 │ │ https://hub.armosec.io/docs/1 │ │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 2 │ Control 2 │ https://hub.armosec.io/docs/2 │ Framework │
│ │ │ │ 2
│ │ │ │ 2
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 3 │ Control 3 │ https://hub.armosec.io/docs/3 │ Framework │
│ │ │ │ 3
│ │ │ │ 3
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 4 │ │ https://hub.armosec.io/docs/4 │ Framework │
│ │ │ │ 4
│ │ │ │ 4
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ │ │ https://hub.armosec.io/docs/ │ │
├────────────┼──────────────┼───────────────────────────────┼────────────┤
│ 5 │ Control 5 │ https://hub.armosec.io/docs/5 │ │
────────────┴──────────────┴───────────────────────────────┴────────────
────────────┴──────────────┴───────────────────────────────┴────────────
`
assert.Equal(t, want, string(got))

View File

@@ -9,7 +9,7 @@ const (
emptySpace = " "
middleItem = "├── "
continueItem = "│ "
lastItem = "── "
lastItem = "── "
)
type (
@@ -66,7 +66,7 @@ func (t *tree) Items() []Tree {
return t.items
}
// Print returns an visual representation of the tree
// Print returns a visual representation of the tree
func (t *tree) Print() string {
return newPrinter().Print(t)
}

View File

@@ -31,7 +31,7 @@ func TestTreePrint(t *testing.T) {
tree: SimpleTreeMock(),
want: "root\n" +
"├── child1\n" +
"── child2\n",
"── child2\n",
},
{
name: "SimpleTreeWithLinesMock",
@@ -42,36 +42,36 @@ func TestTreePrint(t *testing.T) {
"├── child3\n" +
"│ Line2\n" +
"│ Line3\n" +
"── child4\n",
"── child4\n",
},
{
name: "SubTreeMock1",
tree: SubTreeMock1(),
want: "root\n" +
"── child1\n" +
" ── child1.1\n",
"── child1\n" +
" ── child1.1\n",
},
{
name: "SubTreeMock2",
tree: SubTreeMock2(),
want: "root\n" +
"├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n",
"── child3\n" +
" ── child3.1\n",
},
{
name: "SubTreeWithLinesMock",
tree: SubTreeWithLinesMock(),
want: "root\n" +
"├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"│ Line2\n" +
"│ Line3\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n" +
"── child3\n" +
" ── child3.1\n" +
" Line2\n" +
" Line3\n",
},
@@ -85,8 +85,8 @@ func TestTreePrint(t *testing.T) {
}
func TestPrintText_LastTree(t *testing.T) {
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "── Root\n ├── Child1\n ── Child2\n"
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "── Root\n ├── Child1\n ── Child2\n"
result := p.printText(inputText, []bool{}, true)
@@ -94,8 +94,8 @@ func TestPrintText_LastTree(t *testing.T) {
}
func TestPrintText_NotLastTree(t *testing.T) {
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "├── Root\n│ ├── Child1\n│ ── Child2\n"
inputText := "Root\n├── Child1\n── Child2"
expectedOutput := "├── Root\n│ ├── Child1\n│ ── Child2\n"
result := p.printText(inputText, []bool{}, false)
@@ -122,7 +122,7 @@ func Test_printer_printItems(t *testing.T) {
name: "SimpleTreeMock",
tree: SimpleTreeMock(),
want: "├── child1\n" +
"── child2\n",
"── child2\n",
},
{
name: "SimpleTreeWithLinesMock",
@@ -132,33 +132,33 @@ func Test_printer_printItems(t *testing.T) {
"├── child3\n" +
"│ Line2\n" +
"│ Line3\n" +
"── child4\n",
"── child4\n",
},
{
name: "SubTreeMock1",
tree: SubTreeMock1(),
want: "── child1\n" +
" ── child1.1\n",
want: "── child1\n" +
" ── child1.1\n",
},
{
name: "SubTreeMock2",
tree: SubTreeMock2(),
want: "├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n",
"── child3\n" +
" ── child3.1\n",
},
{
name: "SubTreeWithLinesMock",
tree: SubTreeWithLinesMock(),
want: "├── child1\n" +
"│ ── child1.1\n" +
"│ ── child1.1\n" +
"│ Line2\n" +
"│ Line3\n" +
"├── child2\n" +
"── child3\n" +
" ── child3.1\n" +
"── child3\n" +
" ── child3.1\n" +
" Line2\n" +
" Line3\n",
},

View File

@@ -10,6 +10,8 @@ import (
"github.com/anchore/clio"
"github.com/anchore/grype/grype/presenter/models"
"github.com/enescakir/emoji"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/jwalton/gchalk"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
@@ -21,7 +23,6 @@ import (
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
"k8s.io/utils/strings/slices"
)
@@ -173,20 +174,20 @@ func (pp *PrettyPrinter) printHeader(opaSessionObj *cautils.OPASessionObj) {
} else if pp.scanType == cautils.ScanTypeWorkload {
cautils.InfoDisplay(pp.writer, "Workload security posture overview for:\n")
ns := opaSessionObj.SingleResourceScan.GetNamespace()
rows := [][]string{}
var rows []table.Row
if ns != "" {
rows = append(rows, []string{"Namespace", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetNamespace())})
rows = append(rows, table.Row{"Namespace", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetNamespace())})
}
rows = append(rows, []string{"Kind", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetKind())})
rows = append(rows, []string{"Name", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetName())})
rows = append(rows, table.Row{"Kind", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetKind())})
rows = append(rows, table.Row{"Name", gchalk.WithBrightWhite().Bold(opaSessionObj.SingleResourceScan.GetName())})
table := tablewriter.NewWriter(pp.writer)
tableWriter := table.NewWriter()
tableWriter.SetOutputMirror(pp.writer)
table.SetColumnAlignment([]int{tablewriter.ALIGN_RIGHT, tablewriter.ALIGN_LEFT})
table.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
table.AppendBulk(rows)
tableWriter.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}, {Number: 2, Align: text.AlignLeft}})
tableWriter.AppendRows(rows)
table.Render()
tableWriter.Render()
cautils.SimpleDisplay(pp.writer, "\nIn this overview, Kubescape shows you a summary of the security posture of a workload, including key controls that apply to its configuration, and the vulnerability status of the container image.\n\n\n")
}
@@ -208,7 +209,7 @@ func (pp *PrettyPrinter) SetWriter(ctx context.Context, outputFile string) {
pp.SetMainPrinter()
}
func (pp *PrettyPrinter) Score(score float32) {
func (pp *PrettyPrinter) Score(_ float32) {
}
func (pp *PrettyPrinter) printResults(controls *reportsummary.ControlSummaries, allResources map[string]workloadinterface.IMetadata, sortedControlIDs [][]string) {
@@ -217,12 +218,12 @@ func (pp *PrettyPrinter) printResults(controls *reportsummary.ControlSummaries,
controlSummary := controls.GetControl(reportsummary.EControlCriteriaID, c) // summaryDetails.Controls ListControls().All() Controls.GetControl(ca)
pp.printTitle(controlSummary)
pp.printResources(controlSummary, allResources)
pp.printSummary(c, controlSummary)
pp.printSummary(controlSummary)
}
}
}
func (prettyPrinter *PrettyPrinter) printSummary(controlName string, controlSummary reportsummary.IControlSummary) {
func (prettyPrinter *PrettyPrinter) printSummary(controlSummary reportsummary.IControlSummary) {
cautils.SimpleDisplay(prettyPrinter.writer, "Summary - ")
cautils.SuccessDisplay(prettyPrinter.writer, "Passed:%v ", controlSummary.NumberOfResources().Passed())
cautils.WarningDisplay(prettyPrinter.writer, "Action Required:%v ", controlSummary.NumberOfResources().Skipped())

View File

@@ -3,11 +3,11 @@ package configurationprinter
import (
"io"
"github.com/jwalton/gchalk"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
const (
@@ -21,15 +21,15 @@ const (
)
// initializes the table headers and column alignments based on the category type
func initCategoryTableData(categoryType CategoryType) ([]string, []int) {
func initCategoryTableData(categoryType CategoryType) (table.Row, []table.ColumnConfig) {
if categoryType == TypeCounting {
return getCategoryCountingTypeHeaders(), getCountingTypeAlignments()
}
return getCategoryStatusTypeHeaders(), getStatusTypeAlignments()
}
func getCategoryStatusTypeHeaders() []string {
headers := make([]string, 3)
func getCategoryStatusTypeHeaders() table.Row {
headers := make(table.Row, 3)
headers[0] = statusHeader
headers[1] = controlNameHeader
headers[2] = docsHeader
@@ -37,8 +37,8 @@ func getCategoryStatusTypeHeaders() []string {
return headers
}
func getCategoryCountingTypeHeaders() []string {
headers := make([]string, 3)
func getCategoryCountingTypeHeaders() table.Row {
headers := make(table.Row, 3)
headers[0] = controlNameHeader
headers[1] = resourcesHeader
headers[2] = runHeader
@@ -46,16 +46,16 @@ func getCategoryCountingTypeHeaders() []string {
return headers
}
func getStatusTypeAlignments() []int {
return []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER}
func getStatusTypeAlignments() []table.ColumnConfig {
return []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}}
}
func getCountingTypeAlignments() []int {
return []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT}
func getCountingTypeAlignments() []table.ColumnConfig {
return []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}}
}
// returns a row for status type table based on the control summary
func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) []string {
func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary) table.Row {
// show only passed, failed and action required controls
status := controlSummary.GetStatus()
@@ -63,7 +63,7 @@ func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary, inf
return nil
}
rows := make([]string, 3)
rows := make(table.Row, 3)
rows[0] = utils.GetStatusIcon(controlSummary.GetStatus().Status())
@@ -80,31 +80,26 @@ func generateCategoryStatusRow(controlSummary reportsummary.IControlSummary, inf
}
func getCategoryTableWriter(writer io.Writer, headers []string, columnAligments []int) *tablewriter.Table {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAutoFormatHeaders(false)
table.SetColumnAlignment(columnAligments)
table.SetAutoWrapText(false)
table.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
var headerColors []tablewriter.Colors
for range headers {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.FgHiYellowColor})
}
table.SetHeaderColor(headerColors...)
return table
func getCategoryTableWriter(writer io.Writer, headers table.Row, columnAlignments []table.ColumnConfig) table.Writer {
tableWriter := table.NewWriter()
tableWriter.SetOutputMirror(writer)
tableWriter.AppendHeader(headers)
tableWriter.Style().Options.SeparateHeader = true
tableWriter.Style().Format.HeaderAlign = text.AlignLeft
tableWriter.Style().Format.Header = text.FormatDefault
tableWriter.SetColumnConfigs(columnAlignments)
tableWriter.Style().Box = table.StyleBoxRounded
return tableWriter
}
func renderSingleCategory(writer io.Writer, categoryName string, table *tablewriter.Table, rows [][]string, infoToPrintInfo []utils.InfoStars) {
func renderSingleCategory(writer io.Writer, categoryName string, tableWriter table.Writer, rows []table.Row, infoToPrintInfo []utils.InfoStars) {
cautils.InfoDisplay(writer, categoryName+"\n")
table.ClearRows()
table.AppendBulk(rows)
tableWriter.ResetRows()
tableWriter.AppendRows(rows)
table.Render()
tableWriter.Render()
if len(infoToPrintInfo) > 0 {
printCategoryInfo(writer, infoToPrintInfo)

View File

@@ -3,13 +3,13 @@ package configurationprinter
import (
"io"
"os"
"reflect"
"testing"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/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"
"github.com/stretchr/testify/assert"
)
@@ -17,20 +17,20 @@ func TestInitCategoryTableData(t *testing.T) {
tests := []struct {
name string
categoryType CategoryType
expectedHeaders []string
expectedAlignments []int
expectedHeaders table.Row
expectedAlignments []table.ColumnConfig
}{
{
name: "Test1",
categoryType: TypeCounting,
expectedHeaders: []string{"Control name", "Resources", "View details"},
expectedAlignments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
expectedHeaders: table.Row{"Control name", "Resources", "View details"},
expectedAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
},
{
name: "Test2",
categoryType: TypeStatus,
expectedHeaders: []string{"", "Control name", "Docs"},
expectedAlignments: []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER},
expectedHeaders: table.Row{"", "Control name", "Docs"},
expectedAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}},
},
}
for _, tt := range tests {
@@ -42,8 +42,8 @@ func TestInitCategoryTableData(t *testing.T) {
if len(alignments) != len(tt.expectedAlignments) {
t.Errorf("initCategoryTableData() alignments = %v, want %v", alignments, tt.expectedAlignments)
}
assert.True(t, reflect.DeepEqual(headers, tt.expectedHeaders))
assert.True(t, reflect.DeepEqual(alignments, tt.expectedAlignments))
assert.Equal(t, headers, tt.expectedHeaders)
assert.Equal(t, alignments, tt.expectedAlignments)
})
}
}
@@ -88,52 +88,12 @@ func TestGetCategoryCountingTypeHeaders(t *testing.T) {
}
}
func TestGetStatusTypeAlignments(t *testing.T) {
alignments := getStatusTypeAlignments()
if len(alignments) != 3 {
t.Errorf("Expected 3 alignments, got %d", len(alignments))
}
if alignments[0] != tablewriter.ALIGN_CENTER {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_CENTER, alignments[0])
}
if alignments[1] != tablewriter.ALIGN_LEFT {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_LEFT, alignments[1])
}
if alignments[2] != tablewriter.ALIGN_CENTER {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_CENTER, alignments[2])
}
}
func TestGetCountingTypeAlignments(t *testing.T) {
alignments := getCountingTypeAlignments()
if len(alignments) != 3 {
t.Errorf("Expected 3 alignments, got %d", len(alignments))
}
if alignments[0] != tablewriter.ALIGN_LEFT {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_LEFT, alignments[0])
}
if alignments[1] != tablewriter.ALIGN_CENTER {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_CENTER, alignments[1])
}
if alignments[2] != tablewriter.ALIGN_LEFT {
t.Errorf("Expected %d, got %d", tablewriter.ALIGN_LEFT, alignments[2])
}
}
func TestGenerateCategoryStatusRow(t *testing.T) {
tests := []struct {
name string
controlSummary reportsummary.IControlSummary
infoToPrintInfo []utils.InfoStars
expectedRows []string
expectedRows table.Row
}{
{
name: "failed control",
@@ -142,7 +102,7 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
Status: apis.StatusFailed,
ControlID: "ctrlID",
},
expectedRows: []string{"❌", "test", "https://kubescape.io/docs/ctrlid"},
expectedRows: table.Row{"❌", "test", "https://kubescape.io/docs/ctrlid"},
},
{
name: "skipped control",
@@ -154,7 +114,7 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
},
ControlID: "ctrlID",
},
expectedRows: []string{"⚠️", "test", "https://kubescape.io/docs/ctrlid"},
expectedRows: table.Row{"⚠️", "test", "https://kubescape.io/docs/ctrlid"},
infoToPrintInfo: []utils.InfoStars{
{
Info: "testInfo",
@@ -169,7 +129,7 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
Status: apis.StatusPassed,
ControlID: "ctrlID",
},
expectedRows: []string{"✅", "test", "https://kubescape.io/docs/ctrlid"},
expectedRows: table.Row{"✅", "test", "https://kubescape.io/docs/ctrlid"},
},
{
name: "big name",
@@ -178,13 +138,13 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
Status: apis.StatusFailed,
ControlID: "ctrlID",
},
expectedRows: []string{"❌", "testtesttesttesttesttesttesttesttesttesttesttestte...", "https://kubescape.io/docs/ctrlid"},
expectedRows: table.Row{"❌", "testtesttesttesttesttesttesttesttesttesttesttestte...", "https://kubescape.io/docs/ctrlid"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
row := generateCategoryStatusRow(tt.controlSummary, tt.infoToPrintInfo)
row := generateCategoryStatusRow(tt.controlSummary)
assert.Equal(t, tt.expectedRows, row)
})
}
@@ -192,22 +152,22 @@ func TestGenerateCategoryStatusRow(t *testing.T) {
func TestGetCategoryTableWriter(t *testing.T) {
tests := []struct {
name string
headers []string
columnAligments []int
want string
name string
headers table.Row
columnAlignments []table.ColumnConfig
want string
}{
{
name: "Test1",
headers: []string{"Control name", "Resources", "View details"},
columnAligments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
want: "──────────────┬───────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼───────────┼──────────────┤\n──────────────┴───────────┴──────────────\n",
name: "Test1",
headers: table.Row{"Control name", "Resources", "View details"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
want: "──────────────┬───────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼───────────┼──────────────┤\n──────────────┴───────────┴──────────────\n",
},
{
name: "Test2",
headers: []string{"", "Control name", "Docs"},
columnAligments: []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER},
want: "──┬──────────────┬──────\n│ │ Control name │ Docs │\n├──┼──────────────┼──────┤\n──┴──────────────┴──────\n",
name: "Test2",
headers: table.Row{"", "Control name", "Docs"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}},
want: "──┬──────────────┬──────\n│ │ Control name │ Docs │\n├──┼──────────────┼──────┤\n──┴──────────────┴──────\n",
},
}
for _, tt := range tests {
@@ -219,7 +179,7 @@ func TestGetCategoryTableWriter(t *testing.T) {
}
defer f.Close()
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAligments)
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAlignments)
// Redirect stderr to the temporary file
oldStderr := os.Stderr
@@ -245,61 +205,61 @@ func TestGetCategoryTableWriter(t *testing.T) {
func TestRenderSingleCategory(t *testing.T) {
tests := []struct {
name string
categoryName string
rows [][]string
infoToPrintInfo []utils.InfoStars
headers []string
columnAligments []int
want string
name string
categoryName string
rows []table.Row
infoToPrintInfo []utils.InfoStars
headers table.Row
columnAlignments []table.ColumnConfig
want string
}{
{
name: "Test1",
categoryName: "Resources",
rows: [][]string{
rows: []table.Row{
{"Regular", "regular line", "1"},
{"Thick", "particularly thick line", "2"},
{"Double", "double line", "3"},
},
infoToPrintInfo: []utils.InfoStars{
utils.InfoStars{
{
Stars: "1",
Info: "Low severity",
},
utils.InfoStars{
{
Stars: "5",
Info: "Critical severity",
},
},
headers: []string{"Control name", "Resources", "View details"},
columnAligments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
want: "Resources\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n\n",
headers: table.Row{"Control name", "Resources", "View details"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
want: "Resources\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n\n",
},
{
name: "Test2",
categoryName: "Control name",
rows: [][]string{
rows: []table.Row{
{"Regular", "regular line", "1"},
{"Thick", "particularly thick line", "2"},
{"Double", "double line", "3"},
},
infoToPrintInfo: []utils.InfoStars{
utils.InfoStars{
{
Stars: "1",
Info: "Low severity",
},
utils.InfoStars{
{
Stars: "5",
Info: "Critical severity",
},
utils.InfoStars{
{
Stars: "4",
Info: "High severity",
},
},
headers: []string{"Control name", "Resources", "View details"},
columnAligments: []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT},
want: "Control name\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n4 High severity\n\n",
headers: table.Row{"Control name", "Resources", "View details"},
columnAlignments: []table.ColumnConfig{{Number: 1, Align: text.AlignLeft}, {Number: 2, Align: text.AlignCenter}, {Number: 3, Align: text.AlignLeft}},
want: "Control name\n──────────────┬─────────────────────────┬──────────────\n│ Control name │ Resources │ View details │\n├──────────────┼─────────────────────────┼──────────────┤\n│ Regular │ regular line │ 1 │\n│ Thick │ particularly thick line │ 2 │\n│ Double │ double line │ 3 │\n──────────────┴─────────────────────────┴──────────────\n1 Low severity\n5 Critical severity\n4 High severity\n\n",
},
}
for _, tt := range tests {
@@ -311,7 +271,7 @@ func TestRenderSingleCategory(t *testing.T) {
}
defer f.Close()
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAligments)
tableWriter := getCategoryTableWriter(f, tt.headers, tt.columnAlignments)
// Redirect stderr to the temporary file
oldStderr := os.Stderr

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"io"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
@@ -17,11 +18,11 @@ func NewClusterPrinter() *ClusterPrinter {
var _ TablePrinter = &ClusterPrinter{}
func (cp *ClusterPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (cp *ClusterPrinter) PrintSummaryTable(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
}
func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, _ [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapClusterControlsToCategories)
@@ -38,17 +39,17 @@ func (cp *ClusterPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails
func (cp *ClusterPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
sortControlSummaries(controlSummaries)
headers, columnAligments := initCategoryTableData(categoryType)
headers, columnAlignments := initCategoryTableData(categoryType)
table := getCategoryTableWriter(writer, headers, columnAligments)
tableWriter := getCategoryTableWriter(writer, headers, columnAlignments)
var rows [][]string
var rows []table.Row
for _, ctrls := range controlSummaries {
var row []string
var row table.Row
if categoryType == TypeCounting {
row = cp.generateCountingCategoryRow(ctrls)
} else {
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
row = generateCategoryStatusRow(ctrls)
}
if len(row) > 0 {
rows = append(rows, row)
@@ -59,19 +60,19 @@ func (cp *ClusterPrinter) renderSingleCategoryTable(categoryName string, categor
return
}
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
renderSingleCategory(writer, categoryName, tableWriter, rows, infoToPrintInfo)
}
func (cp *ClusterPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary) []string {
func (cp *ClusterPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary) table.Row {
row := make([]string, 3)
row := make(table.Row, 3)
row[0] = controlSummary.GetName()
failedResources := controlSummary.NumberOfResources().Failed()
if failedResources > 0 {
row[1] = string(gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources)))
row[1] = gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources))
} else {
row[1] = fmt.Sprintf("%d", failedResources)
}

View File

@@ -6,11 +6,11 @@ import (
"strconv"
"strings"
"github.com/jwalton/gchalk"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/olekukonko/tablewriter"
)
type FrameworkPrinter struct {
@@ -38,19 +38,21 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
// When scanning controls the framework list will be empty
cautils.SimpleDisplay(writer, utils.FrameworksScoresToString(summaryDetails.ListFrameworks())+"\n")
controlCountersTable := tablewriter.NewWriter(writer)
controlCountersTable := table.NewWriter()
controlCountersTable.SetOutputMirror(writer)
controlCountersTable.SetColumnAlignment([]int{tablewriter.ALIGN_RIGHT, tablewriter.ALIGN_LEFT})
controlCountersTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
controlCountersTable.AppendBulk(ControlCountersForSummary(summaryDetails.NumberOfControls()))
controlCountersTable.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}, {Number: 2, Align: text.AlignLeft}})
controlCountersTable.Style().Box = table.StyleBoxRounded
controlCountersTable.AppendRows(ControlCountersForSummary(summaryDetails.NumberOfControls()))
controlCountersTable.Render()
cautils.SimpleDisplay(writer, "\nFailed resources by severity:\n\n")
severityCountersTable := tablewriter.NewWriter(writer)
severityCountersTable.SetColumnAlignment([]int{tablewriter.ALIGN_RIGHT, tablewriter.ALIGN_LEFT})
severityCountersTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
severityCountersTable.AppendBulk(renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters()))
severityCountersTable := table.NewWriter()
severityCountersTable.SetOutputMirror(writer)
severityCountersTable.SetColumnConfigs([]table.ColumnConfig{{Number: 1, Align: text.AlignRight}, {Number: 2, Align: text.AlignLeft}})
severityCountersTable.Style().Box = table.StyleBoxRounded
severityCountersTable.AppendRows(renderSeverityCountersSummary(summaryDetails.GetResourcesSeverityCounters()))
severityCountersTable.Render()
cautils.SimpleDisplay(writer, "\n")
@@ -59,14 +61,15 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
cautils.SimpleDisplay(writer, "Run with '--verbose'/'-v' to see control failures for each resource.\n\n")
}
summaryTable := tablewriter.NewWriter(writer)
summaryTable := table.NewWriter()
summaryTable.SetOutputMirror(writer)
summaryTable.SetAutoWrapText(false)
summaryTable.SetHeaderLine(true)
summaryTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
summaryTable.SetAutoFormatHeaders(false)
summaryTable.SetColumnAlignment(GetColumnsAlignments())
summaryTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
summaryTable.Style().Options.SeparateHeader = true
summaryTable.Style().Format.HeaderAlign = text.AlignLeft
summaryTable.Style().Format.Header = text.FormatDefault
summaryTable.Style().Format.Footer = text.FormatDefault
summaryTable.SetColumnConfigs(GetColumnsAlignments())
summaryTable.Style().Box = table.StyleBoxRounded
printAll := fp.getVerboseMode()
if summaryDetails.NumberOfResources().Failed() == 0 {
@@ -74,7 +77,7 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
printAll = true
}
dataRows := [][]string{}
var dataRows []table.Row
infoToPrintInfo := utils.MapInfoToPrintInfo(summaryDetails.Controls)
for i := len(sortedControlIDs) - 1; i >= 0; i-- {
@@ -88,28 +91,23 @@ func (fp *FrameworkPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *
short := utils.CheckShortTerminalWidth(dataRows, GetControlTableHeaders(false))
if short {
summaryTable.SetRowLine(true)
summaryTable.Style().Options.SeparateRows = true
dataRows = shortFormatRow(dataRows)
} else {
summaryTable.SetColumnAlignment(GetColumnsAlignments())
summaryTable.SetColumnConfigs(GetColumnsAlignments())
summaryTable.Style().Format.FooterAlign = text.AlignCenter
}
summaryTable.SetHeader(GetControlTableHeaders(short))
summaryTable.SetFooter(GenerateFooter(summaryDetails, short))
summaryTable.AppendHeader(GetControlTableHeaders(short))
summaryTable.AppendFooter(GenerateFooter(summaryDetails, short))
var headerColors []tablewriter.Colors
for range dataRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
summaryTable.SetHeaderColor(headerColors...)
summaryTable.AppendBulk(dataRows)
summaryTable.AppendRows(dataRows)
summaryTable.Render()
utils.PrintInfo(writer, infoToPrintInfo)
}
func shortFormatRow(dataRows [][]string) [][]string {
rows := [][]string{}
func shortFormatRow(dataRows []table.Row) []table.Row {
rows := make([]table.Row, 0, len(dataRows))
for _, dataRow := range dataRows {
// Define the row content using a formatted string
rowContent := fmt.Sprintf("Severity%s: %+v\nControl Name%s: %+v\nFailed Resources%s: %+v\nAll Resources%s: %+v\n%% Compliance-Score%s: %+v",
@@ -125,22 +123,22 @@ func shortFormatRow(dataRows [][]string) [][]string {
dataRow[summaryColumnComplianceScore])
// Append the formatted row content to the rows slice
rows = append(rows, []string{rowContent})
rows = append(rows, table.Row{rowContent})
}
return rows
}
func (fp *FrameworkPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (fp *FrameworkPrinter) PrintCategoriesTables(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
}
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) [][]string {
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) []table.Row {
rows := [][]string{}
rows = append(rows, []string{"Critical", utils.GetColorForVulnerabilitySeverity("Critical")(strconv.Itoa(counters.NumberOfCriticalSeverity()))})
rows = append(rows, []string{"High", utils.GetColorForVulnerabilitySeverity("High")(strconv.Itoa(counters.NumberOfHighSeverity()))})
rows = append(rows, []string{"Medium", utils.GetColorForVulnerabilitySeverity("Medium")(strconv.Itoa(counters.NumberOfMediumSeverity()))})
rows = append(rows, []string{"Low", utils.GetColorForVulnerabilitySeverity("Low")(strconv.Itoa(counters.NumberOfLowSeverity()))})
rows := make([]table.Row, 0, 4)
rows = append(rows, table.Row{"Critical", utils.GetColorForVulnerabilitySeverity("Critical")(strconv.Itoa(counters.NumberOfCriticalSeverity()))})
rows = append(rows, table.Row{"High", utils.GetColorForVulnerabilitySeverity("High")(strconv.Itoa(counters.NumberOfHighSeverity()))})
rows = append(rows, table.Row{"Medium", utils.GetColorForVulnerabilitySeverity("Medium")(strconv.Itoa(counters.NumberOfMediumSeverity()))})
rows = append(rows, table.Row{"Low", utils.GetColorForVulnerabilitySeverity("Low")(strconv.Itoa(counters.NumberOfLowSeverity()))})
return rows
}

View File

@@ -5,6 +5,7 @@ import (
"os"
"testing"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/stretchr/testify/assert"
)
@@ -32,7 +33,7 @@ func (m *MockISeverityCounters) NumberOfLowSeverity() int {
return m.LowCount
}
func (m *MockISeverityCounters) Increase(severity string, amount int) {
func (m *MockISeverityCounters) Increase(_ string, _ int) {
}
func TestNewFrameworkPrinter(t *testing.T) {
@@ -60,28 +61,28 @@ func TestGetVerboseMode(t *testing.T) {
func TestShortRowFormat(t *testing.T) {
tests := []struct {
name string
rows [][]string
expectedRows [][]string
rows []table.Row
expectedRows []table.Row
}{
{
name: "Test Empty rows",
rows: [][]string{},
expectedRows: [][]string{},
rows: []table.Row{},
expectedRows: []table.Row{},
},
{
name: "Test Non empty row",
rows: [][]string{
rows: []table.Row{
{"Medium", "Control 1", "2", "20", "0.8"},
},
expectedRows: [][]string{[]string{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}},
expectedRows: []table.Row{{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}},
},
{
name: "Test Non empty rows",
rows: [][]string{
rows: []table.Row{
{"Medium", "Control 1", "2", "20", "0.8"},
{"Low", "Control 2", "0", "30", "1.0"},
},
expectedRows: [][]string{[]string{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}, []string{"Severity : Low\nControl Name : Control 2\nFailed Resources : 0\nAll Resources : 30\n% Compliance-Score : 1.0"}},
expectedRows: []table.Row{{"Severity : Medium\nControl Name : Control 1\nFailed Resources : 2\nAll Resources : 20\n% Compliance-Score : 0.8"}, {"Severity : Low\nControl Name : Control 2\nFailed Resources : 0\nAll Resources : 30\n% Compliance-Score : 1.0"}},
},
}
@@ -96,12 +97,12 @@ func TestRenderSeverityCountersSummary(t *testing.T) {
tests := []struct {
name string
counters MockISeverityCounters
expected [][]string
expected []table.Row
}{
{
name: "All empty",
counters: MockISeverityCounters{},
expected: [][]string{[]string{"Critical", "0"}, []string{"High", "0"}, []string{"Medium", "0"}, []string{"Low", "0"}},
expected: []table.Row{{"Critical", "0"}, {"High", "0"}, {"Medium", "0"}, {"Low", "0"}},
},
{
name: "All different",
@@ -111,7 +112,7 @@ func TestRenderSeverityCountersSummary(t *testing.T) {
MediumCount: 27,
LowCount: 37,
},
expected: [][]string{[]string{"Critical", "7"}, []string{"High", "17"}, []string{"Medium", "27"}, []string{"Low", "37"}},
expected: []table.Row{{"Critical", "7"}, {"High", "17"}, {"Medium", "27"}, {"Low", "37"}},
},
{
name: "All equal",
@@ -121,7 +122,7 @@ func TestRenderSeverityCountersSummary(t *testing.T) {
MediumCount: 7,
LowCount: 7,
},
expected: [][]string{[]string{"Critical", "7"}, []string{"High", "7"}, []string{"Medium", "7"}, []string{"Low", "7"}},
expected: []table.Row{{"Critical", "7"}, {"High", "7"}, {"Medium", "7"}, {"Low", "7"}},
},
}

View File

@@ -5,6 +5,7 @@ import (
"io"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling"
@@ -24,15 +25,15 @@ func NewRepoPrinter(inputPatterns []string) *RepoPrinter {
var _ TablePrinter = &RepoPrinter{}
func (rp *RepoPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (rp *RepoPrinter) PrintSummaryTable(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
}
func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, _ [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapRepoControlsToCategories)
tableRended := false
tableRendered := false
for _, id := range repoCategoriesDisplayOrder {
categoryControl, ok := categoriesToCategoryControls[id]
if !ok {
@@ -43,10 +44,10 @@ func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *r
continue
}
tableRended = tableRended || rp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
tableRendered = tableRendered || rp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
}
if !tableRended {
if !tableRendered {
fmt.Fprintln(writer, gchalk.WithGreen().Bold("All controls passed. No issues found"))
}
@@ -55,21 +56,21 @@ func (rp *RepoPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *r
func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) bool {
sortControlSummaries(controlSummaries)
headers, columnAligments := initCategoryTableData(categoryType)
headers, columnAlignments := initCategoryTableData(categoryType)
table := getCategoryTableWriter(writer, headers, columnAligments)
tableWriter := getCategoryTableWriter(writer, headers, columnAlignments)
var rows [][]string
var rows []table.Row
for _, ctrls := range controlSummaries {
if ctrls.NumberOfResources().Failed() == 0 {
continue
}
var row []string
var row table.Row
if categoryType == TypeCounting {
row = rp.generateCountingCategoryRow(ctrls, rp.inputPatterns)
} else {
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
row = generateCategoryStatusRow(ctrls)
}
if len(row) > 0 {
rows = append(rows, row)
@@ -80,18 +81,18 @@ func (rp *RepoPrinter) renderSingleCategoryTable(categoryName string, categoryTy
return false
}
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
renderSingleCategory(writer, categoryName, tableWriter, rows, infoToPrintInfo)
return true
}
func (rp *RepoPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary, inputPatterns []string) []string {
rows := make([]string, 3)
func (rp *RepoPrinter) generateCountingCategoryRow(controlSummary reportsummary.IControlSummary, inputPatterns []string) table.Row {
rows := make(table.Row, 3)
rows[0] = controlSummary.GetName()
failedResources := controlSummary.NumberOfResources().Failed()
if failedResources > 0 {
rows[1] = string(gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources)))
rows[1] = gchalk.WithYellow().Bold(fmt.Sprintf("%d", failedResources))
} else {
rows[1] = fmt.Sprintf("%d", failedResources)
}

View File

@@ -5,11 +5,12 @@ import (
"strconv"
"strings"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/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"
)
const (
@@ -21,12 +22,12 @@ const (
_summaryRowLen = iota
)
func ControlCountersForSummary(counters reportsummary.ICounters) [][]string {
rows := [][]string{}
rows = append(rows, []string{"Controls", strconv.Itoa(counters.All())})
rows = append(rows, []string{"Passed", strconv.Itoa(counters.Passed())})
rows = append(rows, []string{"Failed", strconv.Itoa(counters.Failed())})
rows = append(rows, []string{"Action Required", strconv.Itoa(counters.Skipped())})
func ControlCountersForSummary(counters reportsummary.ICounters) []table.Row {
rows := make([]table.Row, 0, 4)
rows = append(rows, table.Row{"Controls", strconv.Itoa(counters.All())})
rows = append(rows, table.Row{"Passed", strconv.Itoa(counters.Passed())})
rows = append(rows, table.Row{"Failed", strconv.Itoa(counters.Failed())})
rows = append(rows, table.Row{"Action Required", strconv.Itoa(counters.Skipped())})
return rows
}
@@ -35,13 +36,13 @@ func GetSeverityColumn(controlSummary reportsummary.IControlSummary) string {
return utils.GetColor(apis.ControlSeverityToInt(controlSummary.GetScoreFactor()))(apis.ControlSeverityToString(controlSummary.GetScoreFactor()))
}
func GetControlTableHeaders(short bool) []string {
var headers []string
func GetControlTableHeaders(short bool) table.Row {
var headers table.Row
if short {
headers = make([]string, 1)
headers = make(table.Row, 1)
headers[0] = "Controls"
} else {
headers = make([]string, _summaryRowLen)
headers = make(table.Row, _summaryRowLen)
headers[summaryColumnName] = "Control name"
headers[summaryColumnCounterFailed] = "Failed resources"
headers[summaryColumnCounterAll] = "All Resources"
@@ -51,22 +52,22 @@ func GetControlTableHeaders(short bool) []string {
return headers
}
func GetColumnsAlignments() []int {
alignments := make([]int, _summaryRowLen)
alignments[summaryColumnSeverity] = tablewriter.ALIGN_CENTER
alignments[summaryColumnName] = tablewriter.ALIGN_LEFT
alignments[summaryColumnCounterFailed] = tablewriter.ALIGN_CENTER
alignments[summaryColumnCounterAll] = tablewriter.ALIGN_CENTER
alignments[summaryColumnComplianceScore] = tablewriter.ALIGN_CENTER
return alignments
func GetColumnsAlignments() []table.ColumnConfig {
return []table.ColumnConfig{
{Number: summaryColumnSeverity + 1, Align: text.AlignCenter},
{Number: summaryColumnName + 1, Align: text.AlignLeft},
{Number: summaryColumnCounterFailed + 1, Align: text.AlignCenter},
{Number: summaryColumnCounterAll + 1, Align: text.AlignCenter},
{Number: summaryColumnComplianceScore + 1, Align: text.AlignCenter},
}
}
func GenerateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars, verbose bool) []string {
row := make([]string, _summaryRowLen)
func GenerateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars, verbose bool) table.Row {
row := make(table.Row, _summaryRowLen)
// ignore passed results
if !verbose && (controlSummary.GetStatus().IsPassed()) {
return []string{}
return table.Row{}
}
row[summaryColumnSeverity] = GetSeverityColumn(controlSummary)
@@ -98,14 +99,14 @@ func GetInfoColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo
return ""
}
func GenerateFooter(summaryDetails *reportsummary.SummaryDetails, short bool) []string {
var row []string
func GenerateFooter(summaryDetails *reportsummary.SummaryDetails, short bool) table.Row {
var row table.Row
if short {
row = make([]string, 1)
row = make(table.Row, 1)
row[0] = fmt.Sprintf("Resource Summary"+strings.Repeat(" ", 0)+"\n\nFailed Resources"+strings.Repeat(" ", 1)+": %d\nAll Resources"+strings.Repeat(" ", 4)+": %d\n%% Compliance-Score"+strings.Repeat(" ", 4)+": %.2f%%", summaryDetails.NumberOfResources().Failed(), summaryDetails.NumberOfResources().All(), summaryDetails.ComplianceScore)
} else {
// Severity | Control name | failed resources | all resources | % success
row = make([]string, _summaryRowLen)
row = make(table.Row, _summaryRowLen)
row[summaryColumnName] = "Resource Summary"
row[summaryColumnCounterFailed] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Failed())
row[summaryColumnCounterAll] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().All())

View File

@@ -3,6 +3,7 @@ package configurationprinter
import (
"io"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
)
@@ -16,11 +17,11 @@ func NewWorkloadPrinter() *WorkloadPrinter {
return &WorkloadPrinter{}
}
func (wp *WorkloadPrinter) PrintSummaryTable(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (wp *WorkloadPrinter) PrintSummaryTable(_ io.Writer, _ *reportsummary.SummaryDetails, _ [][]string) {
}
func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, sortedControlIDs [][]string) {
func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetails *reportsummary.SummaryDetails, _ [][]string) {
categoriesToCategoryControls := mapCategoryToSummary(summaryDetails.ListControls(), mapWorkloadControlsToCategories)
@@ -30,21 +31,20 @@ func (wp *WorkloadPrinter) PrintCategoriesTables(writer io.Writer, summaryDetail
continue
}
wp.renderSingleCategoryTable(categoryControl.CategoryName, mapCategoryToType[id], writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
wp.renderSingleCategoryTable(categoryControl.CategoryName, writer, categoryControl.controlSummaries, utils.MapInfoToPrintInfoFromIface(categoryControl.controlSummaries))
}
}
func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, categoryType CategoryType, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, writer io.Writer, controlSummaries []reportsummary.IControlSummary, infoToPrintInfo []utils.InfoStars) {
sortControlSummaries(controlSummaries)
headers, columnAligments := wp.initCategoryTableData()
headers, columnAlignments := wp.initCategoryTableData()
table := getCategoryTableWriter(writer, headers, columnAligments)
tableWriter := getCategoryTableWriter(writer, headers, columnAlignments)
var rows [][]string
var rows []table.Row
for _, ctrls := range controlSummaries {
var row []string
row = generateCategoryStatusRow(ctrls, infoToPrintInfo)
row := generateCategoryStatusRow(ctrls)
if len(row) > 0 {
rows = append(rows, row)
}
@@ -54,9 +54,9 @@ func (wp *WorkloadPrinter) renderSingleCategoryTable(categoryName string, catego
return
}
renderSingleCategory(writer, categoryName, table, rows, infoToPrintInfo)
renderSingleCategory(writer, categoryName, tableWriter, rows, infoToPrintInfo)
}
func (wp *WorkloadPrinter) initCategoryTableData() ([]string, []int) {
func (wp *WorkloadPrinter) initCategoryTableData() (table.Row, []table.ColumnConfig) {
return getCategoryStatusTypeHeaders(), getStatusTypeAlignments()
}

View File

@@ -3,17 +3,19 @@ package configurationprinter
import (
"testing"
"github.com/olekukonko/tablewriter"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/stretchr/testify/assert"
)
func TestWorkloadScan_InitCategoryTableData(t *testing.T) {
expectedHeader := []string{"", "Control name", "Docs"}
expectedAlign := []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER}
expectedAlign := []table.ColumnConfig{{Number: 1, Align: text.AlignCenter}, {Number: 2, Align: text.AlignLeft}, {Number: 3, Align: text.AlignCenter}}
workloadPrinter := NewWorkloadPrinter()
headers, columnAligments := workloadPrinter.initCategoryTableData()
headers, columnAlignments := workloadPrinter.initCategoryTableData()
for i := range headers {
if headers[i] != expectedHeader[i] {
@@ -21,10 +23,8 @@ func TestWorkloadScan_InitCategoryTableData(t *testing.T) {
}
}
for i := range columnAligments {
if columnAligments[i] != expectedAlign[i] {
t.Errorf("Expected column alignment %d, got %d", expectedAlign[i], columnAligments[i])
}
for i := range columnAlignments {
assert.Equal(t, expectedAlign[i], columnAlignments[i])
}
}

View File

@@ -42,7 +42,7 @@ func TestPrintImageScanningTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
{
name: "check fixed CVEs show versions",
@@ -65,7 +65,7 @@ func TestPrintImageScanningTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
}

View File

@@ -6,33 +6,28 @@ import (
"strings"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/jwalton/gchalk"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/olekukonko/tablewriter"
)
func renderTable(writer io.Writer, headers []string, columnAlignments []int, rows [][]string) {
table := tablewriter.NewWriter(writer)
table.SetHeader(headers)
table.SetHeaderLine(true)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAutoFormatHeaders(false)
table.SetColumnAlignment(columnAlignments)
table.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
func renderTable(writer io.Writer, headers table.Row, columnAlignments []table.ColumnConfig, rows []table.Row) {
tableWriter := table.NewWriter()
tableWriter.SetOutputMirror(writer)
tableWriter.AppendHeader(headers)
tableWriter.Style().Options.SeparateHeader = true
tableWriter.Style().Format.HeaderAlign = text.AlignLeft
tableWriter.Style().Format.Header = text.FormatDefault
tableWriter.SetColumnConfigs(columnAlignments)
tableWriter.Style().Box = table.StyleBoxRounded
var headerColors []tablewriter.Colors
for range rows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
table.SetHeaderColor(headerColors...)
tableWriter.AppendRows(rows)
table.AppendBulk(rows)
table.Render()
tableWriter.Render()
}
func generateRows(summary ImageScanSummary) [][]string {
rows := make([][]string, 0, len(summary.CVEs))
func generateRows(summary ImageScanSummary) []table.Row {
rows := make([]table.Row, 0, len(summary.CVEs))
// sort CVEs by severity
sort.Slice(summary.CVEs, func(i, j int) bool {
@@ -46,8 +41,8 @@ func generateRows(summary ImageScanSummary) [][]string {
return rows
}
func generateRow(cve CVE) []string {
row := make([]string, 5)
func generateRow(cve CVE) table.Row {
row := make(table.Row, 5)
row[imageColumnSeverity] = utils.GetColorForVulnerabilitySeverity(cve.Severity)(cve.Severity)
row[imageColumnName] = cve.ID
row[imageColumnComponent] = cve.Package
@@ -59,13 +54,15 @@ func generateRow(cve CVE) []string {
// if the CVE is not fixed, show the state
} else if cve.FixedState == string(v5.WontFixState) {
row[imageColumnFixedIn] = cve.FixedState
} else {
row[imageColumnFixedIn] = ""
}
return row
}
func getImageScanningHeaders() []string {
headers := make([]string, 5)
func getImageScanningHeaders() table.Row {
headers := make(table.Row, 5)
headers[imageColumnSeverity] = "Severity"
headers[imageColumnName] = "Vulnerability"
headers[imageColumnComponent] = "Component"
@@ -74,6 +71,12 @@ func getImageScanningHeaders() []string {
return headers
}
func getImageScanningColumnsAlignments() []int {
return []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}
func getImageScanningColumnsAlignments() []table.ColumnConfig {
return []table.ColumnConfig{
{Number: 1, Align: text.AlignCenter},
{Number: 2, Align: text.AlignLeft},
{Number: 3, Align: text.AlignLeft},
{Number: 4, Align: text.AlignLeft},
{Number: 5, Align: text.AlignLeft},
}
}

View File

@@ -6,7 +6,6 @@ import (
"testing"
v5 "github.com/anchore/grype/grype/db/v5"
"github.com/olekukonko/tablewriter"
"github.com/stretchr/testify/assert"
)
@@ -46,7 +45,7 @@ func TestRenderTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ │\n│ Medium │ CVE-2020-0003 │ package3 │ 1.0.0 │ │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
{
name: "check fixed CVEs show versions",
@@ -69,7 +68,7 @@ func TestRenderTable(t *testing.T) {
},
},
},
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
want: "──────────┬───────────────┬───────────┬─────────┬──────────\n│ Severity │ Vulnerability │ Component │ Version │ Fixed in │\n├──────────┼───────────────┼───────────┼─────────┼──────────┤\n│ High │ CVE-2020-0002 │ package2 │ 1.0.0 │ v1,v2 │\n│ Low │ CVE-2020-0001 │ package1 │ 1.0.0 │ │\n──────────┴───────────────┴───────────┴─────────┴──────────\n",
},
}
@@ -247,15 +246,3 @@ func TestGetImageScanningHeaders(t *testing.T) {
}
}
}
func TestGetImageScanningColumnsAlignments(t *testing.T) {
alignments := getImageScanningColumnsAlignments()
expectedAlignments := []int{tablewriter.ALIGN_CENTER, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT}
for i := range alignments {
if alignments[i] != expectedAlignments[i] {
t.Errorf("expected %d, got %d", expectedAlignments[i], alignments[i])
}
}
}

View File

@@ -6,6 +6,7 @@ import (
"os"
"github.com/enescakir/emoji"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jwalton/gchalk"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/reporthandling/apis"
@@ -138,19 +139,19 @@ func GetStatusIcon(status apis.ScanningStatus) string {
}
}
func CheckShortTerminalWidth(rows [][]string, headers []string) bool {
func CheckShortTerminalWidth(rows []table.Row, headers table.Row) bool {
maxWidth := 0
for _, row := range rows {
rowWidth := 0
for idx, cell := range row {
cellLen := len(cell)
cellLen := len(cell.(string))
if cellLen > 50 { // Take only 50 characters of each sentence for counting size
cellLen = 50
}
if cellLen > len(headers[idx]) {
if cellLen > len(headers[idx].(string)) {
rowWidth += cellLen
} else {
rowWidth += len(headers[idx])
rowWidth += len(headers[idx].(string))
}
rowWidth += 2
}

View File

@@ -3,18 +3,17 @@ package printer
import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"github.com/jwalton/gchalk"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter"
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
"github.com/olekukonko/tablewriter"
)
const (
@@ -48,52 +47,35 @@ func (prettyPrinter *PrettyPrinter) resourceTable(opaSessionObj *cautils.OPASess
}
fmt.Fprintf(prettyPrinter.writer, "\n%s\n\n", prettyprinter.ControlCountersForResource(result.ListControlsIDs(nil)))
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
summaryTable := table.NewWriter()
summaryTable.SetOutputMirror(prettyPrinter.writer)
summaryTable.SetAutoWrapText(true)
summaryTable.SetAutoMergeCells(true)
summaryTable.SetHeaderLine(true)
summaryTable.SetRowLine(true)
summaryTable.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
summaryTable.SetAutoFormatHeaders(false)
summaryTable.SetUnicodeHVC(tablewriter.Regular, tablewriter.Regular, gchalk.Ansi256(238))
summaryTable.Style().Options.SeparateHeader = true
summaryTable.Style().Options.SeparateRows = true
summaryTable.Style().Format.HeaderAlign = text.AlignLeft
summaryTable.Style().Format.Header = text.FormatDefault
summaryTable.Style().Box = table.StyleBoxRounded
resourceRows := [][]string{}
if raw := generateResourceRows(result.ListControls(), &opaSessionObj.Report.SummaryDetails, resource); len(raw) > 0 {
resourceRows = append(resourceRows, raw...)
}
resourceRows := generateResourceRows(result.ListControls(), &opaSessionObj.Report.SummaryDetails, resource)
short := utils.CheckShortTerminalWidth(resourceRows, generateResourceHeader(false))
if short {
summaryTable.SetAutoWrapText(false)
summaryTable.SetAutoMergeCells(false)
resourceRows = shortFormatResource(resourceRows)
}
summaryTable.SetHeader(generateResourceHeader(short))
summaryTable.AppendHeader(generateResourceHeader(short))
var headerColors []tablewriter.Colors
for range resourceRows[0] {
headerColors = append(headerColors, tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiYellowColor})
}
summaryTable.SetHeaderColor(headerColors...)
data := Matrix{}
data = append(data, resourceRows...)
// For control scan framework will be nil
sort.Sort(data)
summaryTable.AppendBulk(data)
summaryTable.AppendRows(resourceRows)
summaryTable.Render()
}
}
func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl, summaryDetails *reportsummary.SummaryDetails, resource workloadinterface.IMetadata) [][]string {
rows := [][]string{}
func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl, summaryDetails *reportsummary.SummaryDetails, resource workloadinterface.IMetadata) []table.Row {
var rows []table.Row
for i := range controls {
row := make([]string, _resourceRowLen)
row := make(table.Row, _resourceRowLen)
if !controls[i].GetStatus(nil).IsFailed() {
continue
@@ -111,12 +93,13 @@ func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl,
rows = append(rows, row)
}
return rows
}
func addContainerNameToAssistedRemediation(resource workloadinterface.IMetadata, paths *[]string) {
for i := range *paths {
re := regexp.MustCompile(`spec\.containers\[(\d+)\]`)
re := regexp.MustCompile(`spec\.containers\[(\d+)]`)
match := re.FindStringSubmatch((*paths)[i])
if len(match) == 2 {
index, _ := strconv.Atoi(match[1])
@@ -128,22 +111,18 @@ func addContainerNameToAssistedRemediation(resource workloadinterface.IMetadata,
}
}
func generateResourceHeader(short bool) []string {
headers := make([]string, 0)
func generateResourceHeader(short bool) table.Row {
if short {
headers = append(headers, "Resources")
return table.Row{"Resources"}
} else {
headers = append(headers, []string{"Severity", "Control name", "Docs", "Assisted remediation"}...)
return table.Row{"Severity", "Control name", "Docs", "Assisted remediation"}
}
return headers
}
func shortFormatResource(resourceRows [][]string) [][]string {
rows := [][]string{}
for _, resourceRow := range resourceRows {
rows = append(rows, []string{fmt.Sprintf("Severity"+strings.Repeat(" ", 13)+": %+v\nControl Name"+strings.Repeat(" ", 9)+": %+v\nDocs"+strings.Repeat(" ", 17)+": %+v\nAssisted Remediation"+strings.Repeat(" ", 1)+": %+v", resourceRow[resourceColumnSeverity], resourceRow[resourceColumnName], resourceRow[resourceColumnURL], strings.Replace(resourceRow[resourceColumnPath], "\n", "\n"+strings.Repeat(" ", 23), -1))})
func shortFormatResource(resourceRows []table.Row) []table.Row {
rows := make([]table.Row, len(resourceRows))
for i, resourceRow := range resourceRows {
rows[i] = table.Row{fmt.Sprintf("Severity"+strings.Repeat(" ", 13)+": %+v\nControl Name"+strings.Repeat(" ", 9)+": %+v\nDocs"+strings.Repeat(" ", 17)+": %+v\nAssisted Remediation"+strings.Repeat(" ", 1)+": %+v", resourceRow[resourceColumnSeverity], resourceRow[resourceColumnName], resourceRow[resourceColumnURL], strings.Replace(resourceRow[resourceColumnPath].(string), "\n", "\n"+strings.Repeat(" ", 23), -1))}
}
return rows
}

View File

@@ -4,6 +4,7 @@ import (
"testing"
"github.com/armosec/armoapi-go/armotypes"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
@@ -327,15 +328,15 @@ func TestFailedPathsToString(t *testing.T) {
func TestShortFormatResource(t *testing.T) {
// Create a test case with an empty resourceRows slice
emptyResourceRows := [][]string{}
emptyResourceRows := []table.Row{}
// Create a test case with a single resource row
singleResourceRow := [][]string{
singleResourceRow := []table.Row{
{"High", "Control1", "https://example.com/doc1", "Path1"},
}
// Create a test case with multiple resource rows
multipleResourceRows := [][]string{
multipleResourceRows := []table.Row{
{"Medium", "Control2", "https://example.com/doc2", "Path2"},
{"Low", "Control3", "https://example.com/doc3", "Path3"},
}
@@ -344,11 +345,11 @@ func TestShortFormatResource(t *testing.T) {
assert.Empty(t, actualRows)
actualRows = shortFormatResource(singleResourceRow)
expectedRows := [][]string{{"Severity : High\nControl Name : Control1\nDocs : https://example.com/doc1\nAssisted Remediation : Path1"}}
expectedRows := []table.Row{{"Severity : High\nControl Name : Control1\nDocs : https://example.com/doc1\nAssisted Remediation : Path1"}}
assert.Equal(t, expectedRows, actualRows)
actualRows = shortFormatResource(multipleResourceRows)
expectedRows = [][]string{{"Severity : Medium\nControl Name : Control2\nDocs : https://example.com/doc2\nAssisted Remediation : Path2"},
expectedRows = []table.Row{{"Severity : Medium\nControl Name : Control2\nDocs : https://example.com/doc2\nAssisted Remediation : Path2"},
{"Severity : Low\nControl Name : Control3\nDocs : https://example.com/doc3\nAssisted Remediation : Path3"}}
assert.Equal(t, expectedRows, actualRows)
}
@@ -356,12 +357,12 @@ func TestShortFormatResource(t *testing.T) {
func TestGenerateResourceHeader(t *testing.T) {
// Test case 1: Short headers
shortHeaders := generateResourceHeader(true)
expectedShortHeaders := []string{"Resources"}
expectedShortHeaders := table.Row{"Resources"}
assert.Equal(t, expectedShortHeaders, shortHeaders)
// Test case 2: Full headers
fullHeaders := generateResourceHeader(false)
expectedFullHeaders := []string{"Severity", "Control name", "Docs", "Assisted remediation"}
expectedFullHeaders := table.Row{"Severity", "Control name", "Docs", "Assisted remediation"}
assert.Equal(t, expectedFullHeaders, fullHeaders)
}
@@ -396,7 +397,7 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
name: "2 Failed Controls",
summaryDetails: reportsummary.SummaryDetails{},
controls: []resourcesresults.ResourceAssociatedControl{
resourcesresults.ResourceAssociatedControl{
{
ControlID: "control-1",
Name: "Control 1",
Status: apis.StatusInfo{},
@@ -417,7 +418,7 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
},
},
},
resourcesresults.ResourceAssociatedControl{
{
ControlID: "control-2",
Name: "Control 2",
Status: apis.StatusInfo{},
@@ -456,7 +457,7 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
name: "One failed control",
summaryDetails: reportsummary.SummaryDetails{},
controls: []resourcesresults.ResourceAssociatedControl{
resourcesresults.ResourceAssociatedControl{
{
ControlID: "control-1",
Name: "Control 1",
Status: apis.StatusInfo{},
@@ -477,7 +478,7 @@ func TestGenerateResourceRows_Loop(t *testing.T) {
},
},
},
resourcesresults.ResourceAssociatedControl{
{
ControlID: "control-2",
Name: "Control 2",
Status: apis.StatusInfo{},

View File

@@ -28,7 +28,7 @@ Kubescape security posture overview for cluster: minikube
In this overview, Kubescape shows you a summary of your cluster security posture, including the number of users who can perform administrative actions. For each result greater than 0, you should evaluate its need, and then define an exception to allow it. This baseline can be used to detect drift in future.
Control plane
────┬─────────────────────────────────────┬──────────────────────────────────────────────
────┬─────────────────────────────────────┬──────────────────────────────────────────────
│ │ Control Name │ Docs │
├────┼─────────────────────────────────────┼──────────────────────────────────────────────┤
│ ✅ │ API server insecure port is enabled │ https://kubescape.io/docs/controls/c-0005/ │
@@ -36,10 +36,10 @@ Control plane
│ ❌ │ Audit logs enabled │ https://kubescape.io/docs/controls/c-0067/ │
│ ✅ │ RBAC enabled │ https://kubescape.io/docs/controls/c-0088/ │
│ ❌ │ Secret/etcd encryption enabled │ https://kubescape.io/docs/controls/c-0066/ │
────┴─────────────────────────────────────┴──────────────────────────────────────────────
────┴─────────────────────────────────────┴──────────────────────────────────────────────
Access control
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├─────────────────────────────────────────────────┼───────────┼────────────────────────────────────┤
│ Cluster-admin binding │ 1 │ $ kubescape scan control C-0035 -v │
@@ -51,24 +51,24 @@ Access control
│ Portforwarding privileges │ 1 │ $ kubescape scan control C-0063 -v │
│ Validate admission controller (mutating)0 │ $ kubescape scan control C-0039 -v │
│ Validate admission controller (validating)0 │ $ kubescape scan control C-0036 -v │
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
Secrets
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
─────────────────────────────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├─────────────────────────────────────────────────┼───────────┼────────────────────────────────────┤
│ Applications credentials in configuration files │ 1 │ $ kubescape scan control C-0012 -v │
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
─────────────────────────────────────────────────┴───────────┴────────────────────────────────────
Network
────────────────────────┬───────────┬────────────────────────────────────
────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├────────────────────────┼───────────┼────────────────────────────────────┤
│ Missing network policy │ 13 │ $ kubescape scan control C-0260 -v │
────────────────────────┴───────────┴────────────────────────────────────
────────────────────────┴───────────┴────────────────────────────────────
Workload
─────────────────────────┬───────────┬────────────────────────────────────
─────────────────────────┬───────────┬────────────────────────────────────
│ Control Name │ Resources │ View Details │
├─────────────────────────┼───────────┼────────────────────────────────────┤
│ Host PID/IPC privileges │ 2 │ $ kubescape scan control C-0038 -v │
@@ -76,7 +76,7 @@ Workload
│ HostPath mount │ 1 │ $ kubescape scan control C-0048 -v │
│ Non-root containers │ 6 │ $ kubescape scan control C-0013 -v │
│ Privileged container │ 1 │ $ kubescape scan control C-0057 -v │
─────────────────────────┴───────────┴────────────────────────────────────
─────────────────────────┴───────────┴────────────────────────────────────
Highest-stake workloads
────────────────────────

6
go.mod
View File

@@ -25,6 +25,7 @@ require (
github.com/go-git/go-git/v5 v5.13.2
github.com/google/go-containerregistry v0.20.3
github.com/google/uuid v1.6.0
github.com/jedib0t/go-pretty/v6 v6.6.4
github.com/johnfercher/go-tree v1.1.0
github.com/johnfercher/maroto/v2 v2.2.2
github.com/json-iterator/go v1.1.12
@@ -44,7 +45,6 @@ require (
github.com/mattn/go-isatty v0.0.20
github.com/mikefarah/yq/v4 v4.29.1
github.com/moby/buildkit v0.21.0
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576
github.com/open-policy-agent/opa v1.4.0
github.com/owenrumney/go-sarif/v2 v2.2.0
github.com/project-copacetic/copacetic v0.10.0
@@ -370,6 +370,7 @@ require (
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oleiade/reflections v1.0.1 // indirect
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576 // indirect
github.com/olvrng/ujson v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
@@ -546,9 +547,6 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
)
// Using the forked version of tablewriter
replace github.com/olekukonko/tablewriter => github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94
replace github.com/anchore/stereoscope => github.com/matthyx/stereoscope v0.0.0-20250211130420-468901f0e973
replace github.com/google/go-containerregistry => github.com/matthyx/go-containerregistry v0.0.0-20240227132928-63ceb71ae0b9

6
go.sum
View File

@@ -2388,6 +2388,8 @@ github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.6.4 h1:B51RjA+Sytv0C0Je7PHGDXZBF2JpS5dZEWWRueBLP6U=
github.com/jedib0t/go-pretty/v6 v6.6.4/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
@@ -2499,8 +2501,6 @@ github.com/kubescape/sizing-checker v0.0.0-20250323151332-73a18561dc73 h1:XEAryA
github.com/kubescape/sizing-checker v0.0.0-20250323151332-73a18561dc73/go.mod h1:raFzO50jkNPNdBCFNTMn2EGQtmAOcBDvzVTJiArQvfo=
github.com/kubescape/storage v0.0.184 h1:SERswcPINJLexrWmsTkwZOHqgDxdpQdMFUmtnD8m4X8=
github.com/kubescape/storage v0.0.184/go.mod h1:c/m/RfadAIXRUC0JwT+18D0ei8uuT0LQdlVPy+RYvUU=
github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94 h1:uhabZUyrxo60JQrzGCQOp1gsJz06+6+PeDBTvXiKD7k=
github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94/go.mod h1:clwQfF3MN2cpaf7R6hc84aB6fQsRr+bnm66pXXSNAv8=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
@@ -2669,6 +2669,8 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM=
github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60=
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576 h1:V9mPAyDD3S448NMuNKE3GbGSx8vDk8KW5CXPhW4SU/g=
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576/go.mod h1:8Hf+pH6thup1sPZPD+NLg7d6vbpsdilu9CPIeikvgMQ=
github.com/olvrng/ujson v1.1.0 h1:8xVUzVlqwdMVWh5d1UHBtLQ1D50nxoPuPEq9Wozs8oA=
github.com/olvrng/ujson v1.1.0/go.mod h1:Mz4G3RODTUfbkKyvi0lgmPx/7vd3Saksk+1jgk8s9xo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=

View File

@@ -294,6 +294,7 @@ require (
github.com/in-toto/in-toto-golang v0.9.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jedib0t/go-pretty/v6 v6.6.4 // indirect
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect
github.com/jinzhu/copier v0.4.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
@@ -455,7 +456,7 @@ require (
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/transparency-dev/merkle v0.0.2 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/ulikunitz/xz v0.5.14 // indirect
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 // indirect
github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 // indirect
github.com/uptrace/uptrace-go v1.30.1 // indirect
@@ -550,9 +551,6 @@ require (
sigs.k8s.io/yaml v1.5.0 // indirect
)
// Using the forked version of tablewriter
replace github.com/olekukonko/tablewriter => github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94
replace github.com/docker/distribution v2.8.3+incompatible => github.com/docker/distribution v2.8.2+incompatible
replace github.com/docker/docker v27.1.1+incompatible => github.com/docker/docker v26.1.5+incompatible

View File

@@ -2394,6 +2394,8 @@ github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.6.4 h1:B51RjA+Sytv0C0Je7PHGDXZBF2JpS5dZEWWRueBLP6U=
github.com/jedib0t/go-pretty/v6 v6.6.4/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
@@ -2503,8 +2505,6 @@ github.com/kubescape/regolibrary/v2 v2.0.1 h1:7lKj171gslgTbbcmmGVHk34AZNqxForOXZ
github.com/kubescape/regolibrary/v2 v2.0.1/go.mod h1:s0/Mi9PYw7s91vIf1VJTkuu1Blsl5ZLpYn5UA7yk/vM=
github.com/kubescape/storage v0.0.184 h1:SERswcPINJLexrWmsTkwZOHqgDxdpQdMFUmtnD8m4X8=
github.com/kubescape/storage v0.0.184/go.mod h1:c/m/RfadAIXRUC0JwT+18D0ei8uuT0LQdlVPy+RYvUU=
github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94 h1:uhabZUyrxo60JQrzGCQOp1gsJz06+6+PeDBTvXiKD7k=
github.com/kubescape/tablewriter v0.0.6-0.20231106230230-aac7d2659c94/go.mod h1:clwQfF3MN2cpaf7R6hc84aB6fQsRr+bnm66pXXSNAv8=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
@@ -2667,6 +2667,8 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM=
github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60=
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576 h1:V9mPAyDD3S448NMuNKE3GbGSx8vDk8KW5CXPhW4SU/g=
github.com/olekukonko/tablewriter v0.0.6-0.20230417144759-edd1a71a5576/go.mod h1:8Hf+pH6thup1sPZPD+NLg7d6vbpsdilu9CPIeikvgMQ=
github.com/olvrng/ujson v1.1.0 h1:8xVUzVlqwdMVWh5d1UHBtLQ1D50nxoPuPEq9Wozs8oA=
github.com/olvrng/ujson v1.1.0/go.mod h1:Mz4G3RODTUfbkKyvi0lgmPx/7vd3Saksk+1jgk8s9xo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -3060,6 +3062,7 @@ github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 h1:3/aHKUq7qaFMWxyQV0W2ryNgg8x8rVeKVA20KJUkfS0=
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2/go.mod h1:Zit4b8AQXaXvA68+nzmbyDzqiyFRISyw1JiD5JqUBjw=
github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 h1:cj/Z6FKTTYBnstI0Lni9PA+k2foounKIPUmj1LBwNiQ=