Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Empty file added internal/commands/config.yaml
Empty file.
20 changes: 14 additions & 6 deletions internal/commands/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// NewHooksCommand creates the hooks command with pre-commit subcommand
func NewHooksCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func NewHooksCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
hooksCmd := &cobra.Command{
Use: "hooks",
Short: "Manage Git hooks",
Expand All @@ -31,19 +31,27 @@ func NewHooksCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
}

// Add pre-commit and pre-receive subcommand
hooksCmd.AddCommand(PreCommitCommand(jwtWrapper))
hooksCmd.AddCommand(PreReceiveCommand(jwtWrapper))
hooksCmd.AddCommand(PreCommitCommand(jwtWrapper, featureFlagsWrapper))
hooksCmd.AddCommand(PreReceiveCommand(jwtWrapper, featureFlagsWrapper))

return hooksCmd
}

func validateLicense(jwtWrapper wrappers.JWTWrapper) error {
allowed, err := jwtWrapper.IsAllowedEngine(params.EnterpriseSecretsLabel)
func validateLicense(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) error {
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
var licenseName string
if scsLicensingV2Flag.Status {
licenseName = params.SecretDetectionLabel
} else {
licenseName = params.EnterpriseSecretsLabel
}

allowed, err := jwtWrapper.IsAllowedEngine(licenseName)
if err != nil {
return errors.Wrapf(err, "Failed checking license")
}
if !allowed {
return errors.New("Error: License validation failed. Please verify your CxOne license includes Enterprise Secrets.")
return errors.Errorf("Error: License validation failed. Please verify your CxOne license includes %s.", licenseName)
}
return nil
}
22 changes: 15 additions & 7 deletions internal/commands/pre-receive.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const (
SuccessFullSecretsLicenceValidation = "License for pre-receive secret detection has been validated successfully"
)

func PreReceiveCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func PreReceiveCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
preReceiveCmd := &cobra.Command{
Use: "pre-receive",
Short: "Manage pre-receive hooks and run secret detection scans",
Expand All @@ -27,7 +27,7 @@ func PreReceiveCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
),
}
preReceiveCmd.AddCommand(scanSecretsPreReceiveCommand())
preReceiveCmd.AddCommand(validateSecretsLicence(jwtWrapper))
preReceiveCmd.AddCommand(validateSecretsLicence(jwtWrapper, featureFlagsWrapper))

return preReceiveCmd
}
Expand All @@ -54,7 +54,7 @@ func scanSecretsPreReceiveCommand() *cobra.Command {
return scanPrereceiveCmd
}

func validateSecretsLicence(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func validateSecretsLicence(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
validateLicence := &cobra.Command{
Use: "validate",
Short: "Validates the license for pre-receive secret detection",
Expand All @@ -64,19 +64,27 @@ func validateSecretsLicence(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
$ cx hooks pre-receive validate
`,
),
RunE: checkLicence(jwtWrapper),
RunE: checkLicence(jwtWrapper, featureFlagsWrapper),
}
return validateLicence
}

func checkLicence(jwtWrapper wrappers.JWTWrapper) func(cmd *cobra.Command, args []string) error {
func checkLicence(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
isAllowed, err := jwtWrapper.IsAllowedEngine(params.EnterpriseSecretsLabel)
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
var licenseName string
if scsLicensingV2Flag.Status {
licenseName = params.SecretDetectionLabel
} else {
licenseName = params.EnterpriseSecretsLabel
}

isAllowed, err := jwtWrapper.IsAllowedEngine(licenseName)
if err != nil {
log.Fatalf("%s: %s", "Failed the licence check", err)
}
if !isAllowed {
log.Fatalf("Error: License validation failed. Please ensure that your Checkmarx One license includes Enterprise Secrets")
log.Fatalf("Error: License validation failed. Please ensure that your Checkmarx One license includes %s", licenseName)
}
_, _ = fmt.Fprintln(cmd.OutOrStdout(), SuccessFullSecretsLicenceValidation)
return nil
Expand Down
30 changes: 15 additions & 15 deletions internal/commands/pre_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// PreCommitCommand creates the pre-commit subcommand

func PreCommitCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func PreCommitCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
preCommitCmd := &cobra.Command{
Use: "pre-commit",
Short: "Manage pre-commit hooks and run secret detection scans",
Expand All @@ -26,19 +26,19 @@ func PreCommitCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
}
preCommitCmd.PersistentFlags().Bool("global", false, "Install the hook globally for all repositories")

preCommitCmd.AddCommand(secretsInstallGitHookCommand(jwtWrapper))
preCommitCmd.AddCommand(secretsUninstallGitHookCommand(jwtWrapper))
preCommitCmd.AddCommand(secretsUpdateGitHookCommand(jwtWrapper))
preCommitCmd.AddCommand(secretsScanCommand(jwtWrapper))
preCommitCmd.AddCommand(secretsIgnoreCommand(jwtWrapper))
preCommitCmd.AddCommand(secretsInstallGitHookCommand(jwtWrapper, featureFlagsWrapper))
preCommitCmd.AddCommand(secretsUninstallGitHookCommand())
preCommitCmd.AddCommand(secretsUpdateGitHookCommand(jwtWrapper, featureFlagsWrapper))
preCommitCmd.AddCommand(secretsScanCommand(jwtWrapper, featureFlagsWrapper))
preCommitCmd.AddCommand(secretsIgnoreCommand(jwtWrapper, featureFlagsWrapper))
preCommitCmd.AddCommand(secretsHelpCommand())

return preCommitCmd
}

// / validateLicense verifies the user has the required license for secret detection

func secretsInstallGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func secretsInstallGitHookCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
cmd := &cobra.Command{
Use: "secrets-install-git-hook",
Short: "Install the pre-commit hook",
Expand All @@ -49,7 +49,7 @@ func secretsInstallGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command
`,
),
PreRunE: func(cmd *cobra.Command, args []string) error {
return validateLicense(jwtWrapper)
return validateLicense(jwtWrapper, featureFlagsWrapper)
},
RunE: func(cmd *cobra.Command, args []string) error {
global, _ := cmd.Flags().GetBool("global")
Expand All @@ -60,7 +60,7 @@ func secretsInstallGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command
return cmd
}

func secretsUninstallGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func secretsUninstallGitHookCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "secrets-uninstall-git-hook",
Short: "Uninstall the pre-commit hook",
Expand All @@ -79,7 +79,7 @@ func secretsUninstallGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Comma
return cmd
}

func secretsUpdateGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func secretsUpdateGitHookCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
cmd := &cobra.Command{
Use: "secrets-update-git-hook",
Short: "Update the pre-commit hook",
Expand All @@ -90,7 +90,7 @@ func secretsUpdateGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command
`,
),
PreRunE: func(cmd *cobra.Command, args []string) error {
return validateLicense(jwtWrapper)
return validateLicense(jwtWrapper, featureFlagsWrapper)
},
RunE: func(cmd *cobra.Command, args []string) error {
global, _ := cmd.Flags().GetBool("global")
Expand All @@ -101,7 +101,7 @@ func secretsUpdateGitHookCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command
return cmd
}

func secretsScanCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func secretsScanCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
return &cobra.Command{
Use: "secrets-scan",
Short: "Run the real-time secret detection scan",
Expand All @@ -112,15 +112,15 @@ func secretsScanCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
`,
),
PreRunE: func(cmd *cobra.Command, args []string) error {
return validateLicense(jwtWrapper)
return validateLicense(jwtWrapper, featureFlagsWrapper)
},
RunE: func(cmd *cobra.Command, args []string) error {
return precommit.Scan()
},
}
}

func secretsIgnoreCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
func secretsIgnoreCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) *cobra.Command {
var resultIds string
var all bool

Expand All @@ -135,7 +135,7 @@ func secretsIgnoreCommand(jwtWrapper wrappers.JWTWrapper) *cobra.Command {
`,
),
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := validateLicense(jwtWrapper); err != nil {
if err := validateLicense(jwtWrapper, featureFlagsWrapper); err != nil {
return err
}

Expand Down
73 changes: 57 additions & 16 deletions internal/commands/pre_commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package commands
import (
"testing"

"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/checkmarx/ast-cli/internal/wrappers/mock"
"github.com/stretchr/testify/assert"
)

func TestNewHooksCommand(t *testing.T) {
mockJWT := &mock.JWTMockWrapper{}
cmd := NewHooksCommand(mockJWT)
mockFF := &mock.FeatureFlagsMockWrapper{}
cmd := NewHooksCommand(mockJWT, mockFF)

assert.NotNil(t, cmd)
assert.Equal(t, "hooks", cmd.Use)
Expand All @@ -18,7 +20,8 @@ func TestNewHooksCommand(t *testing.T) {

func TestPreCommitCommand(t *testing.T) {
mockJWT := &mock.JWTMockWrapper{}
cmd := PreCommitCommand(mockJWT)
mockFF := &mock.FeatureFlagsMockWrapper{}
cmd := PreCommitCommand(mockJWT, mockFF)

assert.NotNil(t, cmd)
assert.Equal(t, "pre-commit", cmd.Use)
Expand All @@ -45,32 +48,69 @@ func TestPreCommitCommand(t *testing.T) {

func TestValidateLicense(t *testing.T) {
tests := []struct {
name string
aiEnabled int
wantError bool
name string
scsLicensingV2 bool
allowSecretDetection int
allowEnterpriseSecrets int
aiEnabled int
wantErr bool
}{
{
name: "License is valid",
aiEnabled: 0,
wantError: false,
name: "scsLicensing V2 ON and Secret Detection allowed",
scsLicensingV2: true,
allowSecretDetection: 0,
allowEnterpriseSecrets: mock.EnterpriseSecretsDisabled,
wantErr: false,
},
{
name: "License is invalid",
aiEnabled: mock.AIProtectionDisabled,
wantError: true,
name: "scsLicensing V2 ON and Secret Detection denied",
scsLicensingV2: true,
allowSecretDetection: mock.SecretDetectionDisabled,
allowEnterpriseSecrets: 0,
wantErr: true,
},
{
name: "scsLicensing V2 OFF and Enterprise Secrets allowed",
scsLicensingV2: false,
allowSecretDetection: mock.SecretDetectionDisabled,
allowEnterpriseSecrets: 0,
wantErr: false,
},
{
name: "scsLicensing V2 OFF and Enterprise Secrets denied",
scsLicensingV2: false,
allowSecretDetection: 0,
allowEnterpriseSecrets: mock.EnterpriseSecretsDisabled,
wantErr: true,
},
{
name: "AI enabled and secrets license disabled",
aiEnabled: 1,
allowEnterpriseSecrets: mock.EnterpriseSecretsDisabled,
allowSecretDetection: mock.SecretDetectionDisabled,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockJWT := &mock.JWTMockWrapper{
AIEnabled: tt.aiEnabled,
wrappers.ClearCache()
mockFF := &mock.FeatureFlagsMockWrapper{}
mock.Flag = wrappers.FeatureFlagResponseModel{
Name: wrappers.ScsLicensingV2Enabled,
Status: tt.scsLicensingV2,
}

err := validateLicense(mockJWT)
mockJWT := &mock.JWTMockWrapper{}
mockJWT.AIEnabled = tt.aiEnabled
mockJWT.EnterpriseSecretsEnabled = tt.allowEnterpriseSecrets
mockJWT.SecretDetectionEnabled = tt.allowSecretDetection

err := validateLicense(mockJWT, mockFF)

if tt.wantError {
if tt.wantErr {
assert.Error(t, err)
assert.Contains(t, err.Error(), "License validation failed")
} else {
assert.NoError(t, err)
}
Expand All @@ -80,7 +120,8 @@ func TestValidateLicense(t *testing.T) {

func TestSecretsIgnoreCommand(t *testing.T) {
mockJWT := &mock.JWTMockWrapper{}
cmd := secretsIgnoreCommand(mockJWT)
mockFF := &mock.FeatureFlagsMockWrapper{}
cmd := secretsIgnoreCommand(mockJWT, mockFF)
assert.NotNil(t, cmd)

resultIdsFlag := cmd.Flag("resultIds")
Expand Down
3 changes: 2 additions & 1 deletion internal/commands/pre_receive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import (

func TestPreReceiveCommand(t *testing.T) {
mockJWT := &mock.JWTMockWrapper{}
cmd := PreReceiveCommand(mockJWT)
mockFF := &mock.FeatureFlagsMockWrapper{}
cmd := PreReceiveCommand(mockJWT, mockFF)
assert.NotNil(t, cmd)
assert.Equal(t, "pre-receive", cmd.Use)
subCmds := cmd.Commands()
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func NewAstCLI(
triageCmd := NewResultsPredicatesCommand(resultsPredicatesWrapper, featureFlagsWrapper, customStatesWrapper)

chatCmd := NewChatCommand(chatWrapper, tenantWrapper)
hooksCmd := NewHooksCommand(jwtWrapper)
hooksCmd := NewHooksCommand(jwtWrapper, featureFlagsWrapper)
telemetryCmd := NewTelemetryCommand(telemetryWrapper)
rootCmd.AddCommand(
scanCmd,
Expand Down
20 changes: 19 additions & 1 deletion internal/wrappers/mock/jwt-helper-mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import (

type JWTMockWrapper struct {
AIEnabled int
EnterpriseSecretsEnabled int
SecretDetectionEnabled int
CheckmarxOneAssistEnabled int
CustomGetAllowedEngines func(wrappers.FeatureFlagsWrapper) (map[string]bool, error)
}

const AIProtectionDisabled = 1
const CheckmarxOneAssistDisabled = 1
const EnterpriseSecretsDisabled = 1
const SecretDetectionDisabled = 1

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

Expand All @@ -38,13 +42,27 @@ func (*JWTMockWrapper) ExtractTenantFromToken() (tenant string, err error) {

// IsAllowedEngine mock for tests
func (j *JWTMockWrapper) IsAllowedEngine(engine string) (bool, error) {
if engine == params.AiProviderFlag || engine == params.EnterpriseSecretsLabel {
if engine == params.AiProviderFlag {
if j.AIEnabled == AIProtectionDisabled {
return false, nil
}
return true, nil
}

if engine == params.EnterpriseSecretsLabel {
if j.EnterpriseSecretsEnabled == EnterpriseSecretsDisabled {
return false, nil
}
return true, nil
}

if engine == params.SecretDetectionLabel {
if j.SecretDetectionEnabled == SecretDetectionDisabled {
return false, nil
}
return true, nil
}

if engine == params.CheckmarxOneAssistType {
if j.CheckmarxOneAssistEnabled == CheckmarxOneAssistDisabled {
return false, nil
Expand Down
Loading