Skip to content

Commit 9539333

Browse files
allow list of images as argument
1 parent 25dcb9b commit 9539333

File tree

2 files changed

+218
-355
lines changed

2 files changed

+218
-355
lines changed

cmd/container_scan.go

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Package cmd implements the CLI commands for the Codacy CLI tool.
12
package cmd
23

34
import (
@@ -34,27 +35,30 @@ func init() {
3435
}
3536

3637
var containerScanCmd = &cobra.Command{
37-
Use: "container-scan [FLAGS] <IMAGE_NAME>",
38+
Use: "container-scan [FLAGS] <IMAGE_NAME> [IMAGE_NAME...]",
3839
Short: "Scan container images for vulnerabilities using Trivy",
39-
Long: `Scan container images for vulnerabilities using Trivy.
40+
Long: `Scan one or more container images for vulnerabilities using Trivy.
4041
4142
By default, scans for HIGH and CRITICAL vulnerabilities in OS packages,
4243
ignoring unfixed issues. Use flags to override these defaults.
4344
4445
The --exit-code 1 flag is always applied (not user-configurable) to ensure
45-
the command fails when vulnerabilities are found.`,
46-
Example: ` # Default behavior (HIGH,CRITICAL severity, os packages only)
46+
the command fails when vulnerabilities are found in any image.`,
47+
Example: ` # Scan a single image
4748
codacy-cli container-scan myapp:latest
4849
49-
# Scan only for CRITICAL vulnerabilities
50-
codacy-cli container-scan --severity CRITICAL myapp:latest
50+
# Scan multiple images
51+
codacy-cli container-scan myapp:latest nginx:alpine redis:7
52+
53+
# Scan only for CRITICAL vulnerabilities across multiple images
54+
codacy-cli container-scan --severity CRITICAL myapp:latest nginx:alpine
5155
5256
# Scan all severities and package types
5357
codacy-cli container-scan --severity LOW,MEDIUM,HIGH,CRITICAL --pkg-types os,library myapp:latest
5458
5559
# Include unfixed vulnerabilities
5660
codacy-cli container-scan --ignore-unfixed=false myapp:latest`,
57-
Args: cobra.ExactArgs(1),
61+
Args: cobra.MinimumNArgs(1),
5862
Run: runContainerScan,
5963
}
6064

@@ -100,49 +104,61 @@ func getTrivyPath() string {
100104
return trivyPath
101105
}
102106

103-
// handleTrivyResult processes the Trivy command result and exits appropriately
104-
func handleTrivyResult(err error, imageName string) {
105-
if err == nil {
106-
logger.Info("Container scan completed successfully", logrus.Fields{"image": imageName})
107-
fmt.Println()
108-
color.Green("✅ Success: No vulnerabilities found matching the specified criteria")
109-
return
110-
}
107+
func runContainerScan(_ *cobra.Command, args []string) {
108+
imageNames := args
111109

112-
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
113-
logger.Warn("Container scan completed with vulnerabilities", logrus.Fields{
114-
"image": imageName, "exit_code": 1,
115-
})
116-
fmt.Println()
117-
color.Red("❌ Scanning failed: vulnerabilities found in the container image")
118-
os.Exit(1)
110+
// Validate all image names first
111+
for _, imageName := range imageNames {
112+
if err := validateImageName(imageName); err != nil {
113+
logger.Error("Invalid image name", logrus.Fields{"image": imageName, "error": err.Error()})
114+
color.Red("❌ Error: %v", err)
115+
os.Exit(1)
116+
}
119117
}
120118

121-
logger.Error("Failed to run Trivy", logrus.Fields{"error": err.Error()})
122-
color.Red("❌ Error: Failed to run Trivy: %v", err)
123-
os.Exit(1)
124-
}
119+
logger.Info("Starting container scan", logrus.Fields{"images": imageNames, "count": len(imageNames)})
120+
121+
trivyPath := getTrivyPath()
122+
hasVulnerabilities := false
123+
124+
for i, imageName := range imageNames {
125+
if len(imageNames) > 1 {
126+
fmt.Printf("\n📦 [%d/%d] Scanning image: %s\n", i+1, len(imageNames), imageName)
127+
fmt.Println(strings.Repeat("-", 50))
128+
} else {
129+
fmt.Printf("🔍 Scanning container image: %s\n\n", imageName)
130+
}
125131

126-
func runContainerScan(cmd *cobra.Command, args []string) {
127-
imageName := args[0]
132+
trivyCmd := exec.Command(trivyPath, buildTrivyArgs(imageName)...)
133+
trivyCmd.Stdout = os.Stdout
134+
trivyCmd.Stderr = os.Stderr
135+
136+
logger.Info("Running Trivy container scan", logrus.Fields{"command": trivyCmd.String()})
137+
138+
if err := trivyCmd.Run(); err != nil {
139+
if exitError, ok := err.(*exec.ExitError); ok && exitError.ExitCode() == 1 {
140+
logger.Warn("Vulnerabilities found in image", logrus.Fields{"image": imageName})
141+
hasVulnerabilities = true
142+
} else {
143+
logger.Error("Failed to run Trivy", logrus.Fields{"error": err.Error(), "image": imageName})
144+
color.Red("❌ Error: Failed to run Trivy for %s: %v", imageName, err)
145+
os.Exit(1)
146+
}
147+
} else {
148+
logger.Info("No vulnerabilities found in image", logrus.Fields{"image": imageName})
149+
}
150+
}
128151

129-
if err := validateImageName(imageName); err != nil {
130-
logger.Error("Invalid image name", logrus.Fields{"image": imageName, "error": err.Error()})
131-
color.Red("❌ Error: %v", err)
152+
// Print summary for multiple images
153+
fmt.Println()
154+
if hasVulnerabilities {
155+
logger.Warn("Container scan completed with vulnerabilities", logrus.Fields{"images": imageNames})
156+
color.Red("❌ Scanning failed: vulnerabilities found in one or more container images")
132157
os.Exit(1)
133158
}
134159

135-
logger.Info("Starting container scan", logrus.Fields{"image": imageName})
136-
137-
trivyPath := getTrivyPath()
138-
trivyCmd := exec.Command(trivyPath, buildTrivyArgs(imageName)...)
139-
trivyCmd.Stdout = os.Stdout
140-
trivyCmd.Stderr = os.Stderr
141-
142-
logger.Info("Running Trivy container scan", logrus.Fields{"command": trivyCmd.String()})
143-
fmt.Printf("🔍 Scanning container image: %s\n\n", imageName)
144-
145-
handleTrivyResult(trivyCmd.Run(), imageName)
160+
logger.Info("Container scan completed successfully", logrus.Fields{"images": imageNames})
161+
color.Green("✅ Success: No vulnerabilities found matching the specified criteria")
146162
}
147163

148164
// buildTrivyArgs constructs the Trivy command arguments based on flags

0 commit comments

Comments
 (0)