Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
76c597d
Add --skip-default-filter flag to scan
cx-atish-jadhav Jul 31, 2026
da1f4cd
Add timeout wrapper for SCA realtime integration test
cx-atish-jadhav Jul 31, 2026
67f9c0d
Revert "Add timeout wrapper for SCA realtime integration test"
cx-atish-jadhav Jul 31, 2026
8d2e25d
Run SCA Realtime tests in isolated CI job
cx-atish-jadhav Aug 3, 2026
18279ce
Skip SCA realtime test and remove isolated CI job
cx-atish-jadhav Aug 3, 2026
e2561f1
fixing validate in integration check
cx-atish-jadhav Aug 3, 2026
3b96611
Remove unnecessary check
cx-atish-jadhav Aug 4, 2026
0cb06e4
Skip flaky sca-realtime integration test
cx-atish-jadhav Aug 4, 2026
2c17eaa
Fix t.Skip call in TestRunScaRealtimeScan
cx-atish-jadhav Aug 4, 2026
8278e84
Add .trivyignore entry for CVE-2026-58055
cx-atish-jadhav Aug 4, 2026
cf88a5f
AST-164154: Fix KICS agent-hook guardrail never scanning (empty conta…
cx-anurag-dalke Aug 5, 2026
453d5ff
Add --skip-default-filter flag to scan
cx-atish-jadhav Jul 31, 2026
48162d7
Add timeout wrapper for SCA realtime integration test
cx-atish-jadhav Jul 31, 2026
7a407c9
Revert "Add timeout wrapper for SCA realtime integration test"
cx-atish-jadhav Jul 31, 2026
5673478
Run SCA Realtime tests in isolated CI job
cx-atish-jadhav Aug 3, 2026
8f798c7
Skip SCA realtime test and remove isolated CI job
cx-atish-jadhav Aug 3, 2026
7ae5cf7
fixing validate in integration check
cx-atish-jadhav Aug 3, 2026
6344831
Remove unnecessary check
cx-atish-jadhav Aug 4, 2026
1f0c154
Skip flaky sca-realtime integration test
cx-atish-jadhav Aug 4, 2026
546271d
Fix t.Skip call in TestRunScaRealtimeScan
cx-atish-jadhav Aug 4, 2026
0ff563c
Add .trivyignore entry for CVE-2026-58055
cx-atish-jadhav Aug 4, 2026
18090c4
Merge branch 'bug/AST-154378-skipDefaultFilter' of https://github.com…
cx-atish-jadhav Aug 6, 2026
5a67573
trivy fixes
cx-atish-jadhav Aug 6, 2026
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
31 changes: 0 additions & 31 deletions .github/workflows/scan-github-action.yml

This file was deleted.

9 changes: 9 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,12 @@ CVE-2026-48978 exp:2026-12-31
# Risk: Low - affects base OS image, not application code
# Impact: Minimal - only affects base OS components, application uses glibc runtime only
CVE-2026-6791 exp:2026-12-31

# CVE-2026-58055 (MEDIUM): libnghttp2 HTTP Request/Response Smuggling
# Library: libnghttp2-14 v1.69.0-r0
# Image: checkmarx/bash:5.3-r12 (base image)
# Status: Fixed in libnghttp2-14 >= 1.70.0-r0
# Risk: MEDIUM - HTTP/1.1 Upgrade smuggling potential
# Impact: Awaiting checkmarx/bash base image patch
# Tracking: AST-166372
CVE-2026-58055 exp:2027-02-28
28 changes: 21 additions & 7 deletions internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -1752,11 +1753,19 @@ 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 {
logger.PrintIfVerbose("--skip-default-filter set: skipping default base include file filter.")
return buildFilters([]string{}, userIncludeFilter)
}
return buildFilters(commonParams.BaseIncludeFilters, userIncludeFilter)
}

func getExcludeFilters(userExcludeFilter string) []string {
func getExcludeFilters(userExcludeFilter string, skipDefaultFilter bool) []string {
if skipDefaultFilter {
logger.PrintIfVerbose("--skip-default-filter set: skipping default base exclude file filter.")
return buildFilters([]string{}, userExcludeFilter)
}
return buildFilters(commonParams.BaseExcludeFilters, userExcludeFilter)
}

Expand Down Expand Up @@ -2125,6 +2134,7 @@ 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)

scaResolverParams, scaResolver := getScaResolverFlags(cmd)
isSbom, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
Expand Down Expand Up @@ -2190,7 +2200,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 {
Expand Down Expand Up @@ -2284,7 +2298,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
Expand Down
145 changes: 141 additions & 4 deletions internal/commands/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"reflect"
"slices"
"strings"
"testing"

Expand Down Expand Up @@ -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) }()

Expand Down Expand Up @@ -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) }()

Expand Down Expand Up @@ -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) }()

Expand Down Expand Up @@ -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) }()

Expand Down Expand Up @@ -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"))
}
2 changes: 2 additions & 0 deletions internal/params/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
55 changes: 55 additions & 0 deletions test/integration/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,7 @@ func TestRunKicsScanWithAdditionalParams(t *testing.T) {
}

func TestRunScaRealtimeScan(t *testing.T) {
t.Skip("Skip this test cases due to context deadline exceeded")
args := []string{scanCommand, "sca-realtime", "--project-dir", projectDirectory}

err, _ := executeCommand(t, args...)
Expand Down Expand Up @@ -2950,3 +2951,57 @@ 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 exclude file filter."),
"expected skip-default-filter log line to be printed")
assert.Assert(t, strings.Contains(buf.String(),
"--skip-default-filter set: skipping default base include 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 exclude file filter."),
"The skip-default-filter log line should not be printed as expected; however, the ZIP file is not being extracted because the --skip-default-filter flag is passed.")
assert.Assert(t, !strings.Contains(buf.String(),
"--skip-default-filter set: skipping default base include file filter."),
"The skip-default-filter log line should not be printed as expected; however, the ZIP file is not being extracted because the --skip-default-filter flag is passed.")
}
Loading