Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4eb2578
Add macOS Docker and Podman fallback paths; enhance engine path resol…
cx-amol-mane Jan 29, 2026
8f0e389
Refactor createCommandWithEnhancedPath to use macOS Docker fallback p…
cx-amol-mane Jan 29, 2026
1a26711
Enhance MockContainerManager to support image availability checks and…
cx-amol-mane Jan 29, 2026
7c9e8e8
Add container engine constants and enhance fallback path handling for…
cx-amol-mane Jan 29, 2026
14408e3
Refactor MockContainerManager and enhance fallback path handling for …
cx-amol-mane Jan 29, 2026
6f69b52
Add tests for MockContainerManager and enhance fallback path handling…
cx-amol-mane Jan 29, 2026
a4526a6
Refactor fallback path handling in tests to use constants for improve…
cx-amol-mane Jan 29, 2026
aaa704c
Add redundancy tests for ScanResult and related functions
cx-amol-mane Jan 30, 2026
64ca580
Add comprehensive tests for ContainerManager and related functions
cx-amol-mane Jan 30, 2026
2dbf5b4
Enhance macOS support in createCommandWithEnhancedPath and add relate…
cx-amol-mane Jan 30, 2026
128eb98
Refactor createCommandWithEnhancedPath to improve PATH handling and u…
cx-amol-mane Jan 30, 2026
0af77f3
Fix duplicate path handling in getFallbackPaths function
cx-amol-mane Jan 30, 2026
282f6d9
Fix directory existence check in createCommandWithEnhancedPath to ski…
cx-amol-mane Feb 2, 2026
ffc97ea
Fix PATH environment variable setting in createCommandWithEnhancedPat…
cx-amol-mane Feb 2, 2026
0233d32
Refactor path checks in tests to improve readability and maintainability
cx-amol-mane Feb 2, 2026
ed7277a
Merge branch 'main' into other/AST-128580-kics-macos-issue
cx-amol-mane Feb 2, 2026
afb756b
Merge branch 'main' into other/AST-128580-kics-macos-issue
cx-anjali-deore Feb 2, 2026
9587e8c
Refactor EnsureImageAvailable method in ContainerManager to return re…
cx-amol-mane Feb 2, 2026
75142d2
Update logging messages in EnsureImageAvailable method to use 'KICS i…
cx-amol-mane Feb 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions internal/services/realtimeengine/iacrealtime/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,37 @@ const (
ContainerResultsFileName = "results.json"
InfoSeverity = "info"
IacEnginePath = "/usr/local/bin"

// Container engine names
engineDocker = "docker"
enginePodman = "podman"

// engineVerifyTimeout is the timeout in seconds for verifying container engine availability
engineVerifyTimeout = 5

// OS constants
osLinux = "linux"
)

// macOSDockerFallbackPaths contains additional paths to check for Docker on macOS
// These paths cover various Docker installation methods:
// - /usr/local/bin/docker: Standard location (Intel Macs)
// - /opt/homebrew/bin/docker: Homebrew on Apple Silicon
// - /Applications/Docker.app/Contents/Resources/bin/docker: Docker Desktop app bundle
// - ~/.docker/bin/docker: Docker Desktop CLI tools (resolved at runtime)
// - ~/.rd/bin/docker: Rancher Desktop (resolved at runtime)
var macOSDockerFallbackPaths = []string{
"/usr/local/bin",
"/opt/homebrew/bin",
"/Applications/Docker.app/Contents/Resources/bin",
}

// macOSPodmanFallbackPaths contains additional paths to check for Podman on macOS
var macOSPodmanFallbackPaths = []string{
"/usr/local/bin",
"/opt/homebrew/bin",
}

var KicsErrorCodes = []string{"60", "50", "40", "30", "20"}

type LineIndex struct {
Expand Down
131 changes: 127 additions & 4 deletions internal/services/realtimeengine/iacrealtime/container-manager.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package iacrealtime

import (
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/checkmarx/ast-cli/internal/commands/util"
"github.com/checkmarx/ast-cli/internal/logger"
commonParams "github.com/checkmarx/ast-cli/internal/params"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/spf13/viper"
)

// IContainerManager interface for container operations
type IContainerManager interface {
GenerateContainerID() string
RunKicsContainer(engine, volumeMap string) error
EnsureImageAvailable(engine string) (string, error)
}

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

// createCommandWithEnhancedPath creates an exec.Cmd with an enhanced PATH that includes
// Docker/Podman related directories. This is necessary on macOS when the IDE is launched
// via GUI (double-click) because it doesn't inherit the shell's PATH environment variable.
// Without this, Docker credential helpers like docker-credential-desktop won't be found.
func createCommandWithEnhancedPath(enginePath string, args ...string) *exec.Cmd {
cmd := exec.Command(enginePath, args...)

// Only enhance PATH on macOS
if getOS() != osDarwin {
return cmd
}

// Get current PATH
currentPath := os.Getenv("PATH")

// Build list of additional paths to add
var additionalPaths []string

// Add the directory containing the engine itself
engineDir := filepath.Dir(enginePath)
additionalPaths = append(additionalPaths, engineDir)

// Add common Docker-related directories that may contain credential helpers
additionalPaths = append(additionalPaths, macOSDockerFallbackPaths...)

// Add user home-based paths
if homeDir, err := os.UserHomeDir(); err == nil {
additionalPaths = append(additionalPaths,
filepath.Join(homeDir, ".docker", "bin"),
filepath.Join(homeDir, ".rd", "bin"))
}

// Build a set of existing PATH entries for accurate duplicate detection
pathParts := strings.Split(currentPath, string(os.PathListSeparator))
pathSet := make(map[string]bool)
for _, part := range pathParts {
pathSet[part] = true
}

// Build enhanced PATH (prepend additional paths to ensure they take priority)
var enhancedPathParts []string
for _, p := range additionalPaths {
// Skip if already in PATH
if pathSet[p] {
continue
}
// Skip if directory doesn't exist
if _, err := os.Stat(p); err != nil {
continue
}
enhancedPathParts = append(enhancedPathParts, p)
}
enhancedPathParts = append(enhancedPathParts, currentPath)
enhancedPath := strings.Join(enhancedPathParts, string(os.PathListSeparator))

// Set the enhanced PATH in the command's environment (replace existing PATH)
env := os.Environ()
for i, e := range env {
if !strings.HasPrefix(e, "PATH=") {
continue
}
env[i] = "PATH=" + enhancedPath
break
}
cmd.Env = env

logger.PrintIfVerbose("Enhanced PATH for container command: " + enhancedPath)

return cmd
}

// EnsureImageAvailable checks if the KICS image exists locally and pulls it if not available.
// Returns the resolved engine path on success.
func (dm *ContainerManager) EnsureImageAvailable(engine string) (string, error) {
logger.PrintIfVerbose("Resolving container engine: " + engine)

resolvedEngine, err := engineNameResolution(engine, IacEnginePath)
if err != nil {
logger.PrintIfVerbose("Failed to resolve container engine '" + engine + "': " + err.Error())
return "", errors.Wrapf(err, "container engine '%s' not found. On macOS, if Docker/Podman is installed but not found, "+
"try launching the IDE from terminal or ensure Docker/Podman is running", engine)
}

logger.PrintIfVerbose("Using container engine at: " + resolvedEngine)

// Check if image exists locally using 'docker image inspect'
logger.PrintIfVerbose("Checking if KICS image exists locally: " + util.ContainerImage)

inspectCmd := createCommandWithEnhancedPath(resolvedEngine, "image", "inspect", util.ContainerImage)
if err := inspectCmd.Run(); err == nil {
logger.PrintIfVerbose("KICS image found locally: " + util.ContainerImage)
return resolvedEngine, nil
}

// Image not found locally, attempt to pull
logger.PrintIfVerbose("KICS image not found locally. Attempting to pull: " + util.ContainerImage)

pullCmd := createCommandWithEnhancedPath(resolvedEngine, "pull", util.ContainerImage)
output, pullErr := pullCmd.CombinedOutput()
if pullErr != nil {
outputStr := strings.TrimSpace(string(output))
logger.PrintIfVerbose("Failed to pull KICS image. Output: " + outputStr)

if outputStr != "" {
return "", errors.Errorf("Failed to pull KICS image '%s': %s. Please check your network connectivity or pull the image manually using: %s pull %s",
util.ContainerImage, outputStr, resolvedEngine, util.ContainerImage)
}
return "", errors.Errorf("Failed to pull KICS image '%s': %v. Please check your network connectivity or pull the image manually using: %s pull %s",
util.ContainerImage, pullErr, resolvedEngine, util.ContainerImage)
}

logger.PrintIfVerbose("Successfully pulled KICS image: " + util.ContainerImage)
return resolvedEngine, nil
}

func (dm *ContainerManager) RunKicsContainer(engine, volumeMap string) error {
engine, err := engineNameResolution(engine, IacEnginePath)
// Ensure the KICS image is available before running
resolvedEngine, err := dm.EnsureImageAvailable(engine)
if err != nil {
return err
}
args := []string{

cmd := createCommandWithEnhancedPath(resolvedEngine,
"run", "--rm",
"-v", volumeMap,
"--name", viper.GetString(commonParams.KicsContainerNameKey),
Expand All @@ -43,8 +166,8 @@ func (dm *ContainerManager) RunKicsContainer(engine, volumeMap string) error {
"-p", ContainerPath,
"-o", ContainerPath,
"--report-formats", ContainerFormat,
}
_, err = exec.Command(engine, args...).CombinedOutput()
)
_, err = cmd.CombinedOutput()

return err
}
Loading
Loading