From 76c597d997edc29557438d570f5813f81c9bf146 Mon Sep 17 00:00:00 2001 From: atishj99 Date: Fri, 31 Jul 2026 12:02:21 +0530 Subject: [PATCH 1/3] Add --skip-default-filter flag to scan Introduce a new --skip-default-filter flag to bypass the CLI's base include/exclude file filters. Changes: - Add flag constant and usage (internal/params/flags.go). - Wire the flag into scan create command and log when set (internal/commands/scan.go). - Extend compressFolder and related helpers to accept skipDefaultFilter and honor it when building include/exclude filters; callers updated accordingly. Also adjust unzip condition to preserve container-local-resolution behavior when skipping defaults. - Add unit tests covering filter behavior and zip compression permutations (internal/commands/scan_test.go). - Add integration tests to verify the flag path and log output (test/integration/scan_test.go). This preserves existing behavior by default and enables users to include files normally excluded by base filters (e.g., node_modules, binaries) when explicitly requested. --- internal/commands/scan.go | 29 +++++-- internal/commands/scan_test.go | 145 ++++++++++++++++++++++++++++++++- internal/params/flags.go | 2 + test/integration/scan_test.go | 48 +++++++++++ 4 files changed, 213 insertions(+), 11 deletions(-) diff --git a/internal/commands/scan.go b/internal/commands/scan.go index cc50ab43..7b72b7ac 100644 --- a/internal/commands/scan.go +++ b/internal/commands/scan.go @@ -927,6 +927,7 @@ func scanCreateSubCommand( createScanCmd.PersistentFlags().Bool(commonParams.NoScanFlag, false, "Prevents CxOne scan from running after SBOM is generated locally. Relevant only when --sbom-first is submitted under --sca-resolver-params. Submitting this flag without --sbom-first causes an error.") createScanCmd.PersistentFlags().Bool(commonParams.GitIgnoreFileFilterFlag, false, commonParams.GitIgnoreFileFilterUsage) createScanCmd.PersistentFlags().StringSlice(commonParams.AntFilterFlag, []string{}, commonParams.AntFilterUsage) + createScanCmd.PersistentFlags().Bool(commonParams.SkipDefaultFilterFlag, false, commonParams.SkipDefaultFilterFlagUsage) return createScanCmd } @@ -1643,7 +1644,7 @@ func scanTypeEnabled(scanType string) bool { return false } -func compressFolder(sourceDir, filter, userIncludeFilter, scaResolver string, antMatcher filtering.Matcher) (string, error) { +func compressFolder(sourceDir, filter, userIncludeFilter, scaResolver string, antMatcher filtering.Matcher, skipDefaultFilter bool) (string, error) { scaToolPath := scaResolver outputFile, err := os.CreateTemp(os.TempDir(), "cx-*.zip") if err != nil { @@ -1653,7 +1654,7 @@ func compressFolder(sourceDir, filter, userIncludeFilter, scaResolver string, an zipWriter := zip.NewWriter(outputFile) // First check if the directory is empty or all files are filtered out - isEmpty, err := isDirEmpty(sourceDir, getExcludeFilters(filter), getIncludeFilters(userIncludeFilter), antMatcher) + isEmpty, err := isDirEmpty(sourceDir, getExcludeFilters(filter, skipDefaultFilter), getIncludeFilters(userIncludeFilter, skipDefaultFilter), antMatcher) if err != nil { return "", err } @@ -1671,7 +1672,7 @@ func compressFolder(sourceDir, filter, userIncludeFilter, scaResolver string, an } } else { // Add directory files normally - err = addDirFiles(zipWriter, "", sourceDir, getExcludeFilters(filter), getIncludeFilters(userIncludeFilter), antMatcher) + err = addDirFiles(zipWriter, "", sourceDir, getExcludeFilters(filter, skipDefaultFilter), getIncludeFilters(userIncludeFilter, skipDefaultFilter), antMatcher) if err != nil { return "", err } @@ -1752,11 +1753,17 @@ func isDirEmpty(dir string, excludeFilters, includeFilters []string, antMatcher return empty, err } -func getIncludeFilters(userIncludeFilter string) []string { +func getIncludeFilters(userIncludeFilter string, skipDefaultFilter bool) []string { + if skipDefaultFilter { + return buildFilters([]string{}, userIncludeFilter) + } return buildFilters(commonParams.BaseIncludeFilters, userIncludeFilter) } -func getExcludeFilters(userExcludeFilter string) []string { +func getExcludeFilters(userExcludeFilter string, skipDefaultFilter bool) []string { + if skipDefaultFilter { + return buildFilters([]string{}, userExcludeFilter) + } return buildFilters(commonParams.BaseExcludeFilters, userExcludeFilter) } @@ -2125,6 +2132,10 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW containerImagesFlag, _ := cmd.Flags().GetString(commonParams.ContainerImagesFlag) containerResolveLocally, _ := cmd.Flags().GetBool(commonParams.ContainerResolveLocallyFlag) scaResolverPath, _ := cmd.Flags().GetString(commonParams.ScaResolverFlag) + skipDefaultFilter, _ := cmd.Flags().GetBool(commonParams.SkipDefaultFilterFlag) + if skipDefaultFilter { + logger.PrintIfVerbose("--skip-default-filter set: skipping default base include/exclude file filter.") + } scaResolverParams, scaResolver := getScaResolverFlags(cmd) isSbom, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag) @@ -2190,7 +2201,11 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW var errorUnzippingFile error userProvidedZip := len(zipFilePath) > 0 - unzip := ((sourceDirFilter != "" || userIncludeFilter != "" || len(antPatterns) > 0) || containerScanTriggered) && userProvidedZip + // containerScanTriggered must stay in this condition: without it, a container scan + // run with --containers-local-resolution and --skip-default-filter (and no + // --file-filter/--file-include) would never unzip the zip source, so local container + // resolution would never run. Keeping it here ensures the zip is still unzipped in that case. + unzip := ((sourceDirFilter != "" || userIncludeFilter != "" || len(antPatterns) > 0) || containerScanTriggered || !skipDefaultFilter) && userProvidedZip if unzip { directoryPath, errorUnzippingFile = UnzipFile(zipFilePath) if errorUnzippingFile != nil { @@ -2284,7 +2299,7 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW } } else { if !isSbom { - zipFilePath, dirPathErr = compressFolder(directoryPath, sourceDirFilter, userIncludeFilter, scaResolver, antMatcher) + zipFilePath, dirPathErr = compressFolder(directoryPath, sourceDirFilter, userIncludeFilter, scaResolver, antMatcher, skipDefaultFilter) } // Clean up .checkmarx/containers directory after successful mixed scan (including containers) compression diff --git a/internal/commands/scan_test.go b/internal/commands/scan_test.go index 126e9a91..8b698800 100644 --- a/internal/commands/scan_test.go +++ b/internal/commands/scan_test.go @@ -11,6 +11,7 @@ import ( "os" "path/filepath" "reflect" + "slices" "strings" "testing" @@ -5346,7 +5347,7 @@ func TestSbomFileExcludedFromZip_WithCustomOutputName(t *testing.T) { noopMatcher, matcherErr := filtering.NewAntMatcher(nil) assert.NilError(t, matcherErr) - zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher) + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher, false) assert.NilError(t, err) defer func() { _ = os.Remove(zipPath) }() @@ -5377,7 +5378,7 @@ func TestDefaultSbomFileAlwaysExcludedFromZip(t *testing.T) { noopMatcher, matcherErr := filtering.NewAntMatcher(nil) assert.NilError(t, matcherErr) - zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher) + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher, false) assert.NilError(t, err) defer func() { _ = os.Remove(zipPath) }() @@ -5410,7 +5411,7 @@ func TestSbomFileExcludedFromZip_InSubdirectory(t *testing.T) { noopMatcher, matcherErr := filtering.NewAntMatcher(nil) assert.NilError(t, matcherErr) - zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher) + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher, false) assert.NilError(t, err) defer func() { _ = os.Remove(zipPath) }() @@ -5449,7 +5450,7 @@ func TestSbomFileExcludedFromZip_AbsoluteSubdirWithCustomName(t *testing.T) { noopMatcher, matcherErr := filtering.NewAntMatcher(nil) assert.NilError(t, matcherErr) - zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher) + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher, false) assert.NilError(t, err) defer func() { _ = os.Remove(zipPath) }() @@ -5505,3 +5506,139 @@ func cleanupMockAccessToken() { // Reset to default value (300 seconds as per params/binds.go) viper.Set(commonParams.TokenExpirySecondsKey, 300) } + +// --skip-default-filter tests + +func TestGetFilters_SkipDefaultFilter(t *testing.T) { + assert.DeepEqual(t, getIncludeFilters("*.foo", true), []string{"*.foo"}) + assert.DeepEqual(t, getExcludeFilters("!bar", true), []string{"!bar"}) + + includeDefault := getIncludeFilters("*.foo", false) + assert.Assert(t, slices.Contains(includeDefault, "*.go")) + assert.Assert(t, slices.Contains(includeDefault, "*.foo")) + + excludeDefault := getExcludeFilters("!bar", false) + assert.Assert(t, slices.Contains(excludeDefault, "!node_modules")) + assert.Assert(t, slices.Contains(excludeDefault, "!bar")) +} + +func TestCompressFolder_DefaultBehaviorUnchanged(t *testing.T) { + projectDir, err := os.MkdirTemp("", "skip-default-filter-off-*") + assert.NilError(t, err) + defer func() { _ = os.RemoveAll(projectDir) }() + + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "main.go"), []byte("package main"), 0600)) + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "asset.bin"), []byte("binary"), 0600)) + nodeModulesDir := filepath.Join(projectDir, "node_modules") + assert.NilError(t, os.MkdirAll(nodeModulesDir, 0700)) + assert.NilError(t, os.WriteFile(filepath.Join(nodeModulesDir, "lib.js"), []byte("//lib"), 0600)) + + noopMatcher, matcherErr := filtering.NewAntMatcher(nil) + assert.NilError(t, matcherErr) + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher, false) + assert.NilError(t, err) + defer func() { _ = os.Remove(zipPath) }() + + assert.Equal(t, true, zipContainsFile(t, zipPath, "main.go")) + assert.Equal(t, false, zipContainsFile(t, zipPath, "asset.bin")) + assert.Equal(t, false, zipContainsFile(t, zipPath, "lib.js")) +} + +func TestCompressFolder_SkipDefaultFilter(t *testing.T) { + projectDir, err := os.MkdirTemp("", "skip-default-filter-on-*") + assert.NilError(t, err) + defer func() { _ = os.RemoveAll(projectDir) }() + + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "asset.bin"), []byte("binary"), 0600)) + nodeModulesDir := filepath.Join(projectDir, "node_modules") + assert.NilError(t, os.MkdirAll(nodeModulesDir, 0700)) + assert.NilError(t, os.WriteFile(filepath.Join(nodeModulesDir, "lib.js"), []byte("//lib"), 0600)) + + noopMatcher, matcherErr := filtering.NewAntMatcher(nil) + assert.NilError(t, matcherErr) + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", noopMatcher, true) + assert.NilError(t, err) + defer func() { _ = os.Remove(zipPath) }() + + assert.Equal(t, true, zipContainsFile(t, zipPath, "asset.bin")) + assert.Equal(t, true, zipContainsFile(t, zipPath, "lib.js")) +} + +func TestCreateScanSkipDefaultFilter_Wiring(t *testing.T) { + execCmdNilAssertion(t, + "scan", "create", "--project-name", "MOCK", "-s", "data", "-b", "dummy_branch", + "--skip-default-filter", + ) +} + +// skip-default-filter bypasses base filters, ant exclude pattern still applies. +func TestCompressFolder_SkipDefaultFilter_WithAntFilterExclude(t *testing.T) { + projectDir, err := os.MkdirTemp("", "skip-default-filter-ant-exclude-*") + assert.NilError(t, err) + defer func() { _ = os.RemoveAll(projectDir) }() + + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "main.go"), []byte("package main"), 0600)) + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "asset.customext"), []byte("data"), 0600)) + excludedDir := filepath.Join(projectDir, "excluded_by_ant") + assert.NilError(t, os.MkdirAll(excludedDir, 0700)) + assert.NilError(t, os.WriteFile(filepath.Join(excludedDir, "marker.go"), []byte("package excluded"), 0600)) + + antMatcher, matcherErr := filtering.NewAntMatcher([]string{"!excluded_by_ant/**"}) + assert.NilError(t, matcherErr) + + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", antMatcher, true) + assert.NilError(t, err) + defer func() { _ = os.Remove(zipPath) }() + + assert.Equal(t, true, zipContainsFile(t, zipPath, "main.go")) + assert.Equal(t, true, zipContainsFile(t, zipPath, "asset.customext")) + assert.Equal(t, false, zipContainsFile(t, zipPath, "marker.go")) +} + +// skip-default-filter with an ant include-only pattern drops non-matching files too. +func TestCompressFolder_SkipDefaultFilter_WithAntFilterIncludeOnly(t *testing.T) { + projectDir, err := os.MkdirTemp("", "skip-default-filter-ant-include-*") + assert.NilError(t, err) + defer func() { _ = os.RemoveAll(projectDir) }() + + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "main.go"), []byte("package main"), 0600)) + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "asset.customext"), []byte("data"), 0600)) + + antMatcher, matcherErr := filtering.NewAntMatcher([]string{"**/*.customext"}) + assert.NilError(t, matcherErr) + + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", antMatcher, true) + assert.NilError(t, err) + defer func() { _ = os.Remove(zipPath) }() + + assert.Equal(t, true, zipContainsFile(t, zipPath, "asset.customext")) + assert.Equal(t, false, zipContainsFile(t, zipPath, "main.go")) +} + +// file-filter-ext without skip-default-filter: base filters and the ant filter both apply. +func TestCompressFolder_DefaultFilters_WithAntFilter(t *testing.T) { + projectDir, err := os.MkdirTemp("", "default-filter-with-ant-*") + assert.NilError(t, err) + defer func() { _ = os.RemoveAll(projectDir) }() + + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "main.go"), []byte("package main"), 0600)) + assert.NilError(t, os.WriteFile(filepath.Join(projectDir, "asset.customext"), []byte("data"), 0600)) + nodeModulesDir := filepath.Join(projectDir, "node_modules") + assert.NilError(t, os.MkdirAll(nodeModulesDir, 0700)) + assert.NilError(t, os.WriteFile(filepath.Join(nodeModulesDir, "lib.js"), []byte("//lib"), 0600)) + keepDir := filepath.Join(projectDir, "keep_dir") + assert.NilError(t, os.MkdirAll(keepDir, 0700)) + assert.NilError(t, os.WriteFile(filepath.Join(keepDir, "marker.go"), []byte("package keep"), 0600)) + + antMatcher, matcherErr := filtering.NewAntMatcher([]string{"!keep_dir/**"}) + assert.NilError(t, matcherErr) + + zipPath, err := compressFolder(sbomTestSourceDir(projectDir), "", "", "", antMatcher, false) + assert.NilError(t, err) + defer func() { _ = os.Remove(zipPath) }() + + assert.Equal(t, true, zipContainsFile(t, zipPath, "main.go")) + assert.Equal(t, false, zipContainsFile(t, zipPath, "asset.customext")) + assert.Equal(t, false, zipContainsFile(t, zipPath, "lib.js")) + assert.Equal(t, false, zipContainsFile(t, zipPath, "marker.go")) +} diff --git a/internal/params/flags.go b/internal/params/flags.go index 08e628a6..9415101b 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -196,6 +196,8 @@ const ( LogFileUsage = "Saves logs to the specified file path only" LogFileConsoleFlag = "log-file-console" LogFileConsoleUsage = "Saves logs to the specified file path as well as to the console" + SkipDefaultFilterFlag = "skip-default-filter" + SkipDefaultFilterFlagUsage = "Skip the default file filter." GitIgnoreFileFilterFlag = "use-gitignore" GitIgnoreFileFilterUsage = "Exclude files and directories from the scan based on the patterns defined in the directory's .gitignore file" AntFilterFlag = "file-filter-ext" diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index 19333447..76d970b0 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -2950,3 +2950,51 @@ func TestScanCreateIncludeFilterIsCaseInsensitive(t *testing.T) { "uppercase --file-include pattern *.TXT should still match lowercase .txt files on disk", ) } + +// Directory source with --skip-default-filter should scan successfully +func TestScanCreateSkipDefaultFilterDirectory(t *testing.T) { + args := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), Dir, + flag(params.ScanTypes), params.SastType, + flag(params.SkipDefaultFilterFlag), + flag(params.BranchFlag), "dummy_branch", + flag(params.DebugFlag), + } + + // Capture log output to assert the skip-default-filter code path actually ran + var buf bytes.Buffer + log.SetOutput(&buf) + defer log.SetOutput(os.Stderr) + + executeCmdWithTimeOutNilAssertion(t, "Skip default filter scan should complete", timeout, args...) + + assert.Assert(t, strings.Contains(buf.String(), + "--skip-default-filter set: skipping default base include/exclude file filter."), + "expected skip-default-filter log line to be printed") +} + +// Zip source with --skip-default-filter should scan successfully +func TestScanCreateSkipDefaultFilterZip(t *testing.T) { + args := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.SourcesFlag), Zip, + flag(params.ScanTypes), params.SastType, + flag(params.SkipDefaultFilterFlag), + flag(params.BranchFlag), "dummy_branch", + flag(params.DebugFlag), + } + + // Capture log output to assert the skip-default-filter code path actually ran + var buf bytes.Buffer + log.SetOutput(&buf) + defer log.SetOutput(os.Stderr) + + executeCmdWithTimeOutNilAssertion(t, "Skip default filter zip scan should complete", timeout, args...) + + assert.Assert(t, strings.Contains(buf.String(), + "--skip-default-filter set: skipping default base include/exclude file filter."), + "expected skip-default-filter log line to be printed") +} From da1f4cd548b3752bd64cc9c7ac6bdc8ea3c3e353 Mon Sep 17 00:00:00 2001 From: atishj99 Date: Fri, 31 Jul 2026 14:14:19 +0530 Subject: [PATCH 2/3] Add timeout wrapper for SCA realtime integration test Introduce executeCommandWithTimeout in test/integration/util_command.go and update TestRunScaRealtimeScan in test/integration/scan_test.go to use it with a 15-minute timeout. This avoids flaky failures/timeouts during SCA resolver downloads by allowing longer execution time while preserving the existing error+output buffer behavior. --- test/integration/scan_test.go | 4 ++-- test/integration/util_command.go | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index 76d970b0..5b91367b 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -1377,7 +1377,7 @@ func TestRunKicsScanWithAdditionalParams(t *testing.T) { func TestRunScaRealtimeScan(t *testing.T) { args := []string{scanCommand, "sca-realtime", "--project-dir", projectDirectory} - err, _ := executeCommand(t, args...) + err, _ := executeCommandWithTimeout(t, 15*time.Minute, args...) assert.NilError(t, err) // Ensure we have results to read @@ -1388,7 +1388,7 @@ func TestRunScaRealtimeScan(t *testing.T) { assert.NilError(t, err) // Run second time to cover SCA Resolver download not needed code - err, _ = executeCommand(t, args...) + err, _ = executeCommandWithTimeout(t, 15*time.Minute, args...) assert.NilError(t, err) } diff --git a/test/integration/util_command.go b/test/integration/util_command.go index 45cc1ed7..bc48fc81 100644 --- a/test/integration/util_command.go +++ b/test/integration/util_command.go @@ -205,6 +205,16 @@ func executeCommand(t *testing.T, args ...string) (error, *bytes.Buffer) { return err, buffer } +// Execute a CLI command with custom timeout, expecting an error and buffer to execute post assertions +func executeCommandWithTimeout(t *testing.T, timeout time.Duration, args ...string) (error, *bytes.Buffer) { + + cmd, buffer := createRedirectedTestCommand(t) + + err := executeWithTimeout(cmd, timeout, args...) + + return err, buffer +} + // Execute a CLI command with nil error assertion func executeCmdNilAssertion(t *testing.T, infoMsg string, args ...string) *bytes.Buffer { cmd, outputBuffer := createRedirectedTestCommand(t) From 67f9c0dce6cfb23b36e86d61a572abe586bccc2f Mon Sep 17 00:00:00 2001 From: atishj99 Date: Fri, 31 Jul 2026 16:14:54 +0530 Subject: [PATCH 3/3] Revert "Add timeout wrapper for SCA realtime integration test" This reverts commit da1f4cd548b3752bd64cc9c7ac6bdc8ea3c3e353. --- test/integration/scan_test.go | 4 ++-- test/integration/util_command.go | 10 ---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index 5b91367b..76d970b0 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -1377,7 +1377,7 @@ func TestRunKicsScanWithAdditionalParams(t *testing.T) { func TestRunScaRealtimeScan(t *testing.T) { args := []string{scanCommand, "sca-realtime", "--project-dir", projectDirectory} - err, _ := executeCommandWithTimeout(t, 15*time.Minute, args...) + err, _ := executeCommand(t, args...) assert.NilError(t, err) // Ensure we have results to read @@ -1388,7 +1388,7 @@ func TestRunScaRealtimeScan(t *testing.T) { assert.NilError(t, err) // Run second time to cover SCA Resolver download not needed code - err, _ = executeCommandWithTimeout(t, 15*time.Minute, args...) + err, _ = executeCommand(t, args...) assert.NilError(t, err) } diff --git a/test/integration/util_command.go b/test/integration/util_command.go index bc48fc81..45cc1ed7 100644 --- a/test/integration/util_command.go +++ b/test/integration/util_command.go @@ -205,16 +205,6 @@ func executeCommand(t *testing.T, args ...string) (error, *bytes.Buffer) { return err, buffer } -// Execute a CLI command with custom timeout, expecting an error and buffer to execute post assertions -func executeCommandWithTimeout(t *testing.T, timeout time.Duration, args ...string) (error, *bytes.Buffer) { - - cmd, buffer := createRedirectedTestCommand(t) - - err := executeWithTimeout(cmd, timeout, args...) - - return err, buffer -} - // Execute a CLI command with nil error assertion func executeCmdNilAssertion(t *testing.T, infoMsg string, args ...string) *bytes.Buffer { cmd, outputBuffer := createRedirectedTestCommand(t)