@@ -1473,16 +1473,39 @@ func compressFolder(sourceDir, filter, userIncludeFilter, scaResolver string) (s
14731473 }
14741474 defer outputFile .Close ()
14751475 zipWriter := zip .NewWriter (outputFile )
1476- err = addDirFiles (zipWriter , "" , sourceDir , getExcludeFilters (filter ), getIncludeFilters (userIncludeFilter ))
1476+
1477+ // First check if the directory is empty or all files are filtered out
1478+ isEmpty , err := isDirEmpty (sourceDir , getExcludeFilters (filter ), getIncludeFilters (userIncludeFilter ))
14771479 if err != nil {
14781480 return "" , err
14791481 }
1482+
1483+ // If directory is effectively empty, add a placeholder file
1484+ if isEmpty {
1485+ logger .PrintIfVerbose ("Directory is empty or all files are filtered out, adding placeholder file" )
1486+ f , err := zipWriter .Create (".container-scan" )
1487+ if err != nil {
1488+ return "" , errors .Wrapf (err , "Cannot create placeholder file in zip" )
1489+ }
1490+ _ , err = f .Write ([]byte ("1" ))
1491+ if err != nil {
1492+ return "" , errors .Wrapf (err , "Cannot write to placeholder file" )
1493+ }
1494+ } else {
1495+ // Add directory files normally
1496+ err = addDirFiles (zipWriter , "" , sourceDir , getExcludeFilters (filter ), getIncludeFilters (userIncludeFilter ))
1497+ if err != nil {
1498+ return "" , err
1499+ }
1500+ }
1501+
14801502 if len (scaToolPath ) > 0 && len (scaResolverResultsFile ) > 0 {
14811503 err = addScaResults (zipWriter )
14821504 if err != nil {
14831505 return "" , err
14841506 }
14851507 }
1508+
14861509 // Close the file
14871510 err = zipWriter .Close ()
14881511 if err != nil {
@@ -1501,6 +1524,44 @@ func isSingleContainerScanTriggered() bool {
15011524 return len (scanTypeList ) == 1 && scanTypeList [0 ] == commonParams .ContainersType
15021525}
15031526
1527+ // isDirEmpty checks if a directory is empty or if all files are filtered out
1528+ func isDirEmpty (dir string , excludeFilters , includeFilters []string ) (bool , error ) {
1529+ empty := true
1530+
1531+ err := filepath .Walk (dir , func (path string , info os.FileInfo , err error ) error {
1532+ if err != nil {
1533+ return err
1534+ }
1535+
1536+ // Skip the root directory itself
1537+ if path == dir {
1538+ return nil
1539+ }
1540+
1541+ // Skip directories
1542+ if info .IsDir () {
1543+ return nil
1544+ }
1545+
1546+ // Get relative path
1547+ relPath , err := filepath .Rel (dir , path )
1548+ if err != nil {
1549+ return err
1550+ }
1551+
1552+ // Check if file passes filters
1553+ filename := filepath .Base (relPath )
1554+ if filterMatched (includeFilters , filename ) && filterMatched (excludeFilters , filename ) {
1555+ empty = false
1556+ return filepath .SkipAll // We found at least one file that will be included
1557+ }
1558+
1559+ return nil
1560+ })
1561+
1562+ return empty , err
1563+ }
1564+
15041565func getIncludeFilters (userIncludeFilter string ) []string {
15051566 return buildFilters (commonParams .BaseIncludeFilters , userIncludeFilter )
15061567}
@@ -1836,17 +1897,6 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW
18361897 logger .PrintIfVerbose ("Single container scan triggered: compressing only the container resolution file" )
18371898 containerResolutionFilePath := filepath .Join (directoryPath , ".checkmarx" , "containers" , containerResolutionFileName )
18381899 zipFilePath , dirPathErr = util .CompressFile (containerResolutionFilePath , containerResolutionFileName , directoryCreationPrefix )
1839- } else if isSingleContainerScanTriggered () && containerImagesFlag != "" {
1840- logger .PrintIfVerbose ("Single container scan with external images: creating minimal zip file" )
1841- // For container scans with external images, we need to create a minimal zip file
1842- // since the API requires an upload URL even for container-only scans
1843- zipFilePath , dirPathErr = createMinimalZipFile ()
1844- if unzip {
1845- dirRemovalErr := cleanTempUnzipDirectory (directoryPath )
1846- if dirRemovalErr != nil {
1847- return "" , "" , dirRemovalErr
1848- }
1849- }
18501900 } else {
18511901 if ! isSbom {
18521902 zipFilePath , dirPathErr = compressFolder (directoryPath , sourceDirFilter , userIncludeFilter , scaResolver )
@@ -3239,33 +3289,6 @@ func parseArgs(input string) []string {
32393289 return args
32403290}
32413291
3242- // createMinimalZipFile creates a minimal zip file for container scans with external images
3243- // The API requires an upload URL even for container-only scans, so we create a minimal placeholder
3244- func createMinimalZipFile () (string , error ) {
3245- outputFile , err := os .CreateTemp (os .TempDir (), "cx-container-*.zip" )
3246- if err != nil {
3247- return "" , errors .Wrapf (err , "Cannot create temp file for container-only scan" )
3248- }
3249- defer outputFile .Close ()
3250-
3251- zipWriter := zip .NewWriter (outputFile )
3252- defer zipWriter .Close ()
3253-
3254- // Create a minimal placeholder file
3255- f , err := zipWriter .Create (".container-scan" )
3256- if err != nil {
3257- return "" , errors .Wrapf (err , "Cannot create placeholder file in zip" )
3258- }
3259-
3260- // Write minimal content (just a single byte)
3261- _ , err = f .Write ([]byte ("1" ))
3262- if err != nil {
3263- return "" , errors .Wrapf (err , "Cannot write to placeholder file" )
3264- }
3265-
3266- return outputFile .Name (), nil
3267- }
3268-
32693292func isValidJSONOrXML (path string ) (bool , error ) {
32703293 ext := strings .ToLower (filepath .Ext (path ))
32713294 if ext != jsonExt && ext != xmlExt {
0 commit comments