Skip to content

Commit 25dcb9b

Browse files
add tests and codacy suggestion improvments
1 parent c8c5182 commit 25dcb9b

File tree

2 files changed

+484
-48
lines changed

2 files changed

+484
-48
lines changed

cmd/container_scan.go

Lines changed: 74 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"os"
66
"os/exec"
7+
"regexp"
8+
"strings"
79

810
"codacy/cli-v2/utils/logger"
911

@@ -12,6 +14,11 @@ import (
1214
"github.com/spf13/cobra"
1315
)
1416

17+
// validImageNamePattern validates Docker image references
18+
// Allows: registry/namespace/image:tag or image@sha256:digest
19+
// Based on Docker image reference specification
20+
var validImageNamePattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._\-/:@]*$`)
21+
1522
// Flag variables for container-scan command
1623
var (
1724
severityFlag string
@@ -51,72 +58,91 @@ the command fails when vulnerabilities are found.`,
5158
Run: runContainerScan,
5259
}
5360

54-
func runContainerScan(cmd *cobra.Command, args []string) {
55-
imageName := args[0]
61+
// validateImageName checks if the image name is a valid Docker image reference
62+
// and doesn't contain shell metacharacters that could be used for command injection
63+
func validateImageName(imageName string) error {
64+
if imageName == "" {
65+
return fmt.Errorf("image name cannot be empty")
66+
}
5667

57-
logger.Info("Starting container scan", logrus.Fields{
58-
"image": imageName,
59-
})
68+
// Check for maximum length (Docker has a practical limit)
69+
if len(imageName) > 256 {
70+
return fmt.Errorf("image name is too long (max 256 characters)")
71+
}
6072

61-
// Check if Trivy is installed
73+
// Validate against allowed pattern
74+
if !validImageNamePattern.MatchString(imageName) {
75+
return fmt.Errorf("invalid image name format: contains disallowed characters")
76+
}
77+
78+
// Additional check for dangerous shell metacharacters
79+
dangerousChars := []string{";", "&", "|", "$", "`", "(", ")", "{", "}", "<", ">", "!", "\\", "\n", "\r", "'", "\""}
80+
for _, char := range dangerousChars {
81+
if strings.Contains(imageName, char) {
82+
return fmt.Errorf("invalid image name: contains disallowed character '%s'", char)
83+
}
84+
}
85+
86+
return nil
87+
}
88+
89+
// getTrivyPath returns the path to the Trivy binary or exits if not found
90+
func getTrivyPath() string {
6291
trivyPath, err := exec.LookPath("trivy")
6392
if err != nil {
64-
logger.Error("Trivy not found", logrus.Fields{
65-
"error": err.Error(),
66-
})
93+
logger.Error("Trivy not found", logrus.Fields{"error": err.Error()})
6794
color.Red("❌ Error: Trivy is not installed or not found in PATH")
6895
fmt.Println("Please install Trivy to use container scanning.")
6996
fmt.Println("Visit: https://trivy.dev/latest/getting-started/installation/")
7097
os.Exit(1)
7198
}
99+
logger.Info("Found Trivy", logrus.Fields{"path": trivyPath})
100+
return trivyPath
101+
}
72102

73-
logger.Info("Found Trivy", logrus.Fields{
74-
"path": trivyPath,
75-
})
76-
77-
// Build Trivy command arguments
78-
trivyArgs := buildTrivyArgs(imageName)
79-
80-
trivyCmd := exec.Command(trivyPath, trivyArgs...)
81-
trivyCmd.Stdout = os.Stdout
82-
trivyCmd.Stderr = os.Stderr
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+
}
83111

84-
logger.Info("Running Trivy container scan", logrus.Fields{
85-
"command": trivyCmd.String(),
86-
})
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)
119+
}
87120

88-
fmt.Printf("🔍 Scanning container image: %s\n\n", imageName)
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+
}
89125

90-
err = trivyCmd.Run()
91-
if err != nil {
92-
// Check if the error is due to exit code 1 (vulnerabilities found)
93-
if exitError, ok := err.(*exec.ExitError); ok {
94-
exitCode := exitError.ExitCode()
95-
logger.Warn("Container scan completed with vulnerabilities", logrus.Fields{
96-
"image": imageName,
97-
"exit_code": exitCode,
98-
})
99-
if exitCode == 1 {
100-
fmt.Println()
101-
color.Red("❌ Scanning failed: vulnerabilities found in the container image")
102-
os.Exit(1)
103-
}
104-
}
126+
func runContainerScan(cmd *cobra.Command, args []string) {
127+
imageName := args[0]
105128

106-
// Other errors
107-
logger.Error("Failed to run Trivy", logrus.Fields{
108-
"error": err.Error(),
109-
})
110-
color.Red("❌ Error: Failed to run Trivy: %v", err)
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)
111132
os.Exit(1)
112133
}
113134

114-
logger.Info("Container scan completed successfully", logrus.Fields{
115-
"image": imageName,
116-
})
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)
117144

118-
fmt.Println()
119-
color.Green("✅ Success: No vulnerabilities found matching the specified criteria")
145+
handleTrivyResult(trivyCmd.Run(), imageName)
120146
}
121147

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

0 commit comments

Comments
 (0)