Skip to content

Commit 5b9ff97

Browse files
codacy and copilot review changes applied
1 parent 9539333 commit 5b9ff97

File tree

2 files changed

+92
-8
lines changed

2 files changed

+92
-8
lines changed

cmd/container_scan.go

Lines changed: 10 additions & 7 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

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

132+
// #nosec G204 -- imageName is validated by validateImageName() which checks for
133+
// shell metacharacters and enforces a strict character allowlist. Additionally,
134+
// exec.Command passes arguments directly without shell interpretation.
132135
trivyCmd := exec.Command(trivyPath, buildTrivyArgs(imageName)...)
133136
trivyCmd.Stdout = os.Stdout
134137
trivyCmd.Stderr = os.Stderr

cmd/container_scan_test.go

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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)