Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions internal/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ func NewAstCLI(
prWrapper,
learnMoreWrapper,
tenantWrapper,
jwtWrapper,
chatWrapper,
policyWrapper,
scansWrapper,
Expand Down
28 changes: 24 additions & 4 deletions internal/commands/util/tenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/spf13/cobra"
)

func NewTenantConfigurationCommand(wrapper wrappers.TenantConfigurationWrapper) *cobra.Command {
func NewTenantConfigurationCommand(wrapper wrappers.TenantConfigurationWrapper, jwtWrapper wrappers.JWTWrapper) *cobra.Command {
cmd := &cobra.Command{
Use: "tenant",
Short: "Shows the tenant settings",
Expand All @@ -27,7 +27,7 @@ func NewTenantConfigurationCommand(wrapper wrappers.TenantConfigurationWrapper)
`,
),
},
RunE: runTenantCmd(wrapper),
RunE: runTenantCmd(wrapper, jwtWrapper),
}
cmd.PersistentFlags().String(
params.FormatFlag,
Expand All @@ -40,7 +40,7 @@ func NewTenantConfigurationCommand(wrapper wrappers.TenantConfigurationWrapper)
return cmd
}

func runTenantCmd(wrapper wrappers.TenantConfigurationWrapper) func(cmd *cobra.Command, args []string) error {
func runTenantCmd(wrapper wrappers.TenantConfigurationWrapper, jwtWrapper wrappers.JWTWrapper) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
tenantConfigurationResponse, errorModel, err := wrapper.GetTenantConfiguration()
if err != nil {
Expand All @@ -52,10 +52,16 @@ func runTenantCmd(wrapper wrappers.TenantConfigurationWrapper) func(cmd *cobra.C
if tenantConfigurationResponse != nil {
format, _ := cmd.Flags().GetString(params.FormatFlag)
tenantConfigurationResponseView := toTenantConfigurationResponseView(tenantConfigurationResponse)

licenseDetails, err := jwtWrapper.GetLicenseDetails()
if err == nil {
tenantConfigurationResponseView = appendLicenseDetails(tenantConfigurationResponseView, licenseDetails)
}

if format == "" {
format = defaultFormat
}
err := printer.Print(cmd.OutOrStdout(), tenantConfigurationResponseView, format)
err = printer.Print(cmd.OutOrStdout(), tenantConfigurationResponseView, format)
if err != nil {
return err
}
Expand All @@ -76,3 +82,17 @@ func toTenantConfigurationResponseView(response *[]*wrappers.TenantConfiguration
}
return tenantConfigurationResponseView
}

func appendLicenseDetails(responseView interface{}, licenseDetails map[string]string) interface{} {
tenantConfigurationResponseView := responseView.([]*wrappers.TenantConfigurationResponse)

for key, value := range licenseDetails {
licenseEntry := &wrappers.TenantConfigurationResponse{
Key: key,
Value: value,
}
tenantConfigurationResponseView = append(tenantConfigurationResponseView, licenseEntry)
}

return tenantConfigurationResponseView
}
12 changes: 6 additions & 6 deletions internal/commands/util/tenant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,41 @@ import (
)

func TestTenantConfigurationHelp(t *testing.T) {
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{})
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{}, &mock.JWTMockWrapper{})
cmd.SetArgs([]string{"utils", "tenant", "--help"})
err := cmd.Execute()
assert.Assert(t, err == nil)
}

func TestTenantConfigurationJsonFormat(t *testing.T) {
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{})
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{}, &mock.JWTMockWrapper{})
cmd.SetArgs([]string{"utils", "tenant", "--format", "json"})
err := cmd.Execute()
assert.NilError(t, err, "Tenant configuration command should run with no errors and print to json")
}

func TestTenantConfigurationListFormat(t *testing.T) {
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{})
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{}, &mock.JWTMockWrapper{})
cmd.SetArgs([]string{"utils", "tenant", "--format", "list"})
err := cmd.Execute()
assert.NilError(t, err, "Tenant configuration command should run with no errors and print to list")
}

func TestTenantConfigurationTableFormat(t *testing.T) {
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{})
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{}, &mock.JWTMockWrapper{})
cmd.SetArgs([]string{"utils", "tenant", "--format", "table"})
err := cmd.Execute()
assert.NilError(t, err, "Tenant configuration command should run with no errors and print to table")
}

func TestTenantConfigurationInvalidFormat(t *testing.T) {
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{})
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{}, &mock.JWTMockWrapper{})
cmd.SetArgs([]string{"utils", "tenant", "--format", "MOCK"})
err := cmd.Execute()
assert.Assert(t, err.Error() == mockFormatErrorMessage)
}

func TestNewTenantConfigurationCommand(t *testing.T) {
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{})
cmd := NewTenantConfigurationCommand(mock.TenantConfigurationMockWrapper{}, &mock.JWTMockWrapper{})
assert.Assert(t, cmd != nil, "Tenant configuration command must exist")
}
3 changes: 2 additions & 1 deletion internal/commands/util/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func NewUtilsCommand(
prWrapper wrappers.PRWrapper,
learnMoreWrapper wrappers.LearnMoreWrapper,
tenantWrapper wrappers.TenantConfigurationWrapper,
jwtWrapper wrappers.JWTWrapper,
chatWrapper wrappers.ChatWrapper,
policyWrapper wrappers.PolicyWrapper,
scansWrapper wrappers.ScansWrapper,
Expand Down Expand Up @@ -76,7 +77,7 @@ func NewUtilsCommand(

learnMoreCmd := NewLearnMoreCommand(learnMoreWrapper)

tenantCmd := NewTenantConfigurationCommand(tenantWrapper)
tenantCmd := NewTenantConfigurationCommand(tenantWrapper, jwtWrapper)

maskSecretsCmd := NewMaskSecretsCommand(chatWrapper)

Expand Down
1 change: 1 addition & 0 deletions internal/commands/util/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func TestNewUtilsCommand(t *testing.T) {
nil,
mock.LearnMoreMockWrapper{},
mock.TenantConfigurationMockWrapper{},
&mock.JWTMockWrapper{},
mock.ChatMockWrapper{},
nil,
nil,
Expand Down
1 change: 1 addition & 0 deletions internal/params/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ const (
APISecurityType = "api-security"
AIProtectionType = "AI Protection"
CheckmarxOneAssistType = "Checkmarx One Assist"
CheckmarxOneStandAloneType = "Checkmarx Developer Assist"
ContainersType = "containers"
APIDocumentationFlag = "apisec-swagger-filter"
IacType = "iac-security"
Expand Down
7 changes: 6 additions & 1 deletion internal/services/realtimeengine/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ func EnsureLicense(jwtWrapper wrappers.JWTWrapper) error {
return errors.Wrap(err, "failed to check AIProtectionType engine allowance")
}

if aiAllowed || assistAllowed {
devAssistAllowed, err := jwtWrapper.IsAllowedEngine(params.CheckmarxOneStandAloneType)
if err != nil {
return errors.Wrap(err, "failed to check Checkmarx Developer Assist engine allowance")
}

if aiAllowed || assistAllowed || devAssistAllowed {
return nil
}
return errors.New(errorconstants.ErrMissingAIFeatureLicense)
Expand Down
29 changes: 29 additions & 0 deletions internal/wrappers/jwt-helper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package wrappers

import (
"strconv"
"strings"

commonParams "github.com/checkmarx/ast-cli/internal/params"
Expand All @@ -23,6 +24,7 @@ type JWTStruct struct {

type JWTWrapper interface {
GetAllowedEngines(featureFlagsWrapper FeatureFlagsWrapper) (allowedEngines map[string]bool, err error)
GetLicenseDetails() (licenseDetails map[string]string, err error)
IsAllowedEngine(engine string) (bool, error)
ExtractTenantFromToken() (tenant string, err error)
CheckPermissionByAccessToken(requiredPermission string) (permission bool, err error)
Expand Down Expand Up @@ -76,6 +78,33 @@ func (*JWTStruct) GetAllowedEngines(featureFlagsWrapper FeatureFlagsWrapper) (al
return getDefaultEngines(scsLicensingV2Flag.Status), nil
}

func (*JWTStruct) GetLicenseDetails() (licenseDetails map[string]string, err error) {
licenseDetails = make(map[string]string)

jwtStruct, err := getJwtStruct()
if err != nil {
return nil, err
}

assistEnabled := containsIgnoreCase(jwtStruct.AstLicense.LicenseData.AllowedEngines, commonParams.CheckmarxOneAssistType) ||
containsIgnoreCase(jwtStruct.AstLicense.LicenseData.AllowedEngines, commonParams.AIProtectionType)
devAssistEnabled := containsIgnoreCase(jwtStruct.AstLicense.LicenseData.AllowedEngines, commonParams.CheckmarxOneStandAloneType)

licenseDetails["scan.config.plugins.cxoneassist"] = strconv.FormatBool(assistEnabled)
licenseDetails["scan.config.plugins.cxonedevassist"] = strconv.FormatBool(devAssistEnabled)
return licenseDetails, nil
}

// containsIgnoreCase returns true if target exists in arr using case-insensitive comparison
func containsIgnoreCase(arr []string, target string) bool {
for _, s := range arr {
if strings.EqualFold(s, target) {
return true
}
}
return false
}

func getJwtStruct() (*JWTStruct, error) {
accessToken, err := GetAccessToken()
if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions internal/wrappers/mock/jwt-helper-mock.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package mock

import (
"strconv"
"strings"

"github.com/checkmarx/ast-cli/internal/params"
Expand All @@ -22,6 +23,8 @@ const SecretDetectionDisabled = 1

var engines = []string{"sast", "sca", "api-security", "iac-security", "scs", "containers", "enterprise-secrets"}

const licenseEnabledValue = "true"

// GetAllowedEngines mock for tests
func (j *JWTMockWrapper) GetAllowedEngines(featureFlagsWrapper wrappers.FeatureFlagsWrapper) (allowedEngines map[string]bool, err error) {
if j.CustomGetAllowedEngines != nil {
Expand Down Expand Up @@ -75,3 +78,19 @@ func (j *JWTMockWrapper) IsAllowedEngine(engine string) (bool, error) {
func (j *JWTMockWrapper) CheckPermissionByAccessToken(requiredPermission string) (permission bool, err error) {
return true, nil
}

func (j *JWTMockWrapper) GetLicenseDetails() (licenseDetails map[string]string, err error) {
licenseDetails = make(map[string]string)

assistEnabled := (j.CheckmarxOneAssistEnabled != CheckmarxOneAssistDisabled) || (j.AIEnabled != AIProtectionDisabled)
licenseDetails["scan.config.plugins.cxoneassist"] = strconv.FormatBool(assistEnabled)

standaloneEnabled := true
licenseDetails["scan.config.plugins.standalone"] = strconv.FormatBool(standaloneEnabled)

for _, engine := range engines {
licenseDetails[engine] = licenseEnabledValue
}

return licenseDetails, nil
}
Loading