Skip to content
Open
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
2 changes: 1 addition & 1 deletion internal/commands/.scripts/up.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ rm -rf ScaResolver-linux64.tar.gz
# ignore mock, wrappers, cmd, logger, and osinstaller packages, as they checked by integration tests
gotestsum --junitfile junit.xml --format testname -- \
$(go list ./... | grep -v "mock" | grep -v "wrappers" | grep -v "bitbucketserver" | grep -v "logger" | grep -v "cmd" | grep -v "osinstaller") \
-timeout 25m -coverprofile cover.out
-timeout 35m -coverprofile cover.out
5 changes: 3 additions & 2 deletions internal/commands/agenthooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ func NewHookCheckAuthCommand(jwt wrappers.JWTWrapper) *cobra.Command {
// Routes are declared per-agent in cxhooks.Agents (cx package).
// =============================================================================

func HookDispatchCommands(jwt wrappers.JWTWrapper, featureFlags wrappers.FeatureFlagsWrapper, realtimeScanner wrappers.RealtimeScannerWrapper, telemetryWrapper wrappers.TelemetryWrapper) []*cobra.Command {
// HookDispatchCommands returns the hidden dispatch subcommands, one per agent hook route.
func HookDispatchCommands(jwt wrappers.JWTWrapper, realtimeScanner wrappers.RealtimeScannerWrapper, telemetryWrapper wrappers.TelemetryWrapper) []*cobra.Command {
var cmds []*cobra.Command
for _, agent := range cxhooks.Agents {
for _, r := range agent.Routes {
Expand All @@ -175,7 +176,7 @@ func HookDispatchCommands(jwt wrappers.JWTWrapper, featureFlags wrappers.Feature
Run: func(cmd *cobra.Command, _ []string) {
if isLicensed(jwt) {
logger.PrintIfVerbose(fmt.Sprintf("hooks: registering security guardrails for %s", cmd.Use))
cxhooks.RegisterGuardrails(jwt, featureFlags, realtimeScanner, telemetryWrapper)
cxhooks.RegisterGuardrails(jwt, realtimeScanner, telemetryWrapper)
} else {
logger.PrintIfVerbose(fmt.Sprintf("hooks: registering pass-through for %s", cmd.Use))
cxhooks.RegisterPassThrough()
Expand Down
8 changes: 4 additions & 4 deletions internal/commands/agenthooks/cx/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,10 @@ func promptWorkspaceRoots(raw any) []string {

// RegisterGuardrails wires the four guardrail handlers and instantiates the
// SCA, KICS, and secret-detection scanners used by the Bash and FileEdit handlers.
func RegisterGuardrails(jwt wrappers.JWTWrapper, ff wrappers.FeatureFlagsWrapper, rt wrappers.RealtimeScannerWrapper, tel wrappers.TelemetryWrapper) {
scaScanner = sca.NewScanner(jwt, ff, rt)
kicsScanner = kics.NewScanner(jwt, ff)
secretsScanner = secrets.NewScanner(jwt, ff)
func RegisterGuardrails(jwt wrappers.JWTWrapper, rt wrappers.RealtimeScannerWrapper, tel wrappers.TelemetryWrapper) {
scaScanner = sca.NewScanner(jwt, rt)
kicsScanner = kics.NewScanner(jwt)
secretsScanner = secrets.NewScanner(jwt)
telemetryWrapper = tel
agenthooks.WhenAgentIdle(cxWhenAgentIdle)
agenthooks.BeforeToolCall(cxBeforeToolCall)
Expand Down
7 changes: 3 additions & 4 deletions internal/commands/agenthooks/cx/hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,6 @@ func TestRegisterGuardrails_AndPassThrough(t *testing.T) {

RegisterGuardrails(
&mock.JWTMockWrapper{},
mock.FeatureFlagsMockWrapper{},
mock.NewRealtimeScannerMockWrapper(),
mock.TelemetryMockWrapper{},
)
Expand Down Expand Up @@ -831,7 +830,7 @@ func TestRegisterGuardrails_SetsScanners(t *testing.T) {
}()

telemetry := mock.TelemetryMockWrapper{}
RegisterGuardrails(&mock.JWTMockWrapper{}, &mock.FeatureFlagsMockWrapper{}, &mock.RealtimeScannerMockWrapper{}, telemetry)
RegisterGuardrails(&mock.JWTMockWrapper{}, &mock.RealtimeScannerMockWrapper{}, telemetry)

assert.NotNil(t, scaScanner)
assert.NotNil(t, kicsScanner)
Expand All @@ -843,7 +842,7 @@ func TestRegisterPassThrough_ClearsScanners(t *testing.T) {
prevSca, prevKics, prevSecrets := scaScanner, kicsScanner, secretsScanner
defer func() { scaScanner, kicsScanner, secretsScanner = prevSca, prevKics, prevSecrets }()

RegisterGuardrails(&mock.JWTMockWrapper{}, &mock.FeatureFlagsMockWrapper{}, &mock.RealtimeScannerMockWrapper{}, mock.TelemetryMockWrapper{})
RegisterGuardrails(&mock.JWTMockWrapper{}, &mock.RealtimeScannerMockWrapper{}, mock.TelemetryMockWrapper{})
assert.NotNil(t, scaScanner)

RegisterPassThrough()
Expand Down Expand Up @@ -917,7 +916,7 @@ func TestCodexApplyPatch_KICSFinding_DeniesEndToEnd(t *testing.T) {
resetHookGlobals(t)

agenthooks.ClearRoutes()
RegisterGuardrails(&mock.JWTMockWrapper{}, &mock.FeatureFlagsMockWrapper{}, &mock.RealtimeScannerMockWrapper{}, mock.TelemetryMockWrapper{})
RegisterGuardrails(&mock.JWTMockWrapper{}, &mock.RealtimeScannerMockWrapper{}, mock.TelemetryMockWrapper{})
t.Cleanup(agenthooks.ClearRoutes)

// RegisterGuardrails wires up real SCA/KICS scanners; swap KICS's underlying
Expand Down
7 changes: 3 additions & 4 deletions internal/commands/agenthooks/guardrails/kics/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
// substitute scan via NewScannerWithFunc.
type Scanner struct {
jwt wrappers.JWTWrapper
ff wrappers.FeatureFlagsWrapper
scan func(path, ignoreFilePath string) ([]iacrealtime.IacRealtimeResult, error)

// engine that last completed a scan. A delta edit scans twice (proposed and
Expand All @@ -30,8 +29,8 @@ type Scanner struct {
}

// NewScanner returns a Scanner backed by the given wrappers.
func NewScanner(jwt wrappers.JWTWrapper, ff wrappers.FeatureFlagsWrapper) *Scanner {
s := &Scanner{jwt: jwt, ff: ff}
func NewScanner(jwt wrappers.JWTWrapper) *Scanner {
s := &Scanner{jwt: jwt}
s.scan = s.runRealScan
return s
}
Expand Down Expand Up @@ -143,7 +142,7 @@ func scanErrorAfterNoFallback(err error) error {
}

func (s *Scanner) runRealScan(path, ignoreFilePath string) ([]iacrealtime.IacRealtimeResult, error) {
svc := iacrealtime.NewIacRealtimeService(s.jwt, s.ff, iacrealtime.NewContainerManager())
svc := iacrealtime.NewIacRealtimeService(s.jwt, iacrealtime.NewContainerManager())

engine := s.engine
if engine == "" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ import (

func TestNewScanner_ReturnsValidScanner(t *testing.T) {
jwt := &mock.JWTMockWrapper{}
ff := &mock.FeatureFlagsMockWrapper{}

s := NewScanner(jwt, ff)
s := NewScanner(jwt)
if s == nil {
t.Fatal("expected non-nil scanner")
}
Expand All @@ -29,9 +28,8 @@ func TestNewScanner_ReturnsValidScanner(t *testing.T) {

func TestNewScanner_HoldsWrappers(t *testing.T) {
jwt := &mock.JWTMockWrapper{}
ff := &mock.FeatureFlagsMockWrapper{}

s := NewScanner(jwt, ff)
s := NewScanner(jwt)
assert.NotNil(t, s)
// Verify wrappers are internally stored
assert.NotNil(t, s.scan)
Expand Down
7 changes: 3 additions & 4 deletions internal/commands/agenthooks/guardrails/secrets/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import (
// Tests substitute scan via NewScannerWithFunc.
type Scanner struct {
jwt wrappers.JWTWrapper
ff wrappers.FeatureFlagsWrapper
scan func(sourcePath, content, ignoreFilePath string) ([]secretsrealtime.SecretsRealtimeResult, error)
}

// NewScanner returns a Scanner backed by the secrets realtime engine.
func NewScanner(jwt wrappers.JWTWrapper, ff wrappers.FeatureFlagsWrapper) *Scanner {
s := &Scanner{jwt: jwt, ff: ff}
func NewScanner(jwt wrappers.JWTWrapper) *Scanner {
s := &Scanner{jwt: jwt}
s.scan = s.runRealScan
return s
}
Expand All @@ -27,6 +26,6 @@ func NewScannerWithFunc(f func(sourcePath, content, ignoreFilePath string) ([]se
}

func (s *Scanner) runRealScan(sourcePath, content, ignoreFilePath string) ([]secretsrealtime.SecretsRealtimeResult, error) {
svc := secretsrealtime.NewSecretsRealtimeService(s.jwt, s.ff)
svc := secretsrealtime.NewSecretsRealtimeService(s.jwt)
return svc.ScanContent(sourcePath, content, ignoreFilePath)
}
7 changes: 3 additions & 4 deletions internal/commands/agenthooks/sca/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const (
// substitute scan via NewScannerWithFunc.
type Scanner struct {
JWT wrappers.JWTWrapper
FF wrappers.FeatureFlagsWrapper
RT wrappers.RealtimeScannerWrapper
scan func(path string) (*ossrealtime.OssPackageResults, error)
// workDir is the workspace root reported by the hook event (its "cwd"). The
Expand All @@ -35,8 +34,8 @@ type Scanner struct {

// NewScanner returns a Scanner backed by the given wrappers. The scan call
// goes through ossrealtime.NewOssRealtimeService.RunOssRealtimeScan.
func NewScanner(jwt wrappers.JWTWrapper, ff wrappers.FeatureFlagsWrapper, rt wrappers.RealtimeScannerWrapper) *Scanner {
s := &Scanner{JWT: jwt, FF: ff, RT: rt}
func NewScanner(jwt wrappers.JWTWrapper, rt wrappers.RealtimeScannerWrapper) *Scanner {
s := &Scanner{JWT: jwt, RT: rt}
s.scan = s.runRealScan
return s
}
Expand All @@ -48,7 +47,7 @@ func NewScannerWithFunc(f func(path string) (*ossrealtime.OssPackageResults, err
}

func (s *Scanner) runRealScan(path string) (*ossrealtime.OssPackageResults, error) {
svc := ossrealtime.NewOssRealtimeService(s.JWT, s.FF, s.RT)
svc := ossrealtime.NewOssRealtimeService(s.JWT, s.RT)
return svc.RunOssRealtimeScan(path, existingIgnoreFilePath(s.workDir))
}

Expand Down
3 changes: 1 addition & 2 deletions internal/commands/containers-realtime-engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
func RunScanContainersRealtimeCommand(
realtimeScannerWrapper wrappers.RealtimeScannerWrapper,
jwtWrapper wrappers.JWTWrapper,
featureFlagWrapper wrappers.FeatureFlagsWrapper,
) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, _ []string) error {
fileSourceFlag, _ := cmd.Flags().GetString(commonParams.SourcesFlag)
Expand All @@ -22,7 +21,7 @@ func RunScanContainersRealtimeCommand(

ignoredFilePathFlag, _ := cmd.Flags().GetString(commonParams.IgnoredFilePathFlag)

containersRealtimeService := containersrealtime.NewContainersRealtimeService(jwtWrapper, featureFlagWrapper, realtimeScannerWrapper)
containersRealtimeService := containersrealtime.NewContainersRealtimeService(jwtWrapper, realtimeScannerWrapper)

images, err := containersRealtimeService.RunContainersRealtimeScan(fileSourceFlag, ignoredFilePathFlag)
if err != nil {
Expand Down
8 changes: 0 additions & 8 deletions internal/commands/containers-realtime-engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ import (
"strings"
"testing"

"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/checkmarx/ast-cli/internal/wrappers/mock"
"github.com/stretchr/testify/assert"
)

func TestRunScanContainersRealtimeCommand_EmptyFilePath_Fails(t *testing.T) {
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
err := execCmdNotNilAssertion(t, "scan", "containers-realtime", "-s", "")
assert.NotNil(t, err)
assert.True(t, strings.Contains(err.Error(), "file path is required") ||
Expand All @@ -21,29 +18,24 @@ func TestRunScanContainersRealtimeCommand_EmptyFilePath_Fails(t *testing.T) {
}

func TestRunScanContainersRealtimeCommand_MissingSourcesFlag_Fails(t *testing.T) {
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
err := execCmdNotNilAssertion(t, "scan", "containers-realtime")
assert.NotNil(t, err)
}

func TestRunScanContainersRealtimeCommand_Dockerfile_Success(t *testing.T) {
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
execCmdNilAssertion(t, "scan", "containers-realtime", "-s", "data/Dockerfile")
}

func TestRunScanContainersRealtimeCommand_ContainersTestdata_Success(t *testing.T) {
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
execCmdNilAssertion(t, "scan", "containers-realtime", "-s", "data/containers/testdata/Dockerfile")
}

func TestRunScanContainersRealtimeCommand_MissingFile_Fails(t *testing.T) {
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
err := execCmdNotNilAssertion(t, "scan", "containers-realtime", "-s", "data/does-not-exist-Dockerfile")
assert.NotNil(t, err)
}

func TestRunScanContainersRealtimeCommand_WithIgnoredFilePathFlag(t *testing.T) {
mock.Flag = wrappers.FeatureFlagResponseModel{Name: wrappers.OssRealtimeEnabled, Status: true}
// Empty/missing ignore file should still succeed (service fail-opens on ignore load).
execCmdNilAssertion(t,
"scan", "containers-realtime",
Expand Down
18 changes: 6 additions & 12 deletions internal/commands/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

// NewHooksCommand creates the hooks command with pre-commit subcommand
func NewHooksCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper, realtimeScannerWrapper wrappers.RealtimeScannerWrapper, telemetryWrapper wrappers.TelemetryWrapper) *cobra.Command {
func NewHooksCommand(jwtWrapper wrappers.JWTWrapper, realtimeScannerWrapper wrappers.RealtimeScannerWrapper, telemetryWrapper wrappers.TelemetryWrapper) *cobra.Command {
hooksCmd := &cobra.Command{
Use: "hooks",
Short: "Manage Git hooks and AI coding agent hooks",
Expand All @@ -33,8 +33,8 @@ func NewHooksCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrapper
}

// Add pre-commit, pre-receive, and agenthooks management subcommands
hooksCmd.AddCommand(PreCommitCommand(jwtWrapper, featureFlagsWrapper))
hooksCmd.AddCommand(PreReceiveCommand(jwtWrapper, featureFlagsWrapper))
hooksCmd.AddCommand(PreCommitCommand(jwtWrapper))
hooksCmd.AddCommand(PreReceiveCommand(jwtWrapper))
hooksCmd.AddCommand(NewAgentHooksCommand())

// Hidden machine-readable readiness probe: cx hooks check-auth
Expand All @@ -45,21 +45,15 @@ func NewHooksCommand(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrapper
// Register all hidden hook dispatch subcommands so that cx itself acts as
// the hook binary. Agents invoke: cx hooks <route-name>
// e.g. cx hooks claude-pre-tool-use
for _, dispatchCmd := range HookDispatchCommands(jwtWrapper, featureFlagsWrapper, realtimeScannerWrapper, telemetryWrapper) {
for _, dispatchCmd := range HookDispatchCommands(jwtWrapper, realtimeScannerWrapper, telemetryWrapper) {
hooksCmd.AddCommand(dispatchCmd)
}

return hooksCmd
}

func validateLicense(jwtWrapper wrappers.JWTWrapper, featureFlagsWrapper wrappers.FeatureFlagsWrapper) error {
scsLicensingV2Flag, _ := wrappers.GetSpecificFeatureFlag(featureFlagsWrapper, wrappers.ScsLicensingV2Enabled)
var licenseName string
if scsLicensingV2Flag.Status {
licenseName = params.SecretDetectionLabel
} else {
licenseName = params.EnterpriseSecretsLabel
}
func validateLicense(jwtWrapper wrappers.JWTWrapper) error {
licenseName := params.SecretDetectionLabel

logger.PrintIfVerbose("hooks: checking license for " + licenseName)

Expand Down
3 changes: 1 addition & 2 deletions internal/commands/iac-realtime-engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (

func RunScanIacRealtimeCommand(
jwtWrapper wrappers.JWTWrapper,
featureFlagWrapper wrappers.FeatureFlagsWrapper,
) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, _ []string) error {
fileSourceFlag, _ := cmd.Flags().GetString(commonParams.SourcesFlag)
Expand All @@ -22,7 +21,7 @@ func RunScanIacRealtimeCommand(
ignoredFilePathFlag, _ := cmd.Flags().GetString(commonParams.IgnoredFilePathFlag)
engine, _ := cmd.Flags().GetString(commonParams.EngineFlag)

iacRealtimeService := iacrealtime.NewIacRealtimeService(jwtWrapper, featureFlagWrapper, iacrealtime.NewContainerManager())
iacRealtimeService := iacrealtime.NewIacRealtimeService(jwtWrapper, iacrealtime.NewContainerManager())

results, err := iacRealtimeService.RunIacRealtimeScan(fileSourceFlag, engine, ignoredFilePathFlag)
if err != nil {
Expand Down
Loading
Loading