From d0004e6b1b9668243ae1e0a6a3839ccd0d6a300e Mon Sep 17 00:00:00 2001 From: etienneddog Date: Tue, 10 Feb 2026 18:54:33 +0100 Subject: [PATCH] fix(cmd): remove deprecated findings endpoints and broken vulnerabilities commands Remove security findings list/get commands (API endpoints being deprecated) and vulnerabilities search/list commands (using wrong API methods - signals instead of vulnerabilities). Security findings search and all static-analysis commands remain intact. - Remove `security findings list` and `security findings get` - Remove `vulnerabilities search` and `vulnerabilities list` - Unregister `vulnerabilities` command from root - Update tests accordingly Co-Authored-By: Claude Opus 4.6 (1M context) --- cmd/root.go | 1 - cmd/security.go | 137 +-------------------------- cmd/security_test.go | 12 +-- cmd/vulnerabilities.go | 178 ++---------------------------------- cmd/vulnerabilities_test.go | 81 ---------------- 5 files changed, 17 insertions(+), 392 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index f84473c6..0371c34c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -72,7 +72,6 @@ func init() { rootCmd.AddCommand(incidentsCmd) rootCmd.AddCommand(rumCmd) rootCmd.AddCommand(cicdCmd) - rootCmd.AddCommand(vulnerabilitiesCmd) rootCmd.AddCommand(staticAnalysisCmd) rootCmd.AddCommand(downtimeCmd) rootCmd.AddCommand(tagsCmd) diff --git a/cmd/security.go b/cmd/security.go index c96bd1b4..e00a832a 100644 --- a/cmd/security.go +++ b/cmd/security.go @@ -76,38 +76,6 @@ Security findings provide insights into security posture and vulnerabilities across your infrastructure and applications.`, } -var securityFindingsListCmd = &cobra.Command{ - Use: "list", - Short: "List security findings", - Long: `List security findings with optional filtering and pagination. - -EXAMPLES: - # List all findings - pup security findings list - - # Filter by status - pup security findings list --status=critical - - # Paginate results - pup security findings list --page-size=50 --page-number=1`, - RunE: runSecurityFindingsList, -} - -var securityFindingsGetCmd = &cobra.Command{ - Use: "get [finding-id]", - Short: "Get security finding details", - Long: `Get detailed information about a specific security finding. - -EXAMPLES: - # Get finding details - pup security findings get finding-abc-123 - - # Get finding with table output - pup security findings get finding-abc-123 --output=table`, - Args: cobra.ExactArgs(1), - RunE: runSecurityFindingsGet, -} - var securityFindingsSearchCmd = &cobra.Command{ Use: "search", Short: "Search security findings", @@ -133,29 +101,13 @@ EXAMPLES: } var ( - // Findings list flags - findingsPageSize int64 - findingsStatus string - findingsEvaluation string - findingsRuleID string - findingsResourceType string - // Findings search flags - findingsQuery string - findingsLimit int32 - findingsPageCursor string - findingsSort string + findingsQuery string + findingsLimit int32 + findingsSort string ) func init() { - // Findings list flags - securityFindingsListCmd.Flags().Int64Var(&findingsPageSize, "page-size", 100, "Number of findings per page (max: 1000)") - securityFindingsListCmd.Flags().StringVar(&findingsPageCursor, "page-cursor", "", "Page cursor for pagination") - securityFindingsListCmd.Flags().StringVar(&findingsStatus, "status", "", "Filter by status: critical, high, medium, low, info") - securityFindingsListCmd.Flags().StringVar(&findingsEvaluation, "evaluation", "", "Filter by evaluation: pass, fail") - securityFindingsListCmd.Flags().StringVar(&findingsRuleID, "rule-id", "", "Filter by rule ID") - securityFindingsListCmd.Flags().StringVar(&findingsResourceType, "resource-type", "", "Filter by resource type") - // Findings search flags securityFindingsSearchCmd.Flags().StringVar(&findingsQuery, "query", "", "Search query using log search syntax (required)") securityFindingsSearchCmd.Flags().Int32Var(&findingsLimit, "limit", 100, "Maximum results (1-1000)") @@ -165,7 +117,7 @@ func init() { // Command hierarchy securityRulesCmd.AddCommand(securityRulesListCmd, securityRulesGetCmd) securitySignalsCmd.AddCommand(securitySignalsListCmd) - securityFindingsCmd.AddCommand(securityFindingsListCmd, securityFindingsGetCmd, securityFindingsSearchCmd) + securityFindingsCmd.AddCommand(securityFindingsSearchCmd) securityCmd.AddCommand(securityRulesCmd, securitySignalsCmd, securityFindingsCmd) } @@ -239,87 +191,6 @@ func runSecuritySignalsList(cmd *cobra.Command, args []string) error { return nil } -func runSecurityFindingsList(cmd *cobra.Command, args []string) error { - client, err := getClient() - if err != nil { - return err - } - - api := datadogV2.NewSecurityMonitoringApi(client.V2()) - - // Build optional parameters with filtering - opts := datadogV2.ListFindingsOptionalParameters{} - - if findingsPageSize > 0 { - if findingsPageSize > 1000 { - findingsPageSize = 1000 - } - opts.WithPageLimit(findingsPageSize) - } - - if findingsPageCursor != "" { - opts.WithPageCursor(findingsPageCursor) - } - - if findingsStatus != "" { - status, err := datadogV2.NewFindingStatusFromValue(findingsStatus) - if err != nil { - return fmt.Errorf("invalid status value '%s': must be one of critical, high, medium, low, info", findingsStatus) - } - opts.WithFilterStatus(*status) - } - - if findingsEvaluation != "" { - evaluation, err := datadogV2.NewFindingEvaluationFromValue(findingsEvaluation) - if err != nil { - return fmt.Errorf("invalid evaluation value '%s': must be one of pass, fail", findingsEvaluation) - } - opts.WithFilterEvaluation(*evaluation) - } - - if findingsRuleID != "" { - opts.WithFilterRuleId(findingsRuleID) - } - - if findingsResourceType != "" { - opts.WithFilterResourceType(findingsResourceType) - } - - resp, r, err := api.ListFindings(client.Context(), opts) - if err != nil { - return formatAPIError("list security findings", err, r) - } - - output, err := formatter.FormatOutput(resp, formatter.OutputFormat(outputFormat)) - if err != nil { - return err - } - printOutput("%s\n", output) - return nil -} - -func runSecurityFindingsGet(cmd *cobra.Command, args []string) error { - client, err := getClient() - if err != nil { - return err - } - - findingID := args[0] - api := datadogV2.NewSecurityMonitoringApi(client.V2()) - - resp, r, err := api.GetFinding(client.Context(), findingID) - if err != nil { - return formatAPIError("get security finding", err, r) - } - - output, err := formatter.FormatOutput(resp, formatter.OutputFormat(outputFormat)) - if err != nil { - return err - } - printOutput("%s\n", output) - return nil -} - func runSecurityFindingsSearch(cmd *cobra.Command, args []string) error { client, err := getClient() if err != nil { diff --git a/cmd/security_test.go b/cmd/security_test.go index cfdda4df..0a81713c 100644 --- a/cmd/security_test.go +++ b/cmd/security_test.go @@ -119,16 +119,16 @@ func TestSecurityFindingsCmd(t *testing.T) { t.Error("Short description is empty") } - // Check for list subcommand + // Check for search subcommand commands := securityFindingsCmd.Commands() - foundList := false + foundSearch := false for _, cmd := range commands { - if cmd.Use == "list" { - foundList = true + if cmd.Use == "search" { + foundSearch = true } } - if !foundList { - t.Error("Missing findings list subcommand") + if !foundSearch { + t.Error("Missing findings search subcommand") } } diff --git a/cmd/vulnerabilities.go b/cmd/vulnerabilities.go index 0ed7c2f0..f5ae694b 100644 --- a/cmd/vulnerabilities.go +++ b/cmd/vulnerabilities.go @@ -7,46 +7,12 @@ package cmd import ( "fmt" - "time" "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" "github.com/DataDog/pup/pkg/formatter" "github.com/spf13/cobra" ) -var vulnerabilitiesCmd = &cobra.Command{ - Use: "vulnerabilities", - Short: "Manage security vulnerabilities", - Long: `Search and list security vulnerabilities in your applications. - -CAPABILITIES: - • Search vulnerabilities with custom queries - • Filter by severity, status, service, and repository - • Track vulnerability remediation status - -EXAMPLES: - # Search for critical vulnerabilities - pup vulnerabilities search --query="severity:critical" - - # List all open vulnerabilities - pup vulnerabilities list --severity="critical,high" --status="open" - -AUTHENTICATION: - Requires either OAuth2 authentication or API keys.`, -} - -var vulnerabilitiesSearchCmd = &cobra.Command{ - Use: "search", - Short: "Search vulnerabilities", - RunE: runVulnerabilitiesSearch, -} - -var vulnerabilitiesListCmd = &cobra.Command{ - Use: "list", - Short: "List vulnerabilities", - RunE: runVulnerabilitiesList, -} - var staticAnalysisCmd = &cobra.Command{ Use: "static-analysis", Short: "Manage static analysis", @@ -139,41 +105,16 @@ var staticAnalysisCoverageGetCmd = &cobra.Command{ } var ( - vulnQuery string - vulnSeverity string - vulnStatus string - vulnService string - vulnRepository string - vulnFrom string - vulnTo string - vulnLimit int32 - vulnOffset int32 - saRepository string - saBranch string - saLanguage string - saFrom string - saTo string - saSeverity string - saStatus string + saRepository string + saBranch string + saLanguage string + saFrom string + saTo string + saSeverity string + saStatus string ) func init() { - vulnerabilitiesSearchCmd.Flags().StringVarP(&vulnQuery, "query", "q", "", "Search query (required)") - vulnerabilitiesSearchCmd.Flags().StringVar(&vulnFrom, "from", "", "Start time") - vulnerabilitiesSearchCmd.Flags().StringVar(&vulnTo, "to", "", "End time") - vulnerabilitiesSearchCmd.Flags().Int32Var(&vulnLimit, "limit", 100, "Maximum results") - vulnerabilitiesSearchCmd.Flags().Int32Var(&vulnOffset, "offset", 0, "Results offset") - if err := vulnerabilitiesSearchCmd.MarkFlagRequired("query"); err != nil { - panic(fmt.Errorf("failed to mark flag as required: %w", err)) - } - - vulnerabilitiesListCmd.Flags().StringVar(&vulnSeverity, "severity", "", "Filter by severity") - vulnerabilitiesListCmd.Flags().StringVar(&vulnStatus, "status", "", "Filter by status") - vulnerabilitiesListCmd.Flags().StringVar(&vulnService, "service", "", "Filter by service") - vulnerabilitiesListCmd.Flags().StringVar(&vulnRepository, "repository", "", "Filter by repository") - vulnerabilitiesListCmd.Flags().Int32Var(&vulnLimit, "limit", 100, "Maximum results") - vulnerabilitiesListCmd.Flags().Int32Var(&vulnOffset, "offset", 0, "Results offset") - staticAnalysisASTListCmd.Flags().StringVar(&saRepository, "repository", "", "Filter by repository") staticAnalysisASTListCmd.Flags().StringVar(&saBranch, "branch", "", "Filter by branch") staticAnalysisASTListCmd.Flags().StringVar(&saLanguage, "language", "", "Filter by language") @@ -189,7 +130,6 @@ func init() { staticAnalysisCoverageListCmd.Flags().StringVar(&saFrom, "from", "", "Start time") staticAnalysisCoverageListCmd.Flags().StringVar(&saTo, "to", "", "End time") - vulnerabilitiesCmd.AddCommand(vulnerabilitiesSearchCmd, vulnerabilitiesListCmd) staticAnalysisASTCmd.AddCommand(staticAnalysisASTListCmd, staticAnalysisASTGetCmd) staticAnalysisCustomRulesetsCmd.AddCommand(staticAnalysisCustomRulesetsListCmd, staticAnalysisCustomRulesetsGetCmd) staticAnalysisSCACmd.AddCommand(staticAnalysisSCAListCmd, staticAnalysisSCAGetCmd) @@ -197,110 +137,6 @@ func init() { staticAnalysisCmd.AddCommand(staticAnalysisASTCmd, staticAnalysisCustomRulesetsCmd, staticAnalysisSCACmd, staticAnalysisCoverageCmd) } -func runVulnerabilitiesSearch(cmd *cobra.Command, args []string) error { - client, err := getClient() - if err != nil { - return err - } - - api := datadogV2.NewSecurityMonitoringApi(client.V2()) - body := datadogV2.SecurityMonitoringSignalListRequest{ - Filter: &datadogV2.SecurityMonitoringSignalListRequestFilter{ - Query: &vulnQuery, - }, - Page: &datadogV2.SecurityMonitoringSignalListRequestPage{}, - } - - // Parse time strings to time.Time - if vulnFrom != "" { - fromTime, err := time.Parse(time.RFC3339, vulnFrom) - if err != nil { - return fmt.Errorf("invalid from time format (use RFC3339): %w", err) - } - body.Filter.From = &fromTime - } - if vulnTo != "" { - toTime, err := time.Parse(time.RFC3339, vulnTo) - if err != nil { - return fmt.Errorf("invalid to time format (use RFC3339): %w", err) - } - body.Filter.To = &toTime - } - if vulnLimit > 0 { - limit := int32(vulnLimit) - body.Page.Limit = &limit - } - // Note: Offset pagination is not supported, use cursor-based pagination instead - - opts := datadogV2.NewSearchSecurityMonitoringSignalsOptionalParameters() - opts = opts.WithBody(body) - resp, r, err := api.SearchSecurityMonitoringSignals(client.Context(), *opts) - if err != nil { - if r != nil { - return fmt.Errorf("failed to search vulnerabilities: %w (status: %d)", err, r.StatusCode) - } - return fmt.Errorf("failed to search vulnerabilities: %w", err) - } - - output, err := formatter.FormatOutput(resp, formatter.OutputFormat(outputFormat)) - if err != nil { - return err - } - fmt.Println(output) - return nil -} - -func runVulnerabilitiesList(cmd *cobra.Command, args []string) error { - client, err := getClient() - if err != nil { - return err - } - - api := datadogV2.NewSecurityMonitoringApi(client.V2()) - opts := datadogV2.NewListSecurityMonitoringSignalsOptionalParameters() - - var queryParts []string - if vulnSeverity != "" { - queryParts = append(queryParts, fmt.Sprintf("severity:(%s)", vulnSeverity)) - } - if vulnStatus != "" { - queryParts = append(queryParts, fmt.Sprintf("status:(%s)", vulnStatus)) - } - if vulnService != "" { - queryParts = append(queryParts, fmt.Sprintf("service:%s", vulnService)) - } - if vulnRepository != "" { - queryParts = append(queryParts, fmt.Sprintf("repository:%s", vulnRepository)) - } - - if len(queryParts) > 0 { - filterQuery := queryParts[0] - for i := 1; i < len(queryParts); i++ { - filterQuery = fmt.Sprintf("%s AND %s", filterQuery, queryParts[i]) - } - opts = opts.WithFilterQuery(filterQuery) - } - - if vulnLimit > 0 { - opts = opts.WithPageLimit(int32(vulnLimit)) - } - - resp, r, err := api.ListSecurityMonitoringSignals(client.Context(), *opts) - if err != nil { - if r != nil { - return fmt.Errorf("failed to list vulnerabilities: %w (status: %d)", err, r.StatusCode) - } - return fmt.Errorf("failed to list vulnerabilities: %w", err) - } - - output, err := formatter.FormatOutput(resp, formatter.OutputFormat(outputFormat)) - if err != nil { - return err - } - fmt.Println(output) - return nil -} - func runStaticAnalysisASTList(cmd *cobra.Command, args []string) error { result := map[string]interface{}{ "data": []map[string]interface{}{}, diff --git a/cmd/vulnerabilities_test.go b/cmd/vulnerabilities_test.go index 57c9b95e..47e8bbb0 100644 --- a/cmd/vulnerabilities_test.go +++ b/cmd/vulnerabilities_test.go @@ -9,77 +9,6 @@ import ( "testing" ) -func TestVulnerabilitiesCmd(t *testing.T) { - if vulnerabilitiesCmd == nil { - t.Fatal("vulnerabilitiesCmd is nil") - } - - if vulnerabilitiesCmd.Use != "vulnerabilities" { - t.Errorf("Use = %s, want vulnerabilities", vulnerabilitiesCmd.Use) - } - - if vulnerabilitiesCmd.Short == "" { - t.Error("Short description is empty") - } - - if vulnerabilitiesCmd.Long == "" { - t.Error("Long description is empty") - } -} - -func TestVulnerabilitiesCmd_Subcommands(t *testing.T) { - expectedCommands := []string{"search", "list"} - - commands := vulnerabilitiesCmd.Commands() - - commandMap := make(map[string]bool) - for _, cmd := range commands { - commandMap[cmd.Use] = true - } - - for _, expected := range expectedCommands { - if !commandMap[expected] { - t.Errorf("Missing subcommand: %s", expected) - } - } -} - -func TestVulnerabilitiesSearchCmd(t *testing.T) { - if vulnerabilitiesSearchCmd == nil { - t.Fatal("vulnerabilitiesSearchCmd is nil") - } - - if vulnerabilitiesSearchCmd.Use != "search" { - t.Errorf("Use = %s, want search", vulnerabilitiesSearchCmd.Use) - } - - if vulnerabilitiesSearchCmd.Short == "" { - t.Error("Short description is empty") - } - - if vulnerabilitiesSearchCmd.RunE == nil { - t.Error("RunE is nil") - } -} - -func TestVulnerabilitiesListCmd(t *testing.T) { - if vulnerabilitiesListCmd == nil { - t.Fatal("vulnerabilitiesListCmd is nil") - } - - if vulnerabilitiesListCmd.Use != "list" { - t.Errorf("Use = %s, want list", vulnerabilitiesListCmd.Use) - } - - if vulnerabilitiesListCmd.Short == "" { - t.Error("Short description is empty") - } - - if vulnerabilitiesListCmd.RunE == nil { - t.Error("RunE is nil") - } -} - func TestStaticAnalysisCmd(t *testing.T) { if staticAnalysisCmd == nil { t.Fatal("staticAnalysisCmd is nil") @@ -171,16 +100,6 @@ func TestStaticAnalysisCoverageCmd(t *testing.T) { } } -func TestVulnerabilitiesCmd_ParentChild(t *testing.T) { - commands := vulnerabilitiesCmd.Commands() - - for _, cmd := range commands { - if cmd.Parent() != vulnerabilitiesCmd { - t.Errorf("Command %s parent is not vulnerabilitiesCmd", cmd.Use) - } - } -} - func TestStaticAnalysisCmd_ParentChild(t *testing.T) { commands := staticAnalysisCmd.Commands()