Skip to content

Commit ba50ebd

Browse files
Merge pull request #1243 from Checkmarx/feature/add-new-sscs-licensing
Add new SSCS licensing (AST-104600)
2 parents 1dc22ec + 50b3bf1 commit ba50ebd

21 files changed

+1309
-524
lines changed

internal/commands/predicates.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package commands
22

33
import (
4+
"strings"
5+
"time"
6+
47
"github.com/MakeNowJust/heredoc"
58
"github.com/checkmarx/ast-cli/internal/commands/util/printer"
69
"github.com/checkmarx/ast-cli/internal/params"
710
"github.com/checkmarx/ast-cli/internal/wrappers"
811
"github.com/pkg/errors"
912
"github.com/spf13/cobra"
10-
"strings"
11-
"time"
1213
)
1314

1415
var systemStates = []wrappers.CustomState{

internal/commands/predicates_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ package commands
44

55
import (
66
"fmt"
7+
"testing"
8+
79
"github.com/checkmarx/ast-cli/internal/wrappers"
810
"github.com/checkmarx/ast-cli/internal/wrappers/mock"
9-
"testing"
1011

1112
"gotest.tools/assert"
1213
)

internal/commands/scan.go

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ const (
110110
"\nTo use this feature, you would need to purchase a license." +
111111
"\nPlease contact our support team for assistance if you believe you have already purchased a license." +
112112
"\nLicensed packages: %s"
113+
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." +
114+
"\nTo use this feature, you need to purchase the appropriate license." +
115+
"\nIf you think that you have already purchased the relevant license, please contact our support team for assistance." +
116+
"\nLicensed packages: %s"
113117
containerResolutionFileName = "containers-resolution.json"
114118
directoryCreationPrefix = "cx-"
115119
ScsScoreCardType = "scorecard"
@@ -986,7 +990,9 @@ func setupScanTypeProjectAndConfig(
986990
configArr = append(configArr, containersConfig)
987991
}
988992

989-
var SCSConfig, scsErr = addSCSScan(cmd, resubmitConfig, userAllowedEngines[commonParams.EnterpriseSecretsType])
993+
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
994+
var SCSConfig, scsErr = addSCSScan(cmd, resubmitConfig, scsLicensingV2Flag.Status, userAllowedEngines[commonParams.RepositoryHealthType],
995+
userAllowedEngines[commonParams.SecretDetectionType], userAllowedEngines[commonParams.EnterpriseSecretsType])
990996
if scsErr != nil {
991997
return scsErr
992998
} else if SCSConfig != nil {
@@ -1267,17 +1273,18 @@ func addAPISecScan(cmd *cobra.Command) map[string]interface{} {
12671273
return nil
12681274
}
12691275

1270-
func createResubmitConfig(resubmitConfig []wrappers.Config, scsRepoToken, scsRepoURL string, hasEnterpriseSecretsLicense bool) wrappers.SCSConfig {
1276+
func createResubmitConfig(resubmitConfig []wrappers.Config, scsRepoToken, scsRepoURL string, isScsSecretDetectionAllowed,
1277+
isScsScorecardAllowed bool) wrappers.SCSConfig {
12711278
scsConfig := wrappers.SCSConfig{}
12721279
for _, config := range resubmitConfig {
12731280
resubmitTwoms := config.Value[configTwoms]
1274-
if resubmitTwoms != nil && hasEnterpriseSecretsLicense {
1281+
if resubmitTwoms != nil && isScsSecretDetectionAllowed {
12751282
scsConfig.Twoms = resubmitTwoms.(string)
12761283
}
12771284
scsConfig.RepoURL = scsRepoURL
12781285
scsConfig.RepoToken = scsRepoToken
12791286
resubmitScoreCard := config.Value[ScsScoreCardType]
1280-
if resubmitScoreCard == trueString && scsRepoToken != "" && scsRepoURL != "" {
1287+
if resubmitScoreCard == trueString && scsRepoToken != "" && scsRepoURL != "" && isScsScorecardAllowed {
12811288
scsConfig.Scorecard = trueString
12821289
} else {
12831290
scsConfig.Scorecard = falseString
@@ -1330,8 +1337,12 @@ func isScorecardRunnable(isScsEnginesFlagSet, scsScorecardSelected bool, scsRepo
13301337
return isURLSupportedByScorecard(scsRepoURL), nil
13311338
}
13321339

1333-
func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasEnterpriseSecretsLicense bool) (map[string]interface{}, error) {
1334-
if !scanTypeEnabled(commonParams.ScsType) && !scanTypeEnabled(commonParams.MicroEnginesType) {
1340+
func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, scsLicensingV2, hasRepositoryHealthLicense,
1341+
hasSecretDetectionLicense, hasEnterpriseSecretsLicense bool) (map[string]interface{}, error) {
1342+
scsEnabled := scanTypeEnabled(commonParams.ScsType)
1343+
scsScorecardAllowed := isScsScorecardAllowed(scsLicensingV2, hasRepositoryHealthLicense, scsEnabled)
1344+
scsSecretDetectionAllowed := isScsSecretDetectionAllowed(scsLicensingV2, hasSecretDetectionLicense, hasEnterpriseSecretsLicense, scsEnabled)
1345+
if !scsScorecardAllowed && !scsSecretDetectionAllowed {
13351346
return nil, nil
13361347
}
13371348
scsConfig := wrappers.SCSConfig{}
@@ -1352,20 +1363,20 @@ func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasEnterpr
13521363
scsEngines, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)
13531364

13541365
if resubmitConfig != nil {
1355-
scsConfig = createResubmitConfig(resubmitConfig, scsRepoToken, scsRepoURL, hasEnterpriseSecretsLicense)
1366+
scsConfig = createResubmitConfig(resubmitConfig, scsRepoToken, scsRepoURL, scsSecretDetectionAllowed, scsScorecardAllowed)
13561367
scsMapConfig[resultsMapValue] = &scsConfig
13571368
return scsMapConfig, nil
13581369
}
13591370

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

1362-
if scsSecretDetectionSelected && hasEnterpriseSecretsLicense {
1373+
if scsSecretDetectionSelected && scsSecretDetectionAllowed {
13631374
scsConfig.Twoms = trueString
13641375
}
13651376

13661377
isScsEnginesFlagSet := scsEngines != ""
13671378

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

1400+
func isScsScorecardAllowed(scsLicensingV2, hasRepositoryHealthLicense, hasScsLicense bool) bool {
1401+
if scsLicensingV2 {
1402+
return hasRepositoryHealthLicense
1403+
}
1404+
return hasScsLicense
1405+
}
1406+
1407+
func isScsSecretDetectionAllowed(scsLicensingV2, hasSecretDetectionLicense, hasEnterpriseSecretsLicense, hasScsLicense bool) bool {
1408+
if scsLicensingV2 {
1409+
return hasSecretDetectionLicense
1410+
}
1411+
return hasScsLicense && hasEnterpriseSecretsLicense
1412+
}
1413+
13891414
func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) error {
13901415
var scanTypes []string
1391-
var SCSScanTypes []string
13921416

1393-
isSbomScan, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
1417+
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
13941418

13951419
allowedEngines, err := jwtWrapper.GetAllowedEngines(featureFlagsWrapper)
1420+
1421+
isSbomScan, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
1422+
13961423
if err != nil {
13971424
err = errors.Errorf("Error validating scan types: %v", err)
13981425
return err
13991426
}
14001427

14011428
userScanTypes, _ := cmd.Flags().GetString(commonParams.ScanTypes)
1402-
userSCSScanTypes, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)
14031429
if len(userScanTypes) > 0 {
14041430
userScanTypes = strings.ReplaceAll(strings.ToLower(userScanTypes), " ", "")
14051431
userScanTypes = strings.Replace(strings.ToLower(userScanTypes), commonParams.KicsType, commonParams.IacType, 1)
14061432
userScanTypes = strings.Replace(strings.ToLower(userScanTypes), commonParams.ContainersTypeFlag, commonParams.ContainersType, 1)
1407-
userSCSScanTypes = strings.Replace(strings.ToLower(userSCSScanTypes), commonParams.SCSEnginesFlag, commonParams.ScsType, 1)
14081433

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

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

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

1432-
SCSScanTypes = strings.Split(userSCSScanTypes, ",")
1433-
if slices.Contains(SCSScanTypes, ScsSecretDetectionType) && !allowedEngines[commonParams.EnterpriseSecretsType] {
1434-
keys := reflect.ValueOf(allowedEngines).MapKeys()
1435-
err = errors.Errorf(engineNotAllowed, ScsSecretDetectionType, ScsSecretDetectionType, keys)
1436-
return err
1464+
userSCSScanTypes, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)
1465+
if slices.Contains(scanTypes, commonParams.ScsType) {
1466+
err = validateSCSEngines(allowedEngines, userSCSScanTypes, scsLicensingV2Flag.Status)
1467+
if err != nil {
1468+
return err
1469+
}
14371470
}
14381471
} else {
14391472
if isSbomScan {
@@ -1455,6 +1488,25 @@ func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featu
14551488
return nil
14561489
}
14571490

1491+
func validateSCSEngines(allowedEngines map[string]bool, userSCSScanTypes string, scsLicensingV2 bool) error {
1492+
licensesAvailable := reflect.ValueOf(allowedEngines).MapKeys()
1493+
scsScanTypes := strings.Split(userSCSScanTypes, ",")
1494+
if scsLicensingV2 {
1495+
secretDetectionAllowed := allowedEngines[commonParams.SecretDetectionType]
1496+
repositoryHeatlhAllowed := allowedEngines[commonParams.RepositoryHealthType]
1497+
if userSCSScanTypes == "" && !secretDetectionAllowed && !repositoryHeatlhAllowed {
1498+
return errors.Errorf(engineScsNotAllowed, licensesAvailable)
1499+
} else if slices.Contains(scsScanTypes, ScsSecretDetectionType) && !secretDetectionAllowed {
1500+
return errors.Errorf(engineNotAllowed, commonParams.SecretDetectionType, commonParams.SecretDetectionType, licensesAvailable)
1501+
} else if slices.Contains(scsScanTypes, ScsScoreCardType) && !repositoryHeatlhAllowed {
1502+
return errors.Errorf(engineNotAllowed, commonParams.RepositoryHealthType, commonParams.RepositoryHealthType, licensesAvailable)
1503+
}
1504+
} else if slices.Contains(scsScanTypes, ScsSecretDetectionType) && !allowedEngines[commonParams.EnterpriseSecretsType] {
1505+
return errors.Errorf(engineNotAllowed, ScsSecretDetectionType, ScsSecretDetectionType, licensesAvailable)
1506+
}
1507+
return nil
1508+
}
1509+
14581510
func scanTypeEnabled(scanType string) bool {
14591511
scanTypes := strings.Split(actualScanTypes, ",")
14601512
for _, a := range scanTypes {

0 commit comments

Comments
 (0)