Skip to content

Commit

Permalink
CLI | Container Security filter flags (AST-82260) (#1023)
Browse files Browse the repository at this point in the history
* CLI | Container Security filter flags (AST-82260)

* CLI | Container Security filter flags (AST-82260)

* Added resubmit support fo container engine configuration

* refactor initializeContainersConfigWithResubmitValues - deleted unnecessary conditions

* Add integration tests

* Change docs

* Added example to flags documentations

---------

Co-authored-by: AlvoBen <[email protected]>
  • Loading branch information
AlvoBen and BenAlvo1 authored Feb 2, 2025
1 parent 8faaeb0 commit 38ff94d
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 47 deletions.
146 changes: 99 additions & 47 deletions internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,50 +53,54 @@ const (
notExploitable = "NOT_EXPLOITABLE"
ignored = "IGNORED"

git = "git"
invalidSSHSource = "provided source does not need a key. Make sure you are defining the right source or remove the flag --ssh-key"
errorUnzippingFile = "an error occurred while unzipping file. Reason: "
containerRun = "run"
containerVolumeFlag = "-v"
containerNameFlag = "--name"
containerRemove = "--rm"
containerImage = "checkmarx/kics:v2.1.3"
containerScan = "scan"
containerScanPathFlag = "-p"
containerScanPath = "/path"
containerScanOutputFlag = "-o"
containerScanOutput = "/path"
containerScanFormatFlag = "--report-formats"
containerScanFormatOutput = "json"
containerStarting = "Starting kics container"
containerFormatInfo = "The report format and output path cannot be overridden."
containerFolderRemoving = "Removing folder in temp"
containerCreateFolderError = "Error creating temporary directory"
containerWriteFolderError = " Error writing file to temporary directory"
containerFileSourceMissing = "--file is required for kics-realtime command"
containerFileSourceIncompatible = ". Provided file is not supported by kics"
containerFileSourceError = " Error reading file"
containerResultsFileFormat = "%s/results.json"
containerVolumeFormat = "%s:/path"
containerTempDirPattern = "kics"
kicsContainerPrefixName = "cli-kics-realtime-"
cleanupMaxRetries = 3
cleanupRetryWaitSeconds = 15
DanglingSymlinkError = "Skipping dangling symbolic link"
configFilterKey = "filter"
configFilterPlatforms = "platforms"
configIncremental = "incremental"
configFastScan = "fastScanMode"
configPresetName = "presetName"
configEngineVerbose = "engineVerbose"
configLanguageMode = "languageMode"
resultsMapValue = "value"
resultsMapType = "type"
trueString = "true"
configTwoms = "2ms"
falseString = "false"
maxPollingWaitTime = 60
engineNotAllowed = "It looks like the \"%s\" scan type does not exist or you are trying to run a scan without the \"%s\" package license." +
git = "git"
invalidSSHSource = "provided source does not need a key. Make sure you are defining the right source or remove the flag --ssh-key"
errorUnzippingFile = "an error occurred while unzipping file. Reason: "
containerRun = "run"
containerVolumeFlag = "-v"
containerNameFlag = "--name"
containerRemove = "--rm"
containerImage = "checkmarx/kics:v2.1.3"
containerScan = "scan"
containerScanPathFlag = "-p"
containerScanPath = "/path"
containerScanOutputFlag = "-o"
containerScanOutput = "/path"
containerScanFormatFlag = "--report-formats"
containerScanFormatOutput = "json"
containerStarting = "Starting kics container"
containerFormatInfo = "The report format and output path cannot be overridden."
containerFolderRemoving = "Removing folder in temp"
containerCreateFolderError = "Error creating temporary directory"
containerWriteFolderError = " Error writing file to temporary directory"
containerFileSourceMissing = "--file is required for kics-realtime command"
containerFileSourceIncompatible = ". Provided file is not supported by kics"
containerFileSourceError = " Error reading file"
containerResultsFileFormat = "%s/results.json"
containerVolumeFormat = "%s:/path"
containerTempDirPattern = "kics"
kicsContainerPrefixName = "cli-kics-realtime-"
cleanupMaxRetries = 3
cleanupRetryWaitSeconds = 15
DanglingSymlinkError = "Skipping dangling symbolic link"
configFilterKey = "filter"
configFilterPlatforms = "platforms"
configIncremental = "incremental"
configFastScan = "fastScanMode"
configPresetName = "presetName"
configEngineVerbose = "engineVerbose"
configLanguageMode = "languageMode"
ConfigContainersFilesFilterKey = "filesFilter"
ConfigContainersImagesFilterKey = "imagesFilter"
ConfigContainersPackagesFilterKey = "packagesFilter"
ConfigContainersNonFinalStagesFilterKey = "nonFinalStagesFilter"
resultsMapValue = "value"
resultsMapType = "type"
trueString = "true"
configTwoms = "2ms"
falseString = "false"
maxPollingWaitTime = 60
engineNotAllowed = "It looks like the \"%s\" scan type does not exist or you are trying to run a scan without the \"%s\" package license." +
"\nTo use this feature, you would need to purchase a license." +
"\nPlease contact our support team for assistance if you believe you have already purchased a license." +
"\nLicensed packages: %s"
Expand Down Expand Up @@ -657,6 +661,12 @@ func scanCreateSubCommand(
createScanCmd.PersistentFlags().String(commonParams.SCSEnginesFlag, "", "Specify which scs engines will run (default: all licensed engines)")
createScanCmd.PersistentFlags().Bool(commonParams.ScaHideDevAndTestDepFlag, false, scaHideDevAndTestDepFlagDescription)

// Container config flags
createScanCmd.PersistentFlags().String(commonParams.ContainersFileFolderFilterFlag, "", "Specify files and folders to be included or excluded from scans, ex: \"!*.log\"")
createScanCmd.PersistentFlags().String(commonParams.ContainersPackageFilterFlag, "", "Exclude packages by package name or file path using regex, ex: \"^internal-.*\"")
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\"")

return createScanCmd
}

Expand Down Expand Up @@ -764,7 +774,7 @@ func setupScanTypeProjectAndConfig(
if apiSecConfig != nil {
configArr = append(configArr, apiSecConfig)
}
var containersConfig = addContainersScan(containerEngineCLIEnabled.Status)
var containersConfig = addContainersScan(cmd, resubmitConfig, containerEngineCLIEnabled.Status)
if containersConfig != nil {
configArr = append(configArr, containersConfig)
}
Expand Down Expand Up @@ -935,19 +945,61 @@ func addScaScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasContain
return nil
}

func addContainersScan(containerEngineCLIEnabled bool) map[string]interface{} {
func addContainersScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, containerEngineCLIEnabled bool) map[string]interface{} {
if !scanTypeEnabled(commonParams.ContainersType) || !containerEngineCLIEnabled {
return nil
}
containerMapConfig := make(map[string]interface{})
containerMapConfig[resultsMapType] = commonParams.ContainersType

containerConfig := wrappers.ContainerConfig{}

initializeContainersConfigWithResubmitValues(resubmitConfig, &containerConfig)

fileFolderFilter, _ := cmd.PersistentFlags().GetString(commonParams.ContainersFileFolderFilterFlag)
if fileFolderFilter != "" {
containerConfig.FilesFilter = fileFolderFilter
}
packageFilter, _ := cmd.PersistentFlags().GetString(commonParams.ContainersPackageFilterFlag)
if packageFilter != "" {
containerConfig.PackagesFilter = packageFilter
}
excludeNonFinalStages, _ := cmd.PersistentFlags().GetBool(commonParams.ContainersExcludeNonFinalStagesFlag)
if cmd.PersistentFlags().Changed(commonParams.ContainersExcludeNonFinalStagesFlag) {
containerConfig.NonFinalStagesFilter = strconv.FormatBool(excludeNonFinalStages)
}
imageTagFilter, _ := cmd.Flags().GetString(commonParams.ContainersImageTagFilterFlag)
if imageTagFilter != "" {
containerConfig.ImagesFilter = imageTagFilter
}

containerMapConfig[resultsMapValue] = &containerConfig
return containerMapConfig
}

func initializeContainersConfigWithResubmitValues(resubmitConfig []wrappers.Config, containerConfig *wrappers.ContainerConfig) {
for _, config := range resubmitConfig {
if config.Type != commonParams.ContainersType {
continue
}
resubmitFilesFilter := config.Value[ConfigContainersFilesFilterKey]
if resubmitFilesFilter != nil && resubmitFilesFilter != "" {
containerConfig.FilesFilter = resubmitFilesFilter.(string)
}
resubmitPackagesFilter := config.Value[ConfigContainersPackagesFilterKey]
if resubmitPackagesFilter != nil && resubmitPackagesFilter != "" {
containerConfig.PackagesFilter = resubmitPackagesFilter.(string)
}
resubmitNonFinalStagesFilter := config.Value[ConfigContainersNonFinalStagesFilterKey]
if resubmitNonFinalStagesFilter != nil && resubmitNonFinalStagesFilter != "" {
containerConfig.NonFinalStagesFilter = resubmitNonFinalStagesFilter.(string)
}
resubmitImagesFilter := config.Value[ConfigContainersImagesFilterKey]
if resubmitImagesFilter != nil && resubmitImagesFilter != "" {
containerConfig.ImagesFilter = resubmitImagesFilter.(string)
}
}
}

func addAPISecScan(cmd *cobra.Command) map[string]interface{} {
if scanTypeEnabled(commonParams.APISecurityType) {
apiSecMapConfig := make(map[string]interface{})
Expand Down
6 changes: 6 additions & 0 deletions internal/params/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ const (
// SCS (Github)
SCSRepoTokenFlag = "scs-repo-token"
SCSRepoURLFlag = "scs-repo-url"

// Containers Config Flags
ContainersFileFolderFilterFlag = "containers-file-folder-filter"
ContainersImageTagFilterFlag = "containers-image-tag-filter"
ContainersPackageFilterFlag = "containers-package-filter"
ContainersExcludeNonFinalStagesFlag = "containers-exclude-non-final-stages"
)

// Parameter values
Expand Down
4 changes: 4 additions & 0 deletions internal/wrappers/scans.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ type ScaConfig struct {
EnableContainersScan bool `json:"enableContainersScan,omitempty"`
}
type ContainerConfig struct {
FilesFilter string `json:"filesFilter,omitempty"`
ImagesFilter string `json:"imagesFilter,omitempty"`
PackagesFilter string `json:"packagesFilter,omitempty"`
NonFinalStagesFilter string `json:"nonFinalStagesFilter,omitempty"`
}
type APISecConfig struct {
SwaggerFilter string `json:"swaggerFilter,omitempty"`
Expand Down
100 changes: 100 additions & 0 deletions test/integration/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2035,3 +2035,103 @@ func TestCreateScanWithResubmitFlag_ProjectNotExist_ScanCreatedSuccessfullyWithD
err, _ := executeCommand(t, args...)
assert.NilError(t, err)
}
func TestScanCreate_WithContainerFilterFlags_CreatingScanSuccessfully(t *testing.T) {
bindKeysToEnvAndDefault(t)
var createdScan wrappers.ScanResponseModel
var createdScanConfig wrappers.Config
scansPath := viper.GetString(params.ScansPathKey)
scanWrapper := wrappers.NewHTTPScansWrapper(scansPath)

args := []string{
"scan", "create",
flag(params.ProjectName), GenerateRandomProjectNameForScan(),
flag(params.SourcesFlag), ".",
flag(params.ScanTypes), params.ContainersTypeFlag,
flag(params.ContainersFileFolderFilterFlag), "!*.log",
flag(params.ContainersExcludeNonFinalStagesFlag),
flag(params.ContainersImageTagFilterFlag), "*dev",
flag(params.ContainersPackageFilterFlag), "^internal-.*",
flag(params.BranchFlag), "dummy_branch",
flag(params.ScanInfoFormatFlag), printer.FormatJSON,
flag(params.AsyncFlag),
}
scanID, projectID := executeCreateScan(t, args)

mapParams := make(map[string]string)
mapParams["project-id"] = projectID
allScansModel, _, _ := scanWrapper.Get(mapParams)

createdScan = allScansModel.Scans[0]

assert.Assert(t, createdScan.ID == scanID, "Scan ID should be equal")
assert.Equal(t, len(createdScan.Metadata.Configs), 1, "Scan should have only containers config")

createdScanConfig = createdScan.Metadata.Configs[0]

assert.Equal(t, createdScanConfig.Type, params.ContainersType, "Scan type should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersFilesFilterKey], "!*.log", "File/Folder filter should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersNonFinalStagesFilterKey], "true", "Exclude non final stages should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersImagesFilterKey], "*dev", "Image tag filter should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersPackagesFilterKey], "^internal-.*", "Package filter should be equal")

}

func TestScanCreate_WithContainerFilterFlagsAndResubmitFlag_CreatingScanWithLatestScanConfigurationSuccessfully(t *testing.T) {
bindKeysToEnvAndDefault(t)

var createdScan wrappers.ScanResponseModel
var createdScanConfig wrappers.Config

scansPath := viper.GetString(params.ScansPathKey)
scanWrapper := wrappers.NewHTTPScansWrapper(scansPath)

projectName := GenerateRandomProjectNameForScan()

args := []string{
"scan", "create",
flag(params.ProjectName), projectName,
flag(params.SourcesFlag), ".",
flag(params.ScanTypes), params.ContainersTypeFlag,
flag(params.ContainersFileFolderFilterFlag), "!*.log",
flag(params.ContainersImageTagFilterFlag), "*dev",
flag(params.ContainersPackageFilterFlag), "^internal-.*",
flag(params.BranchFlag), "dummy_branch",
flag(params.ScanInfoFormatFlag), printer.FormatJSON,
flag(params.AsyncFlag),
}

_, projectID := executeCreateScan(t, args)

args = []string{
"scan", "create",
flag(params.ProjectName), projectName,
flag(params.SourcesFlag), ".",
flag(params.ScanTypes), params.ContainersTypeFlag,
flag(params.ContainersFileFolderFilterFlag), "!dockerfile",
flag(params.BranchFlag), "dummy_branch",
flag(params.ScanInfoFormatFlag), printer.FormatJSON,
flag(params.AsyncFlag),
flag(params.ScanResubmit),
}

resubmitScanID, resubmitProjectID := executeCreateScan(t, args)

assert.Equal(t, projectID, resubmitProjectID, "Project ID should be equal")

mapParams := make(map[string]string)
mapParams["project-id"] = resubmitProjectID
allScansModel, _, _ := scanWrapper.Get(mapParams)

createdScan = allScansModel.Scans[0]

assert.Assert(t, createdScan.ID == resubmitScanID, "Scan ID should be equal")
assert.Equal(t, len(createdScan.Metadata.Configs), 1, "Scan should have only containers config")

createdScanConfig = createdScan.Metadata.Configs[0]

assert.Equal(t, createdScanConfig.Type, params.ContainersType, "Scan type should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersFilesFilterKey], "!dockerfile", "File/Folder filter should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersNonFinalStagesFilterKey], nil, "Exclude non final stages should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersImagesFilterKey], "*dev", "Image tag filter should be equal")
assert.Equal(t, createdScanConfig.Value[commands.ConfigContainersPackagesFilterKey], "^internal-.*", "Package filter should be equal")
}

0 comments on commit 38ff94d

Please sign in to comment.