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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM checkmarx/bash:5.3-r12-f48dd8a45af577@sha256:f48dd8a45af5771e98cb5d56d204ada0e0dc045093ca3272b4c3dbe3f85e6e4f
FROM checkmarx/bash:5.3-r12-02a1aad732e7ab@sha256:02a1aad732e7ab0659b212d83c2a0bb548d9d8bdec23336f6c0b44f8f3435cb8
USER nonroot

COPY cx /app/bin/cx
Expand Down
2 changes: 2 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ func main() {
risksOverviewWrapper := wrappers.NewHTTPRisksOverviewWrapper(risksOverview, apiSecurityResult)
riskManagementWrapper := wrappers.NewHTTPRiskManagementWrapper(riskManagement)
scsScanOverviewWrapper := wrappers.NewHTTPScanOverviewWrapper(scsScanOverview)
scanSummaryWrapper := wrappers.NewHTTPScanSummaryWrapper(scanSummary)
resultsWrapper := wrappers.NewHTTPResultsWrapper(results, scanSummary)
authWrapper := wrappers.NewAuthHTTPWrapper()
resultsPredicatesWrapper := wrappers.NewResultsPredicatesHTTPWrapper()
Expand Down Expand Up @@ -116,6 +117,7 @@ func main() {
risksOverviewWrapper,
riskManagementWrapper,
scsScanOverviewWrapper,
scanSummaryWrapper,
authWrapper,
logsWrapper,
groupsWrapper,
Expand Down
59 changes: 55 additions & 4 deletions internal/commands/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func NewResultsCommand(
risksOverviewWrapper wrappers.RisksOverviewWrapper,
riskManagementWrapper wrappers.RiskManagementWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
scanSummaryWrapper wrappers.ScanSummaryWrapper,
policyWrapper wrappers.PolicyWrapper,
featureFlagsWrapper wrappers.FeatureFlagsWrapper,
jwtWrapper wrappers.JWTWrapper,
Expand All @@ -222,7 +223,7 @@ func NewResultsCommand(
},
}
showResultCmd := resultShowSubCommand(resultsWrapper, scanWrapper, exportWrapper, resultsPdfReportsWrapper, resultsJSONReportsWrapper,
risksOverviewWrapper, scsScanOverviewWrapper, policyWrapper, featureFlagsWrapper, jwtWrapper)
risksOverviewWrapper, scsScanOverviewWrapper, scanSummaryWrapper, policyWrapper, featureFlagsWrapper, jwtWrapper)
codeBashingCmd := resultCodeBashing(codeBashingWrapper)
bflResultCmd := resultBflSubCommand(bflWrapper)
exitCodeSubcommand := exitCodeSubCommand(scanWrapper)
Expand Down Expand Up @@ -282,6 +283,7 @@ func resultShowSubCommand(
resultsJSONReportsWrapper wrappers.ResultsJSONWrapper,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
scanSummaryWrapper wrappers.ScanSummaryWrapper,
policyWrapper wrappers.PolicyWrapper,
featureFlagsWrapper wrappers.FeatureFlagsWrapper,
jwtWrapper wrappers.JWTWrapper,
Expand All @@ -295,7 +297,7 @@ func resultShowSubCommand(
$ cx results show --scan-id <scan Id>
`,
),
RunE: runGetResultCommand(resultsWrapper, scanWrapper, exportWrapper, resultsPdfReportsWrapper, resultsJSONReportsWrapper, risksOverviewWrapper, scsScanOverviewWrapper, policyWrapper, featureFlagsWrapper, jwtWrapper),
RunE: runGetResultCommand(resultsWrapper, scanWrapper, exportWrapper, resultsPdfReportsWrapper, resultsJSONReportsWrapper, risksOverviewWrapper, scsScanOverviewWrapper, scanSummaryWrapper, policyWrapper, featureFlagsWrapper, jwtWrapper),
}
addScanIDFlag(resultShowCmd, "ID to report on")
addResultFormatFlag(
Expand Down Expand Up @@ -727,6 +729,7 @@ func summaryReport(
policies *wrappers.PolicyResponseModel,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
scanSummaryWrapper wrappers.ScanSummaryWrapper,
featureFlagsWrapper wrappers.FeatureFlagsWrapper,
results *wrappers.ScanResultsCollection,
resultsParams map[string]string,
Expand Down Expand Up @@ -756,6 +759,15 @@ func summaryReport(
summary.SCSOverview = SCSOverview
}

if summary.HasAISC() {
// Getting AISC information from scan-summary API
aiscInfo, err := getAISCInfoFromScanSummary(scanSummaryWrapper, summary.ScanID)
if err != nil {
return nil, err
}
summary.AISCInfo = aiscInfo
}

if policies != nil {
summary.Policies = filterViolatedRules(*policies)
}
Expand Down Expand Up @@ -924,6 +936,10 @@ func writeConsoleSummary(summary *wrappers.ResultSummary, ignorePolicyFlagOmit b
printSCSSummary(summary.SCSOverview.MicroEngineOverviews)
}

if summary.HasAISC() {
printAISCSummary(summary)
}

fmt.Printf(" Checkmarx One - Scan Summary & Details: %s\n", summary.BaseURI)
} else {
fmt.Printf("Scan executed in asynchronous mode or still running. Hence, no results generated.\n")
Expand Down Expand Up @@ -1013,6 +1029,15 @@ func printSCSTableRow(microEngineOverview *wrappers.MicroEngineOverview) {
}
}

func printAISCSummary(summary *wrappers.ResultSummary) {
fmt.Printf(" AI SUPPLY CHAIN ENGINE SUMMARY\n")
fmt.Printf(" --------------------------------------------------------------------- \n")
fmt.Printf(" | %-32s %30s |\n", "Category", "Count")
fmt.Printf(" | %-32s %30d |\n", "Total Assets", summary.AISCAssetsValue())
fmt.Printf(" | %-32s %30d |\n", "Total Asset Types", summary.AISCAssetTypesValue())
fmt.Printf(" --------------------------------------------------------------------- \n\n")
}

func getCountValue(count int) interface{} {
if count < 0 {
return disabledString
Expand Down Expand Up @@ -1063,6 +1088,7 @@ func runGetResultCommand(
resultsJSONReportsWrapper wrappers.ResultsJSONWrapper,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
scanSummaryWrapper wrappers.ScanSummaryWrapper,
policyWrapper wrappers.PolicyWrapper,
featureFlagsWrapper wrappers.FeatureFlagsWrapper,
jwtWrapper wrappers.JWTWrapper,
Expand Down Expand Up @@ -1129,7 +1155,7 @@ func runGetResultCommand(
resultsParams[commonParams.SastRedundancyFlag] = ""
}

_, err = CreateScanReport(resultsWrapper, risksOverviewWrapper, scsScanOverviewWrapper, exportWrapper,
_, err = CreateScanReport(resultsWrapper, risksOverviewWrapper, scsScanOverviewWrapper, scanSummaryWrapper, exportWrapper,
policyResponseModel, resultsPdfReportsWrapper, resultsJSONReportsWrapper, scan, format, formatPdfToEmail, formatPdfOptions,
formatSbomOptions, targetFile, targetPath, agent, resultsParams, featureFlagsWrapper, ignorePolicyFlagOmit)
return err
Expand Down Expand Up @@ -1220,6 +1246,7 @@ func CreateScanReport(
resultsWrapper wrappers.ResultsWrapper,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
scanSummaryWrapper wrappers.ScanSummaryWrapper,
exportWrapper wrappers.ExportWrapper,
policyResponseModel *wrappers.PolicyResponseModel,
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper,
Expand Down Expand Up @@ -1257,7 +1284,7 @@ func CreateScanReport(
}
isSummaryNeeded := verifyFormatsByReportList(reportList, summaryFormats...)
if isSummaryNeeded && !scanPending {
summary, err = summaryReport(summary, policyResponseModel, risksOverviewWrapper, scsScanOverviewWrapper, featureFlagsWrapper, results, resultsParams)
summary, err = summaryReport(summary, policyResponseModel, risksOverviewWrapper, scsScanOverviewWrapper, scanSummaryWrapper, featureFlagsWrapper, results, resultsParams)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1413,6 +1440,30 @@ func getScanOverviewForSCSScanner(
return nil, nil
}

func getAISCInfoFromScanSummary(
scanSummaryWrapper wrappers.ScanSummaryWrapper,
scanID string,
) (*wrappers.AISCInfo, error) {
var scanSummaryModel *wrappers.ScanSummariesModel
var errorModel *wrappers.WebError

scanSummaryModel, errorModel, err := scanSummaryWrapper.GetScanSummaryByScanID(scanID)
if err != nil {
return nil, errors.Wrapf(err, "AISC: %s", failedListingResults)
}
if errorModel != nil {
return nil, errors.Errorf("AISC: %s: CODE: %d, %s", failedListingResults, errorModel.Code, errorModel.Message)
}
if scanSummaryModel != nil && len(scanSummaryModel.ScansSummaries) > 0 {
aiscCounters := scanSummaryModel.ScansSummaries[0].AiscCounters
return &wrappers.AISCInfo{
TotalAssets: aiscCounters.AssetsCounter, // Map from API response
TotalAssetTypes: aiscCounters.AssetTypesCounter, // Map from API response
}, nil
}
return nil, nil
}

func isScanPending(scanStatus string) bool {
return !strings.EqualFold(scanStatus, statusCompleted) &&
!strings.EqualFold(scanStatus, statusPartial) &&
Expand Down
138 changes: 138 additions & 0 deletions internal/commands/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/checkmarx/ast-cli/internal/params"
"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/checkmarx/ast-cli/internal/wrappers/mock"
"github.com/pkg/errors"
assertion "github.com/stretchr/testify/assert"
"golang.org/x/text/cases"
"golang.org/x/text/language"
Expand Down Expand Up @@ -1741,3 +1742,140 @@ func TestGetFilterResultsForAPISecScanner(t *testing.T) {
}
}
}

func TestGetAISCInfoFromScanSummary_Success(t *testing.T) {
mockWrapper := &mock.ScanSummaryMockWrapper{}
scanID := "test-scan-id"

result, err := getAISCInfoFromScanSummary(mockWrapper, scanID)

assert.NilError(t, err)
assert.Assert(t, result != nil, "Expected non-nil result")
if result == nil {
return
}
assert.Equal(t, result.TotalAssets, 0, "Expected TotalAssets to be 0")
assert.Equal(t, result.TotalAssetTypes, 0, "Expected TotalAssetTypes to be 0")
}

func TestGetAISCInfoFromScanSummary_WithNonZeroValues(t *testing.T) {
// Create a custom mock wrapper with non-zero values
mockWrapper := &customScanSummaryMockWrapper{
assetsCounter: 10,
assetTypesCounter: 5,
}
scanID := "test-scan-id"

result, err := getAISCInfoFromScanSummary(mockWrapper, scanID)

assert.NilError(t, err)
assert.Assert(t, result != nil, "Expected non-nil result")
if result == nil {
return
}
assert.Equal(t, result.TotalAssets, 10, "Expected TotalAssets to be 10")
assert.Equal(t, result.TotalAssetTypes, 5, "Expected TotalAssetTypes to be 5")
}

func TestGetAISCInfoFromScanSummary_EmptyScansSummaries(t *testing.T) {
mockWrapper := &customScanSummaryMockWrapper{
emptySummaries: true,
}
scanID := "test-scan-id"

result, err := getAISCInfoFromScanSummary(mockWrapper, scanID)

assert.NilError(t, err)
assert.Assert(t, result == nil, "Expected nil result for empty summaries")
}

func TestGetAISCInfoFromScanSummary_NilScanSummaryModel(t *testing.T) {
mockWrapper := &customScanSummaryMockWrapper{
nilModel: true,
}
scanID := "test-scan-id"

result, err := getAISCInfoFromScanSummary(mockWrapper, scanID)

assert.NilError(t, err)
assert.Assert(t, result == nil, "Expected nil result for nil scan summary model")
}

func TestGetAISCInfoFromScanSummary_Error(t *testing.T) {
mockWrapper := &customScanSummaryMockWrapper{
returnError: true,
}
scanID := "test-scan-id"

result, err := getAISCInfoFromScanSummary(mockWrapper, scanID)

assert.Assert(t, err != nil, "Expected error")
assert.Assert(t, result == nil, "Expected nil result on error")
if err == nil {
return
}
assert.Assert(t, strings.Contains(err.Error(), "AISC"), "Expected error message to contain 'AISC'")
assert.Assert(t, strings.Contains(err.Error(), failedListingResults), "Expected error message to contain failedListingResults")
}

func TestGetAISCInfoFromScanSummary_WebError(t *testing.T) {
mockWrapper := &customScanSummaryMockWrapper{
returnWebError: true,
}
scanID := "test-scan-id"

result, err := getAISCInfoFromScanSummary(mockWrapper, scanID)

assert.Assert(t, err != nil, "Expected error")
assert.Assert(t, result == nil, "Expected nil result on web error")
if err == nil {
return
}
assert.Assert(t, strings.Contains(err.Error(), "AISC"), "Expected error message to contain 'AISC'")
assert.Assert(t, strings.Contains(err.Error(), failedListingResults), "Expected error message to contain failedListingResults")
assert.Assert(t, strings.Contains(err.Error(), "CODE: 400"), "Expected error message to contain error code")
assert.Assert(t, strings.Contains(err.Error(), "Bad Request"), "Expected error message to contain error message")
}

// Custom mock wrapper for testing different scenarios
type customScanSummaryMockWrapper struct {
assetsCounter int
assetTypesCounter int
emptySummaries bool
nilModel bool
returnError bool
returnWebError bool
}

func (m *customScanSummaryMockWrapper) GetScanSummaryByScanID(scanID string) (*wrappers.ScanSummariesModel, *wrappers.WebError, error) {
if m.returnError {
return nil, nil, errors.New("mock error from GetScanSummaryByScanID")
}
if m.returnWebError {
return nil, &wrappers.WebError{
Code: 400,
Message: "Bad Request",
}, nil
}
if m.nilModel {
return nil, nil, nil
}
if m.emptySummaries {
return &wrappers.ScanSummariesModel{
ScansSummaries: []wrappers.ScanSumaries{},
TotalCount: 0,
}, nil, nil
}
return &wrappers.ScanSummariesModel{
ScansSummaries: []wrappers.ScanSumaries{
{
ScanID: scanID,
AiscCounters: wrappers.AiscCounters{
AssetsCounter: m.assetsCounter,
AssetTypesCounter: m.assetTypesCounter,
},
},
},
TotalCount: 1,
}, nil, nil
}
3 changes: 3 additions & 0 deletions internal/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func NewAstCLI(
risksOverviewWrapper wrappers.RisksOverviewWrapper,
riskManagementWrapper wrappers.RiskManagementWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
scanSummaryWrapper wrappers.ScanSummaryWrapper,
authWrapper wrappers.AuthWrapper,
logsWrapper wrappers.LogsWrapper,
groupsWrapper wrappers.GroupsWrapper,
Expand Down Expand Up @@ -189,6 +190,7 @@ func NewAstCLI(
groupsWrapper,
risksOverviewWrapper,
scsScanOverviewWrapper,
scanSummaryWrapper,
jwtWrapper,
scaRealTimeWrapper,
policyWrapper,
Expand All @@ -213,6 +215,7 @@ func NewAstCLI(
risksOverviewWrapper,
riskManagementWrapper,
scsScanOverviewWrapper,
scanSummaryWrapper,
policyWrapper,
featureFlagsWrapper,
jwtWrapper,
Expand Down
2 changes: 2 additions & 0 deletions internal/commands/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func createASTTestCommand() *cobra.Command {
risksOverviewMockWrapper := &mock.RisksOverviewMockWrapper{}
riskManagementMockWrapper := &mock.RiskManagementMockWrapper{}
scsScanOverviewMockWrapper := &mock.ScanOverviewMockWrapper{}
scanSummaryMockWrapper := &mock.ScanSummaryMockWrapper{}
authWrapper := &mock.AuthMockWrapper{}
logsWrapper := &mock.LogsMockWrapper{}
codeBashingWrapper := &mock.CodeBashingMockWrapper{}
Expand Down Expand Up @@ -89,6 +90,7 @@ func createASTTestCommand() *cobra.Command {
risksOverviewMockWrapper,
riskManagementMockWrapper,
scsScanOverviewMockWrapper,
scanSummaryMockWrapper,
authWrapper,
logsWrapper,
groupsMockWrapper,
Expand Down
Loading
Loading