From bd00d153e9c3ade708e720a6dde2735f2932fa73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:33:31 +0000 Subject: [PATCH 1/5] Initial plan From 837a50c903ca489ce4f09f7c596af264c8ba2c75 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:37:43 +0000 Subject: [PATCH 2/5] Fix unsafe interface to string type assertions to prevent panic Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com> --- cmd/mcpserver/mcpserver.go | 42 ++++++++++++++++--- .../prettyprinter/tableprinter/utils/utils.go | 15 +++++-- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/cmd/mcpserver/mcpserver.go b/cmd/mcpserver/mcpserver.go index c16d492e..7f77d0c0 100644 --- a/cmd/mcpserver/mcpserver.go +++ b/cmd/mcpserver/mcpserver.go @@ -294,11 +294,19 @@ func (ksServer *KubescapeMcpserver) CallTool(name string, arguments map[string]i if !ok { namespace = "kubescape" } + namespaceStr, ok := namespace.(string) + if !ok { + return nil, fmt.Errorf("namespace must be a string") + } manifestName, ok := arguments["manifest_name"] if !ok { return nil, fmt.Errorf("manifest_name is required") } - manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{}) + manifestNameStr, ok := manifestName.(string) + if !ok { + return nil, fmt.Errorf("manifest_name must be a string") + } + manifest, err := ksServer.ksClient.VulnerabilityManifests(namespaceStr).Get(context.Background(), manifestNameStr, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err) } @@ -323,21 +331,33 @@ func (ksServer *KubescapeMcpserver) CallTool(name string, arguments map[string]i if !ok { namespace = "kubescape" } + namespaceStr, ok := namespace.(string) + if !ok { + return nil, fmt.Errorf("namespace must be a string") + } manifestName, ok := arguments["manifest_name"] if !ok { return nil, fmt.Errorf("manifest_name is required") } + manifestNameStr, ok := manifestName.(string) + if !ok { + return nil, fmt.Errorf("manifest_name must be a string") + } cveID, ok := arguments["cve_id"] if !ok { return nil, fmt.Errorf("cve_id is required") } - manifest, err := ksServer.ksClient.VulnerabilityManifests(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{}) + cveIDStr, ok := cveID.(string) + if !ok { + return nil, fmt.Errorf("cve_id must be a string") + } + manifest, err := ksServer.ksClient.VulnerabilityManifests(namespaceStr).Get(context.Background(), manifestNameStr, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("failed to get vulnerability manifest: %s", err) } var match []v1beta1.Match for _, m := range manifest.Spec.Payload.Matches { - if m.Vulnerability.ID == cveID.(string) { + if m.Vulnerability.ID == cveIDStr { match = append(match, m) } } @@ -358,7 +378,11 @@ func (ksServer *KubescapeMcpserver) CallTool(name string, arguments map[string]i if !ok { namespace = "kubescape" } - manifests, err := ksServer.ksClient.WorkloadConfigurationScans(namespace.(string)).List(context.Background(), metav1.ListOptions{}) + namespaceStr, ok := namespace.(string) + if !ok { + return nil, fmt.Errorf("namespace must be a string") + } + manifests, err := ksServer.ksClient.WorkloadConfigurationScans(namespaceStr).List(context.Background(), metav1.ListOptions{}) if err != nil { return nil, err } @@ -394,11 +418,19 @@ func (ksServer *KubescapeMcpserver) CallTool(name string, arguments map[string]i if !ok { namespace = "kubescape" } + namespaceStr, ok := namespace.(string) + if !ok { + return nil, fmt.Errorf("namespace must be a string") + } manifestName, ok := arguments["manifest_name"] if !ok { return nil, fmt.Errorf("manifest_name is required") } - manifest, err := ksServer.ksClient.WorkloadConfigurationScans(namespace.(string)).Get(context.Background(), manifestName.(string), metav1.GetOptions{}) + manifestNameStr, ok := manifestName.(string) + if !ok { + return nil, fmt.Errorf("manifest_name must be a string") + } + manifest, err := ksServer.ksClient.WorkloadConfigurationScans(namespaceStr).Get(context.Background(), manifestNameStr, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("failed to get configuration manifest: %s", err) } diff --git a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils.go b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils.go index bc5e6634..4bf38355 100644 --- a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils.go +++ b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils.go @@ -144,14 +144,23 @@ func CheckShortTerminalWidth(rows []table.Row, headers table.Row) bool { for _, row := range rows { rowWidth := 0 for idx, cell := range row { - cellLen := len(cell.(string)) + cellStr, ok := cell.(string) + if !ok { + // If cell is not a string, skip this calculation + continue + } + cellLen := len(cellStr) if cellLen > 50 { // Take only 50 characters of each sentence for counting size cellLen = 50 } - if cellLen > len(headers[idx].(string)) { + headerStr, ok := headers[idx].(string) + if !ok { + // If header is not a string, use cell length + rowWidth += cellLen + } else if cellLen > len(headerStr) { rowWidth += cellLen } else { - rowWidth += len(headers[idx].(string)) + rowWidth += len(headerStr) } rowWidth += 2 } From 32a15acdea90568dcc91873d4da314f95b14a876 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:41:14 +0000 Subject: [PATCH 3/5] Add test for CheckShortTerminalWidth with non-string values Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com> --- .../tableprinter/utils/utils_test.go | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go index cec7a0a1..90f98020 100644 --- a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go +++ b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go @@ -5,6 +5,7 @@ import ( "os" "testing" + "github.com/jedib0t/go-pretty/v6/table" "github.com/jwalton/gchalk" "github.com/kubescape/opa-utils/reporthandling/apis" "github.com/stretchr/testify/assert" @@ -334,3 +335,69 @@ func TestGetColorForVulnerabilitySeverity(t *testing.T) { }) } } + +func TestCheckShortTerminalWidth(t *testing.T) { +tests := []struct { +name string +rows []table.Row +headers table.Row +// We can't predict the exact result since it depends on terminal size +// but we can test it doesn't panic with various inputs +shouldNotPanic bool +}{ +{ +name: "Normal string rows", +rows: []table.Row{ +{"cell1", "cell2", "cell3"}, +{"longer cell 1", "longer cell 2", "longer cell 3"}, +}, +headers: table.Row{"Header1", "Header2", "Header3"}, +shouldNotPanic: true, +}, +{ +name: "Rows with non-string values (map)", +rows: []table.Row{ +{"cell1", map[string]interface{}{"key": "value"}, "cell3"}, +{"cell4", "cell5", "cell6"}, +}, +headers: table.Row{"Header1", "Header2", "Header3"}, +shouldNotPanic: true, +}, +{ +name: "Headers with non-string values", +rows: []table.Row{ +{"cell1", "cell2", "cell3"}, +}, +headers: table.Row{"Header1", map[string]interface{}{"key": "value"}, "Header3"}, +shouldNotPanic: true, +}, +{ +name: "Both rows and headers with non-string values", +rows: []table.Row{ +{map[string]interface{}{"key": "value"}, "cell2", 123}, +}, +headers: table.Row{[]string{"a", "b"}, "Header2", true}, +shouldNotPanic: true, +}, +{ +name: "Empty rows", +rows: []table.Row{}, +headers: table.Row{"Header1", "Header2"}, +shouldNotPanic: true, +}, +} + +for _, tt := range tests { +t.Run(tt.name, func(t *testing.T) { +defer func() { +if r := recover(); r != nil { +if tt.shouldNotPanic { +t.Errorf("CheckShortTerminalWidth() panicked when it shouldn't: %v", r) +} +} +}() +// Call the function - we just want to ensure it doesn't panic +_ = CheckShortTerminalWidth(tt.rows, tt.headers) +}) +} +} From 82ec11b207c9caee82b9bdd2a040f7801186bd59 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:42:42 +0000 Subject: [PATCH 4/5] Fix indentation in test file Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com> --- .../tableprinter/utils/utils_test.go | 125 +++++++++--------- 1 file changed, 63 insertions(+), 62 deletions(-) diff --git a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go index 90f98020..21c559dc 100644 --- a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go +++ b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go @@ -337,67 +337,68 @@ func TestGetColorForVulnerabilitySeverity(t *testing.T) { } func TestCheckShortTerminalWidth(t *testing.T) { -tests := []struct { -name string -rows []table.Row -headers table.Row -// We can't predict the exact result since it depends on terminal size -// but we can test it doesn't panic with various inputs -shouldNotPanic bool -}{ -{ -name: "Normal string rows", -rows: []table.Row{ -{"cell1", "cell2", "cell3"}, -{"longer cell 1", "longer cell 2", "longer cell 3"}, -}, -headers: table.Row{"Header1", "Header2", "Header3"}, -shouldNotPanic: true, -}, -{ -name: "Rows with non-string values (map)", -rows: []table.Row{ -{"cell1", map[string]interface{}{"key": "value"}, "cell3"}, -{"cell4", "cell5", "cell6"}, -}, -headers: table.Row{"Header1", "Header2", "Header3"}, -shouldNotPanic: true, -}, -{ -name: "Headers with non-string values", -rows: []table.Row{ -{"cell1", "cell2", "cell3"}, -}, -headers: table.Row{"Header1", map[string]interface{}{"key": "value"}, "Header3"}, -shouldNotPanic: true, -}, -{ -name: "Both rows and headers with non-string values", -rows: []table.Row{ -{map[string]interface{}{"key": "value"}, "cell2", 123}, -}, -headers: table.Row{[]string{"a", "b"}, "Header2", true}, -shouldNotPanic: true, -}, -{ -name: "Empty rows", -rows: []table.Row{}, -headers: table.Row{"Header1", "Header2"}, -shouldNotPanic: true, -}, + tests := []struct { + name string + rows []table.Row + headers table.Row + // We can't predict the exact result since it depends on terminal size + // but we can test it doesn't panic with various inputs + shouldNotPanic bool + }{ + { + name: "Normal string rows", + rows: []table.Row{ + {"cell1", "cell2", "cell3"}, + {"longer cell 1", "longer cell 2", "longer cell 3"}, + }, + headers: table.Row{"Header1", "Header2", "Header3"}, + shouldNotPanic: true, + }, + { + name: "Rows with non-string values (map)", + rows: []table.Row{ + {"cell1", map[string]interface{}{"key": "value"}, "cell3"}, + {"cell4", "cell5", "cell6"}, + }, + headers: table.Row{"Header1", "Header2", "Header3"}, + shouldNotPanic: true, + }, + { + name: "Headers with non-string values", + rows: []table.Row{ + {"cell1", "cell2", "cell3"}, + }, + headers: table.Row{"Header1", map[string]interface{}{"key": "value"}, "Header3"}, + shouldNotPanic: true, + }, + { + name: "Both rows and headers with non-string values", + rows: []table.Row{ + {map[string]interface{}{"key": "value"}, "cell2", 123}, + }, + headers: table.Row{[]string{"a", "b"}, "Header2", true}, + shouldNotPanic: true, + }, + { + name: "Empty rows", + rows: []table.Row{}, + headers: table.Row{"Header1", "Header2"}, + shouldNotPanic: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + if tt.shouldNotPanic { + t.Errorf("CheckShortTerminalWidth() panicked when it shouldn't: %v", r) + } + } + }() + // Call the function - we just want to ensure it doesn't panic + _ = CheckShortTerminalWidth(tt.rows, tt.headers) + }) + } } -for _, tt := range tests { -t.Run(tt.name, func(t *testing.T) { -defer func() { -if r := recover(); r != nil { -if tt.shouldNotPanic { -t.Errorf("CheckShortTerminalWidth() panicked when it shouldn't: %v", r) -} -} -}() -// Call the function - we just want to ensure it doesn't panic -_ = CheckShortTerminalWidth(tt.rows, tt.headers) -}) -} -} From c32e66580969624ece5cf2ddef9fa3dd65e907cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:46:22 +0000 Subject: [PATCH 5/5] Final verification - all changes complete Co-authored-by: matthyx <20683409+matthyx@users.noreply.github.com> Signed-off-by: Matthias Bertschy --- .../printer/v2/prettyprinter/tableprinter/utils/utils_test.go | 1 - go.mod | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go index 21c559dc..bb8646ea 100644 --- a/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go +++ b/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/utils/utils_test.go @@ -401,4 +401,3 @@ func TestCheckShortTerminalWidth(t *testing.T) { }) } } - diff --git a/go.mod b/go.mod index 224e0380..f6e33cd7 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,6 @@ require ( go.opentelemetry.io/otel v1.39.0 go.opentelemetry.io/otel/metric v1.39.0 golang.org/x/mod v0.31.0 - golang.org/x/sync v0.19.0 golang.org/x/term v0.38.0 gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 gopkg.in/yaml.v3 v3.0.1 @@ -557,6 +556,7 @@ require ( golang.org/x/image v0.25.0 // indirect golang.org/x/net v0.48.0 // indirect golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect golang.org/x/time v0.14.0 // indirect