Skip to content

Commit 078a26a

Browse files
codacy and copilot review changes applied
1 parent 9539333 commit 078a26a

File tree

2 files changed

+105
-16
lines changed

2 files changed

+105
-16
lines changed

cmd/container_scan.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func init() {
3535
}
3636

3737
var containerScanCmd = &cobra.Command{
38-
Use: "container-scan [FLAGS] <IMAGE_NAME> [IMAGE_NAME...]",
38+
Use: "container-scan <IMAGE_NAME> [IMAGE_NAME...]",
3939
Short: "Scan container images for vulnerabilities using Trivy",
4040
Long: `Scan one or more container images for vulnerabilities using Trivy.
4141
@@ -74,19 +74,19 @@ func validateImageName(imageName string) error {
7474
return fmt.Errorf("image name is too long (max 256 characters)")
7575
}
7676

77-
// Validate against allowed pattern
78-
if !validImageNamePattern.MatchString(imageName) {
79-
return fmt.Errorf("invalid image name format: contains disallowed characters")
80-
}
81-
82-
// Additional check for dangerous shell metacharacters
77+
// Check for dangerous shell metacharacters first for specific error messages
8378
dangerousChars := []string{";", "&", "|", "$", "`", "(", ")", "{", "}", "<", ">", "!", "\\", "\n", "\r", "'", "\""}
8479
for _, char := range dangerousChars {
8580
if strings.Contains(imageName, char) {
8681
return fmt.Errorf("invalid image name: contains disallowed character '%s'", char)
8782
}
8883
}
8984

85+
// Validate against allowed pattern for any other invalid characters
86+
if !validImageNamePattern.MatchString(imageName) {
87+
return fmt.Errorf("invalid image name format: contains disallowed characters")
88+
}
89+
9090
return nil
9191
}
9292

@@ -98,7 +98,8 @@ func getTrivyPath() string {
9898
color.Red("❌ Error: Trivy is not installed or not found in PATH")
9999
fmt.Println("Please install Trivy to use container scanning.")
100100
fmt.Println("Visit: https://trivy.dev/latest/getting-started/installation/")
101-
os.Exit(1)
101+
fmt.Println("exit-code 2")
102+
os.Exit(2)
102103
}
103104
logger.Info("Found Trivy", logrus.Fields{"path": trivyPath})
104105
return trivyPath
@@ -112,7 +113,8 @@ func runContainerScan(_ *cobra.Command, args []string) {
112113
if err := validateImageName(imageName); err != nil {
113114
logger.Error("Invalid image name", logrus.Fields{"image": imageName, "error": err.Error()})
114115
color.Red("❌ Error: %v", err)
115-
os.Exit(1)
116+
fmt.Println("exit-code 2")
117+
os.Exit(2)
116118
}
117119
}
118120

@@ -129,6 +131,9 @@ func runContainerScan(_ *cobra.Command, args []string) {
129131
fmt.Printf("🔍 Scanning container image: %s\n\n", imageName)
130132
}
131133

134+
// #nosec G204 -- imageName is validated by validateImageName() which checks for
135+
// shell metacharacters and enforces a strict character allowlist. Additionally,
136+
// exec.Command passes arguments directly without shell interpretation.
132137
trivyCmd := exec.Command(trivyPath, buildTrivyArgs(imageName)...)
133138
trivyCmd.Stdout = os.Stdout
134139
trivyCmd.Stderr = os.Stderr
@@ -142,7 +147,8 @@ func runContainerScan(_ *cobra.Command, args []string) {
142147
} else {
143148
logger.Error("Failed to run Trivy", logrus.Fields{"error": err.Error(), "image": imageName})
144149
color.Red("❌ Error: Failed to run Trivy for %s: %v", imageName, err)
145-
os.Exit(1)
150+
fmt.Println("exit-code 2")
151+
os.Exit(2)
146152
}
147153
} else {
148154
logger.Info("No vulnerabilities found in image", logrus.Fields{"image": imageName})
@@ -154,11 +160,13 @@ func runContainerScan(_ *cobra.Command, args []string) {
154160
if hasVulnerabilities {
155161
logger.Warn("Container scan completed with vulnerabilities", logrus.Fields{"images": imageNames})
156162
color.Red("❌ Scanning failed: vulnerabilities found in one or more container images")
163+
fmt.Println("exit-code 1")
157164
os.Exit(1)
158165
}
159166

160167
logger.Info("Container scan completed successfully", logrus.Fields{"images": imageNames})
161168
color.Green("✅ Success: No vulnerabilities found matching the specified criteria")
169+
fmt.Println("exit-code 0")
162170
}
163171

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

cmd/container_scan_test.go

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ var trivyArgsTestCases = []trivyArgsTestCase{
3131
},
3232
},
3333
{
34-
name: "custom severity only",
35-
imageName: "codacy/engine:1.0.0",
36-
severity: "CRITICAL",
37-
pkgTypes: "",
38-
ignoreUnfixed: true,
34+
name: "custom severity only",
35+
imageName: "codacy/engine:1.0.0",
36+
severity: "CRITICAL",
37+
pkgTypes: "",
38+
ignoreUnfixed: true,
3939
expectedContains: []string{"--severity", "CRITICAL", "--pkg-types", "os", "--ignore-unfixed", "codacy/engine:1.0.0"},
4040
expectedNotContains: []string{"HIGH,CRITICAL"},
4141
},
@@ -153,7 +153,7 @@ func TestContainerScanCommandSkipsValidation(t *testing.T) {
153153
}
154154

155155
func TestContainerScanCommandRequiresArg(t *testing.T) {
156-
assert.Equal(t, "container-scan [FLAGS] <IMAGE_NAME> [IMAGE_NAME...]", containerScanCmd.Use, "Command use should match expected format")
156+
assert.Equal(t, "container-scan <IMAGE_NAME> [IMAGE_NAME...]", containerScanCmd.Use, "Command use should match expected format")
157157

158158
err := containerScanCmd.Args(containerScanCmd, []string{})
159159
assert.Error(t, err, "Should error when no args provided")
@@ -255,3 +255,84 @@ func TestBuildTrivyArgsDefaultsApplied(t *testing.T) {
255255

256256
assert.Contains(t, args, "--ignore-unfixed", "--ignore-unfixed should be present when enabled")
257257
}
258+
259+
// Tests for multiple image support
260+
261+
func TestValidateMultipleImages(t *testing.T) {
262+
// All valid images should pass
263+
validImages := []string{"alpine:latest", "nginx:1.21", "redis:7"}
264+
for _, img := range validImages {
265+
err := validateImageName(img)
266+
assert.NoError(t, err, "Valid image %s should not error", img)
267+
}
268+
}
269+
270+
func TestValidateMultipleImagesFailsOnInvalid(t *testing.T) {
271+
// Test that validation catches invalid images in a list
272+
images := []string{"alpine:latest", "nginx;malicious", "redis:7"}
273+
274+
var firstError error
275+
for _, img := range images {
276+
if err := validateImageName(img); err != nil {
277+
firstError = err
278+
break
279+
}
280+
}
281+
282+
assert.Error(t, firstError, "Should catch invalid image in list")
283+
assert.Contains(t, firstError.Error(), "disallowed character", "Should report specific error")
284+
}
285+
286+
func TestBuildTrivyArgsForMultipleImages(t *testing.T) {
287+
severityFlag = "CRITICAL"
288+
pkgTypesFlag = ""
289+
ignoreUnfixedFlag = true
290+
291+
images := []string{"alpine:latest", "nginx:1.21", "redis:7"}
292+
293+
// Verify each image gets correct args with same flags
294+
for _, img := range images {
295+
args := buildTrivyArgs(img)
296+
297+
assert.Equal(t, img, args[len(args)-1], "Image name should be last argument")
298+
assert.Contains(t, args, "--severity", "Should contain severity flag")
299+
assert.Contains(t, args, "CRITICAL", "Should use configured severity")
300+
}
301+
}
302+
303+
func TestContainerScanCommandAcceptsMultipleImages(t *testing.T) {
304+
tests := []struct {
305+
name string
306+
args []string
307+
errMsg string
308+
}{
309+
{
310+
name: "single image",
311+
args: []string{"alpine:latest"},
312+
},
313+
{
314+
name: "two images",
315+
args: []string{"alpine:latest", "nginx:1.21"},
316+
},
317+
{
318+
name: "three images",
319+
args: []string{"alpine:latest", "nginx:1.21", "redis:7"},
320+
},
321+
{
322+
name: "many images",
323+
args: []string{"img1:v1", "img2:v2", "img3:v3", "img4:v4", "img5:v5"},
324+
},
325+
}
326+
327+
for _, tt := range tests {
328+
t.Run(tt.name, func(t *testing.T) {
329+
err := containerScanCmd.Args(containerScanCmd, tt.args)
330+
assert.NoError(t, err, "Command should accept %d image(s)", len(tt.args))
331+
})
332+
}
333+
}
334+
335+
func TestContainerScanCommandRejectsNoImages(t *testing.T) {
336+
err := containerScanCmd.Args(containerScanCmd, []string{})
337+
assert.Error(t, err, "Command should reject empty image list")
338+
}

0 commit comments

Comments
 (0)