Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
522f679
add new sscs licensing logic
cx-leonardo-fontes Jul 22, 2025
7b2fade
rename licensing variables
cx-leonardo-fontes Jul 24, 2025
184f68b
update and add unit tests for addSCSScan
cx-leonardo-fontes Jul 24, 2025
f424c52
update validateScanTypes logic and update unit tests
cx-leonardo-fontes Jul 24, 2025
920f41f
fix linter and add unit tests
cx-leonardo-fontes Jul 25, 2025
44398dd
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Jul 25, 2025
4d60885
refactor addSCSScan function to reduce complexity and add unit tests
cx-leonardo-fontes Jul 25, 2025
bdae006
rename licensing flag variables
cx-leonardo-fontes Jul 25, 2025
acc02f6
Revert "rename licensing flag variables"
cx-leonardo-fontes Jul 25, 2025
fe17ba0
rename licensing flag variables
cx-leonardo-fontes Jul 25, 2025
7285ff9
fix addSCSScan, double check this commit later
cx-leonardo-fontes Jul 25, 2025
2c32b30
fix addSCSScan and update tests
cx-leonardo-fontes Jul 29, 2025
17c48bc
remove unused
cx-leonardo-fontes Jul 29, 2025
774c349
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Jul 29, 2025
942dd36
add unit test for addSCSScan
cx-leonardo-fontes Jul 29, 2025
b552623
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Jul 31, 2025
780cb9e
run gofmt
cx-leonardo-fontes Aug 4, 2025
9d1bfb0
fix conflict
cx-leonardo-fontes Aug 13, 2025
0fa924f
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Aug 14, 2025
8672dd3
update user message for engineScsNotAllowed
cx-leonardo-fontes Aug 21, 2025
ce15be9
Merge branch 'feature/add-new-sscs-licensing' of https://github.com/C…
cx-leonardo-fontes Aug 21, 2025
c9fd7e0
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Aug 21, 2025
065a4f3
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Aug 21, 2025
96ce309
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Aug 21, 2025
d91ba00
update unit test
cx-leonardo-fontes Aug 21, 2025
c612e55
Merge branch 'feature/add-new-sscs-licensing' of https://github.com/C…
cx-leonardo-fontes Aug 21, 2025
b22b1cd
avoid passing boolean flag around when it is not necessary since ther…
cx-leonardo-fontes Aug 21, 2025
6b5b4ec
removed invalid struct field
cx-leonardo-fontes Aug 21, 2025
4dd28df
fix unit tests
cx-leonardo-fontes Aug 21, 2025
50b3bf1
Merge branch 'main' into feature/add-new-sscs-licensing
cx-leonardo-fontes Aug 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions internal/commands/predicates.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package commands

import (
"strings"
"time"

"github.com/MakeNowJust/heredoc"
"github.com/checkmarx/ast-cli/internal/commands/util/printer"
"github.com/checkmarx/ast-cli/internal/params"
"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"strings"
"time"
)

var systemStates = []wrappers.CustomState{
Expand Down
3 changes: 2 additions & 1 deletion internal/commands/predicates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ package commands

import (
"fmt"
"testing"

"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/checkmarx/ast-cli/internal/wrappers/mock"
"testing"

"gotest.tools/assert"
)
Expand Down
88 changes: 70 additions & 18 deletions internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ const (
"\nTo use this feature, you would need to purchase a license." +
"\nPlease contact our support team for assistance if you believe you have already purchased a license." +
"\nLicensed packages: %s"
engineScsNotAllowed = "You are trying to run a scan with the \"scs\" scan type. This requires either the \"repository‑health\" or the \"secret‑detection\" package license, depending on which scanner you are running." +
"\nTo use this feature, you need to purchase the appropriate license." +
"\nIf you think that you have already purchased the relevant license, please contact our support team for assistance." +
"\nLicensed packages: %s"
containerResolutionFileName = "containers-resolution.json"
directoryCreationPrefix = "cx-"
ScsScoreCardType = "scorecard"
Expand Down Expand Up @@ -986,7 +990,9 @@ func setupScanTypeProjectAndConfig(
configArr = append(configArr, containersConfig)
}

var SCSConfig, scsErr = addSCSScan(cmd, resubmitConfig, userAllowedEngines[commonParams.EnterpriseSecretsType])
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
var SCSConfig, scsErr = addSCSScan(cmd, resubmitConfig, scsLicensingV2Flag.Status, userAllowedEngines[commonParams.RepositoryHealthType],
userAllowedEngines[commonParams.SecretDetectionType], userAllowedEngines[commonParams.EnterpriseSecretsType])
if scsErr != nil {
return scsErr
} else if SCSConfig != nil {
Expand Down Expand Up @@ -1267,17 +1273,18 @@ func addAPISecScan(cmd *cobra.Command) map[string]interface{} {
return nil
}

func createResubmitConfig(resubmitConfig []wrappers.Config, scsRepoToken, scsRepoURL string, hasEnterpriseSecretsLicense bool) wrappers.SCSConfig {
func createResubmitConfig(resubmitConfig []wrappers.Config, scsRepoToken, scsRepoURL string, isScsSecretDetectionAllowed,
isScsScorecardAllowed bool) wrappers.SCSConfig {
scsConfig := wrappers.SCSConfig{}
for _, config := range resubmitConfig {
resubmitTwoms := config.Value[configTwoms]
if resubmitTwoms != nil && hasEnterpriseSecretsLicense {
if resubmitTwoms != nil && isScsSecretDetectionAllowed {
scsConfig.Twoms = resubmitTwoms.(string)
}
scsConfig.RepoURL = scsRepoURL
scsConfig.RepoToken = scsRepoToken
resubmitScoreCard := config.Value[ScsScoreCardType]
if resubmitScoreCard == trueString && scsRepoToken != "" && scsRepoURL != "" {
if resubmitScoreCard == trueString && scsRepoToken != "" && scsRepoURL != "" && isScsScorecardAllowed {
scsConfig.Scorecard = trueString
} else {
scsConfig.Scorecard = falseString
Expand Down Expand Up @@ -1330,8 +1337,12 @@ func isScorecardRunnable(isScsEnginesFlagSet, scsScorecardSelected bool, scsRepo
return isURLSupportedByScorecard(scsRepoURL), nil
}

func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasEnterpriseSecretsLicense bool) (map[string]interface{}, error) {
if !scanTypeEnabled(commonParams.ScsType) && !scanTypeEnabled(commonParams.MicroEnginesType) {
func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, scsLicensingV2, hasRepositoryHealthLicense,
hasSecretDetectionLicense, hasEnterpriseSecretsLicense bool) (map[string]interface{}, error) {
scsEnabled := scanTypeEnabled(commonParams.ScsType)
scsScorecardAllowed := isScsScorecardAllowed(scsLicensingV2, hasRepositoryHealthLicense, scsEnabled)
scsSecretDetectionAllowed := isScsSecretDetectionAllowed(scsLicensingV2, hasSecretDetectionLicense, hasEnterpriseSecretsLicense, scsEnabled)
if !scsScorecardAllowed && !scsSecretDetectionAllowed {
return nil, nil
}
scsConfig := wrappers.SCSConfig{}
Expand All @@ -1352,20 +1363,20 @@ func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasEnterpr
scsEngines, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)

if resubmitConfig != nil {
scsConfig = createResubmitConfig(resubmitConfig, scsRepoToken, scsRepoURL, hasEnterpriseSecretsLicense)
scsConfig = createResubmitConfig(resubmitConfig, scsRepoToken, scsRepoURL, scsSecretDetectionAllowed, scsScorecardAllowed)
scsMapConfig[resultsMapValue] = &scsConfig
return scsMapConfig, nil
}

scsScoreCardSelected, scsSecretDetectionSelected := getSCSEnginesSelected(scsEngines) // secret-detection or scorecard

if scsSecretDetectionSelected && hasEnterpriseSecretsLicense {
if scsSecretDetectionSelected && scsSecretDetectionAllowed {
scsConfig.Twoms = trueString
}

isScsEnginesFlagSet := scsEngines != ""

if scsScoreCardSelected {
if scsScoreCardSelected && scsScorecardAllowed {
canRunScorecard, err := isScorecardRunnable(isScsEnginesFlagSet, scsScoreCardSelected, scsRepoToken, scsRepoURL, userScanTypes)
if err != nil {
return nil, err
Expand All @@ -1386,25 +1397,39 @@ func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasEnterpr
return scsMapConfig, nil
}

func isScsScorecardAllowed(scsLicensingV2, hasRepositoryHealthLicense, hasScsLicense bool) bool {
if scsLicensingV2 {
return hasRepositoryHealthLicense
}
return hasScsLicense
}

func isScsSecretDetectionAllowed(scsLicensingV2, hasSecretDetectionLicense, hasEnterpriseSecretsLicense, hasScsLicense bool) bool {
if scsLicensingV2 {
return hasSecretDetectionLicense
}
return hasScsLicense && hasEnterpriseSecretsLicense
}

func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) error {
var scanTypes []string
var SCSScanTypes []string

isSbomScan, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)

allowedEngines, err := jwtWrapper.GetAllowedEngines(featureFlagsWrapper)

isSbomScan, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)

if err != nil {
err = errors.Errorf("Error validating scan types: %v", err)
return err
}

userScanTypes, _ := cmd.Flags().GetString(commonParams.ScanTypes)
userSCSScanTypes, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)
if len(userScanTypes) > 0 {
userScanTypes = strings.ReplaceAll(strings.ToLower(userScanTypes), " ", "")
userScanTypes = strings.Replace(strings.ToLower(userScanTypes), commonParams.KicsType, commonParams.IacType, 1)
userScanTypes = strings.Replace(strings.ToLower(userScanTypes), commonParams.ContainersTypeFlag, commonParams.ContainersType, 1)
userSCSScanTypes = strings.Replace(strings.ToLower(userSCSScanTypes), commonParams.SCSEnginesFlag, commonParams.ScsType, 1)

scanTypes = strings.Split(userScanTypes, ",")

Expand All @@ -1422,18 +1447,26 @@ func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featu
}

for _, scanType := range scanTypes {
if scanType == commonParams.ScsType && scsLicensingV2Flag.Status {
// the SCS scan type is a special case because it contains two engines.
// Before the new licensing model, the main license was named "scs".
// Licenses are now separated for each engine, so this validation no longer makes sense.
// See validateSCSEngines.
continue
}
if !allowedEngines[scanType] {
keys := reflect.ValueOf(allowedEngines).MapKeys()
err = errors.Errorf(engineNotAllowed, scanType, scanType, keys)
return err
}
}

SCSScanTypes = strings.Split(userSCSScanTypes, ",")
if slices.Contains(SCSScanTypes, ScsSecretDetectionType) && !allowedEngines[commonParams.EnterpriseSecretsType] {
keys := reflect.ValueOf(allowedEngines).MapKeys()
err = errors.Errorf(engineNotAllowed, ScsSecretDetectionType, ScsSecretDetectionType, keys)
return err
userSCSScanTypes, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)
if slices.Contains(scanTypes, commonParams.ScsType) {
err = validateSCSEngines(allowedEngines, userSCSScanTypes, scsLicensingV2Flag.Status)
if err != nil {
return err
}
}
} else {
if isSbomScan {
Expand All @@ -1455,6 +1488,25 @@ func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featu
return nil
}

func validateSCSEngines(allowedEngines map[string]bool, userSCSScanTypes string, scsLicensingV2 bool) error {
licensesAvailable := reflect.ValueOf(allowedEngines).MapKeys()
scsScanTypes := strings.Split(userSCSScanTypes, ",")
if scsLicensingV2 {
secretDetectionAllowed := allowedEngines[commonParams.SecretDetectionType]
repositoryHeatlhAllowed := allowedEngines[commonParams.RepositoryHealthType]
if userSCSScanTypes == "" && !secretDetectionAllowed && !repositoryHeatlhAllowed {
return errors.Errorf(engineScsNotAllowed, licensesAvailable)
} else if slices.Contains(scsScanTypes, ScsSecretDetectionType) && !secretDetectionAllowed {
return errors.Errorf(engineNotAllowed, commonParams.SecretDetectionType, commonParams.SecretDetectionType, licensesAvailable)
} else if slices.Contains(scsScanTypes, ScsScoreCardType) && !repositoryHeatlhAllowed {
return errors.Errorf(engineNotAllowed, commonParams.RepositoryHealthType, commonParams.RepositoryHealthType, licensesAvailable)
}
} else if slices.Contains(scsScanTypes, ScsSecretDetectionType) && !allowedEngines[commonParams.EnterpriseSecretsType] {
return errors.Errorf(engineNotAllowed, ScsSecretDetectionType, ScsSecretDetectionType, licensesAvailable)
}
return nil
}

func scanTypeEnabled(scanType string) bool {
scanTypes := strings.Split(actualScanTypes, ",")
for _, a := range scanTypes {
Expand Down
Loading
Loading