diff --git a/internal/commands/scan.go b/internal/commands/scan.go index 501f6c67b..87d6373e9 100644 --- a/internal/commands/scan.go +++ b/internal/commands/scan.go @@ -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" @@ -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 } @@ -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) } @@ -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{}) diff --git a/internal/params/flags.go b/internal/params/flags.go index ae40c6527..62319542d 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -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 diff --git a/internal/wrappers/scans.go b/internal/wrappers/scans.go index 1bdb71c9c..fe47e96e3 100644 --- a/internal/wrappers/scans.go +++ b/internal/wrappers/scans.go @@ -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"` diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index c4c7b610e..0e5418006 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -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") +}