Skip to content

Commit

Permalink
AST-36384 add support for improved-scan-report invocation (#688)
Browse files Browse the repository at this point in the history
* add support for improved-scan-report

* change tests for active feature flag in tenant

* update go to 1.21.9 to fix vulnerabilities

* update validation of flag for options in pdf report

* fix ci yaml pr number
  • Loading branch information
cx-joses authored Apr 5, 2024
1 parent 7e6c255 commit 5b44ada
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 33 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
pull_request:

env:
GO_VERSION: '1.21.8'
GO_VERSION: '1.21.9'

jobs:
unit-tests:
Expand Down Expand Up @@ -66,7 +66,7 @@ jobs:
PR_GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
PR_GITHUB_NAMESPACE: "checkmarx"
PR_GITHUB_REPO_NAME: "ast-cli"
PR_GITHUB_NUMBER: 418
PR_GITHUB_NUMBER: 419
PR_GITLAB_TOKEN : ${{ secrets.PR_GITLAB_TOKEN }}
PR_GITLAB_NAMESPACE: ${{ secrets.PR_GITLAB_NAMESPACE }}
PR_GITLAB_REPO_NAME: ${{ secrets.PR_GITLAB_REPO_NAME }}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/checkmarx/ast-cli

go 1.21.8
go 1.21.9

require (
github.com/MakeNowJust/heredoc v1.0.0
Expand Down Expand Up @@ -43,4 +43,4 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

)
)
10 changes: 0 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,10 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.20.1-0.20240228204720-0d2316b26734 h1:HutZC8sRIg57ztz3rVaQYl4yxgM+UF0Jal0kAWUSeFU=
golang.org/x/crypto v0.20.1-0.20240228204720-0d2316b26734/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/cx_result_sonar.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"issues":[{"engineId":"sast","type":"VULNERABILITY","primaryLocation":{"filePath":"dummy-file-name","textRange":{"startLine":10,"startColumn":9,"endColumn":10}},"secondaryLocations":[{"filePath":"dummy-file-name","textRange":{"startColumn":2,"endColumn":3}}]},{"engineId":"kics","type":"VULNERABILITY","primaryLocation":{"textRange":{"startColumn":1,"endColumn":2}},"secondaryLocations":null}]}
{"issues":[{"engineId":"sast","ruleId":"1","type":"VULNERABILITY","primaryLocation":{"message":"mock-query-name-1","filePath":"dummy-file-name-1","textRange":{"startLine":10,"startColumn":10,"endColumn":30}},"secondaryLocations":[{"message":"mock-query-name-1","filePath":"dummy-file-name-1","textRange":{"startLine":11,"startColumn":3,"endColumn":13}}]},{"engineId":"sast","ruleId":"2","type":"VULNERABILITY","primaryLocation":{"message":"mock-query-name-2","filePath":"dummy-file-name-2","textRange":{"startLine":10,"startColumn":10,"endColumn":30}},"secondaryLocations":[{"message":"mock-query-name-2","filePath":"dummy-file-name-2","textRange":{"startLine":11,"startColumn":3,"endColumn":13}}]},{"engineId":"sast","ruleId":"3","type":"VULNERABILITY","primaryLocation":{"message":"mock-query-name-2","filePath":"dummy-file-name-2","textRange":{"startLine":10,"startColumn":10,"endColumn":30}},"secondaryLocations":[{"message":"mock-query-name-2","filePath":"dummy-file-name-2","textRange":{"startLine":11,"startColumn":3,"endColumn":13}},{"message":"mock-query-name-2","filePath":"dummy-file-name-2","textRange":{"startLine":12,"startColumn":3,"endColumn":13}}]},{"engineId":"sast","ruleId":"4","type":"VULNERABILITY","primaryLocation":{"message":"mock-query-name-3","filePath":"dummy-file-name-3","textRange":{"startLine":10,"startColumn":10,"endColumn":30}},"secondaryLocations":[{"message":"mock-query-name-3","filePath":"dummy-file-name-3","textRange":{"startLine":11,"startColumn":3,"endColumn":13}}]},{"engineId":"sast","ruleId":"5","type":"VULNERABILITY","primaryLocation":{"message":"mock-query-name-3","filePath":"dummy-file-name-4","textRange":{"startLine":10,"startColumn":10,"endColumn":30}},"secondaryLocations":[{"message":"mock-query-name-3","filePath":"dummy-file-name-4","textRange":{"startLine":11,"startColumn":3,"endColumn":13}}]},{"engineId":"kics","type":"VULNERABILITY","primaryLocation":{"textRange":{"endColumn":1}},"secondaryLocations":null}]}
58 changes: 46 additions & 12 deletions internal/commands/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ const (
pendingStatus = "Pending"
pdfToEmailFlagDescription = "Send the PDF report to the specified email address." +
" Use \",\" as the delimiter for multiple emails"
pdfOptionsFlagDescription = "Sections to generate PDF report. Available options: Iac-Security,Sast,Sca," +
defaultPdfOptionsDataSections
pdfOptionsFlagDescription = "Sections to generate PDF report. Available options: Iac-Security,Sast,Sca and " +
defaultPdfOptionsDataSections + defaultPdfOprtionsImprovedDataSections
sbomReportFlagDescription = "Sections to generate SBOM report. Available options: CycloneDxJson,CycloneDxXml,SpdxJson"
delayValueForReport = 10
reportNameScanReport = "scan-report"
reportNameImprovedScanReport = "improved-scan-report"
reportTypeEmail = "email"
defaultPdfOptionsDataSections = "ScanSummary,ExecutiveSummary,ScanResults"
defaultPdfOptionsDataSections = "ScanSummary,ExecutiveSummary,ScanResults with NEW_SAST_SCAN_REPORT_ENABLED feature flag disabled or "
defaultPdfOprtionsImprovedDataSections = "scan-information,results-overview,scan-results,categories,resolved-results,vulnerability-details with the flag enabled"
defaultSbomOption = "CycloneDxJson"
exploitablePathFlagDescription = "Enable or disable exploitable path in scan. Available options: true,false"
scaLastScanTimeFlagDescription = "SCA last scan time. Available options: integer above 1"
Expand Down Expand Up @@ -202,7 +204,7 @@ func resultShowSubCommand(
)
resultShowCmd.PersistentFlags().String(commonParams.ReportFormatPdfToEmailFlag, "", pdfToEmailFlagDescription)
resultShowCmd.PersistentFlags().String(commonParams.ReportSbomFormatFlag, defaultSbomOption, sbomReportFlagDescription)
resultShowCmd.PersistentFlags().String(commonParams.ReportFormatPdfOptionsFlag, defaultPdfOptionsDataSections, pdfOptionsFlagDescription)
resultShowCmd.PersistentFlags().String(commonParams.ReportFormatPdfOptionsFlag, "", pdfOptionsFlagDescription)
resultShowCmd.PersistentFlags().String(commonParams.TargetFlag, "cx_result", "Output file")
resultShowCmd.PersistentFlags().String(commonParams.TargetPathFlag, ".", "Output Path")
resultShowCmd.PersistentFlags().StringSlice(commonParams.FilterFlag, []string{}, filterResultsListFlagUsage)
Expand Down Expand Up @@ -1223,11 +1225,19 @@ func exportSbomResults(sbomWrapper wrappers.ResultsSbomWrapper,
func exportPdfResults(pdfWrapper wrappers.ResultsPdfWrapper, summary *wrappers.ResultSummary, summaryRpt, formatPdfToEmail, pdfOptions string) error {
pdfReportsPayload := &wrappers.PdfReportsPayload{}
pollingResp := &wrappers.PdfPollingResponse{}
pdfOptionsSections, pdfOptionsEngines, err := parsePDFOptions(pdfOptions, summary.EnginesEnabled)
newScanReportEnabled := wrappers.FeatureFlags[wrappers.NewScanReportEnabled]

if newScanReportEnabled {
pdfReportsPayload.ReportName = reportNameImprovedScanReport
} else {
pdfReportsPayload.ReportName = reportNameScanReport
}

pdfOptionsSections, pdfOptionsEngines, err := parsePDFOptions(pdfOptions, summary.EnginesEnabled, pdfReportsPayload.ReportName)
if err != nil {
return err
}
pdfReportsPayload.ReportName = reportNameScanReport

pdfReportsPayload.ReportType = "cli"
pdfReportsPayload.FileFormat = printer.FormatPDF
pdfReportsPayload.Data.ScanID = summary.ScanID
Expand Down Expand Up @@ -1290,33 +1300,57 @@ func validateSbomOptions(sbomOption string) (string, error) {
return "", errors.Errorf("invalid SBOM option: %s", sbomOption)
}

func parsePDFOptions(pdfOptions string, enabledEngines []string) (pdfOptionsSections, pdfOptionsEngines []string, err error) {
func parsePDFOptions(pdfOptions string, enabledEngines []string, reportName string) (pdfOptionsSections, pdfOptionsEngines []string, err error) {
var pdfOptionsSectionsMap = map[string]string{
"scansummary": "ScanSummary",
"executivesummary": "ExecutiveSummary",
"scanresults": "ScanResults",
}
var pdfOptionsSectionsMapImproved = map[string]string{
"scan-information": "scan-information",
"results-overview": "results-overview",
"scan-results": "scan-results",
"categories": "categories",
"resolved-results": "resolved-results",
"vulnerability-details": "vulnerability-details",
}
var pdfOptionsEnginesMap = map[string]string{
commonParams.ScaType: "SCA",
commonParams.SastType: "SAST",
commonParams.KicsType: "KICS",
commonParams.IacType: "KICS",
}

var pdfReportOptionsSections = map[string]map[string]string{
reportNameImprovedScanReport: pdfOptionsSectionsMapImproved,
reportNameScanReport: pdfOptionsSectionsMap,
}

var pdfReportOptionsEngines = map[string]map[string]string{
reportNameImprovedScanReport: pdfOptionsEnginesMap,
reportNameScanReport: pdfOptionsEnginesMap,
}

pdfOptions = strings.ToLower(strings.ReplaceAll(pdfOptions, " ", ""))
// if no options are provided, report service defaults to all values
if pdfOptions == "" {
return pdfOptionsSections, pdfOptionsSections, nil
}

options := strings.Split(strings.ReplaceAll(pdfOptions, "\n", ""), ",")
for _, s := range options {
if pdfOptionsEnginesMap[s] != "" {
pdfOptionsEngines = append(pdfOptionsEngines, pdfOptionsEnginesMap[s])
} else if pdfOptionsSectionsMap[s] != "" {
pdfOptionsSections = append(pdfOptionsSections, pdfOptionsSectionsMap[s])
if pdfReportOptionsEngines[reportName][s] != "" {
pdfOptionsEngines = append(pdfOptionsEngines, pdfReportOptionsEngines[reportName][s])
} else if pdfReportOptionsSections[reportName][s] != "" {
pdfOptionsSections = append(pdfOptionsSections, pdfReportOptionsSections[reportName][s])
} else {
return nil, nil, errors.Errorf("report option \"%s\" unavailable", s)
}
}
if pdfOptionsEngines == nil {
for _, engine := range enabledEngines {
if pdfOptionsEnginesMap[engine] != "" {
pdfOptionsEngines = append(pdfOptionsEngines, pdfOptionsEnginesMap[engine])
pdfOptionsEngines = append(pdfOptionsEngines, pdfReportOptionsEngines[reportName][engine])
}
}
}
Expand Down
41 changes: 40 additions & 1 deletion internal/commands/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/checkmarx/ast-cli/internal/commands/util/printer"
"github.com/checkmarx/ast-cli/internal/params"
"github.com/checkmarx/ast-cli/internal/wrappers"
"github.com/checkmarx/ast-cli/internal/wrappers/mock"
"gotest.tools/assert"
)

Expand Down Expand Up @@ -249,6 +250,7 @@ func TestRunGetBFLByScanIdAndQueryIdWithFormatList(t *testing.T) {
}

func TestRunGetResultsGeneratingPdfReportWithInvalidEmail(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: wrappers.NewScanReportEnabled, Status: false}}
err := execCmdNotNilAssertion(t,
"results", "show",
"--report-format", "pdf",
Expand All @@ -258,6 +260,7 @@ func TestRunGetResultsGeneratingPdfReportWithInvalidEmail(t *testing.T) {
}

func TestRunGetResultsGeneratingPdfReportWithInvalidOptions(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: wrappers.NewScanReportEnabled, Status: false}}
err := execCmdNotNilAssertion(t,
"results", "show",
"--report-format", "pdf",
Expand All @@ -266,7 +269,18 @@ func TestRunGetResultsGeneratingPdfReportWithInvalidOptions(t *testing.T) {
assert.Equal(t, err.Error(), "report option \"invalid\" unavailable", "Wrong expected error message")
}

func TestRunGetResultsGeneratingPdfReportWithInvalidImprovedOptions(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: wrappers.NewScanReportEnabled, Status: false}}
err := execCmdNotNilAssertion(t,
"results", "show",
"--report-format", "pdf",
"--scan-id", "MOCK",
"--report-pdf-options", "scan-information")
assert.Equal(t, err.Error(), "report option \"scan-information\" unavailable", "Wrong expected error message")
}

func TestRunGetResultsGeneratingPdfReportWithEmailAndOptions(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: wrappers.NewScanReportEnabled, Status: false}}
cmd := createASTTestCommand()
err := executeTestCommand(cmd,
"results", "show",
Expand All @@ -277,7 +291,32 @@ func TestRunGetResultsGeneratingPdfReportWithEmailAndOptions(t *testing.T) {
assert.NilError(t, err)
}

func TestRunGetResultsGeneratingPdfReporWithOptions(t *testing.T) {
func TestRunGetResultsGeneratingPdfReportWithOptionsImproved(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: wrappers.NewScanReportEnabled, Status: true}}
cmd := createASTTestCommand()
err := executeTestCommand(cmd,
"results", "show",
"--report-format", "pdf",
"--scan-id", "MOCK",
"--report-pdf-email", "[email protected],[email protected]",
"--report-pdf-options", "Iac-Security,Sast,Sca,scan-information")
assert.NilError(t, err)
}

func TestRunGetResultsGeneratingPdfReportWithInvalidOptionsImproved(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: wrappers.NewScanReportEnabled, Status: true}}
cmd := createASTTestCommand()
err := executeTestCommand(cmd,
"results", "show",
"--report-format", "pdf",
"--scan-id", "MOCK",
"--report-pdf-email", "[email protected],[email protected]",
"--report-pdf-options", "Iac-Security,Sast,Sca,ScanSummary")
assert.Error(t, err, "report option \"scansummary\" unavailable")
}

func TestRunGetResultsGeneratingPdfReportWithOptions(t *testing.T) {
mock.Flags = wrappers.FeatureFlagsResponseModel{{Name: wrappers.NewScanReportEnabled, Status: false}}
cmd := createASTTestCommand()
err := executeTestCommand(cmd,
"results", "show",
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ func scanCreateSubCommand(
createScanCmd.PersistentFlags().String(commonParams.ScaPrivatePackageVersionFlag, "", scaPrivatePackageVersionFlagDescription)
createScanCmd.PersistentFlags().String(commonParams.ReportFormatPdfToEmailFlag, "", pdfToEmailFlagDescription)
createScanCmd.PersistentFlags().String(commonParams.ReportSbomFormatFlag, defaultSbomOption, sbomReportFlagDescription)
createScanCmd.PersistentFlags().String(commonParams.ReportFormatPdfOptionsFlag, defaultPdfOptionsDataSections, pdfOptionsFlagDescription)
createScanCmd.PersistentFlags().String(commonParams.ReportFormatPdfOptionsFlag, "", pdfOptionsFlagDescription)
createScanCmd.PersistentFlags().String(commonParams.TargetFlag, "cx_result", "Output file")
createScanCmd.PersistentFlags().String(commonParams.TargetPathFlag, ".", "Output Path")
createScanCmd.PersistentFlags().StringSlice(commonParams.FilterFlag, []string{}, filterResultsListFlagUsage)
Expand Down
10 changes: 10 additions & 0 deletions internal/wrappers/feature-flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const tenantIDClaimKey = "tenant_id"
const PackageEnforcementEnabled = "PACKAGE_ENFORCEMENT_ENABLED"
const MinioEnabled = "MINIO_ENABLED"
const ContainerEngineCLIEnabled = "CONTAINER_ENGINE_CLI_ENABLED"
const NewScanReportEnabled = "NEW_SAST_SCAN_REPORT_ENABLED"

var FeatureFlagsBaseMap = []CommandFlags{
{
Expand All @@ -26,6 +27,15 @@ var FeatureFlagsBaseMap = []CommandFlags{
{
CommandName: "cx project create",
},
{
CommandName: "cx results show",
FeatureFlags: []FlagBase{
{
Name: NewScanReportEnabled,
Default: false,
},
},
},
}

var FeatureFlags = map[string]bool{}
Expand Down
5 changes: 2 additions & 3 deletions test/integration/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ func TestResultsGeneratingPdfReportWithPdfOptions(t *testing.T) {
"results", "show",
flag(params.ScanIDFlag), scanID,
flag(params.TargetFormatFlag), "pdf",
flag(params.ReportFormatPdfOptionsFlag), "Iac-Security,ScanSummary,ExecutiveSummary,ScanResults",
flag(params.ReportFormatPdfOptionsFlag), "Iac-Security,scan-information",
flag(params.TargetFlag), fileName,
)
defer func() {
Expand All @@ -220,7 +220,6 @@ func TestResultsGeneratingPdfReportWithPdfOptions(t *testing.T) {
_, err := os.Stat(fmt.Sprintf("%s.%s", fileName, printer.FormatPDF))
assert.NilError(t, err, "Report file should exist: "+fileName+printer.FormatPDF)
assert.Assert(t, outputBuffer != nil, "Scan must complete successfully")

}

func TestResultsGeneratingPdfReportAndSendToEmail(t *testing.T) {
Expand All @@ -230,7 +229,7 @@ func TestResultsGeneratingPdfReportAndSendToEmail(t *testing.T) {
"results", "show",
flag(params.ScanIDFlag), scanID,
flag(params.TargetFormatFlag), "pdf",
flag(params.ReportFormatPdfOptionsFlag), "Iac-Security,ScanSummary,ExecutiveSummary,ScanResults",
flag(params.ReportFormatPdfOptionsFlag), "Iac-Security,scan-information",
flag(params.ReportFormatPdfToEmailFlag), "[email protected],[email protected]",
)
assert.Assert(t, outputBuffer != nil, "Scan must complete successfully")
Expand Down
2 changes: 1 addition & 1 deletion test/integration/scan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,7 @@ func TestScanGeneratingPdfReportWithPdfOptions(t *testing.T) {
flag(params.PresetName), "Checkmarx Default",
flag(params.BranchFlag), "dummy_branch",
flag(params.TargetFormatFlag), "pdf",
flag(params.ReportFormatPdfOptionsFlag), "Iac-Security,ScanSummary,ExecutiveSummary,ScanResults",
flag(params.ReportFormatPdfOptionsFlag), "Iac-Security,scan-information",
flag(params.TargetFlag), fileName,
)
defer func() {
Expand Down

0 comments on commit 5b44ada

Please sign in to comment.