Skip to content
Open
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
5 changes: 5 additions & 0 deletions internal/commands/groups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

func TestCreateScanAndProjectWithGroupFFTrue(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: "ACCESS_MANAGEMENT_ENABLED", Status: true}}
defer clearFlags()
execCmdNilAssertion(
t,
"scan", "create", "--project-name", "new-project", "-b", "dummy_branch", "-s", ".", "--project-groups", "group",
Expand All @@ -17,20 +18,23 @@ func TestCreateScanAndProjectWithGroupFFTrue(t *testing.T) {

func TestCreateScanAndProjectWithGroupFFFalse(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: "ACCESS_MANAGEMENT_ENABLED", Status: false}}
defer clearFlags()
execCmdNilAssertion(
t,
"scan", "create", "--project-name", "new-project", "-b", "dummy_branch", "-s", ".", "--project-groups", "group",
)
}
func TestCreateProjectWithGroupFFTrue(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: "ACCESS_MANAGEMENT_ENABLED", Status: true}}
defer clearFlags()
execCmdNilAssertion(
t, "project", "create", "--project-name", "new-project", "--groups", "group",
)
}

func TestCreateProjectWithGroupFFFalse(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: "ACCESS_MANAGEMENT_ENABLED", Status: false}}
defer clearFlags()
execCmdNilAssertion(
t,
"project", "create", "--project-name", "new-project", "--groups", "group",
Expand All @@ -39,6 +43,7 @@ func TestCreateProjectWithGroupFFFalse(t *testing.T) {

func TestCreateScanForExistingProjectWithGroupFFTrue(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: "ACCESS_MANAGEMENT_ENABLED", Status: true}}
defer clearFlags()
execCmdNilAssertion(
t,
"scan", "create", "--project-name", "MOCK", "-b", "dummy_branch", "-s", ".", "--project-groups", "group",
Expand Down
119 changes: 112 additions & 7 deletions internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,7 @@ func scanCreateSubCommand(
createScanCmd.PersistentFlags().String(commonParams.SCSRepoTokenFlag, "", "Provide a token with read permission for the repo that you are scanning (for scorecard scans)")
createScanCmd.PersistentFlags().String(commonParams.SCSRepoURLFlag, "", "The URL of the repo that you are scanning with scs (for scorecard scans)")
createScanCmd.PersistentFlags().String(commonParams.SCSEnginesFlag, "", "Specify which scs engines will run (default: all licensed engines)")
createScanCmd.PersistentFlags().String(commonParams.GitCommitHistoryFlag, "false", commonParams.GitCommitHistoryFlagDescription)
createScanCmd.PersistentFlags().Bool(commonParams.ScaHideDevAndTestDepFlag, false, scaHideDevAndTestDepFlagDescription)

// Container config flags
Expand Down Expand Up @@ -991,9 +992,8 @@ func setupScanTypeProjectAndConfig(
configArr = append(configArr, containersConfig)
}

scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
var SCSConfig, scsErr = addSCSScan(cmd, resubmitConfig, scsLicensingV2Flag.Status, userAllowedEngines[commonParams.RepositoryHealthType],
userAllowedEngines[commonParams.SecretDetectionType], userAllowedEngines[commonParams.EnterpriseSecretsType])
var SCSConfig, scsErr = addSCSScan(cmd, resubmitConfig, userAllowedEngines[commonParams.RepositoryHealthType],
userAllowedEngines[commonParams.SecretDetectionType], userAllowedEngines[commonParams.EnterpriseSecretsType], featureFlagsWrapper)
if scsErr != nil {
return scsErr
} else if SCSConfig != nil {
Expand Down Expand Up @@ -1369,11 +1369,12 @@ func isScorecardRunnable(isScsEnginesFlagSet, scsScorecardSelected bool, scsRepo
return isURLSupportedByScorecard(scsRepoURL), nil
}

func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, scsLicensingV2, hasRepositoryHealthLicense,
hasSecretDetectionLicense, hasEnterpriseSecretsLicense bool) (map[string]interface{}, error) {
func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasRepositoryHealthLicense,
hasSecretDetectionLicense, hasEnterpriseSecretsLicense bool, featureFlagsWrapper wrappers.FeatureFlagsWrapper) (map[string]interface{}, error) {
scsEnabled := scanTypeEnabled(commonParams.ScsType)
scsScorecardAllowed := isScsScorecardAllowed(scsLicensingV2, hasRepositoryHealthLicense, scsEnabled)
scsSecretDetectionAllowed := isScsSecretDetectionAllowed(scsLicensingV2, hasSecretDetectionLicense, hasEnterpriseSecretsLicense, scsEnabled)
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
scsScorecardAllowed := isScsScorecardAllowed(scsLicensingV2Flag.Status, hasRepositoryHealthLicense, scsEnabled)
scsSecretDetectionAllowed := isScsSecretDetectionAllowed(scsLicensingV2Flag.Status, hasSecretDetectionLicense, hasEnterpriseSecretsLicense, scsEnabled)
if !scsScorecardAllowed && !scsSecretDetectionAllowed {
return nil, nil
}
Expand Down Expand Up @@ -1404,6 +1405,12 @@ func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, scsLicensi

if scsSecretDetectionSelected && scsSecretDetectionAllowed {
scsConfig.Twoms = trueString

// Set git commit history based on FF and validations
commitHistoryFlag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.SscsCommitHistoryEnabled)
if gitCommitHistoryValue := getGitCommitHistoryValue(cmd, commitHistoryFlag.Status); gitCommitHistoryValue != "" {
scsConfig.GitCommitHistory = gitCommitHistoryValue
}
}

isScsEnginesFlagSet := scsEngines != ""
Expand Down Expand Up @@ -3480,6 +3487,13 @@ func validateCreateScanFlags(cmd *cobra.Command) error {
}
}
}

// Validate git-commit-history flag
err = validateGitCommitHistoryFlag(cmd)
if err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -3743,6 +3757,97 @@ func validateBooleanString(value string) error {
return nil
}

// validateGitCommitHistoryFlag validates the git-commit-history flag (needed for Secret Detection)
func validateGitCommitHistoryFlag(cmd *cobra.Command) error {
gitCommitHistory, _ := cmd.Flags().GetString(commonParams.GitCommitHistoryFlag)

err := validateBooleanString(gitCommitHistory)
if err != nil || gitCommitHistory == "" {
return errors.Errorf("Invalid value for --git-commit-history. Use 'true' or 'false'.")
}

return nil
}

// getGitCommitHistoryValue determines the value for git commit history config based on flag and validations
func getGitCommitHistoryValue(cmd *cobra.Command, isFeatureFlagEnabled bool) string {
if !isFeatureFlagEnabled {
fmt.Println("Warning: Git commit history scanning is not available. The flag will be ignored.")
return ""
}

gitCommitHistory, _ := cmd.Flags().GetString(commonParams.GitCommitHistoryFlag)
gitCommitHistoryValue := strings.ToLower(gitCommitHistory)

if !validateGitCommitHistoryContext(cmd) {
return ""
}

return gitCommitHistoryValue
}

// validateGitCommitHistoryContext validates if the context is appropriate for functionality
func validateGitCommitHistoryContext(cmd *cobra.Command) bool {
userScanTypes, _ := cmd.Flags().GetString(commonParams.ScanTypes)
if !strings.Contains(strings.ToLower(userScanTypes), commonParams.ScsType) {
fmt.Println("Warning: '--git-commit-history' was provided, but SCS is not selected. Ignoring this flag.")
return false
}

scsEngines, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)
scsScoreCardSelected, scsSecretDetectionSelected := getSCSEnginesSelected(scsEngines)
if scsScoreCardSelected && !scsSecretDetectionSelected {
fmt.Println("Warning: Commit History applies only to Secret Detection. The flag will be ignored.")
return false
}

source, _ := cmd.Flags().GetString(commonParams.SourcesFlag)
if !hasGitRepository(source) {
fmt.Println("Warning: No Git history found. Secret Detection will scan the working tree only.")
return false
}

return true
}

// hasGitRepository checks if the source directory contains a Git repository
func hasGitRepository(source string) bool {
if source == "" {
return false
}

sourceTrimmed := strings.TrimSpace(source)
info, err := os.Stat(sourceTrimmed)
if err != nil || !info.IsDir() {
return false
}

// Check if .git exists in the root directory
gitPath := filepath.Join(sourceTrimmed, ".git")
if _, err := os.Stat(gitPath); err == nil {
return true
}

// fallback: search for .git in subdirectories
return searchGitInSubdirectories(sourceTrimmed)
}

// searchGitInSubdirectories walks through subdirectories to find a .git folder
func searchGitInSubdirectories(sourcePath string) bool {
found := false
_ = filepath.Walk(sourcePath, func(path string, info os.FileInfo, err error) error {
if err != nil || found {
return nil
}
if info.IsDir() && info.Name() == ".git" {
found = true
return filepath.SkipAll
}
return nil
})
return found
}

func parseArgs(input string) []string {
var args []string
var current strings.Builder
Expand Down
Loading
Loading