From 783275c046eac9a7c7118880b4d57c266d5c0ccc Mon Sep 17 00:00:00 2001 From: anjali-deore <200181980+cx-anjali-deore@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:13:13 +0530 Subject: [PATCH 01/19] Sca Triage Implementation --- internal/commands/predicates.go | 93 +++++++++++++++++++++++++--- internal/params/binds.go | 1 + internal/params/envs.go | 1 + internal/params/flags.go | 3 + internal/params/keys.go | 1 + internal/wrappers/predicates-http.go | 54 ++++++++++++++-- internal/wrappers/predicates.go | 27 +++++++- 7 files changed, 165 insertions(+), 15 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index eedda776e..6682be9ce 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -1,6 +1,7 @@ package commands import ( + "encoding/json" "github.com/MakeNowJust/heredoc" "github.com/checkmarx/ast-cli/internal/commands/util/printer" "github.com/checkmarx/ast-cli/internal/params" @@ -116,6 +117,7 @@ func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesW --scan-type `, ), + RunE: runTriageUpdate(resultsPredicatesWrapper, featureFlagsWrapper, customStatesWrapper), } @@ -126,9 +128,8 @@ func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesW triageUpdateCmd.PersistentFlags().Int(params.CustomStateIDFlag, -1, "Specify the ID of the states that you would like to apply to this result") triageUpdateCmd.PersistentFlags().String(params.CommentFlag, "", "Optional comment") triageUpdateCmd.PersistentFlags().String(params.ScanTypeFlag, "", "Scan Type") + triageUpdateCmd.PersistentFlags().StringSlice(params.VulnerabilitiesFlag, []string{}, "List Vulnerabilities string") - markFlagAsRequired(triageUpdateCmd, params.SimilarityIDFlag) - markFlagAsRequired(triageUpdateCmd, params.SeverityFlag) markFlagAsRequired(triageUpdateCmd, params.ProjectIDFlag) markFlagAsRequired(triageUpdateCmd, params.ScanTypeFlag) @@ -181,6 +182,7 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, _ []string) error { + similarityID, _ := cmd.Flags().GetString(params.SimilarityIDFlag) projectID, _ := cmd.Flags().GetString(params.ProjectIDFlag) severity, _ := cmd.Flags().GetString(params.SeverityFlag) @@ -190,38 +192,109 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, scanType, _ := cmd.Flags().GetString(params.ScanTypeFlag) // check if the current tenant has critical severity available flagResponse, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.CVSSV3Enabled) + vulnerabilityDetails, _ := cmd.Flags().GetStringSlice(params.VulnerabilitiesFlag) + criticalEnabled := flagResponse.Status if !criticalEnabled && strings.EqualFold(severity, "critical") { return errors.Errorf("%s", "Critical severity is not available for your tenant.This severity status will be enabled shortly") } - var err error state, customStateID, err = determineSystemOrCustomState(customStatesWrapper, featureFlagsWrapper, state, customStateID) + predicate, err := handlePredicate(vulnerabilityDetails, similarityID, projectID, severity, state, customStateID, comment, scanType) if err != nil { return err } + _, err = resultsPredicatesWrapper.PredicateSeverityAndState(predicate, scanType) + if err != nil { + return errors.Wrapf(err, "%s", "Failed updating the predicate") + } + return nil + } +} - predicate := &wrappers.PredicateRequest{ +func handlePredicate(vulnerabilityDetails []string, similarityID string, projectID string, severity string, state string, customStateID int, comment string, scanType string) (interface{}, error) { + // TODO trim space here + scanType = strings.ToLower(scanType) + if strings.EqualFold(scanType, Sca) { + state = transformState(state) + payload, err := handleScaTriage(vulnerabilityDetails, comment, state, projectID) + if err != nil { + return nil, errors.Errorf("%s", "Failed Sca handling") + } + return payload, nil + } else { + payload := &wrappers.PredicateRequest{ SimilarityID: similarityID, ProjectID: projectID, Severity: severity, Comment: comment, } - if state != "" { - predicate.State = &state + payload.State = &state } else { - predicate.CustomStateID = &customStateID + payload.CustomStateID = &customStateID } + return payload, nil + } +} - _, err = resultsPredicatesWrapper.PredicateSeverityAndState(predicate, scanType) +func transformState(state string) string { + state = strings.ToLower(strings.TrimSpace(state)) + switch state { + case strings.ToLower(params.ToVerify): + return wrappers.ToVerify + case strings.ToLower(params.URGENT): + return wrappers.Urgent + case strings.ToLower(params.NotExploitable): + return wrappers.NotExploitable + case strings.ToLower(params.ProposedNotExploitable): + return wrappers.ProposedNotExploitable + case strings.ToLower(params.CONFIRMED): + return wrappers.Confirmed + } + return "" +} + +// TODO rename the function +func handleScaTriage(vulnerabilityDetails []string, comment string, state string, projectId string) (interface{}, error) { + scaTriageInfo := make(map[string]interface{}) + for _, vulnerability := range vulnerabilityDetails { + vulnerabilityKeyVal := strings.SplitN(vulnerability, "=", 2) + err := validateVulnerabilityDetails(vulnerabilityKeyVal) if err != nil { - return errors.Wrapf(err, "%s", "Failed updating the predicate") + return nil, err } + scaTriageInfo[vulnerabilityKeyVal[0]] = vulnerabilityKeyVal[1] + } + scaTriageInfo["projectIds"] = []string{projectId} + actionInfo := make(map[string]interface{}) + actionInfo["actionType"] = params.ChangeState + actionInfo["value"] = state + actionInfo["comment"] = comment + scaTriageInfo["actions"] = []map[string]interface{}{actionInfo} + + b, err := json.Marshal(scaTriageInfo) + if err != nil { + return nil, errors.Errorf("Failed to serialize vulnerabilities %s", scaTriageInfo) + } + payload := &wrappers.ScaPredicateRequest{} + err = json.Unmarshal(b, payload) + if err != nil { + return nil, errors.Errorf("Failed to deserialize vulnerabilities %s", b) + } + return payload, nil +} - return nil +func validateVulnerabilityDetails(vulnerability []string) error { + if len(vulnerability) != params.KeyValuePairSize { + return errors.Errorf("Invalid vulnerabilities. It should be in a KEY=VALUE format") + } + if len(strings.Split(vulnerability[1], ",")) > params.SingleValueSize || len(strings.Split(vulnerability[1], ",")) > params.SingleValueSize { + return errors.Errorf("Cannot specify multiple values to key %s", vulnerability[0]) } + return nil } + func determineSystemOrCustomState(customStatesWrapper wrappers.CustomStatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, state string, customStateID int) (string, int, error) { if !isCustomState(state) { return state, -1, nil diff --git a/internal/params/binds.go b/internal/params/binds.go index e8d4140af..d88ae66d8 100644 --- a/internal/params/binds.go +++ b/internal/params/binds.go @@ -25,6 +25,7 @@ var EnvVarsBinds = []struct { {ScsScanOverviewPathKey, ScsScanOverviewPathEnv, "api/micro-engines/read/scans/%s/scan-overview"}, {SastResultsPathKey, SastResultsPathEnv, "api/sast-results"}, {SastResultsPredicatesPathKey, SastResultsPredicatesPathEnv, "api/sast-results-predicates"}, + {ScaResultsPredicatesPathKey, ScaResultsPredicatesPathEnv, "api/sca/management-of-risk/package-vulnerabilities"}, {KicsResultsPathKey, KicsResultsPathEnv, "api/kics-results"}, {KicsResultsPredicatesPathKey, KicsResultsPredicatesPathEnv, "api/kics-results-predicates"}, {ScsResultsReadPredicatesPathKey, ScsResultsReadPredicatesPathEnv, "api/micro-engines/read/predicates"}, diff --git a/internal/params/envs.go b/internal/params/envs.go index ef3bf06ef..467523110 100644 --- a/internal/params/envs.go +++ b/internal/params/envs.go @@ -28,6 +28,7 @@ const ( ScsScanOverviewPathEnv = "CX_SCS_SCAN_OVERVIEW_PATH" SastResultsPathEnv = "CX_SAST_RESULTS_PATH" SastResultsPredicatesPathEnv = "CX_SAST_RESULTS_PREDICATES_PATH" + ScaResultsPredicatesPathEnv = "CX_SCA_RESULTS_PREDICATES_PATH" KicsResultsPathEnv = "CX_KICS_RESULTS_PATH" KicsResultsPredicatesPathEnv = "CX_KICS_RESULTS_PREDICATES_PATH" ScsResultsReadPredicatesPathEnv = "CX_SCS_RESULTS_PREDICATES_READ_PATH" diff --git a/internal/params/flags.go b/internal/params/flags.go index 1800cde07..087b5f515 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -119,7 +119,10 @@ const ( "Example: scan --threshold \"sast-high=10;sca-high=5;iac-security-low=10\"" KeyValuePairSize = 2 WaitDelayDefault = 5 + SingleValueSize = 1 + ChangeState = "ChangeState" SimilarityIDFlag = "similarity-id" + VulnerabilitiesFlag = "vulnerabilities" SeverityFlag = "severity" StateFlag = "state" CustomStateIDFlag = "state-id" diff --git a/internal/params/keys.go b/internal/params/keys.go index 90bb6a09b..f1daa3092 100644 --- a/internal/params/keys.go +++ b/internal/params/keys.go @@ -57,6 +57,7 @@ var ( LogsPathKey = strings.ToLower(LogsPathEnv) LogsEngineLogPathKey = strings.ToLower(LogsEngineLogPathEnv) SastResultsPredicatesPathKey = strings.ToLower(SastResultsPredicatesPathEnv) + ScaResultsPredicatesPathKey = strings.ToLower(ScaResultsPredicatesPathEnv) KicsResultsPredicatesPathKey = strings.ToLower(KicsResultsPredicatesPathEnv) ScsResultsReadPredicatesPathKey = strings.ToLower(ScsResultsReadPredicatesPathEnv) ScsResultsWritePredicatesPathKey = strings.ToLower(ScsResultsWritePredicatesPathEnv) diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index 6f0905728..1d80e8509 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -65,12 +65,56 @@ func (r *ResultsPredicatesHTTPWrapper) SetPath(newPath string) { r.path = newPath } -func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate *PredicateRequest, scanType string) ( +func (r *ResultsPredicatesHTTPWrapper) PredicateScaState(predicate *ScaPredicateRequest) (*WebError, error) { + clientTimeout := viper.GetUint(params.ClientTimeoutKey) + jsonBody, err := json.Marshal(predicate) + if err != nil { + return nil, err + } + var scaTriageAPIPath string + + scaTriageAPIPath = viper.GetString(params.ScaResultsPredicatesPathEnv) + logger.PrintIfVerbose(fmt.Sprintf("Sending POST request to %s", scaTriageAPIPath)) + logger.PrintIfVerbose(fmt.Sprintf("Request Payload: %s", string(jsonBody))) + + r.SetPath(scaTriageAPIPath) + + resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.path, bytes.NewBuffer(jsonBody), true, clientTimeout) + if err != nil { + return nil, err + } + logger.PrintIfVerbose(fmt.Sprintf("Response : %s", resp.Status)) + defer func() { + _ = resp.Body.Close() + }() + + switch resp.StatusCode { + case http.StatusOK, http.StatusCreated: + fmt.Println("Predicate updated successfully.") + return nil, nil + case http.StatusForbidden: + return nil, errors.Errorf("You do not have permission to update state") + case http.StatusNotFound: + return nil, errors.Errorf("Predicate not found.") + case http.StatusBadRequest, http.StatusInternalServerError: + return nil, errors.Errorf("Predicate bad request.") + + default: + return nil, errors.Errorf("response status code %d", resp.StatusCode) + } +} + +func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate interface{}, scanType string) ( *WebError, error, ) { clientTimeout := viper.GetUint(params.ClientTimeoutKey) - b := [...]PredicateRequest{*predicate} - jsonBytes, err := json.Marshal(b) + var predicateModel interface{} + if !strings.EqualFold(strings.TrimSpace(scanType), params.ScaType) { + predicateModel = []interface{}{predicate} + } else { + predicateModel = predicate + } + jsonBytes, err := json.Marshal(predicateModel) if err != nil { return nil, err } @@ -82,6 +126,8 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate *Predi triageAPIPath = viper.GetString(params.KicsResultsPredicatesPathKey) } else if strings.EqualFold(strings.TrimSpace(scanType), params.ScsType) { triageAPIPath = viper.GetString(params.ScsResultsWritePredicatesPathKey) + } else if strings.EqualFold(strings.TrimSpace(scanType), params.ScaType) { + triageAPIPath = viper.GetString(params.ScaResultsPredicatesPathEnv) } else { return nil, errors.Errorf(invalidScanType, scanType) } @@ -91,7 +137,7 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate *Predi r.SetPath(triageAPIPath) - resp, err := SendHTTPRequest(http.MethodPost, r.path, bytes.NewBuffer(jsonBytes), true, clientTimeout) + resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.path, bytes.NewBuffer(jsonBytes), true, clientTimeout) if err != nil { return nil, err } diff --git a/internal/wrappers/predicates.go b/internal/wrappers/predicates.go index 709e5b3be..25a41ce4e 100644 --- a/internal/wrappers/predicates.go +++ b/internal/wrappers/predicates.go @@ -22,6 +22,31 @@ type PredicateRequest struct { Severity string `json:"severity"` } +type ScaPredicateRequest struct { + PackageName string `json:"packageName"` + PackageVersion string `json:"packageVersion"` + PackageManager string `json:"packageManager"` + VulnerabilityId string `json:"vulnerabilityId"` + ProjectIds []string `json:"projectIds"` + Actions []ScaAction `json:"actions"` +} + +type State string + +const ( + ToVerify string = "ToVerify" + Confirmed string = "Confirmed" + NotExploitable string = "NotExploitable" + ProposedNotExploitable string = "ProposedNotExploitable" + Urgent string = "Urgent" +) + +type ScaAction struct { + ActionType string `json:"actionType"` + Value string `json:"value"` + Comment string `json:"comment"` +} + type Predicate struct { BasePredicate ID string `json:"ID"` @@ -52,7 +77,7 @@ type CustomStatesWrapper interface { } type ResultsPredicatesWrapper interface { - PredicateSeverityAndState(predicate *PredicateRequest, scanType string) (*WebError, error) + PredicateSeverityAndState(predicate interface{}, scanType string) (*WebError, error) GetAllPredicatesForSimilarityID( similarityID string, projectID string, scannerType string, ) (*PredicatesCollectionResponseModel, *WebError, error) From 3525d2064e0c51b32e0784d47c8a85bdec01cde7 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 19 Nov 2025 09:40:23 +0530 Subject: [PATCH 02/19] sca-triage-update-show --- internal/commands/predicates.go | 113 ++++++++++++++++------ internal/commands/root_test.go | 4 +- internal/wrappers/mock/predicates-mock.go | 11 ++- internal/wrappers/predicates-http.go | 44 ++++++++- internal/wrappers/predicates.go | 28 ++++++ 5 files changed, 164 insertions(+), 36 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 5bed3a8b4..84ca9e224 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -1,6 +1,8 @@ package commands import ( + "fmt" + "sort" "strings" "time" @@ -94,8 +96,9 @@ func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWra triageShowCmd.PersistentFlags().String(params.SimilarityIDFlag, "", "Similarity ID") triageShowCmd.PersistentFlags().String(params.ProjectIDFlag, "", "Project ID") triageShowCmd.PersistentFlags().String(params.ScanTypeFlag, "", "Scan Type") + triageShowCmd.PersistentFlags().StringSlice(params.VulnerabilitiesFlag, []string{}, "List Vulnerabilities string") - markFlagAsRequired(triageShowCmd, params.SimilarityIDFlag) + // markFlagAsRequired(triageShowCmd, params.SimilarityIDFlag) markFlagAsRequired(triageShowCmd, params.ProjectIDFlag) markFlagAsRequired(triageShowCmd, params.ScanTypeFlag) @@ -147,44 +150,50 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f similarityID, _ := cmd.Flags().GetString(params.SimilarityIDFlag) scanType, _ := cmd.Flags().GetString(params.ScanTypeFlag) projectID, _ := cmd.Flags().GetString(params.ProjectIDFlag) - + vulnerabilityDetails, _ := cmd.Flags().GetStringSlice(params.VulnerabilitiesFlag) projectIDs := strings.Split(projectID, ",") if len(projectIDs) > 1 { return errors.Errorf("%s", "Multiple project-ids are not allowed.") } - predicatesCollection, errorModel, err = resultsPredicatesWrapper.GetAllPredicatesForSimilarityID( - similarityID, - projectID, - scanType, - ) - - if err != nil { - return errors.Wrapf(err, "%s", "Failed showing the predicate") - } - - // Checking the response - if errorModel != nil { - return errors.Errorf( - "%s: CODE: %d, %s", - "Failed showing the predicate.", - errorModel.Code, - errorModel.Message, - ) - } else if predicatesCollection != nil { - err = printByFormat(cmd, toPredicatesView(*predicatesCollection)) + if strings.EqualFold(scanType, params.ScaType) { + //SCA + scaResponse, err := resultsPredicatesWrapper.ScaPredicateResult(vulnerabilityDetails, projectID) + if err != nil { + return errors.Wrapf(err, "%s", "Failed showing the predicate") + } + err = printByFormat(cmd, toScaPredicateResultView(*scaResponse)) if err != nil { return err } + return nil + } else { + //other than SCA + predicatesCollection, errorModel, err = resultsPredicatesWrapper.GetAllPredicatesForSimilarityID(similarityID, projectID, scanType) + + // Checking the response + if errorModel != nil { + return errors.Errorf( + "%s: CODE: %d, %s", + "Failed showing the predicate.", + errorModel.Code, + errorModel.Message, + ) + } else if predicatesCollection != nil { + err = printByFormat(cmd, toPredicatesView(*predicatesCollection)) + if err != nil { + return err + } + } + + return nil } - return nil } } func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) func(*cobra.Command, []string) error { return func(cmd *cobra.Command, _ []string) error { - similarityID, _ := cmd.Flags().GetString(params.SimilarityIDFlag) projectID, _ := cmd.Flags().GetString(params.ProjectIDFlag) severity, _ := cmd.Flags().GetString(params.SeverityFlag) @@ -204,7 +213,7 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, state, customStateID, err = determineSystemOrCustomState(customStatesWrapper, featureFlagsWrapper, state, customStateID) predicate, err := handlePredicate(vulnerabilityDetails, similarityID, projectID, severity, state, customStateID, comment, scanType) if err != nil { - return err + return errors.Wrapf(err, "%s", "Failed updating the predicate") } _, err = resultsPredicatesWrapper.PredicateSeverityAndState(predicate, scanType) if err != nil { @@ -215,13 +224,13 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, } func handlePredicate(vulnerabilityDetails []string, similarityID string, projectID string, severity string, state string, customStateID int, comment string, scanType string) (interface{}, error) { - // TODO trim space here scanType = strings.ToLower(scanType) + scanType = strings.TrimSpace(scanType) if strings.EqualFold(scanType, Sca) { state = transformState(state) payload, err := handleScaTriage(vulnerabilityDetails, comment, state, projectID) if err != nil { - return nil, errors.Errorf("%s", "Failed Sca handling") + return nil, err } return payload, nil } else { @@ -257,7 +266,6 @@ func transformState(state string) string { return "" } -// TODO rename the function func handleScaTriage(vulnerabilityDetails []string, comment string, state string, projectId string) (interface{}, error) { scaTriageInfo := make(map[string]interface{}) for _, vulnerability := range vulnerabilityDetails { @@ -268,18 +276,30 @@ func handleScaTriage(vulnerabilityDetails []string, comment string, state string } scaTriageInfo[vulnerabilityKeyVal[0]] = vulnerabilityKeyVal[1] } + + if scaTriageInfo["packageName"] == nil && scaTriageInfo["packagename"] == nil { + return nil, errors.Errorf("Package name is required") + } + if scaTriageInfo["packageVersion"] == nil && scaTriageInfo["packageversion"] == nil { + return nil, errors.Errorf("Package version is required") + } + if scaTriageInfo["packageManager"] == nil && scaTriageInfo["packagemanager"] == nil { + fmt.Println("Package manager is required") + return nil, errors.Errorf("Package manager is required") + } + scaTriageInfo["projectIds"] = []string{projectId} actionInfo := make(map[string]interface{}) actionInfo["actionType"] = params.ChangeState actionInfo["value"] = state actionInfo["comment"] = comment scaTriageInfo["actions"] = []map[string]interface{}{actionInfo} - b, err := json.Marshal(scaTriageInfo) if err != nil { return nil, errors.Errorf("Failed to serialize vulnerabilities %s", scaTriageInfo) } payload := &wrappers.ScaPredicateRequest{} + fmt.Println("Payload: ", string(b)) err = json.Unmarshal(b, payload) if err != nil { return nil, errors.Errorf("Failed to deserialize vulnerabilities %s", b) @@ -358,6 +378,41 @@ type predicateView struct { CreatedAt time.Time `format:"name:Created at;time:01-02-06 15:04:05"` } +type scaPredicateResultView struct { + VulnerabilityID string `format:"name:Vulnerability ID"` + PackageName string `format:"name:Package Name"` + PackageVersion string `format:"name:Package Version"` + PackageManager string `format:"name:Package Manager"` + Comment string `format:"name:Comment"` + State string `format:"name:State"` + CreatedBy string `format:"name:Created By"` + CreatedAt time.Time `format:"name:Created at;time:01-02-06 15:04:05"` +} + +func toScaPredicateResultView(scaPredicateResult wrappers.ScaPredicateResult) []scaPredicateResultView { + view := []scaPredicateResultView{} + if len(scaPredicateResult.Actions) > 0 { + for _, action := range scaPredicateResult.Actions { + view = append(view, scaPredicateResultView{ + VulnerabilityID: scaPredicateResult.Context.VulnerabilityId, + PackageName: scaPredicateResult.Context.PackageName, + PackageVersion: scaPredicateResult.Context.PackageVersion, + PackageManager: scaPredicateResult.Context.PackageManager, + Comment: action.Message, + State: action.ActionValue, + CreatedBy: action.UserName, + CreatedAt: action.CreatedAt, + }) + } + } + + sort.Slice(view, func(i, j int) bool { + return view[i].CreatedAt.After(view[j].CreatedAt) + }) + + return view +} + func toPredicatesView(predicatesCollection wrappers.PredicatesCollectionResponseModel) []predicateView { projectPredicatesCollection := predicatesCollection.PredicateHistoryPerProject diff --git a/internal/commands/root_test.go b/internal/commands/root_test.go index 654594c5b..e5d8a0735 100644 --- a/internal/commands/root_test.go +++ b/internal/commands/root_test.go @@ -42,7 +42,7 @@ func createASTTestCommand() *cobra.Command { resultsPdfWrapper := &mock.ResultsPdfWrapper{} resultsJSONWrapper := &mock.ResultsJSONWrapper{} scansMockWrapper.Running = true - resultsPredicatesMockWrapper := &mock.ResultsPredicatesMockWrapper{} + resultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} groupsMockWrapper := &mock.GroupsMockWrapper{} uploadsMockWrapper := &mock.UploadsMockWrapper{} projectsMockWrapper := &mock.ProjectsMockWrapper{} @@ -79,7 +79,7 @@ func createASTTestCommand() *cobra.Command { exportWrapper, resultsPdfWrapper, resultsJSONWrapper, - resultsPredicatesMockWrapper, + resultsPredicatesWrapper, customStatesMockWrapper, codeBashingWrapper, uploadsMockWrapper, diff --git a/internal/wrappers/mock/predicates-mock.go b/internal/wrappers/mock/predicates-mock.go index 894932ef7..fb0c4be79 100644 --- a/internal/wrappers/mock/predicates-mock.go +++ b/internal/wrappers/mock/predicates-mock.go @@ -7,17 +7,17 @@ import ( "github.com/checkmarx/ast-cli/internal/wrappers" ) -type ResultsPredicatesMockWrapper struct { +type ResultsPredicatesWrapper struct { } -func (r ResultsPredicatesMockWrapper) PredicateSeverityAndState(predicate *wrappers.PredicateRequest, scanType string) ( +func (r ResultsPredicatesWrapper) PredicateSeverityAndState(predicate interface{}, scanType string) ( *wrappers.WebError, error, ) { fmt.Println("Called 'PredicateSeverityAndState' in ResultsPredicatesMockWrapper") return nil, nil } -func (r ResultsPredicatesMockWrapper) GetAllPredicatesForSimilarityID(similarityID, projectID, scannerType string) ( +func (r ResultsPredicatesWrapper) GetAllPredicatesForSimilarityID(similarityID, projectID, scannerType string) ( *wrappers.PredicatesCollectionResponseModel, *wrappers.WebError, error, ) { fmt.Println("Called 'GetAllPredicatesForSimilarityID' in ResultsPredicatesMockWrapper") @@ -43,3 +43,8 @@ func (r ResultsPredicatesMockWrapper) GetAllPredicatesForSimilarityID(similarity }, }, nil, nil } + +func (r ResultsPredicatesWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectId string) (*wrappers.ScaPredicateResult, error) { + fmt.Println("Called 'ScaPredicateResult' in ResultsPredicatesMockWrapper") + return nil, nil +} diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index 4df1d8a8b..a193bd43d 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -26,6 +26,45 @@ func NewResultsPredicatesHTTPWrapper() ResultsPredicatesWrapper { return &ResultsPredicatesHTTPWrapper{} } +func (r *ResultsPredicatesHTTPWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectId string) (*ScaPredicateResult, error) { + clientTimeout := viper.GetUint(params.ClientTimeoutKey) + r.SetPath(viper.GetString(params.ScaResultsPredicatesPathEnv)) + var request = "/entity-profile/search" + logger.PrintIfVerbose(fmt.Sprintf("Sending POST request to %s", r.path+request)) + + scaPredicateRequest := make(map[string]interface{}) + for _, vulnerability := range vulnerabilityDetails { + vulnerabilityKeyVal := strings.SplitN(vulnerability, "=", 2) + scaPredicateRequest[vulnerabilityKeyVal[0]] = vulnerabilityKeyVal[1] + } + scaPredicateRequest["projectId"] = projectId + scaPredicateRequest["actionType"] = params.ChangeState + jsonBody, err := json.Marshal(scaPredicateRequest) + if err != nil { + return nil, errors.Wrap(err, "Failed to marshal request") + } + resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.path+request, bytes.NewBuffer(jsonBody), true, clientTimeout) + if err != nil { + return nil, errors.Wrap(err, "Failed to send request") + } + defer func() { + if err == nil { + _ = resp.Body.Close() + } + }() + if resp.StatusCode != http.StatusOK { + return nil, errors.Errorf("Failed to get SCA predicate result.") + } + fmt.Println("HM Response: ", resp.Body) + decoder := json.NewDecoder(resp.Body) + var scaPredicates ScaPredicateResult + err = decoder.Decode(&scaPredicates) + if err != nil { + return nil, errors.Wrap(err, "Failed to decode response") + } + return &scaPredicates, nil +} + func (r *ResultsPredicatesHTTPWrapper) GetAllPredicatesForSimilarityID(similarityID, projectID, scannerType string) ( *PredicatesCollectionResponseModel, *WebError, error, ) { @@ -118,7 +157,6 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate interf if err != nil { return nil, err } - var triageAPIPath string if strings.EqualFold(strings.TrimSpace(scanType), params.SastType) { triageAPIPath = viper.GetString(params.SastResultsPredicatesPathKey) @@ -151,7 +189,9 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate interf // in case of ne/pne when mandatory comment arent provided, cli is not transforming error message responseMap := make(map[string]interface{}) if err := json.NewDecoder(resp.Body).Decode(&responseMap); err != nil { - logger.PrintIfVerbose(fmt.Sprintf("failed to read the response, %v", err.Error())) + if scanType != params.ScaType { // for sca, we are not getting any response in the response body, so we are not logging the error + logger.PrintIfVerbose(fmt.Sprintf("failed to read the response, %v", err.Error())) + } } else { if val, ok := responseMap["code"].(float64); ok { if val == 4002 && responseMap["message"] != nil { diff --git a/internal/wrappers/predicates.go b/internal/wrappers/predicates.go index 25a41ce4e..078b73dca 100644 --- a/internal/wrappers/predicates.go +++ b/internal/wrappers/predicates.go @@ -62,10 +62,37 @@ type PredicateHistory struct { } type PredicatesCollectionResponseModel struct { + ScaResponse interface{} `json:"scaPredicate,omitempty"` PredicateHistoryPerProject []PredicateHistory `json:"predicateHistoryPerProject"` TotalCount int `json:"totalCount"` } +type ScaPredicateResult struct { + ID string `json:"id"` + Context Context `json:"context"` + Name string `json:"name"` + Actions []Action `json:"actions"` + EntityType string `json:"entityType"` + Enabled bool `json:"enabled"` + CreatedAt time.Time `json:"createdAt"` +} + +type Context struct { + PackageManager string `json:"PackageManager"` + PackageName string `json:"PackageName"` + PackageVersion string `json:"PackageVersion"` + VulnerabilityId string `json:"VulnerabilityId"` +} + +type Action struct { + ActionType string `json:"actionType"` + ActionValue string `json:"actionValue"` + Enabled bool `json:"enabled"` + CreatedAt time.Time `json:"createdAt"` + UserName string `json:"userName"` + Message string `json:"message"` +} + type CustomState struct { ID int `json:"id"` Name string `json:"name"` @@ -77,6 +104,7 @@ type CustomStatesWrapper interface { } type ResultsPredicatesWrapper interface { + ScaPredicateResult(vulnerabilityDetails []string, projectId string) (*ScaPredicateResult, error) PredicateSeverityAndState(predicate interface{}, scanType string) (*WebError, error) GetAllPredicatesForSimilarityID( similarityID string, projectID string, scannerType string, From 21d8f0abcaa038321a1c4d0f3dbd0fd7f371f66e Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 19 Nov 2025 13:11:54 +0530 Subject: [PATCH 03/19] sca-triage-update-show1 --- internal/commands/predicates.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 84ca9e224..b70b7cd91 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -10,6 +10,7 @@ import ( "github.com/MakeNowJust/heredoc" "github.com/checkmarx/ast-cli/internal/commands/util/printer" + "github.com/checkmarx/ast-cli/internal/logger" "github.com/checkmarx/ast-cli/internal/params" "github.com/checkmarx/ast-cli/internal/wrappers" "github.com/pkg/errors" @@ -211,7 +212,7 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, } var err error state, customStateID, err = determineSystemOrCustomState(customStatesWrapper, featureFlagsWrapper, state, customStateID) - predicate, err := handlePredicate(vulnerabilityDetails, similarityID, projectID, severity, state, customStateID, comment, scanType) + predicate, err := preparePredicateRequest(vulnerabilityDetails, similarityID, projectID, severity, state, customStateID, comment, scanType) if err != nil { return errors.Wrapf(err, "%s", "Failed updating the predicate") } @@ -223,12 +224,12 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, } } -func handlePredicate(vulnerabilityDetails []string, similarityID string, projectID string, severity string, state string, customStateID int, comment string, scanType string) (interface{}, error) { +func preparePredicateRequest(vulnerabilityDetails []string, similarityID string, projectID string, severity string, state string, customStateID int, comment string, scanType string) (interface{}, error) { scanType = strings.ToLower(scanType) scanType = strings.TrimSpace(scanType) if strings.EqualFold(scanType, Sca) { state = transformState(state) - payload, err := handleScaTriage(vulnerabilityDetails, comment, state, projectID) + payload, err := prepareScaTriagePayload(vulnerabilityDetails, comment, state, projectID) if err != nil { return nil, err } @@ -266,7 +267,7 @@ func transformState(state string) string { return "" } -func handleScaTriage(vulnerabilityDetails []string, comment string, state string, projectId string) (interface{}, error) { +func prepareScaTriagePayload(vulnerabilityDetails []string, comment string, state string, projectId string) (interface{}, error) { scaTriageInfo := make(map[string]interface{}) for _, vulnerability := range vulnerabilityDetails { vulnerabilityKeyVal := strings.SplitN(vulnerability, "=", 2) @@ -296,13 +297,14 @@ func handleScaTriage(vulnerabilityDetails []string, comment string, state string scaTriageInfo["actions"] = []map[string]interface{}{actionInfo} b, err := json.Marshal(scaTriageInfo) if err != nil { - return nil, errors.Errorf("Failed to serialize vulnerabilities %s", scaTriageInfo) + logger.PrintIfVerbose(fmt.Sprintf("Failed to serialize vulnerabilities %s", scaTriageInfo)) + return nil, errors.Errorf("Failed to prepare SCA triage request") } - payload := &wrappers.ScaPredicateRequest{} - fmt.Println("Payload: ", string(b)) + payload := wrappers.ScaPredicateRequest{} err = json.Unmarshal(b, payload) if err != nil { - return nil, errors.Errorf("Failed to deserialize vulnerabilities %s", b) + logger.PrintIfVerbose(fmt.Sprintf("Failed to deserialize vulnerabilities %s", string(b))) + return nil, errors.Errorf("Failed to prepare SCA triage request") } return payload, nil } From ecc98ccc65fb5ee1da04f3a5692a9083392ed06b Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:36:03 +0530 Subject: [PATCH 04/19] added-ut-cases --- internal/commands/predicates_test.go | 142 ++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 3 deletions(-) diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 94890dcda..5f2f80519 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -149,7 +149,7 @@ func TestIsCustomState(t *testing.T) { } } func TestRunTriageUpdateWithNotFoundCustomState(t *testing.T) { - mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} + mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} clearFlags() @@ -171,7 +171,7 @@ func TestRunTriageUpdateWithNotFoundCustomState(t *testing.T) { } func TestRunTriageUpdateWithCustomState(t *testing.T) { - mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} + mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} clearFlags() @@ -193,7 +193,7 @@ func TestRunTriageUpdateWithCustomState(t *testing.T) { } func TestRunTriageUpdateWithSystemState(t *testing.T) { - mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} + mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} @@ -339,3 +339,139 @@ func TestDetermineSystemOrCustomState(t *testing.T) { }) } } + +func TestPrepareScaTriagePayload(t *testing.T) { + tests := []struct { + name string + vulnerabilityDetails []string + comment string + state string + projectId string + expectedError string + }{ + { + name: "Valid SCA triage payload", + vulnerabilityDetails: []string{ + "packageName=lodash", + "packageVersion=4.17.20", + "packageManager=npm", + "vulnerabilityId=CVE-2021-23337", + }, + comment: "Testing SCA triage", + state: "NOT_EXPLOITABLE", + projectId: "test-project-123", + expectedError: "", + }, + { + name: "Missing packageName", + vulnerabilityDetails: []string{ + "packageVersion=4.17.20", + "packageManager=npm", + "vulnerabilityId=CVE-2021-23337", + }, + comment: "Testing missing package name", + state: "NOT_EXPLOITABLE", + projectId: "test-project-123", + expectedError: "Package name is required", + }, + { + name: "Missing packageVersion", + vulnerabilityDetails: []string{ + "packageName=lodash", + "packageManager=npm", + "vulnerabilityId=CVE-2021-23337", + }, + comment: "Testing missing package version", + state: "NOT_EXPLOITABLE", + projectId: "test-project-123", + expectedError: "Package version is required", + }, + { + name: "Missing packageManager", + vulnerabilityDetails: []string{ + "packageName=lodash", + "packageVersion=4.17.20", + "vulnerabilityId=CVE-2021-23337", + }, + comment: "Testing missing package manager", + state: "NOT_EXPLOITABLE", + projectId: "test-project-123", + expectedError: "Package manager is required", + }, + { + name: "Invalid vulnerability format - no equals sign", + vulnerabilityDetails: []string{ + "packageNamelodash", + "packageVersion=4.17.20", + "packageManager=npm", + }, + comment: "Testing invalid format", + state: "NOT_EXPLOITABLE", + projectId: "test-project-123", + expectedError: "Invalid vulnerabilities. It should be in a KEY=VALUE format", + }, + { + name: "Case insensitive package name", + vulnerabilityDetails: []string{ + "packagename=lodash", + "packageversion=4.17.20", + "packagemanager=npm", + "vulnerabilityId=CVE-2021-23337", + }, + comment: "Testing case insensitive", + state: "CONFIRMED", + projectId: "test-project-123", + expectedError: "", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + payload, err := prepareScaTriagePayload(tt.vulnerabilityDetails, tt.comment, tt.state, tt.projectId) + if tt.expectedError != "" { + assert.ErrorContains(t, err, tt.expectedError) + } else { + assert.NilError(t, err) + assert.Assert(t, payload != nil, "Expected payload to be non-nil") + } + }) + } +} + +func TestRunUpdateTriageCommandForSCA(t *testing.T) { + execCmdNilAssertion( + t, + "triage", + "update", + "--project-id", + "MOCK", + "--state", + "not_exploitable", + "--comment", + "Testing SCA triage commands.", + "--scan-type", + "sca", + "--vulnerabilities", + "packageName=lodash,packageVersion=4.17.20,packageManager=npm,vulnerabilityId=CVE-2021-23337", + ) +} + +func TestRunUpdateTriageCommandForSCAWithMissingPackageDetails(t *testing.T) { + err := execCmdNotNilAssertion( + t, + "triage", + "update", + "--project-id", + "MOCK", + "--state", + "not_exploitable", + "--comment", + "Testing SCA triage with missing details.", + "--scan-type", + "sca", + "--vulnerabilities", + "packageVersion=4.17.20", + ) + assert.ErrorContains(t, err, "Package name is required") +} From 5b62d85e892c871f530d08d65d043310b64f2781 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 19 Nov 2025 15:51:01 +0530 Subject: [PATCH 05/19] added-ut-cases2 --- internal/commands/predicates.go | 6 ++ internal/commands/predicates_test.go | 134 ++++++++++++++++++--------- 2 files changed, 96 insertions(+), 44 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index b70b7cd91..a8d9d7c9d 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -159,6 +159,9 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f if strings.EqualFold(scanType, params.ScaType) { //SCA + if len(vulnerabilityDetails) == 0 { + return errors.Errorf("%s", "Failed showing the predicate. Vulnerabilities are required for SCA triage") + } scaResponse, err := resultsPredicatesWrapper.ScaPredicateResult(vulnerabilityDetails, projectID) if err != nil { return errors.Wrapf(err, "%s", "Failed showing the predicate") @@ -268,6 +271,9 @@ func transformState(state string) string { } func prepareScaTriagePayload(vulnerabilityDetails []string, comment string, state string, projectId string) (interface{}, error) { + if len(vulnerabilityDetails) == 0 { + return nil, errors.Errorf("Vulnerabilities details are required.") + } scaTriageInfo := make(map[string]interface{}) for _, vulnerability := range vulnerabilityDetails { vulnerabilityKeyVal := strings.SplitN(vulnerability, "=", 2) diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 5f2f80519..8e9207742 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -5,6 +5,7 @@ package commands import ( "fmt" "testing" + "time" "github.com/checkmarx/ast-cli/internal/wrappers" "github.com/checkmarx/ast-cli/internal/wrappers/mock" @@ -349,19 +350,6 @@ func TestPrepareScaTriagePayload(t *testing.T) { projectId string expectedError string }{ - { - name: "Valid SCA triage payload", - vulnerabilityDetails: []string{ - "packageName=lodash", - "packageVersion=4.17.20", - "packageManager=npm", - "vulnerabilityId=CVE-2021-23337", - }, - comment: "Testing SCA triage", - state: "NOT_EXPLOITABLE", - projectId: "test-project-123", - expectedError: "", - }, { name: "Missing packageName", vulnerabilityDetails: []string{ @@ -410,19 +398,6 @@ func TestPrepareScaTriagePayload(t *testing.T) { projectId: "test-project-123", expectedError: "Invalid vulnerabilities. It should be in a KEY=VALUE format", }, - { - name: "Case insensitive package name", - vulnerabilityDetails: []string{ - "packagename=lodash", - "packageversion=4.17.20", - "packagemanager=npm", - "vulnerabilityId=CVE-2021-23337", - }, - comment: "Testing case insensitive", - state: "CONFIRMED", - projectId: "test-project-123", - expectedError: "", - }, } for _, tt := range tests { @@ -439,39 +414,110 @@ func TestPrepareScaTriagePayload(t *testing.T) { } } -func TestRunUpdateTriageCommandForSCA(t *testing.T) { - execCmdNilAssertion( +func TestPrepareScaTriagePayloadWithMissingVulnerabilities(t *testing.T) { + payload, err := prepareScaTriagePayload(nil, "Testing missing vulnerabilities", "NOT_EXPLOITABLE", "test-project-123") + assert.ErrorContains(t, err, "Vulnerabilities details are required.") + assert.Assert(t, payload == nil, "Expected payload to be nil") +} + +func TestRunShowTriageCommandForSCAWithMissingVulnerabilities(t *testing.T) { + err := execCmdNotNilAssertion( t, "triage", - "update", + "show", "--project-id", "MOCK", - "--state", - "not_exploitable", - "--comment", - "Testing SCA triage commands.", "--scan-type", "sca", - "--vulnerabilities", - "packageName=lodash,packageVersion=4.17.20,packageManager=npm,vulnerabilityId=CVE-2021-23337", ) + // SCA triage show requires vulnerabilities flag + assert.Assert(t, err != nil, "Expected error when vulnerabilities flag is missing") } -func TestRunUpdateTriageCommandForSCAWithMissingPackageDetails(t *testing.T) { +func TestRunShowTriageCommandForSCAWithMultipleProjects(t *testing.T) { err := execCmdNotNilAssertion( t, "triage", - "update", + "show", "--project-id", - "MOCK", - "--state", - "not_exploitable", - "--comment", - "Testing SCA triage with missing details.", + "MOCK1,MOCK2", "--scan-type", "sca", "--vulnerabilities", - "packageVersion=4.17.20", + "packageName=lodash,packageVersion=4.17.20,packageManager=npm", ) - assert.ErrorContains(t, err, "Package name is required") + assert.ErrorContains(t, err, "Multiple project-ids are not allowed") +} + +func TestToScaPredicateResultView(t *testing.T) { + // Arrange: Create sample SCA predicate result + createdAt1, _ := time.Parse(time.RFC3339, "2024-01-15T10:00:00Z") + createdAt2, _ := time.Parse(time.RFC3339, "2024-01-16T12:00:00Z") + + scaPredicateResult := wrappers.ScaPredicateResult{ + Context: wrappers.Context{ + VulnerabilityId: "CVE-2021-23337", + PackageName: "lodash", + PackageVersion: "4.17.20", + PackageManager: "npm", + }, + Actions: []wrappers.Action{ + { + ActionType: "ChangeState", + ActionValue: "NOT_EXPLOITABLE", + Message: "This is not exploitable in our context", + UserName: "test-user", + CreatedAt: createdAt1, + Enabled: true, + }, + { + ActionType: "ChangeState", + ActionValue: "CONFIRMED", + Message: "Actually, this needs to be fixed", + UserName: "test-user-2", + CreatedAt: createdAt2, + Enabled: true, + }, + }, + } + + // Act: Call the toScaPredicateResultView function + result := toScaPredicateResultView(scaPredicateResult) + + // Assert: Verify the conversion + assert.Equal(t, len(result), 2, "Expected 2 predicate result views") + + // Check first action + assert.Equal(t, result[1].VulnerabilityID, "CVE-2021-23337") + assert.Equal(t, result[1].PackageName, "lodash") + assert.Equal(t, result[1].PackageVersion, "4.17.20") + assert.Equal(t, result[1].PackageManager, "npm") + assert.Equal(t, result[1].State, "NOT_EXPLOITABLE") + assert.Equal(t, result[1].Comment, "This is not exploitable in our context") + assert.Equal(t, result[1].CreatedBy, "test-user") + assert.Equal(t, result[1].CreatedAt, createdAt1) + + // Check second action + assert.Equal(t, result[0].State, "CONFIRMED") + assert.Equal(t, result[0].Comment, "Actually, this needs to be fixed") + assert.Equal(t, result[0].CreatedBy, "test-user-2") +} + +func TestToScaPredicateResultView_EmptyActions(t *testing.T) { + // Arrange: Create SCA predicate result with no actions + scaPredicateResult := wrappers.ScaPredicateResult{ + Context: wrappers.Context{ + VulnerabilityId: "CVE-2021-23337", + PackageName: "lodash", + PackageVersion: "4.17.20", + PackageManager: "npm", + }, + Actions: []wrappers.Action{}, + } + + // Act: Call the toScaPredicateResultView function + result := toScaPredicateResultView(scaPredicateResult) + + // Assert: Verify empty result + assert.Equal(t, len(result), 0, "Expected empty predicate result views") } From 16dd986e5c6694f5886055b3cbd2f663197c093e Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 19 Nov 2025 17:39:05 +0530 Subject: [PATCH 06/19] checked-lint --- internal/commands/predicates.go | 28 +++--- internal/commands/predicates_test.go | 20 ++--- internal/wrappers/mock/predicates-mock.go | 2 +- internal/wrappers/predicates-http.go | 105 +++++++++++++--------- internal/wrappers/predicates.go | 6 +- 5 files changed, 92 insertions(+), 69 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index a8d9d7c9d..39e15915a 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -99,7 +99,6 @@ func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWra triageShowCmd.PersistentFlags().String(params.ScanTypeFlag, "", "Scan Type") triageShowCmd.PersistentFlags().StringSlice(params.VulnerabilitiesFlag, []string{}, "List Vulnerabilities string") - // markFlagAsRequired(triageShowCmd, params.SimilarityIDFlag) markFlagAsRequired(triageShowCmd, params.ProjectIDFlag) markFlagAsRequired(triageShowCmd, params.ScanTypeFlag) @@ -158,7 +157,7 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f } if strings.EqualFold(scanType, params.ScaType) { - //SCA + // SCA if len(vulnerabilityDetails) == 0 { return errors.Errorf("%s", "Failed showing the predicate. Vulnerabilities are required for SCA triage") } @@ -166,15 +165,17 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f if err != nil { return errors.Wrapf(err, "%s", "Failed showing the predicate") } - err = printByFormat(cmd, toScaPredicateResultView(*scaResponse)) + err = printByFormat(cmd, toScaPredicateResultView(scaResponse)) if err != nil { return err } return nil } else { - //other than SCA + // other than SCA predicatesCollection, errorModel, err = resultsPredicatesWrapper.GetAllPredicatesForSimilarityID(similarityID, projectID, scanType) - + if err != nil { + return errors.Wrapf(err, "%s", "Failed showing the predicate") + } // Checking the response if errorModel != nil { return errors.Errorf( @@ -227,7 +228,7 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, } } -func preparePredicateRequest(vulnerabilityDetails []string, similarityID string, projectID string, severity string, state string, customStateID int, comment string, scanType string) (interface{}, error) { +func preparePredicateRequest(vulnerabilityDetails []string, similarityID, projectID, severity, state string, customStateID int, comment, scanType string) (interface{}, error) { scanType = strings.ToLower(scanType) scanType = strings.TrimSpace(scanType) if strings.EqualFold(scanType, Sca) { @@ -270,13 +271,13 @@ func transformState(state string) string { return "" } -func prepareScaTriagePayload(vulnerabilityDetails []string, comment string, state string, projectId string) (interface{}, error) { +func prepareScaTriagePayload(vulnerabilityDetails []string, comment, state, projectID string) (interface{}, error) { if len(vulnerabilityDetails) == 0 { return nil, errors.Errorf("Vulnerabilities details are required.") } scaTriageInfo := make(map[string]interface{}) for _, vulnerability := range vulnerabilityDetails { - vulnerabilityKeyVal := strings.SplitN(vulnerability, "=", 2) + vulnerabilityKeyVal := strings.Split(vulnerability, "=") err := validateVulnerabilityDetails(vulnerabilityKeyVal) if err != nil { return nil, err @@ -295,7 +296,7 @@ func prepareScaTriagePayload(vulnerabilityDetails []string, comment string, stat return nil, errors.Errorf("Package manager is required") } - scaTriageInfo["projectIds"] = []string{projectId} + scaTriageInfo["projectIds"] = []string{projectID} actionInfo := make(map[string]interface{}) actionInfo["actionType"] = params.ChangeState actionInfo["value"] = state @@ -307,7 +308,7 @@ func prepareScaTriagePayload(vulnerabilityDetails []string, comment string, stat return nil, errors.Errorf("Failed to prepare SCA triage request") } payload := wrappers.ScaPredicateRequest{} - err = json.Unmarshal(b, payload) + err = json.Unmarshal(b, &payload) if err != nil { logger.PrintIfVerbose(fmt.Sprintf("Failed to deserialize vulnerabilities %s", string(b))) return nil, errors.Errorf("Failed to prepare SCA triage request") @@ -319,9 +320,6 @@ func validateVulnerabilityDetails(vulnerability []string) error { if len(vulnerability) != params.KeyValuePairSize { return errors.Errorf("Invalid vulnerabilities. It should be in a KEY=VALUE format") } - if len(strings.Split(vulnerability[1], ",")) > params.SingleValueSize || len(strings.Split(vulnerability[1], ",")) > params.SingleValueSize { - return errors.Errorf("Cannot specify multiple values to key %s", vulnerability[0]) - } return nil } @@ -397,12 +395,12 @@ type scaPredicateResultView struct { CreatedAt time.Time `format:"name:Created at;time:01-02-06 15:04:05"` } -func toScaPredicateResultView(scaPredicateResult wrappers.ScaPredicateResult) []scaPredicateResultView { +func toScaPredicateResultView(scaPredicateResult *wrappers.ScaPredicateResult) []scaPredicateResultView { view := []scaPredicateResultView{} if len(scaPredicateResult.Actions) > 0 { for _, action := range scaPredicateResult.Actions { view = append(view, scaPredicateResultView{ - VulnerabilityID: scaPredicateResult.Context.VulnerabilityId, + VulnerabilityID: scaPredicateResult.Context.VulnerabilityID, PackageName: scaPredicateResult.Context.PackageName, PackageVersion: scaPredicateResult.Context.PackageVersion, PackageManager: scaPredicateResult.Context.PackageManager, diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 8e9207742..b4f2d0240 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -347,7 +347,7 @@ func TestPrepareScaTriagePayload(t *testing.T) { vulnerabilityDetails []string comment string state string - projectId string + projectID string expectedError string }{ { @@ -359,7 +359,7 @@ func TestPrepareScaTriagePayload(t *testing.T) { }, comment: "Testing missing package name", state: "NOT_EXPLOITABLE", - projectId: "test-project-123", + projectID: "test-project-123", expectedError: "Package name is required", }, { @@ -371,7 +371,7 @@ func TestPrepareScaTriagePayload(t *testing.T) { }, comment: "Testing missing package version", state: "NOT_EXPLOITABLE", - projectId: "test-project-123", + projectID: "test-project-123", expectedError: "Package version is required", }, { @@ -383,7 +383,7 @@ func TestPrepareScaTriagePayload(t *testing.T) { }, comment: "Testing missing package manager", state: "NOT_EXPLOITABLE", - projectId: "test-project-123", + projectID: "test-project-123", expectedError: "Package manager is required", }, { @@ -395,7 +395,7 @@ func TestPrepareScaTriagePayload(t *testing.T) { }, comment: "Testing invalid format", state: "NOT_EXPLOITABLE", - projectId: "test-project-123", + projectID: "test-project-123", expectedError: "Invalid vulnerabilities. It should be in a KEY=VALUE format", }, } @@ -403,7 +403,7 @@ func TestPrepareScaTriagePayload(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { - payload, err := prepareScaTriagePayload(tt.vulnerabilityDetails, tt.comment, tt.state, tt.projectId) + payload, err := prepareScaTriagePayload(tt.vulnerabilityDetails, tt.comment, tt.state, tt.projectID) if tt.expectedError != "" { assert.ErrorContains(t, err, tt.expectedError) } else { @@ -454,9 +454,9 @@ func TestToScaPredicateResultView(t *testing.T) { createdAt1, _ := time.Parse(time.RFC3339, "2024-01-15T10:00:00Z") createdAt2, _ := time.Parse(time.RFC3339, "2024-01-16T12:00:00Z") - scaPredicateResult := wrappers.ScaPredicateResult{ + scaPredicateResult := &wrappers.ScaPredicateResult{ Context: wrappers.Context{ - VulnerabilityId: "CVE-2021-23337", + VulnerabilityID: "CVE-2021-23337", PackageName: "lodash", PackageVersion: "4.17.20", PackageManager: "npm", @@ -505,9 +505,9 @@ func TestToScaPredicateResultView(t *testing.T) { func TestToScaPredicateResultView_EmptyActions(t *testing.T) { // Arrange: Create SCA predicate result with no actions - scaPredicateResult := wrappers.ScaPredicateResult{ + scaPredicateResult := &wrappers.ScaPredicateResult{ Context: wrappers.Context{ - VulnerabilityId: "CVE-2021-23337", + VulnerabilityID: "CVE-2021-23337", PackageName: "lodash", PackageVersion: "4.17.20", PackageManager: "npm", diff --git a/internal/wrappers/mock/predicates-mock.go b/internal/wrappers/mock/predicates-mock.go index fb0c4be79..13b91e664 100644 --- a/internal/wrappers/mock/predicates-mock.go +++ b/internal/wrappers/mock/predicates-mock.go @@ -44,7 +44,7 @@ func (r ResultsPredicatesWrapper) GetAllPredicatesForSimilarityID(similarityID, }, nil, nil } -func (r ResultsPredicatesWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectId string) (*wrappers.ScaPredicateResult, error) { +func (r ResultsPredicatesWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectID string) (*wrappers.ScaPredicateResult, error) { fmt.Println("Called 'ScaPredicateResult' in ResultsPredicatesMockWrapper") return nil, nil } diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index a193bd43d..c4ec9fb29 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "io" "net/http" "strings" @@ -26,7 +27,7 @@ func NewResultsPredicatesHTTPWrapper() ResultsPredicatesWrapper { return &ResultsPredicatesHTTPWrapper{} } -func (r *ResultsPredicatesHTTPWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectId string) (*ScaPredicateResult, error) { +func (r *ResultsPredicatesHTTPWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectID string) (*ScaPredicateResult, error) { clientTimeout := viper.GetUint(params.ClientTimeoutKey) r.SetPath(viper.GetString(params.ScaResultsPredicatesPathEnv)) var request = "/entity-profile/search" @@ -34,10 +35,13 @@ func (r *ResultsPredicatesHTTPWrapper) ScaPredicateResult(vulnerabilityDetails [ scaPredicateRequest := make(map[string]interface{}) for _, vulnerability := range vulnerabilityDetails { - vulnerabilityKeyVal := strings.SplitN(vulnerability, "=", 2) + vulnerabilityKeyVal := strings.Split(vulnerability, "=") + if len(vulnerabilityKeyVal) != params.KeyValuePairSize { + return nil, errors.Errorf("Invalid vulnerability details format: %s", vulnerability) + } scaPredicateRequest[vulnerabilityKeyVal[0]] = vulnerabilityKeyVal[1] } - scaPredicateRequest["projectId"] = projectId + scaPredicateRequest["projectId"] = projectID scaPredicateRequest["actionType"] = params.ChangeState jsonBody, err := json.Marshal(scaPredicateRequest) if err != nil { @@ -55,7 +59,6 @@ func (r *ResultsPredicatesHTTPWrapper) ScaPredicateResult(vulnerabilityDetails [ if resp.StatusCode != http.StatusOK { return nil, errors.Errorf("Failed to get SCA predicate result.") } - fmt.Println("HM Response: ", resp.Body) decoder := json.NewDecoder(resp.Body) var scaPredicates ScaPredicateResult err = decoder.Decode(&scaPredicates) @@ -147,27 +150,16 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate interf *WebError, error, ) { clientTimeout := viper.GetUint(params.ClientTimeoutKey) - var predicateModel interface{} - if !strings.EqualFold(strings.TrimSpace(scanType), params.ScaType) { - predicateModel = []interface{}{predicate} - } else { - predicateModel = predicate - } + + predicateModel := preparePredicateModel(predicate, scanType) jsonBytes, err := json.Marshal(predicateModel) if err != nil { return nil, err } - var triageAPIPath string - if strings.EqualFold(strings.TrimSpace(scanType), params.SastType) { - triageAPIPath = viper.GetString(params.SastResultsPredicatesPathKey) - } else if strings.EqualFold(strings.TrimSpace(scanType), params.KicsType) || strings.EqualFold(strings.TrimSpace(scanType), params.IacType) { - triageAPIPath = viper.GetString(params.KicsResultsPredicatesPathKey) - } else if strings.EqualFold(strings.TrimSpace(scanType), params.ScsType) { - triageAPIPath = viper.GetString(params.ScsResultsWritePredicatesPathKey) - } else if strings.EqualFold(strings.TrimSpace(scanType), params.ScaType) { - triageAPIPath = viper.GetString(params.ScaResultsPredicatesPathEnv) - } else { - return nil, errors.Errorf(invalidScanType, scanType) + + triageAPIPath, err := getTriageAPIPath(scanType) + if err != nil { + return nil, err } logger.PrintIfVerbose(fmt.Sprintf("Sending POST request to %s", triageAPIPath)) @@ -186,38 +178,71 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate interf _ = resp.Body.Close() }() - // in case of ne/pne when mandatory comment arent provided, cli is not transforming error message + if err := checkMandatoryCommentError(resp.Body, scanType); err != nil { + return nil, err + } + + return nil, handlePredicateStatusCode(resp.StatusCode) +} + +func preparePredicateModel(predicate interface{}, scanType string) interface{} { + if !strings.EqualFold(strings.TrimSpace(scanType), params.ScaType) { + return []interface{}{predicate} + } + return predicate +} + +func getTriageAPIPath(scanType string) (string, error) { + ScanType := strings.ToLower(strings.TrimSpace(scanType)) + + switch ScanType { + case strings.ToLower(params.SastType): + return viper.GetString(params.SastResultsPredicatesPathKey), nil + case strings.ToLower(params.KicsType), strings.ToLower(params.IacType): + return viper.GetString(params.KicsResultsPredicatesPathKey), nil + case strings.ToLower(params.ScsType): + return viper.GetString(params.ScsResultsWritePredicatesPathKey), nil + case strings.ToLower(params.ScaType): + return viper.GetString(params.ScaResultsPredicatesPathEnv), nil + default: + return "", errors.Errorf(invalidScanType, scanType) + } +} + +func checkMandatoryCommentError(body io.ReadCloser, scanType string) error { responseMap := make(map[string]interface{}) - if err := json.NewDecoder(resp.Body).Decode(&responseMap); err != nil { - if scanType != params.ScaType { // for sca, we are not getting any response in the response body, so we are not logging the error + if err := json.NewDecoder(body).Decode(&responseMap); err != nil { + if scanType != params.ScaType { logger.PrintIfVerbose(fmt.Sprintf("failed to read the response, %v", err.Error())) } - } else { - if val, ok := responseMap["code"].(float64); ok { - if val == 4002 && responseMap["message"] != nil { - if errMsg, ok := responseMap["message"].(string); ok { - if errMsg == "A comment is required to make changes to the result state" { - return nil, errors.Errorf(errMsg) - } - } + return nil + } + + if val, ok := responseMap["code"].(float64); ok && val == 4002 { + if errMsg, ok := responseMap["message"].(string); ok { + if errMsg == "A comment is required to make changes to the result state" { + return errors.Errorf(errMsg) } } } + return nil +} - switch resp.StatusCode { - case http.StatusBadRequest, http.StatusInternalServerError: - return nil, errors.Errorf("Predicate bad request.") +func handlePredicateStatusCode(statusCode int) error { + switch statusCode { case http.StatusOK, http.StatusCreated: fmt.Println("Predicate updated successfully.") - return nil, nil + return nil case http.StatusNotModified: - return nil, errors.Errorf("No changes to update.") + return errors.Errorf("No changes to update.") case http.StatusForbidden: - return nil, errors.Errorf("No permission to update predicate.") + return errors.Errorf("No permission to update predicate.") case http.StatusNotFound: - return nil, errors.Errorf("Predicate not found.") + return errors.Errorf("Predicate not found.") + case http.StatusBadRequest, http.StatusInternalServerError: + return errors.Errorf("Predicate bad request.") default: - return nil, errors.Errorf("response status code %d", resp.StatusCode) + return errors.Errorf("response status code %d", statusCode) } } diff --git a/internal/wrappers/predicates.go b/internal/wrappers/predicates.go index 078b73dca..0fe9be946 100644 --- a/internal/wrappers/predicates.go +++ b/internal/wrappers/predicates.go @@ -26,7 +26,7 @@ type ScaPredicateRequest struct { PackageName string `json:"packageName"` PackageVersion string `json:"packageVersion"` PackageManager string `json:"packageManager"` - VulnerabilityId string `json:"vulnerabilityId"` + VulnerabilityID string `json:"vulnerabilityId"` ProjectIds []string `json:"projectIds"` Actions []ScaAction `json:"actions"` } @@ -81,7 +81,7 @@ type Context struct { PackageManager string `json:"PackageManager"` PackageName string `json:"PackageName"` PackageVersion string `json:"PackageVersion"` - VulnerabilityId string `json:"VulnerabilityId"` + VulnerabilityID string `json:"VulnerabilityId"` } type Action struct { @@ -104,7 +104,7 @@ type CustomStatesWrapper interface { } type ResultsPredicatesWrapper interface { - ScaPredicateResult(vulnerabilityDetails []string, projectId string) (*ScaPredicateResult, error) + ScaPredicateResult(vulnerabilityDetails []string, projectID string) (*ScaPredicateResult, error) PredicateSeverityAndState(predicate interface{}, scanType string) (*WebError, error) GetAllPredicatesForSimilarityID( similarityID string, projectID string, scannerType string, From 92f46482b7edcc024a16b30e6a15ec8cd3abea0b Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:42:31 +0530 Subject: [PATCH 07/19] checked-ut --- internal/commands/predicates.go | 4 ++++ internal/commands/predicates_test.go | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 39e15915a..24efb1e86 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -216,6 +216,10 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, } var err error state, customStateID, err = determineSystemOrCustomState(customStatesWrapper, featureFlagsWrapper, state, customStateID) + if err != nil { + return errors.Wrapf(err, "%s", "Failed updating the predicate") + } + predicate, err := preparePredicateRequest(vulnerabilityDetails, similarityID, projectID, severity, state, customStateID, comment, scanType) if err != nil { return errors.Wrapf(err, "%s", "Failed updating the predicate") diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index b4f2d0240..78e094203 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -13,6 +13,8 @@ import ( "gotest.tools/assert" ) +var requiredFlagsError = "required flag(s) \"project-id\", \"scan-type\" not set" + func TestTriageHelp(t *testing.T) { execCmdNilAssertion(t, "help", "triage") } @@ -42,7 +44,7 @@ func TestRunUpdateTriageCommand(t *testing.T) { func TestRunShowTriageCommandWithNoInput(t *testing.T) { err := execCmdNotNilAssertion(t, "triage", "show") - assert.Assert(t, err.Error() == "required flag(s) \"project-id\", \"scan-type\", \"similarity-id\" not set") + assert.Assert(t, err.Error() == requiredFlagsError) } func TestRunUpdateTriageCommandWithNoInput(t *testing.T) { @@ -50,7 +52,7 @@ func TestRunUpdateTriageCommandWithNoInput(t *testing.T) { fmt.Println(err) assert.Assert( t, - err.Error() == "required flag(s) \"project-id\", \"scan-type\", \"severity\", \"similarity-id\" not set") + err.Error() == requiredFlagsError) } func TestTriageGetStatesFlag(t *testing.T) { From 72caadaa55dfc084f650d6a321afd0d58dc0d788 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:06:11 +0530 Subject: [PATCH 08/19] log-lineremoved --- internal/commands/predicates.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 24efb1e86..411350b8f 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -296,7 +296,6 @@ func prepareScaTriagePayload(vulnerabilityDetails []string, comment, state, proj return nil, errors.Errorf("Package version is required") } if scaTriageInfo["packageManager"] == nil && scaTriageInfo["packagemanager"] == nil { - fmt.Println("Package manager is required") return nil, errors.Errorf("Package manager is required") } From 0a07bbeb7879f2ea2ca78a59776d214d3a748d3f Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:35:19 +0530 Subject: [PATCH 09/19] testcases-added --- internal/commands/predicates_test.go | 198 +++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 78e094203..67ddec8e5 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -523,3 +523,201 @@ func TestToScaPredicateResultView_EmptyActions(t *testing.T) { // Assert: Verify empty result assert.Equal(t, len(result), 0, "Expected empty predicate result views") } + +func TestTransformState(t *testing.T) { + tests := []struct { + name string + inputState string + expectedState string + }{ + { + name: "TO_VERIFY uppercase", + inputState: "TO_VERIFY", + expectedState: "ToVerify", + }, + { + name: "to_verify lowercase", + inputState: "to_verify", + expectedState: "ToVerify", + }, + { + name: "NOT_EXPLOITABLE uppercase", + inputState: "NOT_EXPLOITABLE", + expectedState: "NotExploitable", + }, + { + name: "not_exploitable lowercase", + inputState: "not_exploitable", + expectedState: "NotExploitable", + }, + { + name: "PROPOSED_NOT_EXPLOITABLE uppercase", + inputState: "PROPOSED_NOT_EXPLOITABLE", + expectedState: "ProposedNotExploitable", + }, + { + name: "CONFIRMED uppercase", + inputState: "CONFIRMED", + expectedState: "Confirmed", + }, + { + name: "URGENT uppercase", + inputState: "URGENT", + expectedState: "Urgent", + }, + { + name: "State with whitespace", + inputState: " TO_VERIFY ", + expectedState: "ToVerify", + }, + { + name: "Unknown state", + inputState: "CUSTOM_STATE", + expectedState: "", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + result := transformState(tt.inputState) + assert.Equal(t, result, tt.expectedState) + }) + } +} + +func TestPrepareScaTriagePayloadWithValidData(t *testing.T) { + vulnerabilityDetails := []string{ + "packageName=lodash", + "packageVersion=4.17.20", + "packageManager=npm", + "vulnerabilityId=CVE-2021-23337", + } + comment := "This is a test comment" + state := "NOT_EXPLOITABLE" + projectID := "test-project-123" + + payload, err := prepareScaTriagePayload(vulnerabilityDetails, comment, state, projectID) + + assert.NilError(t, err) + assert.Assert(t, payload != nil, "Expected payload to be non-nil") +} + +func TestPrepareScaTriagePayloadWithCaseInsensitiveFields(t *testing.T) { + tests := []struct { + name string + vulnerabilityDetails []string + shouldSucceed bool + expectedError string + }{ + { + name: "Lowercase package fields", + vulnerabilityDetails: []string{ + "packagename=lodash", + "packageversion=4.17.20", + "packagemanager=npm", + }, + shouldSucceed: true, + }, + { + name: "Mixed case package fields", + vulnerabilityDetails: []string{ + "packageName=lodash", + "packageversion=4.17.20", + "packageManager=npm", + }, + shouldSucceed: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + payload, err := prepareScaTriagePayload(tt.vulnerabilityDetails, "test comment", "NOT_EXPLOITABLE", "project-123") + if tt.shouldSucceed { + assert.NilError(t, err) + assert.Assert(t, payload != nil, "Expected payload to be non-nil") + } else { + assert.ErrorContains(t, err, tt.expectedError) + } + }) + } +} + +func TestValidateVulnerabilityDetails(t *testing.T) { + tests := []struct { + name string + vulnerability []string + expectError bool + expectedError string + }{ + { + name: "Valid key-value pair", + vulnerability: []string{"packageName", "lodash"}, + expectError: false, + }, + { + name: "Invalid - no value", + vulnerability: []string{"packageName"}, + expectError: true, + expectedError: "Invalid vulnerabilities. It should be in a KEY=VALUE format", + }, + { + name: "Invalid - too many values", + vulnerability: []string{"packageName", "lodash", "extra"}, + expectError: true, + expectedError: "Invalid vulnerabilities. It should be in a KEY=VALUE format", + }, + { + name: "Invalid - empty array", + vulnerability: []string{}, + expectError: true, + expectedError: "Invalid vulnerabilities. It should be in a KEY=VALUE format", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + err := validateVulnerabilityDetails(tt.vulnerability) + if tt.expectError { + assert.ErrorContains(t, err, tt.expectedError) + } else { + assert.NilError(t, err) + } + }) + } +} + +func TestRunUpdateTriageCommandForSCA(t *testing.T) { + mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} + mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} + mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} + + cmd := triageUpdateSubCommand(mockResultsPredicatesWrapper, mockFeatureFlagsWrapper, mockCustomStatesWrapper) + cmd.SetArgs([]string{ + "--project-id", "MOCK", + "--state", "NOT_EXPLOITABLE", + "--scan-type", "sca", + "--vulnerabilities", "packageName=lodash,packageVersion=4.17.20,packageManager=npm", + "--comment", "Testing SCA update", + }) + + err := cmd.Execute() + assert.NilError(t, err) +} + +func TestPrepareScaTriagePayloadWithMissingVulnerabilityId(t *testing.T) { + vulnerabilityDetails := []string{ + "packageName=lodash", + "packageVersion=4.17.20", + "packageManager=npm", + } + comment := "Testing without vulnerability ID" + state := "NOT_EXPLOITABLE" + projectID := "test-project-123" + + payload, err := prepareScaTriagePayload(vulnerabilityDetails, comment, state, projectID) + assert.NilError(t, err) + assert.Assert(t, payload != nil, "Expected payload to be non-nil even without vulnerabilityId") +} From af0afcfccd7fae7c93c4c41d3009f05c6a88e51d Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Mon, 24 Nov 2025 14:53:30 +0530 Subject: [PATCH 10/19] lint-checked --- internal/commands/predicates_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 67ddec8e5..8b35c0c3b 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -586,6 +586,7 @@ func TestTransformState(t *testing.T) { } } +//nolint:goconst func TestPrepareScaTriagePayloadWithValidData(t *testing.T) { vulnerabilityDetails := []string{ "packageName=lodash", From b01adf834f4a850811176f11011ad997d509a959 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:01:23 +0530 Subject: [PATCH 11/19] space-trimmed --- internal/commands/predicates.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 411350b8f..4d2e7d651 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -286,7 +286,7 @@ func prepareScaTriagePayload(vulnerabilityDetails []string, comment, state, proj if err != nil { return nil, err } - scaTriageInfo[vulnerabilityKeyVal[0]] = vulnerabilityKeyVal[1] + scaTriageInfo[vulnerabilityKeyVal[0]] = strings.TrimSpace(vulnerabilityKeyVal[1]) } if scaTriageInfo["packageName"] == nil && scaTriageInfo["packagename"] == nil { From 8a9b4eb583e5e9aee3087ba39d17780a0533841b Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:51:19 +0530 Subject: [PATCH 12/19] renamed-func --- internal/commands/predicates.go | 4 ++-- internal/wrappers/mock/predicates-mock.go | 4 ++-- internal/wrappers/predicates-http.go | 2 +- internal/wrappers/predicates.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index 4d2e7d651..c91a6a33f 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -161,11 +161,11 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f if len(vulnerabilityDetails) == 0 { return errors.Errorf("%s", "Failed showing the predicate. Vulnerabilities are required for SCA triage") } - scaResponse, err := resultsPredicatesWrapper.ScaPredicateResult(vulnerabilityDetails, projectID) + scaPredicates, err := resultsPredicatesWrapper.GetScaPredicates(vulnerabilityDetails, projectID) if err != nil { return errors.Wrapf(err, "%s", "Failed showing the predicate") } - err = printByFormat(cmd, toScaPredicateResultView(scaResponse)) + err = printByFormat(cmd, toScaPredicateResultView(scaPredicates)) if err != nil { return err } diff --git a/internal/wrappers/mock/predicates-mock.go b/internal/wrappers/mock/predicates-mock.go index 13b91e664..081682362 100644 --- a/internal/wrappers/mock/predicates-mock.go +++ b/internal/wrappers/mock/predicates-mock.go @@ -44,7 +44,7 @@ func (r ResultsPredicatesWrapper) GetAllPredicatesForSimilarityID(similarityID, }, nil, nil } -func (r ResultsPredicatesWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectID string) (*wrappers.ScaPredicateResult, error) { - fmt.Println("Called 'ScaPredicateResult' in ResultsPredicatesMockWrapper") +func (r ResultsPredicatesWrapper) GetScaPredicates(vulnerabilityDetails []string, projectID string) (*wrappers.ScaPredicateResult, error) { + fmt.Println("Called 'GetScaPredicates' in ResultsPredicatesMockWrapper") return nil, nil } diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index c4ec9fb29..f3b324633 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -27,7 +27,7 @@ func NewResultsPredicatesHTTPWrapper() ResultsPredicatesWrapper { return &ResultsPredicatesHTTPWrapper{} } -func (r *ResultsPredicatesHTTPWrapper) ScaPredicateResult(vulnerabilityDetails []string, projectID string) (*ScaPredicateResult, error) { +func (r *ResultsPredicatesHTTPWrapper) GetScaPredicates(vulnerabilityDetails []string, projectID string) (*ScaPredicateResult, error) { clientTimeout := viper.GetUint(params.ClientTimeoutKey) r.SetPath(viper.GetString(params.ScaResultsPredicatesPathEnv)) var request = "/entity-profile/search" diff --git a/internal/wrappers/predicates.go b/internal/wrappers/predicates.go index 0fe9be946..de719fa83 100644 --- a/internal/wrappers/predicates.go +++ b/internal/wrappers/predicates.go @@ -104,7 +104,7 @@ type CustomStatesWrapper interface { } type ResultsPredicatesWrapper interface { - ScaPredicateResult(vulnerabilityDetails []string, projectID string) (*ScaPredicateResult, error) + GetScaPredicates(vulnerabilityDetails []string, projectID string) (*ScaPredicateResult, error) PredicateSeverityAndState(predicate interface{}, scanType string) (*WebError, error) GetAllPredicatesForSimilarityID( similarityID string, projectID string, scannerType string, From 2ae7e4c5130b466a3beb5c4aeaa579bb02ee7c69 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Tue, 2 Dec 2025 17:33:19 +0530 Subject: [PATCH 13/19] added--tests --- internal/commands/predicates.go | 2 +- internal/wrappers/predicates-http.go | 2 +- test/integration/predicate_test.go | 47 ++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index c91a6a33f..fc1b84dd6 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -286,7 +286,7 @@ func prepareScaTriagePayload(vulnerabilityDetails []string, comment, state, proj if err != nil { return nil, err } - scaTriageInfo[vulnerabilityKeyVal[0]] = strings.TrimSpace(vulnerabilityKeyVal[1]) + scaTriageInfo[strings.TrimSpace(vulnerabilityKeyVal[0])] = strings.TrimSpace(vulnerabilityKeyVal[1]) } if scaTriageInfo["packageName"] == nil && scaTriageInfo["packagename"] == nil { diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index f3b324633..7dab71af6 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -39,7 +39,7 @@ func (r *ResultsPredicatesHTTPWrapper) GetScaPredicates(vulnerabilityDetails []s if len(vulnerabilityKeyVal) != params.KeyValuePairSize { return nil, errors.Errorf("Invalid vulnerability details format: %s", vulnerability) } - scaPredicateRequest[vulnerabilityKeyVal[0]] = vulnerabilityKeyVal[1] + scaPredicateRequest[strings.TrimSpace(vulnerabilityKeyVal[0])] = strings.TrimSpace(vulnerabilityKeyVal[1]) } scaPredicateRequest["projectId"] = projectID scaPredicateRequest["actionType"] = params.ChangeState diff --git a/test/integration/predicate_test.go b/test/integration/predicate_test.go index 5bd27d029..1e925e6ed 100644 --- a/test/integration/predicate_test.go +++ b/test/integration/predicate_test.go @@ -292,3 +292,50 @@ func TestTriageShowAndUpdateWithCustomStates(t *testing.T) { assert.Assert(t, found, "Updated predicate should have state set to state2") } + +func TestScaUpdateWithVulnerabilityDetails(t *testing.T) { + + fmt.Println("Testing the command 'triage update' with scan-type sca using vulnerability-details.") + + _, projectID := getRootScan(t) + + // Hardcoded vulnerability details for testing SCA triage + packageName := "Maven-org.apache.tomcat.embed:tomcat-embed-core" + packageVersion := "9.0.14" + vulnerabilityID := "CVE-2024-56337" + packageManager := "maven" + state := "NOT_EXPLOITABLE" + comment := "Testing CLI Command for triage with SCA scan type." + + args := []string{ + "triage", "update", + flag(params.ProjectIDFlag), projectID, + flag(params.VulnerabilitiesFlag), fmt.Sprintf("packagename=%s", packageName), + flag(params.VulnerabilitiesFlag), fmt.Sprintf("packageversion=%s", packageVersion), + flag(params.VulnerabilitiesFlag), fmt.Sprintf("vulnerabilityId=%s", vulnerabilityID), + flag(params.VulnerabilitiesFlag), fmt.Sprintf("packageManager=%s", packageManager), + flag(params.StateFlag), state, + flag(params.CommentFlag), comment, + flag(params.ScanTypeFlag), params.ScaType, + } + + err, outputBufferForStep1 := executeCommand(t, args...) + _, readingError := io.ReadAll(outputBufferForStep1) + assert.NilError(t, readingError, "Reading result should pass") + + assert.NilError(t, err, "Updating the SCA predicate with vulnerability-details should pass.") + + fmt.Println("Testing the command 'triage show' with scan-type sca to verify the update.") + outputBufferForStep2 := executeCmdNilAssertion( + t, "SCA Predicates should be fetched.", "triage", "show", + flag(params.FormatFlag), printer.FormatJSON, + flag(params.ProjectIDFlag), projectID, + flag(params.VulnerabilitiesFlag), fmt.Sprintf("packagename=%s", packageName), + flag(params.VulnerabilitiesFlag), fmt.Sprintf("packageversion=%s", packageVersion), + flag(params.VulnerabilitiesFlag), fmt.Sprintf("vulnerabilityId=%s", vulnerabilityID), + flag(params.VulnerabilitiesFlag), fmt.Sprintf("packageManager=%s", packageManager), + flag(params.ScanTypeFlag), params.ScaType, + ) + + fmt.Println(outputBufferForStep2) +} From dcd37e5f496b358d89f9fc859c0465621b515f07 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:23:53 +0530 Subject: [PATCH 14/19] clean-code --- internal/wrappers/predicates-http.go | 39 ---------------------------- 1 file changed, 39 deletions(-) diff --git a/internal/wrappers/predicates-http.go b/internal/wrappers/predicates-http.go index 7dab71af6..a6484ac64 100644 --- a/internal/wrappers/predicates-http.go +++ b/internal/wrappers/predicates-http.go @@ -107,45 +107,6 @@ func (r *ResultsPredicatesHTTPWrapper) SetPath(newPath string) { r.path = newPath } -func (r *ResultsPredicatesHTTPWrapper) PredicateScaState(predicate *ScaPredicateRequest) (*WebError, error) { - clientTimeout := viper.GetUint(params.ClientTimeoutKey) - jsonBody, err := json.Marshal(predicate) - if err != nil { - return nil, err - } - var scaTriageAPIPath string - - scaTriageAPIPath = viper.GetString(params.ScaResultsPredicatesPathEnv) - logger.PrintIfVerbose(fmt.Sprintf("Sending POST request to %s", scaTriageAPIPath)) - logger.PrintIfVerbose(fmt.Sprintf("Request Payload: %s", string(jsonBody))) - - r.SetPath(scaTriageAPIPath) - - resp, err := SendHTTPRequestWithJSONContentType(http.MethodPost, r.path, bytes.NewBuffer(jsonBody), true, clientTimeout) - if err != nil { - return nil, err - } - logger.PrintIfVerbose(fmt.Sprintf("Response : %s", resp.Status)) - defer func() { - _ = resp.Body.Close() - }() - - switch resp.StatusCode { - case http.StatusOK, http.StatusCreated: - fmt.Println("Predicate updated successfully.") - return nil, nil - case http.StatusForbidden: - return nil, errors.Errorf("You do not have permission to update state") - case http.StatusNotFound: - return nil, errors.Errorf("Predicate not found.") - case http.StatusBadRequest, http.StatusInternalServerError: - return nil, errors.Errorf("Predicate bad request.") - - default: - return nil, errors.Errorf("response status code %d", resp.StatusCode) - } -} - func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate interface{}, scanType string) ( *WebError, error, ) { From 31730cb121d07a89f4891e07e53664445c49bf04 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 3 Dec 2025 13:46:33 +0530 Subject: [PATCH 15/19] code-review-changes --- internal/commands/predicates.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/commands/predicates.go b/internal/commands/predicates.go index fc1b84dd6..f0501291a 100644 --- a/internal/commands/predicates.go +++ b/internal/commands/predicates.go @@ -97,7 +97,7 @@ func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWra triageShowCmd.PersistentFlags().String(params.SimilarityIDFlag, "", "Similarity ID") triageShowCmd.PersistentFlags().String(params.ProjectIDFlag, "", "Project ID") triageShowCmd.PersistentFlags().String(params.ScanTypeFlag, "", "Scan Type") - triageShowCmd.PersistentFlags().StringSlice(params.VulnerabilitiesFlag, []string{}, "List Vulnerabilities string") + triageShowCmd.PersistentFlags().StringSlice(params.VulnerabilitiesFlag, []string{}, "SCA Vulnerabilities details") markFlagAsRequired(triageShowCmd, params.ProjectIDFlag) markFlagAsRequired(triageShowCmd, params.ScanTypeFlag) @@ -133,7 +133,7 @@ func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesW triageUpdateCmd.PersistentFlags().Int(params.CustomStateIDFlag, -1, "Specify the ID of the states that you would like to apply to this result") triageUpdateCmd.PersistentFlags().String(params.CommentFlag, "", "Optional comment") triageUpdateCmd.PersistentFlags().String(params.ScanTypeFlag, "", "Scan Type") - triageUpdateCmd.PersistentFlags().StringSlice(params.VulnerabilitiesFlag, []string{}, "List Vulnerabilities string") + triageUpdateCmd.PersistentFlags().StringSlice(params.VulnerabilitiesFlag, []string{}, "SCA Vulnerabilities details") markFlagAsRequired(triageUpdateCmd, params.ProjectIDFlag) markFlagAsRequired(triageUpdateCmd, params.ScanTypeFlag) @@ -156,8 +156,7 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f return errors.Errorf("%s", "Multiple project-ids are not allowed.") } - if strings.EqualFold(scanType, params.ScaType) { - // SCA + if strings.EqualFold(strings.ToLower(strings.TrimSpace(scanType)), params.ScaType) { if len(vulnerabilityDetails) == 0 { return errors.Errorf("%s", "Failed showing the predicate. Vulnerabilities are required for SCA triage") } @@ -171,7 +170,6 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f } return nil } else { - // other than SCA predicatesCollection, errorModel, err = resultsPredicatesWrapper.GetAllPredicatesForSimilarityID(similarityID, projectID, scanType) if err != nil { return errors.Wrapf(err, "%s", "Failed showing the predicate") From 50333b752b38ad494298ad66600c6190cf41f577 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:39:48 +0530 Subject: [PATCH 16/19] name-change-of-mock-struct-undone --- internal/commands/root_test.go | 4 ++-- internal/wrappers/mock/predicates-mock.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/commands/root_test.go b/internal/commands/root_test.go index e5d8a0735..654594c5b 100644 --- a/internal/commands/root_test.go +++ b/internal/commands/root_test.go @@ -42,7 +42,7 @@ func createASTTestCommand() *cobra.Command { resultsPdfWrapper := &mock.ResultsPdfWrapper{} resultsJSONWrapper := &mock.ResultsJSONWrapper{} scansMockWrapper.Running = true - resultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} + resultsPredicatesMockWrapper := &mock.ResultsPredicatesMockWrapper{} groupsMockWrapper := &mock.GroupsMockWrapper{} uploadsMockWrapper := &mock.UploadsMockWrapper{} projectsMockWrapper := &mock.ProjectsMockWrapper{} @@ -79,7 +79,7 @@ func createASTTestCommand() *cobra.Command { exportWrapper, resultsPdfWrapper, resultsJSONWrapper, - resultsPredicatesWrapper, + resultsPredicatesMockWrapper, customStatesMockWrapper, codeBashingWrapper, uploadsMockWrapper, diff --git a/internal/wrappers/mock/predicates-mock.go b/internal/wrappers/mock/predicates-mock.go index 081682362..9f8d7a513 100644 --- a/internal/wrappers/mock/predicates-mock.go +++ b/internal/wrappers/mock/predicates-mock.go @@ -7,17 +7,17 @@ import ( "github.com/checkmarx/ast-cli/internal/wrappers" ) -type ResultsPredicatesWrapper struct { +type ResultsPredicatesMockWrapper struct { } -func (r ResultsPredicatesWrapper) PredicateSeverityAndState(predicate interface{}, scanType string) ( +func (r ResultsPredicatesMockWrapper) PredicateSeverityAndState(predicate interface{}, scanType string) ( *wrappers.WebError, error, ) { fmt.Println("Called 'PredicateSeverityAndState' in ResultsPredicatesMockWrapper") return nil, nil } -func (r ResultsPredicatesWrapper) GetAllPredicatesForSimilarityID(similarityID, projectID, scannerType string) ( +func (r ResultsPredicatesMockWrapper) GetAllPredicatesForSimilarityID(similarityID, projectID, scannerType string) ( *wrappers.PredicatesCollectionResponseModel, *wrappers.WebError, error, ) { fmt.Println("Called 'GetAllPredicatesForSimilarityID' in ResultsPredicatesMockWrapper") @@ -44,7 +44,7 @@ func (r ResultsPredicatesWrapper) GetAllPredicatesForSimilarityID(similarityID, }, nil, nil } -func (r ResultsPredicatesWrapper) GetScaPredicates(vulnerabilityDetails []string, projectID string) (*wrappers.ScaPredicateResult, error) { +func (r ResultsPredicatesMockWrapper) GetScaPredicates(vulnerabilityDetails []string, projectID string) (*wrappers.ScaPredicateResult, error) { fmt.Println("Called 'GetScaPredicates' in ResultsPredicatesMockWrapper") return nil, nil } From 63b7e2b60c874f8ed607f18bac61de9a50316dc2 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:32:09 +0530 Subject: [PATCH 17/19] changed-vulnerability-flag-name --- internal/params/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/params/flags.go b/internal/params/flags.go index 9b25bf56c..b201078b6 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -131,7 +131,7 @@ const ( SingleValueSize = 1 ChangeState = "ChangeState" SimilarityIDFlag = "similarity-id" - VulnerabilitiesFlag = "vulnerabilities" + VulnerabilitiesFlag = "vulnerability-identifiers" SeverityFlag = "severity" StateFlag = "state" CustomStateIDFlag = "state-id" From 7aedf92233892cdde200889a7c3b503cf7757d89 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 10 Dec 2025 07:56:50 +0530 Subject: [PATCH 18/19] corrected-struct-name --- internal/commands/predicates_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 8b35c0c3b..76942bbbf 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -152,7 +152,7 @@ func TestIsCustomState(t *testing.T) { } } func TestRunTriageUpdateWithNotFoundCustomState(t *testing.T) { - mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} + mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} clearFlags() @@ -174,7 +174,7 @@ func TestRunTriageUpdateWithNotFoundCustomState(t *testing.T) { } func TestRunTriageUpdateWithCustomState(t *testing.T) { - mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} + mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} clearFlags() @@ -196,7 +196,7 @@ func TestRunTriageUpdateWithCustomState(t *testing.T) { } func TestRunTriageUpdateWithSystemState(t *testing.T) { - mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} + mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} @@ -691,7 +691,7 @@ func TestValidateVulnerabilityDetails(t *testing.T) { } func TestRunUpdateTriageCommandForSCA(t *testing.T) { - mockResultsPredicatesWrapper := &mock.ResultsPredicatesWrapper{} + mockResultsPredicatesWrapper := &mock.ResultsPredicatesMockWrapper{} mockFeatureFlagsWrapper := &mock.FeatureFlagsMockWrapper{} mockCustomStatesWrapper := &mock.CustomStatesMockWrapper{} From 2352d5bb529b7c736be3ecec705f99ac9ea34ff4 Mon Sep 17 00:00:00 2001 From: Hitesh Madgulkar <212497904+cx-hitesh-madgulkar@users.noreply.github.com> Date: Wed, 10 Dec 2025 08:48:48 +0530 Subject: [PATCH 19/19] changed-flag-name-in-ut --- internal/commands/predicates_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/commands/predicates_test.go b/internal/commands/predicates_test.go index 76942bbbf..c7d49461c 100644 --- a/internal/commands/predicates_test.go +++ b/internal/commands/predicates_test.go @@ -445,7 +445,7 @@ func TestRunShowTriageCommandForSCAWithMultipleProjects(t *testing.T) { "MOCK1,MOCK2", "--scan-type", "sca", - "--vulnerabilities", + "--vulnerability-identifiers", "packageName=lodash,packageVersion=4.17.20,packageManager=npm", ) assert.ErrorContains(t, err, "Multiple project-ids are not allowed") @@ -700,7 +700,7 @@ func TestRunUpdateTriageCommandForSCA(t *testing.T) { "--project-id", "MOCK", "--state", "NOT_EXPLOITABLE", "--scan-type", "sca", - "--vulnerabilities", "packageName=lodash,packageVersion=4.17.20,packageManager=npm", + "--vulnerability-identifiers", "packageName=lodash,packageVersion=4.17.20,packageManager=npm", "--comment", "Testing SCA update", })