@@ -43,17 +43,19 @@ import (
4343)
4444
4545const (
46- failedCreating = "Failed creating a scan"
47- failedGetting = "Failed showing a scan"
48- failedGettingTags = "Failed getting tags"
49- failedDeleting = "Failed deleting a scan"
50- failedCanceling = "Failed canceling a scan"
51- failedGettingAll = "Failed listing"
52- thresholdLog = "%s: Limit = %d, Current = %v"
53- thresholdMsgLog = "Threshold check finished with status %s : %s"
54- mbBytes = 1024.0 * 1024.0
55- notExploitable = "NOT_EXPLOITABLE"
56- ignored = "IGNORED"
46+ failedCreating = "Failed creating a scan"
47+ failedGetting = "Failed showing a scan"
48+ failedGettingTags = "Failed getting tags"
49+ failedDeleting = "Failed deleting a scan"
50+ failedCanceling = "Failed canceling a scan"
51+ failedGettingAll = "Failed listing"
52+ thresholdLog = "%s: Limit = %d, Current = %v"
53+ thresholdMsgLog = "Threshold check finished with status %s : %s"
54+ mbBytes = 1024.0 * 1024.0
55+ notExploitable = "NOT_EXPLOITABLE"
56+ ignored = "IGNORED"
57+ minWindowsPathLength = 3
58+ containerImagesFlagError = "--container-images flag error"
5759
5860 git = "git"
5961 invalidSSHSource = "provided source does not need a key. Make sure you are defining the right source or remove the flag --ssh-key"
@@ -1262,7 +1264,7 @@ func addContainersScan(cmd *cobra.Command, resubmitConfig []wrappers.Config) (ma
12621264 continue
12631265 }
12641266 if containerImagesErr := validateContainerImageFormat (containerImageName ); containerImagesErr != nil {
1265- errorMsg := strings .TrimPrefix (containerImagesErr .Error (), "--container-images flag error : " )
1267+ errorMsg := strings .TrimPrefix (containerImagesErr .Error (), containerImagesFlagError + " : " )
12661268 validationErrors = append (validationErrors , fmt .Sprintf ("User input: '%s' error: %s" , containerImageName , errorMsg ))
12671269 }
12681270 }
@@ -2322,20 +2324,11 @@ func enforceLocalResolutionForTarFiles(cmd *cobra.Command) error {
23222324}
23232325
23242326// isTarFileReference checks if a container image reference points to a tar file.
2325- // Container-security scan-type related function .
2327+ // Handles both Unix and Windows paths (e.g., C:\path\file.tar) .
23262328func isTarFileReference (imageRef string ) bool {
2327- // Known prefixes that might precede the actual file path
2328- knownPrefixes := []string {
2329- dockerArchivePrefix ,
2330- ociArchivePrefix ,
2331- filePrefix ,
2332- ociDirPrefix ,
2333- }
2329+ knownPrefixes := []string {dockerArchivePrefix , ociArchivePrefix , filePrefix , ociDirPrefix }
23342330
2335- // First, trim quotes from the entire input
23362331 actualRef := strings .Trim (imageRef , "'\" " )
2337-
2338- // Strip known prefixes to get the actual reference
23392332 for _ , prefix := range knownPrefixes {
23402333 if strings .HasPrefix (actualRef , prefix ) {
23412334 actualRef = strings .TrimPrefix (actualRef , prefix )
@@ -2344,31 +2337,35 @@ func isTarFileReference(imageRef string) bool {
23442337 }
23452338 }
23462339
2347- // Check if the reference ends with .tar (case-insensitive)
23482340 lowerRef := strings .ToLower (actualRef )
2349-
2350- // If it ends with .tar, it's a tar file (no tag suffix allowed)
23512341 if strings .HasSuffix (lowerRef , ".tar" ) {
23522342 return true
23532343 }
23542344
2355- // If it contains a colon but doesn't end with .tar, check if it's a file.tar:tag format (invalid)
2356- // A tar file cannot have a tag suffix like file.tar:tag
2345+ if isWindowsAbsolutePath (actualRef ) {
2346+ return strings .Contains (lowerRef , ".tar" )
2347+ }
2348+
23572349 if strings .Contains (actualRef , ":" ) {
23582350 parts := strings .Split (actualRef , ":" )
2359- const minPartsForTaggedImage = 2
2360- if len (parts ) >= minPartsForTaggedImage {
2361- firstPart := strings .ToLower (parts [0 ])
2362- // If the part before the colon is a tar file, this is invalid (tar files don't have tags)
2363- if strings .HasSuffix (firstPart , ".tar" ) {
2364- return false
2365- }
2351+ if len (parts ) >= 2 && strings .HasSuffix (strings .ToLower (parts [0 ]), ".tar" ) {
2352+ return false
23662353 }
23672354 }
23682355
23692356 return false
23702357}
23712358
2359+ // isWindowsAbsolutePath checks for Windows drive letter paths (e.g., C:\, D:/).
2360+ func isWindowsAbsolutePath (path string ) bool {
2361+ if len (path ) < minWindowsPathLength {
2362+ return false
2363+ }
2364+ firstChar := path [0 ]
2365+ isLetter := (firstChar >= 'A' && firstChar <= 'Z' ) || (firstChar >= 'a' && firstChar <= 'z' )
2366+ return isLetter && path [1 ] == ':' && (path [2 ] == '\\' || path [2 ] == '/' )
2367+ }
2368+
23722369func runCreateScanCommand (
23732370 scansWrapper wrappers.ScansWrapper ,
23742371 exportWrapper wrappers.ExportWrapper ,
@@ -3586,7 +3583,7 @@ const (
35863583// Container-security scan-type related function.
35873584// This function implements comprehensive validation logic for all supported container image formats:
35883585// - Standard image:tag format
3589- // - Tar files (.tar)
3586+ // - Tar files (.tar) - including full file paths on Windows (C:\path\file.tar) and Unix (/path/file.tar)
35903587// - Prefixed formats (docker:, podman:, containerd:, registry:, docker-archive:, oci-archive:, oci-dir:, file:)
35913588// It provides helpful error messages and hints for common user mistakes.
35923589func validateContainerImageFormat (containerImage string ) error {
@@ -3626,6 +3623,11 @@ func validateContainerImageFormat(containerImage string) error {
36263623 sanitizedInput = containerImage
36273624 }
36283625
3626+ // Check if this looks like a file path before parsing colons
3627+ if looksLikeFilePath (sanitizedInput ) {
3628+ return validateFilePath (sanitizedInput )
3629+ }
3630+
36293631 // Step 2: Look for the last colon (:) in the sanitized input
36303632 lastColonIndex := strings .LastIndex (sanitizedInput , ":" )
36313633
@@ -3661,23 +3663,22 @@ func validateContainerImageFormat(containerImage string) error {
36613663 // It's a tar file - check if it exists locally
36623664 exists , err := osinstaller .FileExists (sanitizedInput )
36633665 if err != nil {
3664- return errors .Errorf ("--container-images flag error : %v" , err )
3666+ return errors .Errorf ("%s : %v" , containerImagesFlagError , err )
36653667 }
36663668 if ! exists {
3667- return errors .Errorf ("--container-images flag error : file '%s' does not exist" , sanitizedInput )
3669+ return errors .Errorf ("%s : file '%s' does not exist" , containerImagesFlagError , sanitizedInput )
36683670 }
36693671 return nil // Valid tar file
36703672 }
36713673
36723674 // Check for compressed tar files
3673- if strings .HasSuffix (lowerInput , ".tar.gz" ) || strings .HasSuffix (lowerInput , ".tar.bz2" ) ||
3674- strings .HasSuffix (lowerInput , ".tar.xz" ) || strings .HasSuffix (lowerInput , ".tgz" ) {
3675- return errors .Errorf ("--container-images flag error: file '%s' is compressed, use non-compressed format (tar)" , sanitizedInput )
3675+ if isCompressedTarFile (sanitizedInput ) {
3676+ return errors .Errorf ("%s: file '%s' is compressed, use non-compressed format (tar)" , containerImagesFlagError , sanitizedInput )
36763677 }
36773678
36783679 // Check if it looks like a tar file extension (contains ".tar." but not a valid extension)
36793680 if strings .Contains (lowerInput , ".tar." ) {
3680- return errors .Errorf ("--container-images flag error : image does not have a tag. Did you try to scan a tar file?" )
3681+ return errors .Errorf ("%s : image does not have a tag. Did you try to scan a tar file?" , containerImagesFlagError )
36813682 }
36823683
36833684 // Step 4: Special handling for prefixes that don't require tags (e.g., oci-dir:)
@@ -3694,7 +3695,69 @@ func validateContainerImageFormat(containerImage string) error {
36943695 }
36953696
36963697 // Step 5: Not a tar file, no special prefix, and no colon - assume user forgot to add tag (error)
3697- return errors .Errorf ("--container-images flag error: image does not have a tag" )
3698+ return errors .Errorf ("%s: image does not have a tag" , containerImagesFlagError )
3699+ }
3700+
3701+ // isCompressedTarFile checks if the given path has a compressed tar file extension.
3702+ func isCompressedTarFile (path string ) bool {
3703+ lowerPath := strings .ToLower (path )
3704+ return strings .HasSuffix (lowerPath , ".tar.gz" ) || strings .HasSuffix (lowerPath , ".tar.bz2" ) ||
3705+ strings .HasSuffix (lowerPath , ".tar.xz" ) || strings .HasSuffix (lowerPath , ".tgz" )
3706+ }
3707+
3708+ // looksLikeFilePath checks if input looks like a file path rather than image:tag.
3709+ func looksLikeFilePath (input string ) bool {
3710+ lowerInput := strings .ToLower (input )
3711+
3712+ if isWindowsAbsolutePath (input ) {
3713+ return true
3714+ }
3715+
3716+ // If colon exists and part before it looks like a prefix (no separators/dots), it's not a file path
3717+ if colonIndex := strings .Index (input , ":" ); colonIndex > 0 {
3718+ beforeColon := input [:colonIndex ]
3719+ if ! strings .Contains (beforeColon , "/" ) && ! strings .Contains (beforeColon , "\\ " ) && ! strings .Contains (beforeColon , "." ) {
3720+ return false
3721+ }
3722+ }
3723+
3724+ if strings .HasSuffix (lowerInput , ".tar" ) {
3725+ return true
3726+ }
3727+
3728+ if isCompressedTarFile (input ) {
3729+ return true
3730+ }
3731+
3732+ hasPathSeparators := strings .Contains (input , "/" ) || strings .Contains (input , "\\ " )
3733+ if hasPathSeparators && strings .Contains (lowerInput , ".tar" ) {
3734+ return true
3735+ }
3736+
3737+ return false
3738+ }
3739+
3740+ // validateFilePath validates file path input for tar files.
3741+ func validateFilePath (filePath string ) error {
3742+ lowerPath := strings .ToLower (filePath )
3743+
3744+ if isCompressedTarFile (filePath ) {
3745+ return errors .Errorf ("%s: file '%s' is compressed, use non-compressed format (tar)" , containerImagesFlagError , filePath )
3746+ }
3747+
3748+ if ! strings .HasSuffix (lowerPath , ".tar" ) {
3749+ return errors .Errorf ("%s: file '%s' is not a valid tar file. Expected .tar extension" , containerImagesFlagError , filePath )
3750+ }
3751+
3752+ exists , err := osinstaller .FileExists (filePath )
3753+ if err != nil {
3754+ return errors .Errorf ("%s: %v" , containerImagesFlagError , err )
3755+ }
3756+ if ! exists {
3757+ return errors .Errorf ("%s: file '%s' does not exist" , containerImagesFlagError , filePath )
3758+ }
3759+
3760+ return nil
36983761}
36993762
37003763// getPrefixFromInput extracts the prefix from a container image reference.
@@ -3744,14 +3807,14 @@ func validatePrefixedContainerImage(containerImage, prefix string) error {
37443807func validateArchivePrefix (imageRef string ) error {
37453808 exists , err := osinstaller .FileExists (imageRef )
37463809 if err != nil {
3747- return errors .Errorf ("--container-images flag error : %v" , err )
3810+ return errors .Errorf ("%s : %v" , containerImagesFlagError , err )
37483811 }
37493812 if ! exists {
37503813 // Check if user mistakenly used archive prefix with an image name:tag format
37513814 if strings .Contains (imageRef , ":" ) && ! strings .HasSuffix (strings .ToLower (imageRef ), ".tar" ) {
3752- return errors .Errorf ("--container-images flag error : file '%s' does not exist. Did you try to scan an image using image name and tag?" , imageRef )
3815+ return errors .Errorf ("%s : file '%s' does not exist. Did you try to scan an image using image name and tag?" , containerImagesFlagError , imageRef )
37533816 }
3754- return errors .Errorf ("--container-images flag error : file '%s' does not exist" , imageRef )
3817+ return errors .Errorf ("%s : file '%s' does not exist" , containerImagesFlagError , imageRef )
37553818 }
37563819 return nil
37573820}
@@ -3775,10 +3838,10 @@ func validateOCIDirPrefix(imageRef string) error {
37753838
37763839 exists , err := osinstaller .FileExists (pathToCheck )
37773840 if err != nil {
3778- return errors .Errorf ("--container-images flag error : path %s does not exist: %v" , pathToCheck , err )
3841+ return errors .Errorf ("%s : path %s does not exist: %v" , containerImagesFlagError , pathToCheck , err )
37793842 }
37803843 if ! exists {
3781- return errors .Errorf ("--container-images flag error : path %s does not exist" , pathToCheck )
3844+ return errors .Errorf ("%s : path %s does not exist" , containerImagesFlagError , pathToCheck )
37823845 }
37833846 return nil
37843847}
0 commit comments