|
| 1 | +// Package cmd implements the CLI commands for the Codacy CLI tool. |
1 | 2 | package cmd |
2 | 3 |
|
3 | 4 | import ( |
@@ -34,27 +35,30 @@ func init() { |
34 | 35 | } |
35 | 36 |
|
36 | 37 | var containerScanCmd = &cobra.Command{ |
37 | | - Use: "container-scan [FLAGS] <IMAGE_NAME>", |
| 38 | + Use: "container-scan [FLAGS] <IMAGE_NAME> [IMAGE_NAME...]", |
38 | 39 | 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. |
40 | 41 |
|
41 | 42 | By default, scans for HIGH and CRITICAL vulnerabilities in OS packages, |
42 | 43 | ignoring unfixed issues. Use flags to override these defaults. |
43 | 44 |
|
44 | 45 | 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 |
47 | 48 | codacy-cli container-scan myapp:latest |
48 | 49 |
|
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 |
51 | 55 |
|
52 | 56 | # Scan all severities and package types |
53 | 57 | codacy-cli container-scan --severity LOW,MEDIUM,HIGH,CRITICAL --pkg-types os,library myapp:latest |
54 | 58 |
|
55 | 59 | # Include unfixed vulnerabilities |
56 | 60 | codacy-cli container-scan --ignore-unfixed=false myapp:latest`, |
57 | | - Args: cobra.ExactArgs(1), |
| 61 | + Args: cobra.MinimumNArgs(1), |
58 | 62 | Run: runContainerScan, |
59 | 63 | } |
60 | 64 |
|
@@ -100,49 +104,61 @@ func getTrivyPath() string { |
100 | 104 | return trivyPath |
101 | 105 | } |
102 | 106 |
|
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 |
111 | 109 |
|
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 | + } |
119 | 117 | } |
120 | 118 |
|
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 | + } |
125 | 131 |
|
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 | + } |
128 | 151 |
|
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") |
132 | 157 | os.Exit(1) |
133 | 158 | } |
134 | 159 |
|
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") |
146 | 162 | } |
147 | 163 |
|
148 | 164 | // buildTrivyArgs constructs the Trivy command arguments based on flags |
|
0 commit comments