Skip to content

Commit 7b44fb2

Browse files
author
Checkmarx Automation
committed
Fix container-images flag to support prefix syntax and restrict to single images
- Add support for Syft-compatible prefix syntax (docker:, podman:, containerd:, registry:, docker-archive:, oci-archive:, oci-dir:, file:) - Restrict scanning to single images only (prevent bulk directory/registry scanning) - Remove singularity support - Add comprehensive validation for all supported formats - Maintain backward compatibility with traditional image:tag format Fixes AST-108903
1 parent c199543 commit 7b44fb2

2 files changed

Lines changed: 394 additions & 11 deletions

File tree

internal/commands/scan.go

Lines changed: 132 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3318,18 +3318,147 @@ func validateCreateScanFlags(cmd *cobra.Command) error {
33183318
}
33193319

33203320
func validateContainerImageFormat(containerImage string) error {
3321-
if strings.HasSuffix(containerImage, ".tar") {
3322-
_, err := osinstaller.FileExists(containerImage)
3321+
// Define supported prefixes for container image references
3322+
// Note: 'dir:' prefix is intentionally excluded to prevent scanning entire directories
3323+
supportedPrefixes := []string{
3324+
"docker:",
3325+
"podman:",
3326+
"containerd:",
3327+
"registry:",
3328+
"docker-archive:",
3329+
"oci-archive:",
3330+
"oci-dir:",
3331+
"file:",
3332+
}
3333+
3334+
// Check for explicitly forbidden prefixes first
3335+
if strings.HasPrefix(containerImage, "dir:") {
3336+
return errors.Errorf("Invalid value for --container-images flag. The 'dir:' prefix is not supported as it would scan entire directories rather than a single image")
3337+
}
3338+
3339+
// Check if the input uses a supported prefix
3340+
for _, prefix := range supportedPrefixes {
3341+
if strings.HasPrefix(containerImage, prefix) {
3342+
return validatePrefixedContainerImage(containerImage, prefix)
3343+
}
3344+
}
3345+
3346+
// If no prefix is used, validate as traditional format
3347+
return validateTraditionalContainerImage(containerImage)
3348+
}
3349+
3350+
func validatePrefixedContainerImage(containerImage, prefix string) error {
3351+
// Remove the prefix to get the actual image reference
3352+
imageRef := strings.TrimPrefix(containerImage, prefix)
3353+
3354+
if imageRef == "" {
3355+
return errors.Errorf("Invalid value for --container-images flag. After prefix '%s', the image reference cannot be empty", prefix)
3356+
}
3357+
3358+
// Handle archive-based prefixes that expect existing files
3359+
if prefix == "docker-archive:" || prefix == "oci-archive:" {
3360+
// These should point to existing archive files (typically .tar files)
3361+
exists, err := osinstaller.FileExists(imageRef)
33233362
if err != nil {
33243363
return errors.Errorf("--container-images flag error: %v", err)
33253364
}
3365+
if !exists {
3366+
return errors.Errorf("--container-images flag error: file does not exist")
3367+
}
3368+
return nil
3369+
}
33263370

3371+
// Handle oci-dir prefix - can be directories OR files (like .tar files)
3372+
if prefix == "oci-dir:" {
3373+
// oci-dir can handle:
3374+
// 1. Directories (OCI layout directories)
3375+
// 2. Files (like .tar files)
3376+
// 3. Can have optional :tag suffix
3377+
3378+
pathToCheck := imageRef
3379+
if strings.Contains(imageRef, ":") {
3380+
// Handle case like "oci-dir:/path/to/dir:tag" or "oci-dir:name.tar:tag"
3381+
pathParts := strings.Split(imageRef, ":")
3382+
if len(pathParts) > 0 && pathParts[0] != "" {
3383+
pathToCheck = pathParts[0]
3384+
}
3385+
}
3386+
3387+
exists, err := osinstaller.FileExists(pathToCheck)
3388+
if err != nil {
3389+
return errors.Errorf("--container-images flag error: path %s does not exist: %v", pathToCheck, err)
3390+
}
3391+
if !exists {
3392+
return errors.Errorf("--container-images flag error: path %s does not exist", pathToCheck)
3393+
}
3394+
return nil
3395+
}
3396+
3397+
// Handle file prefix - can be any single file
3398+
if prefix == "file:" {
3399+
exists, err := osinstaller.FileExists(imageRef)
3400+
if err != nil {
3401+
return errors.Errorf("--container-images flag error: %v", err)
3402+
}
3403+
if !exists {
3404+
return errors.Errorf("--container-images flag error: file does not exist")
3405+
}
3406+
return nil
3407+
}
3408+
3409+
// Handle registry prefix - RESTRICTION: must specify a single image, not just registry
3410+
if prefix == "registry:" {
3411+
// Registry must specify a single image, not just a registry URL
3412+
// Valid: registry:ubuntu:latest, registry:registry.example.com/namespace/image:tag
3413+
// Invalid: registry:registry.example.com (just registry without image)
3414+
3415+
// Basic validation - should not be empty and should not be obviously just a registry URL
3416+
if strings.HasSuffix(imageRef, ".com") || strings.HasSuffix(imageRef, ".io") ||
3417+
strings.HasSuffix(imageRef, ".org") || strings.HasSuffix(imageRef, ".net") {
3418+
return errors.Errorf("Invalid value for --container-images flag. Registry format must specify a single image, not just a registry URL. Use format: registry:<registry-url>/<image>:<tag> or registry:<image>:<tag>")
3419+
}
3420+
3421+
// Check for registry:host:port format (just registry URL with port)
3422+
if strings.Contains(imageRef, ":") {
3423+
parts := strings.Split(imageRef, ":")
3424+
if len(parts) == 2 && len(parts[1]) <= 5 && !strings.Contains(imageRef, "/") {
3425+
// This looks like registry:port format without image
3426+
return errors.Errorf("Invalid value for --container-images flag. Registry format must specify a single image, not just a registry URL. Use format: registry:<registry-url>/<image>:<tag>")
3427+
}
3428+
}
3429+
3430+
return nil
3431+
}
3432+
3433+
// For daemon-based prefixes (docker:, podman:, containerd:)
3434+
// Validate they follow the image:tag format, but be flexible with complex registry URLs
3435+
if prefix == "docker:" || prefix == "podman:" || prefix == "containerd:" {
3436+
imageParts := strings.Split(imageRef, ":")
3437+
if len(imageParts) < 2 || imageParts[0] == "" || imageParts[1] == "" {
3438+
return errors.Errorf("Invalid value for --container-images flag. Prefix '%s' expects format <image-name>:<image-tag>", prefix)
3439+
}
3440+
}
3441+
3442+
return nil
3443+
}
3444+
3445+
func validateTraditionalContainerImage(containerImage string) error {
3446+
// Handle legacy .tar file format
3447+
if strings.HasSuffix(containerImage, ".tar") {
3448+
exists, err := osinstaller.FileExists(containerImage)
3449+
if err != nil {
3450+
return errors.Errorf("--container-images flag error: %v", err)
3451+
}
3452+
if !exists {
3453+
return errors.Errorf("--container-images flag error: file does not exist")
3454+
}
33273455
return nil
33283456
}
33293457

3458+
// Handle traditional image:tag format
33303459
imageParts := strings.Split(containerImage, ":")
33313460
if len(imageParts) != 2 || imageParts[0] == "" || imageParts[1] == "" {
3332-
return errors.Errorf("Invalid value for --container-images flag. The value must be in the format <image-name>:<image-tag> or <image-name>.tar")
3461+
return errors.Errorf("Invalid value for --container-images flag. The value must be in the format <image-name>:<image-tag>, <image-name>.tar, or use a supported prefix (docker:, podman:, containerd:, registry:, docker-archive:, oci-archive:, oci-dir:, file:)")
33333462
}
33343463
return nil
33353464
}

0 commit comments

Comments
 (0)