Skip to content

Commit a4bf451

Browse files
Other/ast 128580 kics macos issue(AST-128580) (#1423)
* Add macOS Docker and Podman fallback paths; enhance engine path resolution * Refactor createCommandWithEnhancedPath to use macOS Docker fallback paths and improve PATH handling * Enhance MockContainerManager to support image availability checks and add related fields * Add container engine constants and enhance fallback path handling for macOS * Refactor MockContainerManager and enhance fallback path handling for unknown engines * Add tests for MockContainerManager and enhance fallback path handling for macOS * Refactor fallback path handling in tests to use constants for improved maintainability * Add redundancy tests for ScanResult and related functions * Add comprehensive tests for ContainerManager and related functions - Implement tests for the real ContainerManager, including ID generation and command creation. - Ensure unique container IDs and validate UUID format in tests. - Add tests for createCommandWithEnhancedPath to verify argument passing and environment variable settings. - Enhance tests for getFallbackPaths to check for duplicates and correct primary path ordering. - Introduce additional tests for verifyEnginePath and engineNameResolution functions to cover edge cases. - Expand tests for buildIgnoreMap and filterIgnoredFindings to ensure correct behavior with various inputs. * Enhance macOS support in createCommandWithEnhancedPath and add related tests * Refactor createCommandWithEnhancedPath to improve PATH handling and update TestVerifyEnginePath to skip on non-Windows systems * Fix duplicate path handling in getFallbackPaths function * Fix directory existence check in createCommandWithEnhancedPath to skip non-existent paths * Fix PATH environment variable setting in createCommandWithEnhancedPath to ensure proper enhancement * Refactor path checks in tests to improve readability and maintainability * Refactor EnsureImageAvailable method in ContainerManager to return resolved engine path and error. Update related tests to handle new return values accordingly. * Update logging messages in EnsureImageAvailable method to use 'KICS image' instead of 'KICS Docker image' for consistency and clarity. --------- Co-authored-by: Anjali Deore <200181980+cx-anjali-deore@users.noreply.github.com>
1 parent 79dafba commit a4bf451

5 files changed

Lines changed: 1365 additions & 19 deletions

File tree

internal/services/realtimeengine/iacrealtime/constants.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,37 @@ const (
88
ContainerResultsFileName = "results.json"
99
InfoSeverity = "info"
1010
IacEnginePath = "/usr/local/bin"
11+
12+
// Container engine names
13+
engineDocker = "docker"
14+
enginePodman = "podman"
15+
16+
// engineVerifyTimeout is the timeout in seconds for verifying container engine availability
17+
engineVerifyTimeout = 5
18+
19+
// OS constants
20+
osLinux = "linux"
1121
)
1222

23+
// macOSDockerFallbackPaths contains additional paths to check for Docker on macOS
24+
// These paths cover various Docker installation methods:
25+
// - /usr/local/bin/docker: Standard location (Intel Macs)
26+
// - /opt/homebrew/bin/docker: Homebrew on Apple Silicon
27+
// - /Applications/Docker.app/Contents/Resources/bin/docker: Docker Desktop app bundle
28+
// - ~/.docker/bin/docker: Docker Desktop CLI tools (resolved at runtime)
29+
// - ~/.rd/bin/docker: Rancher Desktop (resolved at runtime)
30+
var macOSDockerFallbackPaths = []string{
31+
"/usr/local/bin",
32+
"/opt/homebrew/bin",
33+
"/Applications/Docker.app/Contents/Resources/bin",
34+
}
35+
36+
// macOSPodmanFallbackPaths contains additional paths to check for Podman on macOS
37+
var macOSPodmanFallbackPaths = []string{
38+
"/usr/local/bin",
39+
"/opt/homebrew/bin",
40+
}
41+
1342
var KicsErrorCodes = []string{"60", "50", "40", "30", "20"}
1443

1544
type LineIndex struct {

internal/services/realtimeengine/iacrealtime/container-manager.go

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
package iacrealtime
22

33
import (
4+
"os"
45
"os/exec"
6+
"path/filepath"
7+
"strings"
58

69
"github.com/checkmarx/ast-cli/internal/commands/util"
10+
"github.com/checkmarx/ast-cli/internal/logger"
711
commonParams "github.com/checkmarx/ast-cli/internal/params"
812
"github.com/google/uuid"
13+
"github.com/pkg/errors"
914
"github.com/spf13/viper"
1015
)
1116

1217
// IContainerManager interface for container operations
1318
type IContainerManager interface {
1419
GenerateContainerID() string
1520
RunKicsContainer(engine, volumeMap string) error
21+
EnsureImageAvailable(engine string) (string, error)
1622
}
1723

1824
// ContainerManager handles Docker container operations
@@ -29,12 +35,129 @@ func (dm *ContainerManager) GenerateContainerID() string {
2935
return containerName
3036
}
3137

38+
// createCommandWithEnhancedPath creates an exec.Cmd with an enhanced PATH that includes
39+
// Docker/Podman related directories. This is necessary on macOS when the IDE is launched
40+
// via GUI (double-click) because it doesn't inherit the shell's PATH environment variable.
41+
// Without this, Docker credential helpers like docker-credential-desktop won't be found.
42+
func createCommandWithEnhancedPath(enginePath string, args ...string) *exec.Cmd {
43+
cmd := exec.Command(enginePath, args...)
44+
45+
// Only enhance PATH on macOS
46+
if getOS() != osDarwin {
47+
return cmd
48+
}
49+
50+
// Get current PATH
51+
currentPath := os.Getenv("PATH")
52+
53+
// Build list of additional paths to add
54+
var additionalPaths []string
55+
56+
// Add the directory containing the engine itself
57+
engineDir := filepath.Dir(enginePath)
58+
additionalPaths = append(additionalPaths, engineDir)
59+
60+
// Add common Docker-related directories that may contain credential helpers
61+
additionalPaths = append(additionalPaths, macOSDockerFallbackPaths...)
62+
63+
// Add user home-based paths
64+
if homeDir, err := os.UserHomeDir(); err == nil {
65+
additionalPaths = append(additionalPaths,
66+
filepath.Join(homeDir, ".docker", "bin"),
67+
filepath.Join(homeDir, ".rd", "bin"))
68+
}
69+
70+
// Build a set of existing PATH entries for accurate duplicate detection
71+
pathParts := strings.Split(currentPath, string(os.PathListSeparator))
72+
pathSet := make(map[string]bool)
73+
for _, part := range pathParts {
74+
pathSet[part] = true
75+
}
76+
77+
// Build enhanced PATH (prepend additional paths to ensure they take priority)
78+
var enhancedPathParts []string
79+
for _, p := range additionalPaths {
80+
// Skip if already in PATH
81+
if pathSet[p] {
82+
continue
83+
}
84+
// Skip if directory doesn't exist
85+
if _, err := os.Stat(p); err != nil {
86+
continue
87+
}
88+
enhancedPathParts = append(enhancedPathParts, p)
89+
}
90+
enhancedPathParts = append(enhancedPathParts, currentPath)
91+
enhancedPath := strings.Join(enhancedPathParts, string(os.PathListSeparator))
92+
93+
// Set the enhanced PATH in the command's environment (replace existing PATH)
94+
env := os.Environ()
95+
for i, e := range env {
96+
if !strings.HasPrefix(e, "PATH=") {
97+
continue
98+
}
99+
env[i] = "PATH=" + enhancedPath
100+
break
101+
}
102+
cmd.Env = env
103+
104+
logger.PrintIfVerbose("Enhanced PATH for container command: " + enhancedPath)
105+
106+
return cmd
107+
}
108+
109+
// EnsureImageAvailable checks if the KICS image exists locally and pulls it if not available.
110+
// Returns the resolved engine path on success.
111+
func (dm *ContainerManager) EnsureImageAvailable(engine string) (string, error) {
112+
logger.PrintIfVerbose("Resolving container engine: " + engine)
113+
114+
resolvedEngine, err := engineNameResolution(engine, IacEnginePath)
115+
if err != nil {
116+
logger.PrintIfVerbose("Failed to resolve container engine '" + engine + "': " + err.Error())
117+
return "", errors.Wrapf(err, "container engine '%s' not found. On macOS, if Docker/Podman is installed but not found, "+
118+
"try launching the IDE from terminal or ensure Docker/Podman is running", engine)
119+
}
120+
121+
logger.PrintIfVerbose("Using container engine at: " + resolvedEngine)
122+
123+
// Check if image exists locally using 'docker image inspect'
124+
logger.PrintIfVerbose("Checking if KICS image exists locally: " + util.ContainerImage)
125+
126+
inspectCmd := createCommandWithEnhancedPath(resolvedEngine, "image", "inspect", util.ContainerImage)
127+
if err := inspectCmd.Run(); err == nil {
128+
logger.PrintIfVerbose("KICS image found locally: " + util.ContainerImage)
129+
return resolvedEngine, nil
130+
}
131+
132+
// Image not found locally, attempt to pull
133+
logger.PrintIfVerbose("KICS image not found locally. Attempting to pull: " + util.ContainerImage)
134+
135+
pullCmd := createCommandWithEnhancedPath(resolvedEngine, "pull", util.ContainerImage)
136+
output, pullErr := pullCmd.CombinedOutput()
137+
if pullErr != nil {
138+
outputStr := strings.TrimSpace(string(output))
139+
logger.PrintIfVerbose("Failed to pull KICS image. Output: " + outputStr)
140+
141+
if outputStr != "" {
142+
return "", errors.Errorf("Failed to pull KICS image '%s': %s. Please check your network connectivity or pull the image manually using: %s pull %s",
143+
util.ContainerImage, outputStr, resolvedEngine, util.ContainerImage)
144+
}
145+
return "", errors.Errorf("Failed to pull KICS image '%s': %v. Please check your network connectivity or pull the image manually using: %s pull %s",
146+
util.ContainerImage, pullErr, resolvedEngine, util.ContainerImage)
147+
}
148+
149+
logger.PrintIfVerbose("Successfully pulled KICS image: " + util.ContainerImage)
150+
return resolvedEngine, nil
151+
}
152+
32153
func (dm *ContainerManager) RunKicsContainer(engine, volumeMap string) error {
33-
engine, err := engineNameResolution(engine, IacEnginePath)
154+
// Ensure the KICS image is available before running
155+
resolvedEngine, err := dm.EnsureImageAvailable(engine)
34156
if err != nil {
35157
return err
36158
}
37-
args := []string{
159+
160+
cmd := createCommandWithEnhancedPath(resolvedEngine,
38161
"run", "--rm",
39162
"-v", volumeMap,
40163
"--name", viper.GetString(commonParams.KicsContainerNameKey),
@@ -43,8 +166,8 @@ func (dm *ContainerManager) RunKicsContainer(engine, volumeMap string) error {
43166
"-p", ContainerPath,
44167
"-o", ContainerPath,
45168
"--report-formats", ContainerFormat,
46-
}
47-
_, err = exec.Command(engine, args...).CombinedOutput()
169+
)
170+
_, err = cmd.CombinedOutput()
48171

49172
return err
50173
}

0 commit comments

Comments
 (0)