Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7504a5e
feat: Add git-commit-history flag for Secret Detection scans
cx-rui-oliveira Nov 19, 2025
00789f3
test: add UTs for flag validation and enabling logic
cx-rui-oliveira Nov 19, 2025
87ae9d5
refactor: update git-commit-history flag description for clarity
cx-rui-oliveira Nov 19, 2025
c7f6bd4
refactor: reduce cyclomatic complexity
cx-rui-oliveira Nov 19, 2025
f19cf79
refactor: enhance functionality with feature flag integration for com…
cx-rui-oliveira Nov 19, 2025
8f1624c
test: add and update UTs considering commit history FF
cx-rui-oliveira Nov 19, 2025
e9a647c
test: add clearFlags() calls for consistency
cx-rui-oliveira Nov 20, 2025
c7656ed
Merge branch 'main' into AST-120196-cli-enable-disable-commit-history…
cx-rui-oliveira Nov 20, 2025
885dc30
test: add wrappers.ClearCache() calls to clear cache
cx-rui-oliveira Nov 20, 2025
bb6a103
test: ensure that flags are cleared after use
cx-rui-oliveira Nov 21, 2025
801179e
refactor: set git commit history to false if configured correctly to …
cx-rui-oliveira Nov 24, 2025
975f35b
Merge branch 'main' into AST-120196-cli-enable-disable-commit-history…
cx-rui-oliveira Nov 24, 2025
b62b503
test: remove redundant case insensitive TRUE/FALSE tests for flag values
cx-rui-oliveira Nov 24, 2025
6b53fee
refactor: improve use of warning messages and remove default value
cx-rui-oliveira Nov 25, 2025
ea4e5e1
Merge branch 'main' into AST-120196-cli-enable-disable-commit-history…
cx-margarita-levitm Dec 8, 2025
0580ad4
Merge branch 'main' into AST-120196-cli-enable-disable-commit-history…
cx-anurag-dalke Dec 9, 2025
ac7da18
refactor: update warning messages for git commit history flag usage
cx-rui-oliveira Dec 10, 2025
592c70f
Merge branch 'main' into AST-120196-cli-enable-disable-commit-history…
cx-anurag-dalke Dec 10, 2025
37015c1
feat: consider git urls as source
cx-rui-oliveira Dec 11, 2025
2eec2d0
feat: consider zip as source
cx-rui-oliveira Dec 12, 2025
18fd15b
chore: merge and resolve conflicts
cx-rui-oliveira Dec 15, 2025
e135a9a
chore: divide GetGitCommitHistoryValue tests
cx-rui-oliveira Dec 15, 2025
e580cac
Merge branch 'main' into AST-120196-cli-enable-disable-commit-history…
cx-anurag-dalke Dec 23, 2025
eecd081
Merge branch 'main' into AST-120196-cli-enable-disable-commit-history…
cx-rahul-pidde Dec 30, 2025
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
132 changes: 124 additions & 8 deletions internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ const (
"--scs-repo-url your_repo_url --scs-repo-token your_repo_token"
ScsScorecardUnsupportedHostWarningMsg = "SCS scan warning: Unable to run Scorecard scanner due to unsupported repo host. Currently, Scorecard can only run on GitHub Cloud repos."

gitCommitHistoryInvalidValueErrorMsg = "Invalid value for --git-commit-history. Valid values are: 'true' or 'false'"
gitCommitHistoryNotAvailableWarningMsg = "Secret Detection scan warning: --git-commit-history flag ignored because git commit history scanning is not available."
gitCommitHistoryNotSelectedWarningMsg = "Secret Detection scan warning: --git-commit-history flag ignored because scs was not specified in scan types."
gitCommitHistoryNotApplicableWarningMsg = "Secret Detection scan warning: --git-commit-history flag ignored because secret detection wasn't run on this scan."
gitCommitHistoryNoGitRepositoryWarningMsg = "Secret Detection scan warning: No Git history found. Secret Detection will scan the working tree only."

jsonExt = ".json"
xmlExt = ".xml"
sbomScanTypeErrMsg = "The --sbom-only flag can only be used when the scan type is sca"
Expand Down Expand Up @@ -884,6 +890,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, "", commonParams.GitCommitHistoryFlagDescription)
createScanCmd.PersistentFlags().Bool(commonParams.ScaHideDevAndTestDepFlag, false, scaHideDevAndTestDepFlagDescription)

// Container config flags
Expand Down Expand Up @@ -1010,9 +1017,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 @@ -1388,14 +1394,15 @@ 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) {
scsEnabled := isScsEnabled(scsLicensingV2)
func addSCSScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasRepositoryHealthLicense,
hasSecretDetectionLicense, hasEnterpriseSecretsLicense bool, featureFlagsWrapper wrappers.FeatureFlagsWrapper) (map[string]interface{}, error) {
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
scsEnabled := isScsEnabled(scsLicensingV2Flag.Status)
if !scsEnabled {
return nil, nil
}
scsScorecardAllowed := isScsScorecardAllowed(scsLicensingV2, hasRepositoryHealthLicense)
scsSecretDetectionAllowed := isScsSecretDetectionAllowed(scsLicensingV2, hasSecretDetectionLicense, hasEnterpriseSecretsLicense)
scsScorecardAllowed := isScsScorecardAllowed(scsLicensingV2Flag.Status, hasRepositoryHealthLicense)
scsSecretDetectionAllowed := isScsSecretDetectionAllowed(scsLicensingV2Flag.Status, hasSecretDetectionLicense, hasEnterpriseSecretsLicense)
if !scsScorecardAllowed && !scsSecretDetectionAllowed {
return nil, nil
}
Expand Down Expand Up @@ -1426,6 +1433,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 @@ -3512,6 +3525,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 @@ -3783,6 +3803,102 @@ 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 {
return errors.Errorf(gitCommitHistoryInvalidValueErrorMsg)
}

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(gitCommitHistoryNotAvailableWarningMsg)
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(gitCommitHistoryNotSelectedWarningMsg)
return false
}

scsEngines, _ := cmd.Flags().GetString(commonParams.SCSEnginesFlag)
scsScoreCardSelected, scsSecretDetectionSelected := getSCSEnginesSelected(scsEngines)
if scsScoreCardSelected && !scsSecretDetectionSelected {
fmt.Println(gitCommitHistoryNotApplicableWarningMsg)
return false
}

source, _ := cmd.Flags().GetString(commonParams.SourcesFlag)
if !hasGitRepository(source) {
fmt.Println(gitCommitHistoryNoGitRepositoryWarningMsg)
return false
}

return true
}

// hasGitRepository checks if the source directory contains a Git repository (skipping validation for git URLs or zip files)
func hasGitRepository(source string) bool {
if source == "" {
return false
}

sourceTrimmed := strings.TrimSpace(source)

if util.IsGitURL(sourceTrimmed) || filepath.Ext(sourceTrimmed) == constants.ZipExtension {
return true
}

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