From 5d957d924fb195a41c0a3a3a389679e90f3c0f0e Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Mon, 12 Jan 2026 12:13:20 +0200 Subject: [PATCH 01/13] start adding code and test --- internal/commands/util/license.go | 65 +++++++++++++++++++++++ internal/commands/util/license_test.go | 35 ++++++++++++ internal/wrappers/jwt-helper.go | 34 ++++++++++++ internal/wrappers/mock/jwt-helper-mock.go | 15 ++++++ 4 files changed, 149 insertions(+) create mode 100644 internal/commands/util/license.go create mode 100644 internal/commands/util/license_test.go diff --git a/internal/commands/util/license.go b/internal/commands/util/license.go new file mode 100644 index 000000000..24cffc97c --- /dev/null +++ b/internal/commands/util/license.go @@ -0,0 +1,65 @@ +package util + +import ( + "fmt" + + "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" +) + +func NewLicenseCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command { + cmd := &cobra.Command{ + Use: "license", + Short: "Shows the license details from the JWT token", + Long: "Returns license information extracted from the JWT token", + Example: heredoc.Doc( + ` + $ cx utils license + $ cx utils license --format json + `, + ), + Annotations: map[string]string{ + "command:doc": heredoc.Doc( + ` + https://checkmarx.com/resource/documents/en/todo + `, + ), + }, + RunE: runLicenseCmd(jwtWrapper), + } + cmd.PersistentFlags().String( + params.FormatFlag, + "", + fmt.Sprintf( + params.FormatFlagUsageFormat, + []string{printer.FormatTable, printer.FormatJSON, printer.FormatList}, + ), + ) + return cmd +} + +func runLicenseCmd(jwtWrapper wrappers.JWTWrapper) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + jwtClaims, err := jwtWrapper.GetAllJwtClaims() + if err != nil { + return errors.Wrap(err, "Failed to get license details from JWT token") + } + + if jwtClaims != nil { + format, _ := cmd.Flags().GetString(params.FormatFlag) + + if format == "" { + format = defaultFormat + } + err = printer.Print(cmd.OutOrStdout(), jwtClaims, format) + if err != nil { + return err + } + } + return nil + } +} diff --git a/internal/commands/util/license_test.go b/internal/commands/util/license_test.go new file mode 100644 index 000000000..78aff29f5 --- /dev/null +++ b/internal/commands/util/license_test.go @@ -0,0 +1,35 @@ +package util + +import ( + "bytes" + "strings" + "testing" + + "github.com/checkmarx/ast-cli/internal/wrappers/mock" + "gotest.tools/assert" +) + +func TestLicenseCommandDefaultListFormat(t *testing.T) { + mockJWT := &mock.JWTMockWrapper{ + DastEnabled: true, + } + + cmd := NewLicenseCommand(mockJWT) + + // Capture output + var buf bytes.Buffer + cmd.SetOut(&buf) + + err := cmd.Execute() + assert.NilError(t, err, "License command should run with no errors") + + output := buf.String() + + // Verify output contains expected fields in list format + assert.Assert(t, strings.Contains(output, "TenantName"), "Output should contain TenantName") + assert.Assert(t, strings.Contains(output, "test-tenant"), "Output should contain test-tenant value") + assert.Assert(t, strings.Contains(output, "DastEnabled"), "Output should contain DastEnabled") + assert.Assert(t, strings.Contains(output, "true"), "Output should contain true for DastEnabled") + assert.Assert(t, strings.Contains(output, "AllowedEngines"), "Output should contain AllowedEngines") +} + diff --git a/internal/wrappers/jwt-helper.go b/internal/wrappers/jwt-helper.go index 25012ed28..c0130dbc5 100644 --- a/internal/wrappers/jwt-helper.go +++ b/internal/wrappers/jwt-helper.go @@ -22,15 +22,25 @@ type JWTStruct struct { LicenseData struct { AllowedEngines []string `json:"allowedEngines"` } `json:"LicenseData"` + DastEnabled bool `json:"dastEnabled"` } `json:"ast-license"` ASTRoles []string `json:"roles_ast"` jwt.RegisteredClaims // Embedding the standard claims } +// JwtClaims represents all license-related information extracted from the JWT token +type JwtClaims struct { + TenantName string `json:"tenantName"` + DastEnabled bool `json:"dastEnabled"` + AllowedEngines []string `json:"allowedEngines"` +} + type JWTWrapper interface { GetAllowedEngines(featureFlagsWrapper FeatureFlagsWrapper) (allowedEngines map[string]bool, err error) GetLicenseDetails() (licenseDetails map[string]string, err error) + GetAllJwtClaims() (*JwtClaims, error) IsAllowedEngine(engine string) (bool, error) + IsDastEnabled() (bool, error) ExtractTenantFromToken() (tenant string, err error) CheckPermissionByAccessToken(requiredPermission string) (permission bool, err error) } @@ -133,6 +143,30 @@ func (*JWTStruct) IsAllowedEngine(engine string) (bool, error) { return false, nil } +// IsDastEnabled will return if DAST is enabled in the user license +func (*JWTStruct) IsDastEnabled() (bool, error) { + jwtStruct, err := getJwtStruct() + if err != nil { + return false, err + } + + return jwtStruct.AstLicense.DastEnabled, nil +} + +// GetAllJwtClaims returns all license-related information from the JWT token +func (*JWTStruct) GetAllJwtClaims() (*JwtClaims, error) { + jwtStruct, err := getJwtStruct() + if err != nil { + return nil, err + } + + return &JwtClaims{ + TenantName: jwtStruct.Tenant, + DastEnabled: jwtStruct.AstLicense.DastEnabled, + AllowedEngines: jwtStruct.AstLicense.LicenseData.AllowedEngines, + }, nil +} + func prepareEngines(engines []string, scsLicensingV2 bool) map[string]bool { m := make(map[string]bool) for _, value := range engines { diff --git a/internal/wrappers/mock/jwt-helper-mock.go b/internal/wrappers/mock/jwt-helper-mock.go index eeb751aba..4e9121e04 100644 --- a/internal/wrappers/mock/jwt-helper-mock.go +++ b/internal/wrappers/mock/jwt-helper-mock.go @@ -13,6 +13,7 @@ type JWTMockWrapper struct { EnterpriseSecretsEnabled int SecretDetectionEnabled int CheckmarxOneAssistEnabled int + DastEnabled bool CustomGetAllowedEngines func(wrappers.FeatureFlagsWrapper) (map[string]bool, error) } @@ -79,6 +80,20 @@ func (j *JWTMockWrapper) CheckPermissionByAccessToken(requiredPermission string) return true, nil } +// IsDastEnabled mock for tests +func (j *JWTMockWrapper) IsDastEnabled() (bool, error) { + return j.DastEnabled, nil +} + +// GetAllJwtClaims mock for tests +func (j *JWTMockWrapper) GetAllJwtClaims() (*wrappers.JwtClaims, error) { + return &wrappers.JwtClaims{ + TenantName: "test-tenant", + DastEnabled: j.DastEnabled, + AllowedEngines: engines, + }, nil +} + func (j *JWTMockWrapper) GetLicenseDetails() (licenseDetails map[string]string, err error) { licenseDetails = make(map[string]string) From c717b9ce70fa4a33bd52491149222f2ac18d506e Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Mon, 12 Jan 2026 12:20:19 +0200 Subject: [PATCH 02/13] add to test --- internal/commands/util/license_test.go | 15 ++++++++------- internal/wrappers/mock/jwt-helper-mock.go | 16 +++++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/internal/commands/util/license_test.go b/internal/commands/util/license_test.go index 78aff29f5..5d225182b 100644 --- a/internal/commands/util/license_test.go +++ b/internal/commands/util/license_test.go @@ -11,25 +11,26 @@ import ( func TestLicenseCommandDefaultListFormat(t *testing.T) { mockJWT := &mock.JWTMockWrapper{ - DastEnabled: true, + TenantName: "test-tenant", + DastEnabled: false, + AllowedEngines: []string{"sast", "sca"}, } cmd := NewLicenseCommand(mockJWT) - + // Capture output var buf bytes.Buffer cmd.SetOut(&buf) - + err := cmd.Execute() assert.NilError(t, err, "License command should run with no errors") - + output := buf.String() - + // Verify output contains expected fields in list format assert.Assert(t, strings.Contains(output, "TenantName"), "Output should contain TenantName") assert.Assert(t, strings.Contains(output, "test-tenant"), "Output should contain test-tenant value") assert.Assert(t, strings.Contains(output, "DastEnabled"), "Output should contain DastEnabled") - assert.Assert(t, strings.Contains(output, "true"), "Output should contain true for DastEnabled") + assert.Assert(t, strings.Contains(output, "false"), "Output should contain false for DastEnabled") assert.Assert(t, strings.Contains(output, "AllowedEngines"), "Output should contain AllowedEngines") } - diff --git a/internal/wrappers/mock/jwt-helper-mock.go b/internal/wrappers/mock/jwt-helper-mock.go index 4e9121e04..3bcd0f179 100644 --- a/internal/wrappers/mock/jwt-helper-mock.go +++ b/internal/wrappers/mock/jwt-helper-mock.go @@ -14,6 +14,8 @@ type JWTMockWrapper struct { SecretDetectionEnabled int CheckmarxOneAssistEnabled int DastEnabled bool + TenantName string + AllowedEngines []string CustomGetAllowedEngines func(wrappers.FeatureFlagsWrapper) (map[string]bool, error) } @@ -33,15 +35,19 @@ func (j *JWTMockWrapper) GetAllowedEngines(featureFlagsWrapper wrappers.FeatureF return allowedEngines, err } allowedEngines = make(map[string]bool) + enginesToCopy := engines + if j.AllowedEngines != nil { + enginesToCopy = j.AllowedEngines + } - for _, value := range engines { + for _, value := range enginesToCopy { allowedEngines[strings.ToLower(value)] = true } return allowedEngines, nil } -func (*JWTMockWrapper) ExtractTenantFromToken() (tenant string, err error) { - return "test-tenant", nil +func (j *JWTMockWrapper) ExtractTenantFromToken() (tenant string, err error) { + return j.TenantName, nil } // IsAllowedEngine mock for tests @@ -88,9 +94,9 @@ func (j *JWTMockWrapper) IsDastEnabled() (bool, error) { // GetAllJwtClaims mock for tests func (j *JWTMockWrapper) GetAllJwtClaims() (*wrappers.JwtClaims, error) { return &wrappers.JwtClaims{ - TenantName: "test-tenant", + TenantName: j.TenantName, DastEnabled: j.DastEnabled, - AllowedEngines: engines, + AllowedEngines: j.AllowedEngines, }, nil } From 07fe2292c8030a5af3e9d76e5e5a83eb44a35116 Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Mon, 12 Jan 2026 13:13:15 +0200 Subject: [PATCH 03/13] improve test --- internal/commands/util/license_test.go | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/internal/commands/util/license_test.go b/internal/commands/util/license_test.go index 5d225182b..3eb785426 100644 --- a/internal/commands/util/license_test.go +++ b/internal/commands/util/license_test.go @@ -2,11 +2,13 @@ package util import ( "bytes" - "strings" + "encoding/json" "testing" + "github.com/checkmarx/ast-cli/internal/params" + "github.com/checkmarx/ast-cli/internal/wrappers" "github.com/checkmarx/ast-cli/internal/wrappers/mock" - "gotest.tools/assert" + "github.com/stretchr/testify/require" ) func TestLicenseCommandDefaultListFormat(t *testing.T) { @@ -17,20 +19,22 @@ func TestLicenseCommandDefaultListFormat(t *testing.T) { } cmd := NewLicenseCommand(mockJWT) + cmd.SetArgs([]string{"--" + params.FormatFlag, "json"}) // Capture output var buf bytes.Buffer cmd.SetOut(&buf) err := cmd.Execute() - assert.NilError(t, err, "License command should run with no errors") + require.NoError(t, err) - output := buf.String() + // Parse JSON output + var result wrappers.JwtClaims + err = json.Unmarshal(buf.Bytes(), &result) + require.NoError(t, err) - // Verify output contains expected fields in list format - assert.Assert(t, strings.Contains(output, "TenantName"), "Output should contain TenantName") - assert.Assert(t, strings.Contains(output, "test-tenant"), "Output should contain test-tenant value") - assert.Assert(t, strings.Contains(output, "DastEnabled"), "Output should contain DastEnabled") - assert.Assert(t, strings.Contains(output, "false"), "Output should contain false for DastEnabled") - assert.Assert(t, strings.Contains(output, "AllowedEngines"), "Output should contain AllowedEngines") + // Verify structured output + require.Equal(t, "test-tenant", result.TenantName) + require.Equal(t, false, result.DastEnabled) + require.ElementsMatch(t, result.AllowedEngines, []string{"sast", "sca"}) } From d73c7bbaeb8df028fb92373f6e46ba7d0e98a0ac Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Mon, 12 Jan 2026 14:53:37 +0200 Subject: [PATCH 04/13] fix --- internal/commands/util/utils.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/commands/util/utils.go b/internal/commands/util/utils.go index c16db6f93..7a643d789 100644 --- a/internal/commands/util/utils.go +++ b/internal/commands/util/utils.go @@ -81,6 +81,8 @@ func NewUtilsCommand( maskSecretsCmd := NewMaskSecretsCommand(chatWrapper) + licenseCmd := NewLicenseCommand(jwtWrapper) + utilsCmd.AddCommand( completionCmd, envCheckCmd, @@ -96,6 +98,7 @@ func NewUtilsCommand( remediationCmd, tenantCmd, maskSecretsCmd, + licenseCmd, importCmd, ) From 07c3e1329db235904a6c73b3cbc95d621a0cf1f4 Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Mon, 12 Jan 2026 15:29:49 +0200 Subject: [PATCH 05/13] fix --- internal/wrappers/jwt-helper.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/wrappers/jwt-helper.go b/internal/wrappers/jwt-helper.go index c0130dbc5..b09621534 100644 --- a/internal/wrappers/jwt-helper.go +++ b/internal/wrappers/jwt-helper.go @@ -21,8 +21,8 @@ type JWTStruct struct { AstLicense struct { LicenseData struct { AllowedEngines []string `json:"allowedEngines"` + DastEnabled bool `json:"dastEnabled"` } `json:"LicenseData"` - DastEnabled bool `json:"dastEnabled"` } `json:"ast-license"` ASTRoles []string `json:"roles_ast"` jwt.RegisteredClaims // Embedding the standard claims @@ -150,7 +150,7 @@ func (*JWTStruct) IsDastEnabled() (bool, error) { return false, err } - return jwtStruct.AstLicense.DastEnabled, nil + return jwtStruct.AstLicense.LicenseData.DastEnabled, nil } // GetAllJwtClaims returns all license-related information from the JWT token @@ -162,7 +162,7 @@ func (*JWTStruct) GetAllJwtClaims() (*JwtClaims, error) { return &JwtClaims{ TenantName: jwtStruct.Tenant, - DastEnabled: jwtStruct.AstLicense.DastEnabled, + DastEnabled: jwtStruct.AstLicense.LicenseData.DastEnabled, AllowedEngines: jwtStruct.AstLicense.LicenseData.AllowedEngines, }, nil } From b1d0c5f43c82885a22ab25d02ebbc62be7d7565a Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Mon, 12 Jan 2026 15:46:27 +0200 Subject: [PATCH 06/13] use assert instead of require --- internal/commands/util/license_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/commands/util/license_test.go b/internal/commands/util/license_test.go index 3eb785426..bff26905b 100644 --- a/internal/commands/util/license_test.go +++ b/internal/commands/util/license_test.go @@ -8,7 +8,7 @@ import ( "github.com/checkmarx/ast-cli/internal/params" "github.com/checkmarx/ast-cli/internal/wrappers" "github.com/checkmarx/ast-cli/internal/wrappers/mock" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) func TestLicenseCommandDefaultListFormat(t *testing.T) { @@ -26,15 +26,15 @@ func TestLicenseCommandDefaultListFormat(t *testing.T) { cmd.SetOut(&buf) err := cmd.Execute() - require.NoError(t, err) + assert.NoError(t, err) // Parse JSON output var result wrappers.JwtClaims err = json.Unmarshal(buf.Bytes(), &result) - require.NoError(t, err) + assert.NoError(t, err) // Verify structured output - require.Equal(t, "test-tenant", result.TenantName) - require.Equal(t, false, result.DastEnabled) - require.ElementsMatch(t, result.AllowedEngines, []string{"sast", "sca"}) + assert.Equal(t, "test-tenant", result.TenantName) + assert.Equal(t, false, result.DastEnabled) + assert.ElementsMatch(t, []string{"sast", "sca"}, result.AllowedEngines) } From f8e4c08cce69e2ebde3351d45716f5fdd24051cd Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Tue, 13 Jan 2026 10:17:21 +0200 Subject: [PATCH 07/13] use tenant command --- internal/commands/util/license.go | 65 ----------------------- internal/commands/util/license_test.go | 40 -------------- internal/params/flags.go | 7 +++ internal/wrappers/jwt-helper.go | 38 ++----------- internal/wrappers/mock/jwt-helper-mock.go | 19 ++----- 5 files changed, 13 insertions(+), 156 deletions(-) delete mode 100644 internal/commands/util/license.go delete mode 100644 internal/commands/util/license_test.go diff --git a/internal/commands/util/license.go b/internal/commands/util/license.go deleted file mode 100644 index 24cffc97c..000000000 --- a/internal/commands/util/license.go +++ /dev/null @@ -1,65 +0,0 @@ -package util - -import ( - "fmt" - - "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" -) - -func NewLicenseCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command { - cmd := &cobra.Command{ - Use: "license", - Short: "Shows the license details from the JWT token", - Long: "Returns license information extracted from the JWT token", - Example: heredoc.Doc( - ` - $ cx utils license - $ cx utils license --format json - `, - ), - Annotations: map[string]string{ - "command:doc": heredoc.Doc( - ` - https://checkmarx.com/resource/documents/en/todo - `, - ), - }, - RunE: runLicenseCmd(jwtWrapper), - } - cmd.PersistentFlags().String( - params.FormatFlag, - "", - fmt.Sprintf( - params.FormatFlagUsageFormat, - []string{printer.FormatTable, printer.FormatJSON, printer.FormatList}, - ), - ) - return cmd -} - -func runLicenseCmd(jwtWrapper wrappers.JWTWrapper) func(cmd *cobra.Command, args []string) error { - return func(cmd *cobra.Command, args []string) error { - jwtClaims, err := jwtWrapper.GetAllJwtClaims() - if err != nil { - return errors.Wrap(err, "Failed to get license details from JWT token") - } - - if jwtClaims != nil { - format, _ := cmd.Flags().GetString(params.FormatFlag) - - if format == "" { - format = defaultFormat - } - err = printer.Print(cmd.OutOrStdout(), jwtClaims, format) - if err != nil { - return err - } - } - return nil - } -} diff --git a/internal/commands/util/license_test.go b/internal/commands/util/license_test.go deleted file mode 100644 index bff26905b..000000000 --- a/internal/commands/util/license_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package util - -import ( - "bytes" - "encoding/json" - "testing" - - "github.com/checkmarx/ast-cli/internal/params" - "github.com/checkmarx/ast-cli/internal/wrappers" - "github.com/checkmarx/ast-cli/internal/wrappers/mock" - "github.com/stretchr/testify/assert" -) - -func TestLicenseCommandDefaultListFormat(t *testing.T) { - mockJWT := &mock.JWTMockWrapper{ - TenantName: "test-tenant", - DastEnabled: false, - AllowedEngines: []string{"sast", "sca"}, - } - - cmd := NewLicenseCommand(mockJWT) - cmd.SetArgs([]string{"--" + params.FormatFlag, "json"}) - - // Capture output - var buf bytes.Buffer - cmd.SetOut(&buf) - - err := cmd.Execute() - assert.NoError(t, err) - - // Parse JSON output - var result wrappers.JwtClaims - err = json.Unmarshal(buf.Bytes(), &result) - assert.NoError(t, err) - - // Verify structured output - assert.Equal(t, "test-tenant", result.TenantName) - assert.Equal(t, false, result.DastEnabled) - assert.ElementsMatch(t, []string{"sast", "sca"}, result.AllowedEngines) -} diff --git a/internal/params/flags.go b/internal/params/flags.go index 85abf3a4c..169a2aace 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -291,6 +291,13 @@ const ( ResultPolicyDefaultTimeout = 1 ) +// License +const ( + CxOneAssistEnabledKey = "scan.config.plugins.cxoneassist" + CxDevAssistEnabledKey = "scan.config.plugins.cxdevassist" + DastEnabledKey = "scan.config.plugins.dastenabled" +) + // Results const ( SastType = "sast" diff --git a/internal/wrappers/jwt-helper.go b/internal/wrappers/jwt-helper.go index b09621534..fd10eb883 100644 --- a/internal/wrappers/jwt-helper.go +++ b/internal/wrappers/jwt-helper.go @@ -28,19 +28,10 @@ type JWTStruct struct { jwt.RegisteredClaims // Embedding the standard claims } -// JwtClaims represents all license-related information extracted from the JWT token -type JwtClaims struct { - TenantName string `json:"tenantName"` - DastEnabled bool `json:"dastEnabled"` - AllowedEngines []string `json:"allowedEngines"` -} - type JWTWrapper interface { GetAllowedEngines(featureFlagsWrapper FeatureFlagsWrapper) (allowedEngines map[string]bool, err error) GetLicenseDetails() (licenseDetails map[string]string, err error) - GetAllJwtClaims() (*JwtClaims, error) IsAllowedEngine(engine string) (bool, error) - IsDastEnabled() (bool, error) ExtractTenantFromToken() (tenant string, err error) CheckPermissionByAccessToken(requiredPermission string) (permission bool, err error) } @@ -105,8 +96,9 @@ func (*JWTStruct) GetLicenseDetails() (licenseDetails map[string]string, err err containsIgnoreCase(jwtStruct.AstLicense.LicenseData.AllowedEngines, commonParams.AIProtectionType) devAssistEnabled := containsIgnoreCase(jwtStruct.AstLicense.LicenseData.AllowedEngines, commonParams.CheckmarxDevAssistType) - licenseDetails["scan.config.plugins.cxoneassist"] = strconv.FormatBool(assistEnabled) - licenseDetails["scan.config.plugins.cxdevassist"] = strconv.FormatBool(devAssistEnabled) + licenseDetails[commonParams.CxOneAssistEnabledKey] = strconv.FormatBool(assistEnabled) + licenseDetails[commonParams.CxDevAssistEnabledKey] = strconv.FormatBool(devAssistEnabled) + licenseDetails[commonParams.DastEnabledKey] = strconv.FormatBool(jwtStruct.AstLicense.LicenseData.DastEnabled) return licenseDetails, nil } @@ -143,30 +135,6 @@ func (*JWTStruct) IsAllowedEngine(engine string) (bool, error) { return false, nil } -// IsDastEnabled will return if DAST is enabled in the user license -func (*JWTStruct) IsDastEnabled() (bool, error) { - jwtStruct, err := getJwtStruct() - if err != nil { - return false, err - } - - return jwtStruct.AstLicense.LicenseData.DastEnabled, nil -} - -// GetAllJwtClaims returns all license-related information from the JWT token -func (*JWTStruct) GetAllJwtClaims() (*JwtClaims, error) { - jwtStruct, err := getJwtStruct() - if err != nil { - return nil, err - } - - return &JwtClaims{ - TenantName: jwtStruct.Tenant, - DastEnabled: jwtStruct.AstLicense.LicenseData.DastEnabled, - AllowedEngines: jwtStruct.AstLicense.LicenseData.AllowedEngines, - }, nil -} - func prepareEngines(engines []string, scsLicensingV2 bool) map[string]bool { m := make(map[string]bool) for _, value := range engines { diff --git a/internal/wrappers/mock/jwt-helper-mock.go b/internal/wrappers/mock/jwt-helper-mock.go index 3bcd0f179..c4fc38958 100644 --- a/internal/wrappers/mock/jwt-helper-mock.go +++ b/internal/wrappers/mock/jwt-helper-mock.go @@ -86,28 +86,15 @@ func (j *JWTMockWrapper) CheckPermissionByAccessToken(requiredPermission string) return true, nil } -// IsDastEnabled mock for tests -func (j *JWTMockWrapper) IsDastEnabled() (bool, error) { - return j.DastEnabled, nil -} - -// GetAllJwtClaims mock for tests -func (j *JWTMockWrapper) GetAllJwtClaims() (*wrappers.JwtClaims, error) { - return &wrappers.JwtClaims{ - TenantName: j.TenantName, - DastEnabled: j.DastEnabled, - AllowedEngines: j.AllowedEngines, - }, 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) + licenseDetails[params.CxOneAssistEnabledKey] = strconv.FormatBool(assistEnabled) standaloneEnabled := true - licenseDetails["scan.config.plugins.cxdevassist"] = strconv.FormatBool(standaloneEnabled) + licenseDetails[params.CxDevAssistEnabledKey] = strconv.FormatBool(standaloneEnabled) + licenseDetails[params.DastEnabledKey] = strconv.FormatBool(j.DastEnabled) for _, engine := range engines { licenseDetails[engine] = licenseEnabledValue From 698a9f564b406c9e008e228bd249be7ed5beabcf Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Tue, 13 Jan 2026 10:19:07 +0200 Subject: [PATCH 08/13] remove command --- internal/commands/util/utils.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/commands/util/utils.go b/internal/commands/util/utils.go index 7a643d789..c16db6f93 100644 --- a/internal/commands/util/utils.go +++ b/internal/commands/util/utils.go @@ -81,8 +81,6 @@ func NewUtilsCommand( maskSecretsCmd := NewMaskSecretsCommand(chatWrapper) - licenseCmd := NewLicenseCommand(jwtWrapper) - utilsCmd.AddCommand( completionCmd, envCheckCmd, @@ -98,7 +96,6 @@ func NewUtilsCommand( remediationCmd, tenantCmd, maskSecretsCmd, - licenseCmd, importCmd, ) From 63882fa79078f5c328f5cfe989e9b405ef7095b4 Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Tue, 13 Jan 2026 11:04:46 +0200 Subject: [PATCH 09/13] add test --- internal/wrappers/jwt-helper.go | 18 +++++- internal/wrappers/jwt-helper_test.go | 88 ++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/internal/wrappers/jwt-helper.go b/internal/wrappers/jwt-helper.go index fd10eb883..77d52de59 100644 --- a/internal/wrappers/jwt-helper.go +++ b/internal/wrappers/jwt-helper.go @@ -85,12 +85,15 @@ func (*JWTStruct) GetAllowedEngines(featureFlagsWrapper FeatureFlagsWrapper) (al } func (*JWTStruct) GetLicenseDetails() (licenseDetails map[string]string, err error) { - licenseDetails = make(map[string]string) - jwtStruct, err := getJwtStruct() if err != nil { return nil, err } + return buildLicenseDetailsFromJWT(jwtStruct), nil +} + +func buildLicenseDetailsFromJWT(jwtStruct *JWTStruct) map[string]string { + licenseDetails := make(map[string]string) assistEnabled := containsIgnoreCase(jwtStruct.AstLicense.LicenseData.AllowedEngines, commonParams.CheckmarxOneAssistType) || containsIgnoreCase(jwtStruct.AstLicense.LicenseData.AllowedEngines, commonParams.AIProtectionType) @@ -99,7 +102,8 @@ func (*JWTStruct) GetLicenseDetails() (licenseDetails map[string]string, err err licenseDetails[commonParams.CxOneAssistEnabledKey] = strconv.FormatBool(assistEnabled) licenseDetails[commonParams.CxDevAssistEnabledKey] = strconv.FormatBool(devAssistEnabled) licenseDetails[commonParams.DastEnabledKey] = strconv.FormatBool(jwtStruct.AstLicense.LicenseData.DastEnabled) - return licenseDetails, nil + + return licenseDetails } // containsIgnoreCase returns true if target exists in arr using case-insensitive comparison @@ -112,7 +116,15 @@ func containsIgnoreCase(arr []string, target string) bool { return false } +// getJwtStructFunc is a variable that holds the function to get JWT struct +// This allows for dependency injection in tests +var getJwtStructFunc = getJwtStructImpl + func getJwtStruct() (*JWTStruct, error) { + return getJwtStructFunc() +} + +func getJwtStructImpl() (*JWTStruct, error) { accessToken, err := GetAccessToken() if err != nil { return nil, err diff --git a/internal/wrappers/jwt-helper_test.go b/internal/wrappers/jwt-helper_test.go index ca51c1e19..4a63ec0c2 100644 --- a/internal/wrappers/jwt-helper_test.go +++ b/internal/wrappers/jwt-helper_test.go @@ -115,3 +115,91 @@ func TestGetUniqueID(t *testing.T) { assert.Assert(t, !strings.Contains(parts[1], "\\"), "Username should not contain backslash") }) } + +func TestBuildLicenseDetailsFromJWT(t *testing.T) { + tests := []struct { + name string + allowedEngines []string + dastEnabled bool + expectedCxOneAssist string + expectedCxDevAssist string + expectedDast string + }{ + { + name: "all features enabled", + allowedEngines: []string{"sast", "sca", commonParams.CheckmarxOneAssistType, commonParams.CheckmarxDevAssistType}, + dastEnabled: true, + expectedCxOneAssist: "true", + expectedCxDevAssist: "true", + expectedDast: "true", + }, + { + name: "all features enabled - AIProtection", + allowedEngines: []string{"sast", "sca", commonParams.CheckmarxOneAssistType, commonParams.AIProtectionType}, + dastEnabled: true, + expectedCxOneAssist: "true", + expectedCxDevAssist: "false", + expectedDast: "true", + }, + { + name: "only dev assist enabled", + allowedEngines: []string{"sast", commonParams.CheckmarxDevAssistType}, + dastEnabled: false, + expectedCxOneAssist: "false", + expectedCxDevAssist: "true", + expectedDast: "false", + }, + { + name: "no assist features enabled", + allowedEngines: []string{"sast", "sca", "iac-security"}, + dastEnabled: false, + expectedCxOneAssist: "false", + expectedCxDevAssist: "false", + expectedDast: "false", + }, + { + name: "only dast enabled", + allowedEngines: []string{"sast"}, + dastEnabled: true, + expectedCxOneAssist: "false", + expectedCxDevAssist: "false", + expectedDast: "true", + }, + { + name: "case insensitive matching", + allowedEngines: []string{"checkmarx one assist", "ai protection"}, + dastEnabled: false, + expectedCxOneAssist: "true", + expectedCxDevAssist: "true", + expectedDast: "false", + }, + { + name: "empty allowed engines", + allowedEngines: []string{}, + dastEnabled: false, + expectedCxOneAssist: "false", + expectedCxDevAssist: "false", + expectedDast: "false", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a JWT struct with test data + jwtStruct := &JWTStruct{} + jwtStruct.AstLicense.LicenseData.AllowedEngines = tt.allowedEngines + jwtStruct.AstLicense.LicenseData.DastEnabled = tt.dastEnabled + + // Call the function under test + licenseDetails := buildLicenseDetailsFromJWT(jwtStruct) + + // Assert the results + assert.Equal(t, tt.expectedCxOneAssist, licenseDetails[commonParams.CxOneAssistEnabledKey], + "CxOneAssist should be %s", tt.expectedCxOneAssist) + assert.Equal(t, tt.expectedCxDevAssist, licenseDetails[commonParams.CxDevAssistEnabledKey], + "CxDevAssist should be %s", tt.expectedCxDevAssist) + assert.Equal(t, tt.expectedDast, licenseDetails[commonParams.DastEnabledKey], + "Dast should be %s", tt.expectedDast) + }) + } +} From d3f3c3365db27c24bcdd8a94b3eeb38d3bf133a1 Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Tue, 13 Jan 2026 11:07:06 +0200 Subject: [PATCH 10/13] fix test --- internal/wrappers/jwt-helper_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/wrappers/jwt-helper_test.go b/internal/wrappers/jwt-helper_test.go index 4a63ec0c2..cae93448d 100644 --- a/internal/wrappers/jwt-helper_test.go +++ b/internal/wrappers/jwt-helper_test.go @@ -167,7 +167,7 @@ func TestBuildLicenseDetailsFromJWT(t *testing.T) { }, { name: "case insensitive matching", - allowedEngines: []string{"checkmarx one assist", "ai protection"}, + allowedEngines: []string{"checkmarx one assist", "checkmarx developer assist"}, dastEnabled: false, expectedCxOneAssist: "true", expectedCxDevAssist: "true", From ee6af7d281ed82d980e2209df54ec7f607860270 Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Tue, 13 Jan 2026 11:16:01 +0200 Subject: [PATCH 11/13] undo change --- internal/wrappers/jwt-helper.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/internal/wrappers/jwt-helper.go b/internal/wrappers/jwt-helper.go index 77d52de59..5ef0c1d15 100644 --- a/internal/wrappers/jwt-helper.go +++ b/internal/wrappers/jwt-helper.go @@ -116,15 +116,7 @@ func containsIgnoreCase(arr []string, target string) bool { return false } -// getJwtStructFunc is a variable that holds the function to get JWT struct -// This allows for dependency injection in tests -var getJwtStructFunc = getJwtStructImpl - func getJwtStruct() (*JWTStruct, error) { - return getJwtStructFunc() -} - -func getJwtStructImpl() (*JWTStruct, error) { accessToken, err := GetAccessToken() if err != nil { return nil, err From 7e638ffcc721c528e3834d22eddd37fb2174054d Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Tue, 13 Jan 2026 11:17:08 +0200 Subject: [PATCH 12/13] undo changes --- internal/wrappers/mock/jwt-helper-mock.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/internal/wrappers/mock/jwt-helper-mock.go b/internal/wrappers/mock/jwt-helper-mock.go index c4fc38958..2de24e675 100644 --- a/internal/wrappers/mock/jwt-helper-mock.go +++ b/internal/wrappers/mock/jwt-helper-mock.go @@ -14,8 +14,6 @@ type JWTMockWrapper struct { SecretDetectionEnabled int CheckmarxOneAssistEnabled int DastEnabled bool - TenantName string - AllowedEngines []string CustomGetAllowedEngines func(wrappers.FeatureFlagsWrapper) (map[string]bool, error) } @@ -35,19 +33,15 @@ func (j *JWTMockWrapper) GetAllowedEngines(featureFlagsWrapper wrappers.FeatureF return allowedEngines, err } allowedEngines = make(map[string]bool) - enginesToCopy := engines - if j.AllowedEngines != nil { - enginesToCopy = j.AllowedEngines - } - for _, value := range enginesToCopy { + for _, value := range engines { allowedEngines[strings.ToLower(value)] = true } return allowedEngines, nil } func (j *JWTMockWrapper) ExtractTenantFromToken() (tenant string, err error) { - return j.TenantName, nil + return "test-tenant", nil } // IsAllowedEngine mock for tests From d1058d23f7e4994dbe3d3bc1c7a1b538d85724b6 Mon Sep 17 00:00:00 2001 From: Ronit Steinberg Date: Tue, 13 Jan 2026 11:17:33 +0200 Subject: [PATCH 13/13] undo --- internal/wrappers/mock/jwt-helper-mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/wrappers/mock/jwt-helper-mock.go b/internal/wrappers/mock/jwt-helper-mock.go index 2de24e675..9991b9629 100644 --- a/internal/wrappers/mock/jwt-helper-mock.go +++ b/internal/wrappers/mock/jwt-helper-mock.go @@ -40,7 +40,7 @@ func (j *JWTMockWrapper) GetAllowedEngines(featureFlagsWrapper wrappers.FeatureF return allowedEngines, nil } -func (j *JWTMockWrapper) ExtractTenantFromToken() (tenant string, err error) { +func (*JWTMockWrapper) ExtractTenantFromToken() (tenant string, err error) { return "test-tenant", nil }