Skip to content

Commit 75760ca

Browse files
Merge branch 'main' into feature/get-states-custom-flag
2 parents 2c0fdac + 0a68efd commit 75760ca

32 files changed

+1142
-155
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ permissions:
3030

3131
jobs:
3232
build:
33-
runs-on: macos-13
33+
runs-on: macos-15
3434
env:
3535
AC_PASSWORD: ${{ secrets.AC_PASSWORD }}
3636
APPLE_DEVELOPER_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/checkmarx/ast-cli
33
go 1.24.11
44

55
require (
6-
github.com/Checkmarx/containers-resolver v1.0.27
6+
github.com/Checkmarx/containers-resolver v1.0.28
77
github.com/Checkmarx/containers-types v1.0.9
88
github.com/Checkmarx/gen-ai-prompts v0.0.0-20240807143411-708ceec12b63
99
github.com/Checkmarx/gen-ai-wrapper v1.0.3
@@ -48,8 +48,8 @@ require (
4848
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
4949
github.com/BobuSumisu/aho-corasick v1.0.3 // indirect
5050
github.com/BurntSushi/toml v1.5.0 // indirect
51-
github.com/Checkmarx/containers-images-extractor v1.0.20
52-
github.com/Checkmarx/containers-syft-packages-extractor v1.0.22 // indirect
51+
github.com/Checkmarx/containers-images-extractor v1.0.21
52+
github.com/Checkmarx/containers-syft-packages-extractor v1.0.23 // indirect
5353
github.com/CycloneDX/cyclonedx-go v0.9.2 // indirect
5454
github.com/DataDog/zstd v1.5.6 // indirect
5555
github.com/Masterminds/goutils v1.1.1 // indirect

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
6565
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
6666
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
6767
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
68-
github.com/Checkmarx/containers-images-extractor v1.0.20 h1:PGTtBMsjF77HrTtnmzzVGywFkuUtXfc/PBo46kMYORw=
69-
github.com/Checkmarx/containers-images-extractor v1.0.20/go.mod h1:HyzVb8TtTDf56hGlSakalPXtzjJ6VhTYe9fmAcOS+V8=
70-
github.com/Checkmarx/containers-resolver v1.0.27 h1:fEZkgQR+PLyIOunLRQAzofUX97I9qKGG9gAoKNI4ajw=
71-
github.com/Checkmarx/containers-resolver v1.0.27/go.mod h1:zxQja33k9SvDXG7eWq03U8WxkHIu/XchzjXsoKfhDFY=
72-
github.com/Checkmarx/containers-syft-packages-extractor v1.0.22 h1:5zzTrAgKOiqFvAwSS0DRmWyWuKK66jXj54wc8xroObQ=
73-
github.com/Checkmarx/containers-syft-packages-extractor v1.0.22/go.mod h1:OPGYISPnKtVFl2mZrClErv83ZLjUPKjdQQsXLmx++oY=
68+
github.com/Checkmarx/containers-images-extractor v1.0.21 h1:SEo4FyxUZnOkZnHqdpqDLcztHj/1IyEkvAnlTNBsNOA=
69+
github.com/Checkmarx/containers-images-extractor v1.0.21/go.mod h1:HyzVb8TtTDf56hGlSakalPXtzjJ6VhTYe9fmAcOS+V8=
70+
github.com/Checkmarx/containers-resolver v1.0.28 h1:FikNmHIAYqJ1G1qHixASDUjJirl+Dp635TuMYq/RfUY=
71+
github.com/Checkmarx/containers-resolver v1.0.28/go.mod h1:X6KwE/vFIDlgyBZKnkhRGitt65hWCZp0sdvgNTRyvSw=
72+
github.com/Checkmarx/containers-syft-packages-extractor v1.0.23 h1:qP4OBlCVF6BbOO0gzcoOzAtfdx7+M1kU3OsY2xBvy8E=
73+
github.com/Checkmarx/containers-syft-packages-extractor v1.0.23/go.mod h1:OPGYISPnKtVFl2mZrClErv83ZLjUPKjdQQsXLmx++oY=
7474
github.com/Checkmarx/containers-types v1.0.9 h1:LbHDj9LZ0x3f28wDx398WC19sw0U0EfEewHMLStBwvs=
7575
github.com/Checkmarx/containers-types v1.0.9/go.mod h1:KR0w8XCosq3+6jRCfQrH7i//Nj2u11qaUJM62CREFZA=
7676
github.com/Checkmarx/gen-ai-prompts v0.0.0-20240807143411-708ceec12b63 h1:SCuTcE+CFvgjbIxUNL8rsdB2sAhfuNx85HvxImKta3g=

internal/commands/auth.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66

77
"github.com/MakeNowJust/heredoc"
8+
"github.com/checkmarx/ast-cli/internal/logger"
89
"github.com/checkmarx/ast-cli/internal/params"
910
"github.com/checkmarx/ast-cli/internal/wrappers"
1011
"github.com/google/uuid"
@@ -38,7 +39,7 @@ type ClientCreated struct {
3839
Secret string `json:"secret"`
3940
}
4041

41-
func NewAuthCommand(authWrapper wrappers.AuthWrapper) *cobra.Command {
42+
func NewAuthCommand(authWrapper wrappers.AuthWrapper, telemetryWrapper wrappers.TelemetryWrapper) *cobra.Command {
4243
authCmd := &cobra.Command{
4344
Use: "auth",
4445
Short: "Validate authentication and create OAuth2 credentials",
@@ -110,13 +111,13 @@ func NewAuthCommand(authWrapper wrappers.AuthWrapper) *cobra.Command {
110111
`,
111112
),
112113
},
113-
RunE: validLogin(),
114+
RunE: validLogin(telemetryWrapper),
114115
}
115116
authCmd.AddCommand(createClientCmd, validLoginCmd)
116117
return authCmd
117118
}
118119

119-
func validLogin() func(cmd *cobra.Command, args []string) error {
120+
func validLogin(telemetryWrapper wrappers.TelemetryWrapper) func(cmd *cobra.Command, args []string) error {
120121
return func(cmd *cobra.Command, args []string) error {
121122
clientID := viper.GetString(params.AccessKeyIDConfigKey)
122123
clientSecret := viper.GetString(params.AccessKeySecretConfigKey)
@@ -125,6 +126,19 @@ func validLogin() func(cmd *cobra.Command, args []string) error {
125126
authWrapper := wrappers.NewAuthHTTPWrapper()
126127
authWrapper.SetPath(viper.GetString(params.ScansPathKey))
127128
err := authWrapper.ValidateLogin()
129+
130+
uniqueID := wrappers.GetUniqueID()
131+
if uniqueID != "" {
132+
telemetryErr := telemetryWrapper.SendAIDataToLog(&wrappers.DataForAITelemetry{
133+
UniqueID: uniqueID,
134+
Type: "authentication",
135+
SubType: "authentication",
136+
})
137+
if telemetryErr != nil {
138+
logger.PrintIfVerbose("Failed to send telemetry data: " + telemetryErr.Error())
139+
}
140+
}
141+
128142
if err != nil {
129143
return errors.Errorf("%s", err)
130144
}

internal/commands/predicates.go

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

33
import (
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+
246347
func 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+
307443
func toPredicatesView(predicatesCollection wrappers.PredicatesCollectionResponseModel) []predicateView {
308444
projectPredicatesCollection := predicatesCollection.PredicateHistoryPerProject
309445

0 commit comments

Comments
 (0)