Skip to content
Closed
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
bec5010
added-sbom-flag-feature
cx-hitesh-madgulkar Jul 18, 2025
d21eedd
Merge branch 'main' of https://github.com/Checkmarx/ast-cli into feat…
cx-hitesh-madgulkar Jul 18, 2025
b7221a4
checked-lint-issues
cx-hitesh-madgulkar Jul 18, 2025
1e8cb8b
checked-lint-issues1
cx-hitesh-madgulkar Jul 18, 2025
d854e20
removed-log-line
cx-hitesh-madgulkar Jul 19, 2025
8c46520
unit-test-fix
cx-hitesh-madgulkar Jul 19, 2025
6deea32
added-XML-JSON-validation
cx-hitesh-madgulkar Jul 19, 2025
588929b
Merge branch 'main' of https://github.com/Checkmarx/ast-cli into feat…
cx-hitesh-madgulkar Jul 21, 2025
e8dee9e
fixed-lint-and-error-msg
cx-hitesh-madgulkar Jul 21, 2025
3651a22
err-lint-issue
cx-hitesh-madgulkar Jul 21, 2025
ec4c3e9
added-ut
cx-hitesh-madgulkar Jul 21, 2025
69dbcfb
added-ut1
cx-hitesh-madgulkar Jul 21, 2025
f17e409
added-integration-tests
cx-hitesh-madgulkar Jul 21, 2025
9bcd4d9
merge-pull-main
cx-hitesh-madgulkar Jul 21, 2025
0afb631
added-ut-sbom-scan
cx-hitesh-madgulkar Jul 21, 2025
0ca02ea
Merge branch 'main' of https://github.com/Checkmarx/ast-cli into feat…
cx-hitesh-madgulkar Jul 22, 2025
f51efbf
logging-temp-zip-path-correctly
cx-hitesh-madgulkar Jul 22, 2025
82a2e2c
Merge branch 'main' of https://github.com/Checkmarx/ast-cli into feat…
cx-hitesh-madgulkar Jul 22, 2025
0e26d9b
error-message-and-flag-description-changed
cx-hitesh-madgulkar Jul 23, 2025
432b7ab
Merge branch 'main' of https://github.com/Checkmarx/ast-cli into feat…
cx-hitesh-madgulkar Jul 23, 2025
ba71598
Merge branch 'main' of https://github.com/Checkmarx/ast-cli into feat…
cx-hitesh-madgulkar Jul 24, 2025
7930750
removed-log-line
cx-hitesh-madgulkar Jul 24, 2025
428fd41
checked-branch-condition-for-sbom
cx-hitesh-madgulkar Jul 26, 2025
8d6539a
Merge branch 'main' of https://github.com/Checkmarx/ast-cli into feat…
cx-hitesh-madgulkar Jul 29, 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
102 changes: 93 additions & 9 deletions internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import (
"archive/zip"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"io/fs"
Expand Down Expand Up @@ -118,7 +119,11 @@
ScsRepoWarningMsg = "SCS scan warning: Unable to start Scorecard scan due to missing required flags, please include in the ast-cli arguments: " +
"--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."
BranchPrimaryPrefix = "--branch-primary="

jsonExt = ".json"
xmlExt = ".xml"
sbomScanTypeErrMsg = "The --sbom-only flag can only be used when the scan type is sca"
BranchPrimaryPrefix = "--branch-primary="
)

var (
Expand Down Expand Up @@ -808,6 +813,9 @@
createScanCmd.PersistentFlags().Bool(commonParams.ContainersExcludeNonFinalStagesFlag, false, "Scan only the final deployable image")
createScanCmd.PersistentFlags().String(commonParams.ContainersImageTagFilterFlag, "", "Exclude images by image name and/or tag, ex: \"*dev\"")

// reading sbom-only flag
createScanCmd.PersistentFlags().Bool(commonParams.SbomFlag, false, "Scan only the specified SBOM file (supported formats xml or json)")

return createScanCmd
}

Expand Down Expand Up @@ -1090,6 +1098,7 @@
scaMapConfig := make(map[string]interface{})
scaConfig := wrappers.ScaConfig{}
scaMapConfig[resultsMapType] = commonParams.ScaType
isSbom, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
scaConfig.Filter, _ = cmd.Flags().GetString(commonParams.ScaFilterFlag)
scaConfig.LastSastScanTime, _ = cmd.Flags().GetString(commonParams.LastSastScanTime)
scaConfig.PrivatePackageVersion, _ = cmd.Flags().GetString(commonParams.ScaPrivatePackageVersionFlag)
Expand All @@ -1108,6 +1117,7 @@
}
}
}
scaConfig.SBom = strconv.FormatBool(isSbom)
scaMapConfig[resultsMapValue] = &scaConfig
return scaMapConfig
}
Expand Down Expand Up @@ -1323,6 +1333,8 @@
var scanTypes []string
var SCSScanTypes []string

isSbomScan, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)

allowedEngines, err := jwtWrapper.GetAllowedEngines(featureFlagsWrapper)
if err != nil {
err = errors.Errorf("Error validating scan types: %v", err)
Expand All @@ -1338,6 +1350,20 @@
userSCSScanTypes = strings.Replace(strings.ToLower(userSCSScanTypes), commonParams.SCSEnginesFlag, commonParams.ScsType, 1)

scanTypes = strings.Split(userScanTypes, ",")

// check scan-types, when sbom-only flag is used
if isSbomScan {
if len(scanTypes) > 1 {
err = errors.Errorf(sbomScanTypeErrMsg)
return err
}

if scanTypes[0] != "sca" {
err = errors.Errorf(sbomScanTypeErrMsg)
return err
}
}

for _, scanType := range scanTypes {
if !allowedEngines[scanType] {
keys := reflect.ValueOf(allowedEngines).MapKeys()
Expand All @@ -1353,11 +1379,19 @@
return err
}
} else {
for k := range allowedEngines {
scanTypes = append(scanTypes, k)
if isSbomScan {
if allowedEngines["sca"] {
// for sbom-flag, setting scan-type as only "sca"
scanTypes = append(scanTypes, "sca")
} else {
return errors.Errorf("sbom needs sca engine to be allowed")
}
} else {
for k := range allowedEngines {
scanTypes = append(scanTypes, k)
}
}
}

actualScanTypes = strings.Join(scanTypes, ",")
actualScanTypes = strings.Replace(strings.ToLower(actualScanTypes), commonParams.IacType, commonParams.KicsType, 1)

Expand Down Expand Up @@ -1656,8 +1690,24 @@
scaResolverPath, _ := cmd.Flags().GetString(commonParams.ScaResolverFlag)

scaResolverParams, scaResolver := getScaResolverFlags(cmd)

zipFilePath, directoryPath, err := definePathForZipFileOrDirectory(cmd)
isSbom, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
var directoryPath string
if isSbom {
sbomFile, _ := cmd.Flags().GetString(commonParams.SourcesFlag)
isValid, err := isValidJSONOrXML(sbomFile)
if err != nil {
return "", "", errors.Wrapf(err, "%s: Input in bad format", failedCreating)
}
if !isValid {
return "", "", errors.Wrapf(err, "%s: Input in bad format", failedCreating)
}
zipFilePath, err = util.CompressFile(sbomFile, "sbomFileCompress", directoryCreationPrefix)
if err != nil {
return "", "", errors.Wrapf(err, "%s: Input in bad format", failedCreating)
}
} else {
zipFilePath, directoryPath, err = definePathForZipFileOrDirectory(cmd)
}

if zipFilePath != "" && scaResolverPath != "" {
return "", "", errors.New("Scanning Zip files is not supported by ScaResolver.Please use non-zip source")
Expand Down Expand Up @@ -1717,7 +1767,9 @@
}
}
} else {
zipFilePath, dirPathErr = compressFolder(directoryPath, sourceDirFilter, userIncludeFilter, scaResolver)
if !isSbom {
zipFilePath, dirPathErr = compressFolder(directoryPath, sourceDirFilter, userIncludeFilter, scaResolver)

Check failure on line 1771 in internal/commands/scan.go

View workflow job for this annotation

GitHub Actions / lint

emptyStringTest: replace `len(sourceDirFilter) > 0` with `sourceDirFilter != ""` (gocritic)
}
}
if dirPathErr != nil {
return "", "", dirPathErr
Expand All @@ -1731,8 +1783,10 @@
}
}

if zipFilePath != "" {
if zipFilePath != "" && !isSbom {
return uploadZip(uploadsWrapper, zipFilePath, unzip, userProvidedZip, featureFlagsWrapper)
} else if zipFilePath != "" && isSbom {
return uploadZip(uploadsWrapper, zipFilePath, unzip, false, featureFlagsWrapper)
}
return preSignedURL, zipFilePath, nil
}
Expand Down Expand Up @@ -2978,8 +3032,9 @@
}

func validateCreateScanFlags(cmd *cobra.Command) error {
isSbomScan, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
branch := strings.TrimSpace(viper.GetString(commonParams.BranchKey))
if branch == "" {
if branch == "" && !isSbomScan {
return errors.Errorf("%s: Please provide a branch", failedCreating)
}
exploitablePath, _ := cmd.Flags().GetString(commonParams.ExploitablePathFlag)
Expand Down Expand Up @@ -3109,3 +3164,32 @@

return outputFile.Name(), nil
}

func isValidJSONOrXML(path string) (bool, error) {
ext := strings.ToLower(filepath.Ext(path))
if ext != jsonExt && ext != xmlExt {
return false, fmt.Errorf("not a JSON/XML file, provide valid JSON/XMl file")
}

data, err := ioutil.ReadFile(path)
if err != nil {
return false, fmt.Errorf("failed to read file: %w", err)
}

switch ext {
case jsonExt:
var js interface{}
if err := json.Unmarshal(data, &js); err != nil {
return false, fmt.Errorf("invalid JSON format. %w", err) // Invalid JSON
}
case xmlExt:
var x interface{}
if err := xml.Unmarshal(data, &x); err != nil {
return false, fmt.Errorf("invalid XML format.%w", err) // Invalid XML
}
default:
return false, nil
}

return true, nil
}
30 changes: 30 additions & 0 deletions internal/commands/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@ func TestAddScaScan(t *testing.T) {
ExploitablePath: "true",
LastSastScanTime: "1",
PrivatePackageVersion: "1.1.1",
SBom: "false",
}
scaMapConfig := make(map[string]interface{})
scaMapConfig[resultsMapType] = commonParams.ScaType
Expand Down Expand Up @@ -2412,3 +2413,32 @@ func Test_parseArgs(t *testing.T) {
}
}
}

func Test_isValidJSONOrXML(t *testing.T) {
tests := []struct {
description string
inputPath string
output bool
}{
{"wrong extension", "somefile.txt", false},
{"wrong json file", "wrongfilepath.json", false},
{"wrong xml file", "wrongfilepath.xml", false},
{"correct file", "data/package.json", true},
}

for _, test := range tests {
isValid, _ := isValidJSONOrXML(test.inputPath)
if isValid != test.output {
t.Errorf(" test case failed for params %v", test)
}
}
}

func Test_CreateScanWithSbomFlag(t *testing.T) {
err := execCmdNotNilAssertion(
t,
"scan", "create", "--project-name", "newProject", "-s", "data/sbom.json", "--branch", "dummy_branch", "--sbom-only",
)

assert.ErrorContains(t, err, "Failed creating a scan: Input in bad format: failed to read file:")
}
3 changes: 3 additions & 0 deletions internal/params/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ const (
ContainersImageTagFilterFlag = "containers-image-tag-filter"
ContainersPackageFilterFlag = "containers-package-filter"
ContainersExcludeNonFinalStagesFlag = "containers-exclude-non-final-stages"

// SBOM - flag
SbomFlag = "sbom-only"
)

// Parameter values
Expand Down
1 change: 1 addition & 0 deletions internal/wrappers/scans.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ type ScaConfig struct {
LastSastScanTime string `json:"LastSastScanTime,omitempty"`
PrivatePackageVersion string `json:"privatePackageVersion,omitempty"`
EnableContainersScan bool `json:"enableContainersScan,omitempty"`
SBom string `json:"sbom,omitempty"`
}
type ContainerConfig struct {
FilesFilter string `json:"filesFilter,omitempty"`
Expand Down
45 changes: 45 additions & 0 deletions test/integration/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2383,3 +2383,48 @@ func TestCreateScan_WithScaResolver_ZipSource_Fail(t *testing.T) {
err, _ := executeCommand(t, args...)
assert.Error(t, err, "Scanning Zip files is not supported by ScaResolver.Please use non-zip source")
}

func TestCreateScan_SbomScanForInvalidScanTypes(t *testing.T) {
args := []string{
"scan", "create",
flag(params.ProjectName), "random_proj",
flag(params.SourcesFlag), "data/project-with-directory-symlink",
flag(params.ScanTypes), "sast,sca",
flag(params.BranchFlag), "dummy_branch",
flag(params.SbomFlag),
}

err, _ := executeCommand(t, args...)
assert.Error(t, err, "The --sbom-only flag can only be used when the scan type is sca")

}

func TestCreateScan_SbomScanForInvalidFileExtension(t *testing.T) {
args := []string{
"scan", "create",
flag(params.ProjectName), "random_proj",
flag(params.SourcesFlag), "data/project-with-directory-symlink",
flag(params.ScanTypes), "sca",
flag(params.BranchFlag), "dummy_branch",
flag(params.SbomFlag),
}

err, _ := executeCommand(t, args...)
assert.Error(t, err, "Failed creating a scan: Input in bad format: not a JSON/XML file, provide valid JSON/XMl file")

}

func TestCreateScan_SbomScanForNotExistingFile(t *testing.T) {
args := []string{
"scan", "create",
flag(params.ProjectName), "random_proj",
flag(params.SourcesFlag), "data/sbom.json",
flag(params.ScanTypes), "sca",
flag(params.BranchFlag), "dummy_branch",
flag(params.SbomFlag),
}

err, _ := executeCommand(t, args...)
assert.ErrorContains(t, err, "Failed creating a scan: Input in bad format: failed to read file:")

}
Loading