Skip to content

Commit 027955b

Browse files
not use PATH
1 parent d4d0d2c commit 027955b

File tree

2 files changed

+53
-38
lines changed

2 files changed

+53
-38
lines changed

cmd/container_scan.go

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"regexp"
99
"strings"
1010

11+
"codacy/cli-v2/config"
12+
config_file "codacy/cli-v2/config-file"
1113
"codacy/cli-v2/utils/logger"
1214

1315
"github.com/fatih/color"
@@ -20,9 +22,6 @@ import (
2022
// Based on Docker image reference specification
2123
var validImageNamePattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._\-/:@]*$`)
2224

23-
// lookPath is a variable to allow mocking exec.LookPath in tests
24-
var lookPath = exec.LookPath
25-
2625
// exitFunc is a variable to allow mocking os.Exit in tests
2726
var exitFunc = os.Exit
2827

@@ -36,7 +35,7 @@ type ExecCommandRunner struct{}
3635

3736
// Run executes a command and returns its exit error
3837
func (r *ExecCommandRunner) Run(name string, args []string) error {
39-
// #nosec G204 -- name comes from exec.LookPath("trivy") with a literal string,
38+
// #nosec G204 -- name comes from config (codacy-installed Trivy path),
4039
// and args are validated by validateImageName() which checks for shell metacharacters.
4140
// exec.Command passes arguments directly without shell interpretation.
4241
cmd := exec.Command(name, args...)
@@ -131,11 +130,31 @@ func validateImageName(imageName string) error {
131130
return nil
132131
}
133132

134-
// getTrivyPath returns the path to the Trivy binary and an error if not found
133+
// getTrivyPathResolver is set by tests to mock Trivy path resolution; when nil, real config/install logic is used
134+
var getTrivyPathResolver func() (string, error)
135+
136+
// getTrivyPath returns the path to the Trivy binary (codacy-installed, installed on demand if needed) and an error if not found
135137
func getTrivyPath() (string, error) {
136-
trivyPath, err := lookPath("trivy")
137-
if err != nil {
138-
return "", err
138+
if getTrivyPathResolver != nil {
139+
return getTrivyPathResolver()
140+
}
141+
if err := config.Config.CreateCodacyDirs(); err != nil {
142+
return "", fmt.Errorf("failed to create codacy directories: %w", err)
143+
}
144+
_ = config_file.ReadConfigFile(config.Config.ProjectConfigFile())
145+
tool := config.Config.Tools()["trivy"]
146+
if tool == nil || !config.Config.IsToolInstalled("trivy", tool) {
147+
if err := config.InstallTool("trivy", tool, ""); err != nil {
148+
return "", fmt.Errorf("failed to install Trivy: %w", err)
149+
}
150+
tool = config.Config.Tools()["trivy"]
151+
}
152+
if tool == nil {
153+
return "", fmt.Errorf("trivy not in config after install")
154+
}
155+
trivyPath, ok := tool.Binaries["trivy"]
156+
if !ok || trivyPath == "" {
157+
return "", fmt.Errorf("trivy binary path not found")
139158
}
140159
logger.Info("Found Trivy", logrus.Fields{"path": trivyPath})
141160
return trivyPath, nil
@@ -144,9 +163,8 @@ func getTrivyPath() (string, error) {
144163
// handleTrivyNotFound prints error message and exits with code 2
145164
func handleTrivyNotFound(err error) {
146165
logger.Error("Trivy not found", logrus.Fields{"error": err.Error()})
147-
color.Red("❌ Error: Trivy is not installed or not found in PATH")
148-
fmt.Println("Please install Trivy to use container scanning.")
149-
fmt.Println("Visit: https://trivy.dev/latest/getting-started/installation/")
166+
color.Red("❌ Error: Trivy could not be installed or found")
167+
fmt.Println("Run 'codacy-cli init' if you have no project yet, then try container-scan again so Trivy can be installed automatically.")
150168
fmt.Println("exit-code 2")
151169
exitFunc(2)
152170
}
@@ -236,7 +254,6 @@ func printScanSummary(hasVulnerabilities bool, imageNames []string) int {
236254
}
237255
logger.Info("Container scan completed successfully", logrus.Fields{"images": imageNames})
238256
color.Green("✅ Success: No vulnerabilities found matching the specified criteria")
239-
fmt.Println("exit-code 0")
240257
return 0
241258
}
242259

cmd/container_scan_test.go

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,27 @@ func (m *MockCommandRunner) Run(name string, args []string) error {
2929

3030
// Helper to save and restore global state for tests
3131
type testState struct {
32-
lookPath func(file string) (string, error)
33-
exitFunc func(code int)
34-
commandRunner CommandRunner
35-
severityFlag string
36-
pkgTypesFlag string
37-
ignoreUnfixed bool
32+
getTrivyPathResolver func() (string, error)
33+
exitFunc func(code int)
34+
commandRunner CommandRunner
35+
severityFlag string
36+
pkgTypesFlag string
37+
ignoreUnfixed bool
3838
}
3939

4040
func saveState() testState {
4141
return testState{
42-
lookPath: lookPath,
43-
exitFunc: exitFunc,
44-
commandRunner: commandRunner,
45-
severityFlag: severityFlag,
46-
pkgTypesFlag: pkgTypesFlag,
47-
ignoreUnfixed: ignoreUnfixedFlag,
42+
getTrivyPathResolver: getTrivyPathResolver,
43+
exitFunc: exitFunc,
44+
commandRunner: commandRunner,
45+
severityFlag: severityFlag,
46+
pkgTypesFlag: pkgTypesFlag,
47+
ignoreUnfixed: ignoreUnfixedFlag,
4848
}
4949
}
5050

5151
func (s testState) restore() {
52-
lookPath = s.lookPath
52+
getTrivyPathResolver = s.getTrivyPathResolver
5353
exitFunc = s.exitFunc
5454
commandRunner = s.commandRunner
5555
severityFlag = s.severityFlag
@@ -63,8 +63,7 @@ func TestGetTrivyPath_Found(t *testing.T) {
6363
state := saveState()
6464
defer state.restore()
6565

66-
lookPath = func(file string) (string, error) {
67-
assert.Equal(t, "trivy", file)
66+
getTrivyPathResolver = func() (string, error) {
6867
return "/usr/local/bin/trivy", nil
6968
}
7069

@@ -77,8 +76,8 @@ func TestGetTrivyPath_NotFound(t *testing.T) {
7776
state := saveState()
7877
defer state.restore()
7978

80-
lookPath = func(_ string) (string, error) {
81-
return "", errors.New("executable file not found in $PATH")
79+
getTrivyPathResolver = func() (string, error) {
80+
return "", errors.New("trivy not found")
8281
}
8382

8483
path, err := getTrivyPath()
@@ -93,8 +92,7 @@ func TestExecuteContainerScan_Success(t *testing.T) {
9392
state := saveState()
9493
defer state.restore()
9594

96-
// Mock trivy found
97-
lookPath = func(_ string) (string, error) {
95+
getTrivyPathResolver = func() (string, error) {
9896
return "/usr/local/bin/trivy", nil
9997
}
10098

@@ -135,7 +133,7 @@ func TestExecuteContainerScan_VulnerabilitiesFound(t *testing.T) {
135133
state := saveState()
136134
defer state.restore()
137135

138-
lookPath = func(_ string) (string, error) {
136+
getTrivyPathResolver = func() (string, error) {
139137
return "/usr/local/bin/trivy", nil
140138
}
141139

@@ -160,7 +158,7 @@ func TestExecuteContainerScan_MultipleImages_SomeWithVulnerabilities(t *testing.
160158
state := saveState()
161159
defer state.restore()
162160

163-
lookPath = func(_ string) (string, error) {
161+
getTrivyPathResolver = func() (string, error) {
164162
return "/usr/local/bin/trivy", nil
165163
}
166164

@@ -198,8 +196,8 @@ func TestExecuteContainerScan_TrivyNotFound(t *testing.T) {
198196
state := saveState()
199197
defer state.restore()
200198

201-
lookPath = func(_ string) (string, error) {
202-
return "", errors.New("executable file not found in $PATH")
199+
getTrivyPathResolver = func() (string, error) {
200+
return "", errors.New("trivy not in config after install")
203201
}
204202

205203
// Mock exitFunc to capture exit code instead of exiting
@@ -218,7 +216,7 @@ func TestExecuteContainerScan_MultipleImages_AllPass(t *testing.T) {
218216
state := saveState()
219217
defer state.restore()
220218

221-
lookPath = func(_ string) (string, error) {
219+
getTrivyPathResolver = func() (string, error) {
222220
return "/usr/local/bin/trivy", nil
223221
}
224222

@@ -251,7 +249,7 @@ func TestExecuteContainerScan_TrivyExecutionError(t *testing.T) {
251249
state := saveState()
252250
defer state.restore()
253251

254-
lookPath = func(_ string) (string, error) {
252+
getTrivyPathResolver = func() (string, error) {
255253
return "/usr/local/bin/trivy", nil
256254
}
257255

@@ -275,7 +273,7 @@ func TestExecuteContainerScan_EmptyImageList(t *testing.T) {
275273
state := saveState()
276274
defer state.restore()
277275

278-
lookPath = func(_ string) (string, error) {
276+
getTrivyPathResolver = func() (string, error) {
279277
return "/usr/local/bin/trivy", nil
280278
}
281279

0 commit comments

Comments
 (0)