Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MC-553-Add SCS report to console #694

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func main() {
scanSummary := viper.GetString(params.ScanSummaryPathKey)
scaPackage := viper.GetString(params.ScaPackagePathKey)
risksOverview := viper.GetString(params.RisksOverviewPathKey)
scsScanOverview := viper.GetString(params.ScsScanOverviewPathKey)
uploads := viper.GetString(params.UploadsPathKey)
codebashing := viper.GetString(params.CodeBashingPathKey)
bfl := viper.GetString(params.BflPathKey)
Expand All @@ -61,6 +62,7 @@ func main() {
projectsWrapper := wrappers.NewHTTPProjectsWrapper(projects)
applicationsWrapper := wrappers.NewApplicationsHTTPWrapper(applications)
risksOverviewWrapper := wrappers.NewHTTPRisksOverviewWrapper(risksOverview)
scsScanOverviewWrapper := wrappers.NewHTTPScanOverviewWrapper(scsScanOverview)
resultsWrapper := wrappers.NewHTTPResultsWrapper(results, scaPackage, scanSummary)
authWrapper := wrappers.NewAuthHTTPWrapper()
resultsPredicatesWrapper := wrappers.NewResultsPredicatesHTTPWrapper()
Expand Down Expand Up @@ -93,6 +95,7 @@ func main() {
projectsWrapper,
resultsWrapper,
risksOverviewWrapper,
scsScanOverviewWrapper,
authWrapper,
logsWrapper,
groupsWrapper,
Expand Down
97 changes: 93 additions & 4 deletions internal/commands/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@ const (
scanFailedString = "Failed"
scanCanceledString = "Canceled"
scanSuccessString = "Completed"
scanPartialString = "Partial"
scsScanUnavailableString = ""
notAvailableNumber = -1
scanFailedNumber = -2
scanCanceledNumber = -3
scanPartialNumber = -4
defaultPaddingSize = -13
boldFormat = "\033[1m%s\033[0m"
scanPendingMessage = "Scan triggered in asynchronous mode or still running. Click more details to get the full status."
Expand Down Expand Up @@ -148,6 +151,7 @@ func NewResultsCommand(
codeBashingWrapper wrappers.CodeBashingWrapper,
bflWrapper wrappers.BflWrapper,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
policyWrapper wrappers.PolicyWrapper,
) *cobra.Command {
resultCmd := &cobra.Command{
Expand All @@ -161,7 +165,7 @@ func NewResultsCommand(
),
},
}
showResultCmd := resultShowSubCommand(resultsWrapper, scanWrapper, resultsSbomWrapper, resultsPdfReportsWrapper, risksOverviewWrapper, policyWrapper)
showResultCmd := resultShowSubCommand(resultsWrapper, scanWrapper, resultsSbomWrapper, resultsPdfReportsWrapper, risksOverviewWrapper, scsScanOverviewWrapper, policyWrapper)
codeBashingCmd := resultCodeBashing(codeBashingWrapper)
bflResultCmd := resultBflSubCommand(bflWrapper)
resultCmd.AddCommand(
Expand All @@ -176,6 +180,7 @@ func resultShowSubCommand(
resultsSbomWrapper wrappers.ResultsSbomWrapper,
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
policyWrapper wrappers.PolicyWrapper,
) *cobra.Command {
resultShowCmd := &cobra.Command{
Expand All @@ -187,7 +192,7 @@ func resultShowSubCommand(
$ cx results show --scan-id <scan Id>
`,
),
RunE: runGetResultCommand(resultsWrapper, scanWrapper, resultsSbomWrapper, resultsPdfReportsWrapper, risksOverviewWrapper, policyWrapper),
RunE: runGetResultCommand(resultsWrapper, scanWrapper, resultsSbomWrapper, resultsPdfReportsWrapper, risksOverviewWrapper, scsScanOverviewWrapper, policyWrapper),
}
addScanIDFlag(resultShowCmd, "ID to report on.")
addResultFormatFlag(
Expand Down Expand Up @@ -358,11 +363,13 @@ func convertScanToResultsSummary(scanInfo *wrappers.ScanResponseModel, resultsWr
sastIssues := 0
scaIssues := 0
kicsIssues := 0
scsIssues := 0
enginesStatusCode := map[string]int{
commonParams.SastType: 0,
commonParams.ScaType: 0,
commonParams.KicsType: 0,
commonParams.APISecType: 0,
commonParams.ScsType: 0,
}

if len(scanInfo.StatusDetails) > 0 {
Expand All @@ -374,6 +381,8 @@ func convertScanToResultsSummary(scanInfo *wrappers.ScanResponseModel, resultsWr
scaIssues = notAvailableNumber
} else if statusDetailItem.Name == commonParams.KicsType {
kicsIssues = notAvailableNumber
} else if statusDetailItem.Name == commonParams.ScsType {
scsIssues = notAvailableNumber
}
}
switch statusDetailItem.Status {
Expand All @@ -398,6 +407,7 @@ func convertScanToResultsSummary(scanInfo *wrappers.ScanResponseModel, resultsWr
SastIssues: sastIssues,
KicsIssues: kicsIssues,
ScaIssues: scaIssues,
ScsIssues: scsIssues,
Tags: scanInfo.Tags,
ProjectName: scanInfo.ProjectName,
BranchName: scanInfo.Branch,
Expand All @@ -407,6 +417,7 @@ func convertScanToResultsSummary(scanInfo *wrappers.ScanResponseModel, resultsWr
commonParams.ScaType: {StatusCode: enginesStatusCode[commonParams.ScaType]},
commonParams.KicsType: {StatusCode: enginesStatusCode[commonParams.KicsType]},
commonParams.APISecType: {StatusCode: enginesStatusCode[commonParams.APISecType]},
commonParams.ScsType: {StatusCode: enginesStatusCode[commonParams.ScsType]},
},
}

Expand Down Expand Up @@ -434,6 +445,7 @@ func summaryReport(
summary *wrappers.ResultSummary,
policies *wrappers.PolicyResponseModel,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
results *wrappers.ScanResultsCollection,
) (*wrappers.ResultSummary, error) {
if summary.HasAPISecurity() {
Expand All @@ -444,6 +456,14 @@ func summaryReport(
summary.APISecurity = *apiSecRisks
}

if summary.HasSCS() {
SCSOverview, err := getScanOverviewForSCSScanner(scsScanOverviewWrapper, summary.ScanID)
if err != nil {
return nil, err
}
summary.SCSOverview = *SCSOverview
}

if policies != nil {
summary.Policies = filterViolatedRules(*policies)
}
Expand All @@ -453,6 +473,7 @@ func summaryReport(
setNotAvailableNumberIfZero(summary, &summary.SastIssues, commonParams.SastType)
setNotAvailableNumberIfZero(summary, &summary.ScaIssues, commonParams.ScaType)
setNotAvailableNumberIfZero(summary, &summary.KicsIssues, commonParams.KicsType)
setNotAvailableNumberIfZero(summary, &summary.ScsIssues, commonParams.ScsType)
setRiskMsgAndStyle(summary)
setNotAvailableEnginesStatusCode(summary)

Expand Down Expand Up @@ -495,7 +516,21 @@ func enhanceWithScanSummary(summary *wrappers.ResultSummary, results *wrappers.S
summary.EnginesResult[commonParams.APISecType].Medium = summary.APISecurity.Risks[2]
summary.EnginesResult[commonParams.APISecType].High = summary.APISecurity.Risks[1]
}
summary.TotalIssues = summary.SastIssues + summary.ScaIssues + summary.KicsIssues + summary.GetAPISecurityDocumentationTotal()

if summary.HasSCS() {
summary.EnginesResult[commonParams.ScsType].Info = summary.SCSOverview.RiskSummary[infoLabel]
summary.EnginesResult[commonParams.ScsType].Low = summary.SCSOverview.RiskSummary[lowLabel]
summary.EnginesResult[commonParams.ScsType].Medium = summary.SCSOverview.RiskSummary[mediumLabel]
summary.EnginesResult[commonParams.ScsType].High = summary.SCSOverview.RiskSummary[highLabel]

summary.ScsIssues = summary.SCSOverview.TotalRisksCount

// Special case for SCS where status is partial if any microengines failed
if summary.SCSOverview.Status == scanPartialString {
summary.EnginesResult[commonParams.ScsType].StatusCode = scanPartialNumber
}
}
summary.TotalIssues = summary.SastIssues + summary.ScaIssues + summary.KicsIssues + summary.GetAPISecurityDocumentationTotal() + summary.ScsIssues
}

func writeHTMLSummary(targetFile string, summary *wrappers.ResultSummary) error {
Expand Down Expand Up @@ -552,6 +587,10 @@ func writeConsoleSummary(summary *wrappers.ResultSummary) error {
printAPIsSecuritySummary(summary)
}

if summary.HasSCS() {
printSCSSummary(summary.SCSOverview.MicroEngineOverviews)
}

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 @@ -601,11 +640,38 @@ func printTableRow(title string, counts *wrappers.EngineResultSummary, statusNum
fmt.Printf(formatString, title, counts.High, counts.Medium, counts.Low, counts.Info, scanFailedString)
case scanCanceledNumber:
fmt.Printf(formatString, title, counts.High, counts.Medium, counts.Low, counts.Info, scanCanceledString)
case scanPartialNumber:
fmt.Printf(formatString, title, counts.High, counts.Medium, counts.Low, counts.Info, scanPartialString)
default:
fmt.Printf(formatString, title, counts.High, counts.Medium, counts.Low, counts.Info, scanSuccessString)
}
}

func printSCSSummary(microEngineOverviews []*wrappers.MicroEngineOverview) {
fmt.Printf(" Supply Chain Security Results\n")
fmt.Printf(" --------------------------------------------------------------- \n")
fmt.Println(" | High Medium Low Info Status |")
for _, microEngineOverview := range microEngineOverviews {
printSCSTableRow(microEngineOverview)
}
fmt.Printf(" --------------------------------------------------------------- \n\n")
}

func printSCSTableRow(microEngineOverview *wrappers.MicroEngineOverview) {
formatString := " | %-16s %4d %6d %4d %4d %-9s |\n"
notAvailableFormatString := " | %-16s %4s %6s %4s %4s %5s |\n"

riskSummary := microEngineOverview.RiskSummary
microEngineName := microEngineOverview.FullName

switch microEngineOverview.Status {
case scsScanUnavailableString:
fmt.Printf(notAvailableFormatString, microEngineName, notAvailableString, notAvailableString, notAvailableString, notAvailableString, notAvailableString)
default:
fmt.Printf(formatString, microEngineName, riskSummary[highLabel], riskSummary[mediumLabel], riskSummary[lowLabel], riskSummary[infoLabel], microEngineOverview.Status)
}
}

func printResultsSummaryTable(summary *wrappers.ResultSummary) {
totalHighIssues := summary.EnginesResult.GetHighIssues()
totalMediumIssues := summary.EnginesResult.GetMediumIssues()
Expand All @@ -620,6 +686,7 @@ func printResultsSummaryTable(summary *wrappers.ResultSummary) {
printTableRow("IAC", summary.EnginesResult[commonParams.KicsType], summary.EnginesResult[commonParams.KicsType].StatusCode)
printTableRow("SAST", summary.EnginesResult[commonParams.SastType], summary.EnginesResult[commonParams.SastType].StatusCode)
printTableRow("SCA", summary.EnginesResult[commonParams.ScaType], summary.EnginesResult[commonParams.ScaType].StatusCode)
printTableRow("SCS", summary.EnginesResult[commonParams.ScsType], summary.EnginesResult[commonParams.ScsType].StatusCode)

fmt.Println(" --------------------------------------------------- ")
fmt.Printf(" | %-4s %4d %6d %4d %4d %-9s |\n",
Expand All @@ -641,6 +708,7 @@ func runGetResultCommand(
resultsSbomWrapper wrappers.ResultsSbomWrapper,
resultsPdfReportsWrapper wrappers.ResultsPdfWrapper,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
policyWrapper wrappers.PolicyWrapper,
) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -693,6 +761,7 @@ func runGetResultCommand(
return CreateScanReport(
resultsWrapper,
risksOverviewWrapper,
scsScanOverviewWrapper,
resultsSbomWrapper,
policyResponseModel,
useSCALocalFlow,
Expand Down Expand Up @@ -752,6 +821,7 @@ func runGetCodeBashingCommand(
func CreateScanReport(
resultsWrapper wrappers.ResultsWrapper,
risksOverviewWrapper wrappers.RisksOverviewWrapper,
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
resultsSbomWrapper wrappers.ResultsSbomWrapper,
policyResponseModel *wrappers.PolicyResponseModel,
useSCALocalFlow bool,
Expand Down Expand Up @@ -788,7 +858,7 @@ func CreateScanReport(
}
isSummaryNeeded := verifyFormatsByReportList(reportList, summaryFormats...)
if isSummaryNeeded && !scanPending {
summary, err = summaryReport(summary, policyResponseModel, risksOverviewWrapper, results)
summary, err = summaryReport(summary, policyResponseModel, risksOverviewWrapper, scsScanOverviewWrapper, results)
if err != nil {
return err
}
Expand Down Expand Up @@ -875,6 +945,25 @@ func getResultsForAPISecScanner(
return nil, nil
}

func getScanOverviewForSCSScanner(
scsScanOverviewWrapper wrappers.ScanOverviewWrapper,
scanID string,
) (results *wrappers.SCSOverview, err error) {
var scsOverview *wrappers.SCSOverview
var errorModel *wrappers.WebError

scsOverview, errorModel, err = scsScanOverviewWrapper.GetSCSOverviewByScanID(scanID)
if err != nil {
return nil, errors.Wrapf(err, "SCS: %s", failedListingResults)
}
if errorModel != nil {
return nil, errors.Errorf("SCS: %s: CODE: %d, %s", failedListingResults, errorModel.Code, errorModel.Message)
} else if scsOverview != nil {
return scsOverview, nil
}
return nil, nil
}

func isScanPending(scanStatus string) bool {
return !(strings.EqualFold(scanStatus, "Completed") || strings.EqualFold(
scanStatus,
Expand Down
84 changes: 76 additions & 8 deletions internal/commands/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package commands
import (
"fmt"
"os"
"regexp"
"strings"
"testing"

"github.com/checkmarx/ast-cli/internal/commands/util/printer"
Expand All @@ -17,14 +19,15 @@ import (
const fileName = "cx_result"

const (
resultsCommand = "results"
codeBashingCommand = "codebashing"
vulnerabilityValue = "Reflected XSS All Clients"
languageValue = "PHP"
cweValue = "79"
jsonValue = "json"
tableValue = "table"
listValue = "list"
resultsCommand = "results"
codeBashingCommand = "codebashing"
vulnerabilityValue = "Reflected XSS All Clients"
languageValue = "PHP"
cweValue = "79"
jsonValue = "json"
tableValue = "table"
listValue = "list"
secretDetectionLine = "| Secret Detection 5 3 2 0 Completed |"
)

func flag(f string) string {
Expand Down Expand Up @@ -406,3 +409,68 @@ func Test_addPackageInformation(t *testing.T) {
actualFixLink := resultsModel.Results[0].ScanResultData.ScaPackageCollection.FixLink
assert.Equal(t, expectedFixLink, actualFixLink, "FixLink should match the result ID")
}

func TestRunGetResultsByScanIdSummaryConsoleFormatWithScsNotScanned(t *testing.T) {
buffer, err := executeRedirectedOsStdoutTestCommand(createASTTestCommandWithScs(false, false, false),
"results", "show", "--scan-id", "MOCK", "--report-format", "summaryConsole")
assert.NilError(t, err)

stdoutString := buffer.String()
fmt.Print(stdoutString)

scsSummary := "| SCS - - - - - |"
assert.Equal(t, strings.Contains(stdoutString, scsSummary), true,
"Expected SCS summary:"+scsSummary)
secretDetectionSummary := "Secret Detection"
assert.Equal(t, !strings.Contains(stdoutString, secretDetectionSummary), true,
"Expected Secret Detection summary to be missing:"+secretDetectionSummary)
scorecardSummary := "Scorecard"
assert.Equal(t, !strings.Contains(stdoutString, scorecardSummary), true,
"Expected Scorecard summary to be missing:"+scorecardSummary)
}

func TestRunGetResultsByScanIdSummaryConsoleFormatWithScsPartial(t *testing.T) {
buffer, err := executeRedirectedOsStdoutTestCommand(createASTTestCommandWithScs(true, true, true),
"results", "show", "--scan-id", "MOCK", "--report-format", "summaryConsole")
assert.NilError(t, err)

stdoutString := buffer.String()
ansiRegexp := regexp.MustCompile("\x1b\\[[0-9;]*[mK]")
cleanString := ansiRegexp.ReplaceAllString(stdoutString, "")
fmt.Print(stdoutString)

TotalResults := "Total Results: 17"
assert.Equal(t, strings.Contains(cleanString, TotalResults), true,
"Expected: "+TotalResults)
TotalSummary := "| TOTAL 10 4 3 0 Completed |"
assert.Equal(t, strings.Contains(cleanString, TotalSummary), true,
"Expected TOTAL summary: "+TotalSummary)
scsSummary := "| SCS 5 3 2 0 Partial |"
assert.Equal(t, strings.Contains(cleanString, scsSummary), true,
"Expected SCS summary:"+scsSummary)
secretDetectionSummary := secretDetectionLine
assert.Equal(t, strings.Contains(cleanString, secretDetectionSummary), true,
"Expected Secret Detection summary:"+secretDetectionSummary)
scorecardSummary := "| Scorecard 0 0 0 0 Failed |"
assert.Equal(t, strings.Contains(cleanString, scorecardSummary), true,
"Expected Scorecard summary:"+scorecardSummary)
}

func TestRunGetResultsByScanIdSummaryConsoleFormatWithScsScorecardNotScanned(t *testing.T) {
buffer, err := executeRedirectedOsStdoutTestCommand(createASTTestCommandWithScs(true, false, false),
"results", "show", "--scan-id", "MOCK", "--report-format", "summaryConsole")
assert.NilError(t, err)

stdoutString := buffer.String()
fmt.Print(stdoutString)

scsSummary := "| SCS 5 3 2 0 Completed |"
assert.Equal(t, strings.Contains(stdoutString, scsSummary), true,
"Expected SCS summary:"+scsSummary)
secretDetectionSummary := secretDetectionLine
assert.Equal(t, strings.Contains(stdoutString, secretDetectionSummary), true,
"Expected Secret Detection summary:"+secretDetectionSummary)
scorecardSummary := "| Scorecard - - - - - |"
assert.Equal(t, strings.Contains(stdoutString, scorecardSummary), true,
"Expected Scorecard summary:"+scorecardSummary)
}
Loading
Loading