Skip to content

Commit 1574ebb

Browse files
Merge pull request #1039 from Checkmarx/feature/saraChen/triageUpdateWithCustomState
Triage update support custom state.(AST-81674)
2 parents f851d81 + 31e189a commit 1574ebb

File tree

7 files changed

+372
-39
lines changed

7 files changed

+372
-39
lines changed

internal/commands/predicates.go

Lines changed: 83 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
11
package commands
22

33
import (
4-
"strings"
5-
"time"
6-
74
"github.com/MakeNowJust/heredoc"
85
"github.com/checkmarx/ast-cli/internal/commands/util/printer"
96
"github.com/checkmarx/ast-cli/internal/params"
107
"github.com/checkmarx/ast-cli/internal/wrappers"
118
"github.com/pkg/errors"
129
"github.com/spf13/cobra"
10+
"strings"
11+
"time"
1312
)
1413

15-
var constantsStates = []wrappers.CustomState{
16-
{ID: -1, Name: "To Verify", Type: ""},
17-
{ID: -1, Name: "Not Exploitable", Type: ""},
18-
{ID: -1, Name: "Proposed Not Exploitable", Type: ""},
19-
{ID: -1, Name: "Confirmed", Type: ""},
20-
{ID: -1, Name: "Urgent", Type: ""},
14+
var systemStates = []wrappers.CustomState{
15+
{ID: -1, Name: params.ToVerify, Type: ""},
16+
{ID: -1, Name: params.NotExploitable, Type: ""},
17+
{ID: -1, Name: params.ProposedNotExploitable, Type: ""},
18+
{ID: -1, Name: params.CONFIRMED, Type: ""},
19+
{ID: -1, Name: params.URGENT, Type: ""},
2120
}
2221

23-
func NewResultsPredicatesCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper ) *cobra.Command {
22+
func NewResultsPredicatesCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) *cobra.Command {
2423
triageCmd := &cobra.Command{
2524
Use: "triage",
2625
Short: "Manage results",
2726
Long: "The 'triage' command enables the ability to manage results in Checkmarx One.",
2827
}
2928
triageShowCmd := triageShowSubCommand(resultsPredicatesWrapper)
30-
triageUpdateCmd := triageUpdateSubCommand(resultsPredicatesWrapper, featureFlagsWrapper)
29+
triageUpdateCmd := triageUpdateSubCommand(resultsPredicatesWrapper, featureFlagsWrapper, customStatesWrapper)
3130
triageGetStatesCmd := triageGetStatesSubCommand(customStatesWrapper, featureFlagsWrapper)
3231

33-
3432
addFormatFlagToMultipleCommands(
3533
[]*cobra.Command{triageShowCmd},
3634
printer.FormatList, printer.FormatTable, printer.FormatJSON,
@@ -44,7 +42,7 @@ func triageGetStatesSubCommand(customStatesWrapper wrappers.CustomStatesWrapper,
4442
triageGetStatesCmd := &cobra.Command{
4543
Use: "get-states",
4644
Short: "Show the custom states that have been configured in your tenant",
47-
Long: "The get-states command shows information about each of the custom states that have been configured in your tenant account",
45+
Long: "The get-states command shows information about each of the custom states that have been configured in your tenant account",
4846
Example: heredoc.Doc(
4947
`
5048
$ cx triage get-states
@@ -63,21 +61,19 @@ func runTriageGetStates(customStatesWrapper wrappers.CustomStatesWrapper, featur
6361
return func(cmd *cobra.Command, _ []string) error {
6462
flagResponse, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.CustomStatesFeatureFlag)
6563
if !flagResponse.Status {
66-
return printer.Print(cmd.OutOrStdout(), constantsStates, printer.FormatJSON)
64+
return printer.Print(cmd.OutOrStdout(), systemStates, printer.FormatJSON)
6765
}
6866
includeDeleted, _ := cmd.Flags().GetBool(params.AllStatesFlag)
6967
states, err := customStatesWrapper.GetAllCustomStates(includeDeleted)
7068
if err != nil {
7169
return errors.Wrap(err, "Failed to fetch custom states")
7270
}
73-
states = append(states, constantsStates...)
71+
states = append(states, systemStates...)
7472
err = printer.Print(cmd.OutOrStdout(), states, printer.FormatJSON)
7573
return err
7674
}
7775
}
7876

79-
80-
8177
func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) *cobra.Command {
8278
triageShowCmd := &cobra.Command{
8379
Use: "show",
@@ -103,7 +99,7 @@ func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWra
10399
return triageShowCmd
104100
}
105101

106-
func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
102+
func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) *cobra.Command {
107103
triageUpdateCmd := &cobra.Command{
108104
Use: "update",
109105
Short: "Update the state, severity or comment for the given issue",
@@ -113,26 +109,27 @@ func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesW
113109
$ cx triage update
114110
--similarity-id <SimilarityID>
115111
--project-id <ProjectID>
116-
--state <TO_VERIFY|NOT_EXPLOITABLE|PROPOSED_NOT_EXPLOITABLE|CONFIRMED|URGENT>
112+
--state <TO_VERIFY|NOT_EXPLOITABLE|PROPOSED_NOT_EXPLOITABLE|CONFIRMED|URGENT|custom state>
113+
--state-id <custom state ID>
117114
--severity <CRITICAL|HIGH|MEDIUM|LOW|INFO>
118115
--comment <Comment(Optional)>
119116
--scan-type <SAST|IAC-SECURITY>
120117
`,
121118
),
122-
RunE: runTriageUpdate(resultsPredicatesWrapper, featureFlagsWrapper),
119+
RunE: runTriageUpdate(resultsPredicatesWrapper, featureFlagsWrapper, customStatesWrapper),
123120
}
124121

125122
triageUpdateCmd.PersistentFlags().String(params.SimilarityIDFlag, "", "Similarity ID")
126123
triageUpdateCmd.PersistentFlags().String(params.SeverityFlag, "", "Severity")
127124
triageUpdateCmd.PersistentFlags().String(params.ProjectIDFlag, "", "Project ID.")
128-
triageUpdateCmd.PersistentFlags().String(params.StateFlag, "", "State")
125+
triageUpdateCmd.PersistentFlags().String(params.StateFlag, "", "Specify the state that you would like to apply. Can be a pre-configured state (e.g., not_exploitable) or a custom state created in your account.")
126+
triageUpdateCmd.PersistentFlags().Int(params.CustomStateIDFlag, -1, "Specify the ID of the states that you would like to apply to this result.")
129127
triageUpdateCmd.PersistentFlags().String(params.CommentFlag, "", "Optional comment.")
130128
triageUpdateCmd.PersistentFlags().String(params.ScanTypeFlag, "", "Scan Type")
131129

132130
markFlagAsRequired(triageUpdateCmd, params.SimilarityIDFlag)
133131
markFlagAsRequired(triageUpdateCmd, params.SeverityFlag)
134132
markFlagAsRequired(triageUpdateCmd, params.ProjectIDFlag)
135-
markFlagAsRequired(triageUpdateCmd, params.StateFlag)
136133
markFlagAsRequired(triageUpdateCmd, params.ScanTypeFlag)
137134

138135
return triageUpdateCmd
@@ -182,12 +179,13 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f
182179
}
183180
}
184181

185-
func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) func(*cobra.Command, []string) error {
182+
func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, customStatesWrapper wrappers.CustomStatesWrapper) func(*cobra.Command, []string) error {
186183
return func(cmd *cobra.Command, _ []string) error {
187184
similarityID, _ := cmd.Flags().GetString(params.SimilarityIDFlag)
188185
projectID, _ := cmd.Flags().GetString(params.ProjectIDFlag)
189186
severity, _ := cmd.Flags().GetString(params.SeverityFlag)
190187
state, _ := cmd.Flags().GetString(params.StateFlag)
188+
customStateID, _ := cmd.Flags().GetInt(params.CustomStateIDFlag)
191189
comment, _ := cmd.Flags().GetString(params.CommentFlag)
192190
scanType, _ := cmd.Flags().GetString(params.ScanTypeFlag)
193191
// check if the current tenant has critical severity available
@@ -196,22 +194,82 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper,
196194
if !criticalEnabled && strings.EqualFold(severity, "critical") {
197195
return errors.Errorf("%s", "Critical severity is not available for your tenant.This severity status will be enabled shortly")
198196
}
197+
198+
var err error
199+
state, customStateID, err = determineSystemOrCustomState(customStatesWrapper, featureFlagsWrapper, state, customStateID)
200+
if err != nil {
201+
return err
202+
}
203+
199204
predicate := &wrappers.PredicateRequest{
200205
SimilarityID: similarityID,
201206
ProjectID: projectID,
202207
Severity: severity,
203-
State: state,
204208
Comment: comment,
205209
}
206210

207-
_, err := resultsPredicatesWrapper.PredicateSeverityAndState(predicate, scanType)
211+
if state != "" {
212+
predicate.State = &state
213+
} else {
214+
predicate.CustomStateID = &customStateID
215+
}
216+
217+
_, err = resultsPredicatesWrapper.PredicateSeverityAndState(predicate, scanType)
208218
if err != nil {
209219
return errors.Wrapf(err, "%s", "Failed updating the predicate")
210220
}
211221

212222
return nil
213223
}
214224
}
225+
func determineSystemOrCustomState(customStatesWrapper wrappers.CustomStatesWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, state string, customStateID int) (string, int, error) {
226+
if !isCustomState(state) {
227+
return state, -1, nil
228+
}
229+
230+
flagResponse, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.SastCustomStateEnabled)
231+
sastCustomStateEnabled := flagResponse.Status
232+
if !sastCustomStateEnabled {
233+
return "", -1, errors.Errorf("%s", "Custom state is not available for your tenant.")
234+
}
235+
236+
if customStateID == -1 {
237+
if state == "" {
238+
return "", -1, errors.Errorf("state-id is required when state is not provided")
239+
}
240+
241+
var err error
242+
customStateID, err = getCustomStateID(customStatesWrapper, state)
243+
if err != nil {
244+
return "", -1, errors.Wrapf(err, "Failed to get custom state ID for state: %s", state)
245+
}
246+
}
247+
return "", customStateID, nil
248+
}
249+
func isCustomState(state string) bool {
250+
if state == "" {
251+
return true
252+
}
253+
for _, systemState := range systemStates {
254+
if strings.EqualFold(state, systemState.Name) {
255+
return false
256+
}
257+
}
258+
return true
259+
}
260+
261+
func getCustomStateID(customStatesWrapper wrappers.CustomStatesWrapper, state string) (int, error) {
262+
customStates, err := customStatesWrapper.GetAllCustomStates(false)
263+
if err != nil {
264+
return -1, errors.Wrap(err, "Failed to fetch custom states")
265+
}
266+
for _, customState := range customStates {
267+
if customState.Name == state {
268+
return customState.ID, nil
269+
}
270+
}
271+
return -1, errors.Errorf("No matching state found for %s", state)
272+
}
215273

216274
type predicateView struct {
217275
ID string `format:"name:ID"`

0 commit comments

Comments
 (0)