11package commands
22
33import (
4+ "fmt"
5+ "sort"
46 "strings"
57 "time"
68
9+ "encoding/json"
10+
711 "github.com/MakeNowJust/heredoc"
812 "github.com/checkmarx/ast-cli/internal/commands/util/printer"
13+ "github.com/checkmarx/ast-cli/internal/logger"
914 "github.com/checkmarx/ast-cli/internal/params"
1015 "github.com/checkmarx/ast-cli/internal/wrappers"
1116 "github.com/pkg/errors"
@@ -112,8 +117,8 @@ func triageShowSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesWra
112117 triageShowCmd .PersistentFlags ().String (params .SimilarityIDFlag , "" , "Similarity ID" )
113118 triageShowCmd .PersistentFlags ().String (params .ProjectIDFlag , "" , "Project ID" )
114119 triageShowCmd .PersistentFlags ().String (params .ScanTypeFlag , "" , "Scan Type" )
120+ triageShowCmd .PersistentFlags ().StringSlice (params .VulnerabilitiesFlag , []string {}, "SCA Vulnerabilities details" )
115121
116- markFlagAsRequired (triageShowCmd , params .SimilarityIDFlag )
117122 markFlagAsRequired (triageShowCmd , params .ProjectIDFlag )
118123 markFlagAsRequired (triageShowCmd , params .ScanTypeFlag )
119124
@@ -137,6 +142,7 @@ func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesW
137142 --scan-type <SAST|IAC-SECURITY>
138143 ` ,
139144 ),
145+
140146 RunE : runTriageUpdate (resultsPredicatesWrapper , featureFlagsWrapper , customStatesWrapper ),
141147 }
142148
@@ -147,9 +153,8 @@ func triageUpdateSubCommand(resultsPredicatesWrapper wrappers.ResultsPredicatesW
147153 triageUpdateCmd .PersistentFlags ().Int (params .CustomStateIDFlag , - 1 , "Specify the ID of the states that you would like to apply to this result" )
148154 triageUpdateCmd .PersistentFlags ().String (params .CommentFlag , "" , "Optional comment" )
149155 triageUpdateCmd .PersistentFlags ().String (params .ScanTypeFlag , "" , "Scan Type" )
156+ triageUpdateCmd .PersistentFlags ().StringSlice (params .VulnerabilitiesFlag , []string {}, "SCA Vulnerabilities details" )
150157
151- markFlagAsRequired (triageUpdateCmd , params .SimilarityIDFlag )
152- markFlagAsRequired (triageUpdateCmd , params .SeverityFlag )
153158 markFlagAsRequired (triageUpdateCmd , params .ProjectIDFlag )
154159 markFlagAsRequired (triageUpdateCmd , params .ScanTypeFlag )
155160
@@ -165,38 +170,48 @@ func runTriageShow(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper) f
165170 similarityID , _ := cmd .Flags ().GetString (params .SimilarityIDFlag )
166171 scanType , _ := cmd .Flags ().GetString (params .ScanTypeFlag )
167172 projectID , _ := cmd .Flags ().GetString (params .ProjectIDFlag )
168-
173+ vulnerabilityDetails , _ := cmd . Flags (). GetStringSlice ( params . VulnerabilitiesFlag )
169174 projectIDs := strings .Split (projectID , "," )
170175 if len (projectIDs ) > 1 {
171176 return errors .Errorf ("%s" , "Multiple project-ids are not allowed." )
172177 }
173178
174- predicatesCollection , errorModel , err = resultsPredicatesWrapper .GetAllPredicatesForSimilarityID (
175- similarityID ,
176- projectID ,
177- scanType ,
178- )
179-
180- if err != nil {
181- return errors .Wrapf (err , "%s" , "Failed showing the predicate" )
182- }
183-
184- // Checking the response
185- if errorModel != nil {
186- return errors .Errorf (
187- "%s: CODE: %d, %s" ,
188- "Failed showing the predicate." ,
189- errorModel .Code ,
190- errorModel .Message ,
191- )
192- } else if predicatesCollection != nil {
193- err = printByFormat (cmd , toPredicatesView (* predicatesCollection ))
179+ if strings .EqualFold (strings .ToLower (strings .TrimSpace (scanType )), params .ScaType ) {
180+ if len (vulnerabilityDetails ) == 0 {
181+ return errors .Errorf ("%s" , "Failed showing the predicate. Vulnerabilities are required for SCA triage" )
182+ }
183+ scaPredicates , err := resultsPredicatesWrapper .GetScaPredicates (vulnerabilityDetails , projectID )
184+ if err != nil {
185+ return errors .Wrapf (err , "%s" , "Failed showing the predicate" )
186+ }
187+ err = printByFormat (cmd , toScaPredicateResultView (scaPredicates ))
194188 if err != nil {
195189 return err
196190 }
191+ return nil
192+ } else {
193+ predicatesCollection , errorModel , err = resultsPredicatesWrapper .GetAllPredicatesForSimilarityID (similarityID , projectID , scanType )
194+ if err != nil {
195+ return errors .Wrapf (err , "%s" , "Failed showing the predicate" )
196+ }
197+ // Checking the response
198+ if errorModel != nil {
199+ return errors .Errorf (
200+ "%s: CODE: %d, %s" ,
201+ "Failed showing the predicate." ,
202+ errorModel .Code ,
203+ errorModel .Message ,
204+ )
205+ } else if predicatesCollection != nil {
206+ err = printByFormat (cmd , toPredicatesView (* predicatesCollection ))
207+ if err != nil {
208+ return err
209+ }
210+ }
211+
212+ return nil
197213 }
198214
199- return nil
200215 }
201216}
202217
@@ -211,38 +226,124 @@ func runTriageUpdate(resultsPredicatesWrapper wrappers.ResultsPredicatesWrapper,
211226 scanType , _ := cmd .Flags ().GetString (params .ScanTypeFlag )
212227 // check if the current tenant has critical severity available
213228 flagResponse , _ := wrappers .GetSpecificFeatureFlag (featureFlagsWrapper , wrappers .CVSSV3Enabled )
229+ vulnerabilityDetails , _ := cmd .Flags ().GetStringSlice (params .VulnerabilitiesFlag )
230+
214231 criticalEnabled := flagResponse .Status
215232 if ! criticalEnabled && strings .EqualFold (severity , "critical" ) {
216233 return errors .Errorf ("%s" , "Critical severity is not available for your tenant.This severity status will be enabled shortly" )
217234 }
218-
219235 var err error
220236 state , customStateID , err = determineSystemOrCustomState (customStatesWrapper , featureFlagsWrapper , state , customStateID )
221237 if err != nil {
222- return err
238+ return errors . Wrapf ( err , "%s" , "Failed updating the predicate" )
223239 }
224240
225- predicate := & wrappers.PredicateRequest {
241+ predicate , err := preparePredicateRequest (vulnerabilityDetails , similarityID , projectID , severity , state , customStateID , comment , scanType )
242+ if err != nil {
243+ return errors .Wrapf (err , "%s" , "Failed updating the predicate" )
244+ }
245+ _ , err = resultsPredicatesWrapper .PredicateSeverityAndState (predicate , scanType )
246+ if err != nil {
247+ return errors .Wrapf (err , "%s" , "Failed updating the predicate" )
248+ }
249+ return nil
250+ }
251+ }
252+
253+ func preparePredicateRequest (vulnerabilityDetails []string , similarityID , projectID , severity , state string , customStateID int , comment , scanType string ) (interface {}, error ) {
254+ scanType = strings .ToLower (scanType )
255+ scanType = strings .TrimSpace (scanType )
256+ if strings .EqualFold (scanType , Sca ) {
257+ state = transformState (state )
258+ payload , err := prepareScaTriagePayload (vulnerabilityDetails , comment , state , projectID )
259+ if err != nil {
260+ return nil , err
261+ }
262+ return payload , nil
263+ } else {
264+ payload := & wrappers.PredicateRequest {
226265 SimilarityID : similarityID ,
227266 ProjectID : projectID ,
228267 Severity : severity ,
229268 Comment : comment ,
230269 }
231-
232270 if state != "" {
233- predicate .State = & state
271+ payload .State = & state
234272 } else {
235- predicate .CustomStateID = & customStateID
273+ payload .CustomStateID = & customStateID
236274 }
275+ return payload , nil
276+ }
277+ }
237278
238- _ , err = resultsPredicatesWrapper .PredicateSeverityAndState (predicate , scanType )
279+ func transformState (state string ) string {
280+ state = strings .ToLower (strings .TrimSpace (state ))
281+ switch state {
282+ case strings .ToLower (params .ToVerify ):
283+ return wrappers .ToVerify
284+ case strings .ToLower (params .URGENT ):
285+ return wrappers .Urgent
286+ case strings .ToLower (params .NotExploitable ):
287+ return wrappers .NotExploitable
288+ case strings .ToLower (params .ProposedNotExploitable ):
289+ return wrappers .ProposedNotExploitable
290+ case strings .ToLower (params .CONFIRMED ):
291+ return wrappers .Confirmed
292+ }
293+ return ""
294+ }
295+
296+ func prepareScaTriagePayload (vulnerabilityDetails []string , comment , state , projectID string ) (interface {}, error ) {
297+ if len (vulnerabilityDetails ) == 0 {
298+ return nil , errors .Errorf ("Vulnerabilities details are required." )
299+ }
300+ scaTriageInfo := make (map [string ]interface {})
301+ for _ , vulnerability := range vulnerabilityDetails {
302+ vulnerabilityKeyVal := strings .Split (vulnerability , "=" )
303+ err := validateVulnerabilityDetails (vulnerabilityKeyVal )
239304 if err != nil {
240- return errors . Wrapf ( err , "%s" , "Failed updating the predicate" )
305+ return nil , err
241306 }
307+ scaTriageInfo [strings .TrimSpace (vulnerabilityKeyVal [0 ])] = strings .TrimSpace (vulnerabilityKeyVal [1 ])
308+ }
242309
243- return nil
310+ if scaTriageInfo ["packageName" ] == nil && scaTriageInfo ["packagename" ] == nil {
311+ return nil , errors .Errorf ("Package name is required" )
244312 }
313+ if scaTriageInfo ["packageVersion" ] == nil && scaTriageInfo ["packageversion" ] == nil {
314+ return nil , errors .Errorf ("Package version is required" )
315+ }
316+ if scaTriageInfo ["packageManager" ] == nil && scaTriageInfo ["packagemanager" ] == nil {
317+ return nil , errors .Errorf ("Package manager is required" )
318+ }
319+
320+ scaTriageInfo ["projectIds" ] = []string {projectID }
321+ actionInfo := make (map [string ]interface {})
322+ actionInfo ["actionType" ] = params .ChangeState
323+ actionInfo ["value" ] = state
324+ actionInfo ["comment" ] = comment
325+ scaTriageInfo ["actions" ] = []map [string ]interface {}{actionInfo }
326+ b , err := json .Marshal (scaTriageInfo )
327+ if err != nil {
328+ logger .PrintIfVerbose (fmt .Sprintf ("Failed to serialize vulnerabilities %s" , scaTriageInfo ))
329+ return nil , errors .Errorf ("Failed to prepare SCA triage request" )
330+ }
331+ payload := wrappers.ScaPredicateRequest {}
332+ err = json .Unmarshal (b , & payload )
333+ if err != nil {
334+ logger .PrintIfVerbose (fmt .Sprintf ("Failed to deserialize vulnerabilities %s" , string (b )))
335+ return nil , errors .Errorf ("Failed to prepare SCA triage request" )
336+ }
337+ return payload , nil
338+ }
339+
340+ func validateVulnerabilityDetails (vulnerability []string ) error {
341+ if len (vulnerability ) != params .KeyValuePairSize {
342+ return errors .Errorf ("Invalid vulnerabilities. It should be in a KEY=VALUE format" )
343+ }
344+ return nil
245345}
346+
246347func determineSystemOrCustomState (customStatesWrapper wrappers.CustomStatesWrapper , featureFlagsWrapper wrappers.FeatureFlagsWrapper , state string , customStateID int ) (string , int , error ) {
247348 if ! isCustomState (state ) {
248349 return state , - 1 , nil
@@ -304,6 +405,41 @@ type predicateView struct {
304405 CreatedAt time.Time `format:"name:Created at;time:01-02-06 15:04:05"`
305406}
306407
408+ type scaPredicateResultView struct {
409+ VulnerabilityID string `format:"name:Vulnerability ID"`
410+ PackageName string `format:"name:Package Name"`
411+ PackageVersion string `format:"name:Package Version"`
412+ PackageManager string `format:"name:Package Manager"`
413+ Comment string `format:"name:Comment"`
414+ State string `format:"name:State"`
415+ CreatedBy string `format:"name:Created By"`
416+ CreatedAt time.Time `format:"name:Created at;time:01-02-06 15:04:05"`
417+ }
418+
419+ func toScaPredicateResultView (scaPredicateResult * wrappers.ScaPredicateResult ) []scaPredicateResultView {
420+ view := []scaPredicateResultView {}
421+ if len (scaPredicateResult .Actions ) > 0 {
422+ for _ , action := range scaPredicateResult .Actions {
423+ view = append (view , scaPredicateResultView {
424+ VulnerabilityID : scaPredicateResult .Context .VulnerabilityID ,
425+ PackageName : scaPredicateResult .Context .PackageName ,
426+ PackageVersion : scaPredicateResult .Context .PackageVersion ,
427+ PackageManager : scaPredicateResult .Context .PackageManager ,
428+ Comment : action .Message ,
429+ State : action .ActionValue ,
430+ CreatedBy : action .UserName ,
431+ CreatedAt : action .CreatedAt ,
432+ })
433+ }
434+ }
435+
436+ sort .Slice (view , func (i , j int ) bool {
437+ return view [i ].CreatedAt .After (view [j ].CreatedAt )
438+ })
439+
440+ return view
441+ }
442+
307443func toPredicatesView (predicatesCollection wrappers.PredicatesCollectionResponseModel ) []predicateView {
308444 projectPredicatesCollection := predicatesCollection .PredicateHistoryPerProject
309445
0 commit comments