Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions internal/commands/data/manifests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ tzdata==2025.1
# Exact version

flask==3.1.2
werkzeug>=3.0.6

# Range: greater than or equal and less than

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const (
KicsContainerPrefix = "cli-iac-realtime-"
ContainerResultsFileName = "results.json"
InfoSeverity = "info"
IacEnginePath = "/usr/local/bin"
)

var KicsErrorCodes = []string{"60", "50", "40", "30", "20"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func (dm *ContainerManager) GenerateContainerID() string {
}

func (dm *ContainerManager) RunKicsContainer(engine, volumeMap string) error {
engine, err := engineNameResolution(engine, IacEnginePath)
if err != nil {
return err
}
args := []string{
"run", "--rm",
"-v", volumeMap,
Expand All @@ -40,7 +44,7 @@ func (dm *ContainerManager) RunKicsContainer(engine, volumeMap string) error {
"-o", ContainerPath,
"--report-formats", ContainerFormat,
}
_, err = exec.Command(engine, args...).CombinedOutput()

_, err := exec.Command(engine, args...).CombinedOutput()
return err
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ func TestMockContainerManager_RunKicsContainer(t *testing.T) {
volumeMap: "/tmp/test:/path",
expectErr: false, // Mock doesn't validate parameters
},
{
name: "FallBack engine Path verification",
engine: "/usr/local/bin/docker",
volumeMap: "/tmp/test:/path",
expectErr: false, // Mock doesn't validate parameters

},
}

for _, tt := range tests {
Expand Down
31 changes: 30 additions & 1 deletion internal/services/realtimeengine/iacrealtime/iac-realtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"

errorconstants "github.com/checkmarx/ast-cli/internal/constants/errors"
"github.com/checkmarx/ast-cli/internal/services/realtimeengine"
"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/pkg/errors"

errorconstants "github.com/checkmarx/ast-cli/internal/constants/errors"
)

const (
osWindows = "windows"
)

type IacRealtimeService struct {
Expand Down Expand Up @@ -136,3 +145,23 @@ func (svc *IacRealtimeService) validateFilePath(filePath string) error {
}
return nil
}

func engineNameResolution(engineName, fallBackDir string) (string, error) {
var err error
if _, err = exec.LookPath(engineName); err == nil {
return engineName, nil
}
if err != nil && getOS() == osWindows {
return "", errors.New(engineName + ": executable file not found in PATH")
}
fallbackPath := filepath.Join(fallBackDir, engineName)
info, err := os.Stat(fallbackPath)
if err == nil && !info.IsDir() {
return fallbackPath, nil
}
return "", errors.New(engineName + " not found in PATH or in " + IacEnginePath)
}

var getOS = func() string {
return runtime.GOOS
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package iacrealtime
import (
"os"
"path/filepath"
"runtime"
"testing"

commonParams "github.com/checkmarx/ast-cli/internal/params"
Expand Down Expand Up @@ -448,3 +449,64 @@ func TestFilterIgnoredFindings_WithOneIgnored(t *testing.T) {
t.Errorf("Unexpected result after filtering: got %s, expected 'Memory Not Limited'", filtered[0].Title)
}
}

func createExecutable(t *testing.T, tempDir, name string) string {
t.Helper()
path := filepath.Join(tempDir, name)
if runtime.GOOS == "windows" {
path += ".exe"
}

err := os.WriteFile(path, []byte("#!/bin/sh\necho test"), 0755)
if err != nil {
t.Fatalf("failed to create executable: %v", err)
}
return filepath.Base(path)
}

func TestEngineName_Resolution_FoundInPATH(t *testing.T) {
tmpDir := t.TempDir()
engineName := createExecutable(t, tmpDir, "docker")
previousPath := os.Getenv("PATH")

err := os.Setenv("PATH", tmpDir+string(os.PathListSeparator)+previousPath)
if err != nil {
t.Fatalf("Failed to set the PATH in env")
}
defer func() {
_ = os.Setenv("PATH", previousPath)
}()
res, err := engineNameResolution(engineName, IacEnginePath)
if err != nil || res != engineName {
t.Fatalf("Expected enginename in return , got %v , err %d", res, err)
}
}

func TestEngineName_Resolution_check_fallBackPath_for_MAC_Linux(t *testing.T) {
origGOOS := getOS
defer func() { getOS = origGOOS }()
getOS = func() string { return "darwin" } // or "linux"

testPath := IacEnginePath
testFile := filepath.Join(testPath, "docker")

err := os.WriteFile(testFile, []byte("#!/bin/sh\necho test"), 0755)
if err != nil {
t.Skipf("skipping test, cannot write file: %v", err)
}
defer func() { _ = os.Remove(testFile) }()

oldPATH := os.Getenv("PATH")
defer func() { _ = os.Setenv("PATH", oldPATH) }()
_ = os.Setenv("PATH", "")

result, err := engineNameResolution("docker", IacEnginePath)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}

expected := filepath.Join(IacEnginePath, "docker")
if result != expected {
t.Fatalf("expected %q, got %q", expected, result)
}
}
1 change: 1 addition & 0 deletions test/integration/data/manifests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ tzdata==2025.1
# Exact version

flask==3.1.2
werkzeug>=3.0.6

# Range: greater than or equal and less than

Expand Down
52 changes: 52 additions & 0 deletions test/integration/iac-realtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"runtime"
"testing"

commonParams "github.com/checkmarx/ast-cli/internal/params"
Expand Down Expand Up @@ -258,4 +259,55 @@ func TestIacRealtimeScan_ResultsValidation_DetailedCheck(t *testing.T) {
"EndIndex should be >= StartIndex")
}
}

}

func TestEngineNameResolution_engine_NotFound(t *testing.T) {
oldPath := os.Getenv("PATH")
t.Cleanup(func() {
_ = os.Setenv("PATH", oldPath)
})
_ = os.Setenv("PATH", "")

args := []string{
"scan", "iac-realtime",
flag(commonParams.SourcesFlag), "data/positive1.tf",
flag(commonParams.EngineFlag), "docker",
}
err, _ := executeCommand(t, args...)

if err == nil {
t.Fatalf("expected error, got nil")
}
assert.NotNil(t, err, "docker executables not set in PATH or usr/local/bin")
}

func TestEngineNameResolution_containerEngine_Found_inPATH_exists(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping test on windows")
}
oldPath := os.Getenv("PATH")
t.Cleanup(func() {
_ = os.Setenv("PATH", oldPath)
})
_ = os.Setenv("PATH", "/usr/local/bin:"+os.Getenv("PATH"))
path := "/usr/local/bin"
testFile := filepath.Join(path, "docker.exe")

err := os.WriteFile(testFile, []byte("#!/bin/sh\necho test"), 0755)
if err != nil {
t.Skipf("skipping test , cannot write the file %s", err)
}
defer func() {
_ = os.Remove(testFile)
}()

args := []string{
"scan", "iac-realtime",
flag(commonParams.SourcesFlag), "data/positive1.tf",
flag(commonParams.EngineFlag), "docker",
}
err, _ = executeCommand(t, args...)

assert.Nil(t, err, "docker executables are found in PATH or usr/local/bin")
}
Loading