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 } 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..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 @@ -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) + }) + } +} 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