From d86cfce6e67f24ab142305d2143f5931413eaf67 Mon Sep 17 00:00:00 2001 From: thtrinh Date: Fri, 25 Feb 2022 14:20:36 +0100 Subject: [PATCH] Checkmarx json report (#3565) * feat(checkmarx) : Checkmarx JSON Report * Test cases with some fix * Information total and audited test assertions * feat(checkmarx): align total/audited with existing calculation * fix(checkmarx): Reporting unit test Co-authored-by: Sumeet PATIL Co-authored-by: Sven Merk <33895725+nevskrem@users.noreply.github.com> Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com> --- cmd/checkmarxExecuteScan.go | 10 +++ pkg/checkmarx/reporting.go | 79 +++++++++++++++++ pkg/checkmarx/reporting_test.go | 150 ++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 pkg/checkmarx/reporting_test.go diff --git a/cmd/checkmarxExecuteScan.go b/cmd/checkmarxExecuteScan.go index 777ebe0b6..76a0c5ad1 100644 --- a/cmd/checkmarxExecuteScan.go +++ b/cmd/checkmarxExecuteScan.go @@ -311,6 +311,16 @@ func verifyCxProjectCompliance(config checkmarxExecuteScanOptions, sys checkmarx reports = append(reports, piperutils.Path{Target: toolRecordFileName}) } + // create JSON report (regardless vulnerabilityThreshold enabled or not) + jsonReport := checkmarx.CreateJSONReport(results) + paths, err := checkmarx.WriteJSONReport(jsonReport) + if err != nil { + log.Entry().Warning("failed to write JSON report...", err) + } else { + // add JSON report to archiving list + reports = append(reports, paths...) + } + links := []piperutils.Path{{Target: results["DeepLink"].(string), Name: "Checkmarx Web UI"}} piperutils.PersistReportsAndLinks("checkmarxExecuteScan", utils.GetWorkspace(), reports, links) diff --git a/pkg/checkmarx/reporting.go b/pkg/checkmarx/reporting.go index 71d83ca0b..de5cb133e 100644 --- a/pkg/checkmarx/reporting.go +++ b/pkg/checkmarx/reporting.go @@ -2,8 +2,10 @@ package checkmarx import ( "crypto/sha1" + "encoding/json" "fmt" "path/filepath" + "strconv" "strings" "time" @@ -14,6 +16,27 @@ import ( "github.com/pkg/errors" ) +type CheckmarxReportData struct { + ToolName string `json:"toolName"` + ProjectName string `json:"projectName"` + ProjectID int64 `json:"projectID"` + ScanID int64 `json:"scanID"` + TeamName string `json:"teamName"` + TeamPath string `json:"teamPath"` + DeepLink string `json:"deepLink"` + Preset string `json:"preset"` + CheckmarxVersion string `json:"checkmarxVersion"` + ScanType string `json:"scanType"` + HighTotal int `json:"highTotal"` + HighAudited int `json:"highAudited"` + MediumTotal int `json:"mediumTotal"` + MediumAudited int `json:"mediumAudited"` + LowTotal int `json:"lowTotal"` + LowAudited int `json:"lowAudited"` + InformationTotal int `json:"informationTotal"` + InformationAudited int `json:"informationAudited"` +} + func CreateCustomReport(data map[string]interface{}, insecure, neutral []string) reporting.ScanReport { deepLink := fmt.Sprintf(`Link to scan in CX UI`, data["DeepLink"]) @@ -103,6 +126,62 @@ func CreateCustomReport(data map[string]interface{}, insecure, neutral []string) return scanReport } +func CreateJSONReport(data map[string]interface{}) CheckmarxReportData { + checkmarxReportData := CheckmarxReportData{ + ToolName: `checkmarx`, + ProjectName: fmt.Sprint(data["ProjectName"]), + TeamName: fmt.Sprint(data["Team"]), + TeamPath: fmt.Sprint(data["TeamFullPathOnReportDate"]), + DeepLink: fmt.Sprint(data["DeepLink"]), + Preset: fmt.Sprint(data["Preset"]), + CheckmarxVersion: fmt.Sprint(data["CheckmarxVersion"]), + ScanType: fmt.Sprint(data["ScanType"]), + } + + if s, err := strconv.ParseInt(fmt.Sprint(data["ProjectId"]), 10, 64); err == nil { + checkmarxReportData.ProjectID = s + } + + if s, err := strconv.ParseInt(fmt.Sprint(data["ScanId"]), 10, 64); err == nil { + checkmarxReportData.ScanID = s + } + + checkmarxReportData.HighAudited = data["High"].(map[string]int)["Issues"] - data["High"].(map[string]int)["NotFalsePositive"] + checkmarxReportData.HighTotal = data["High"].(map[string]int)["Issues"] + + checkmarxReportData.MediumAudited = data["Medium"].(map[string]int)["Issues"] - data["Medium"].(map[string]int)["NotFalsePositive"] + checkmarxReportData.MediumTotal = data["Medium"].(map[string]int)["Issues"] + + checkmarxReportData.LowAudited = data["Low"].(map[string]int)["Issues"] - data["Low"].(map[string]int)["NotFalsePositive"] + checkmarxReportData.LowTotal = data["Low"].(map[string]int)["Issues"] + + checkmarxReportData.InformationAudited = data["Information"].(map[string]int)["Issues"] - data["Information"].(map[string]int)["NotFalsePositive"] + checkmarxReportData.InformationTotal = data["Information"].(map[string]int)["Issues"] + + return checkmarxReportData +} + +func WriteJSONReport(jsonReport CheckmarxReportData) ([]piperutils.Path, error) { + utils := piperutils.Files{} + reportPaths := []piperutils.Path{} + + // Standard JSON Report + jsonComplianceReportPath := filepath.Join(ReportsDirectory, "piper_checkmarx_report.json") + // Ensure reporting directory exists + if err := utils.MkdirAll(ReportsDirectory, 0777); err != nil { + return reportPaths, errors.Wrapf(err, "failed to create report directory") + } + + file, _ := json.Marshal(jsonReport) + if err := utils.FileWrite(jsonComplianceReportPath, file, 0666); err != nil { + log.SetErrorCategory(log.ErrorConfiguration) + return reportPaths, errors.Wrapf(err, "failed to write Checkmarx JSON compliance report") + } + reportPaths = append(reportPaths, piperutils.Path{Name: "Checkmarx JSON Compliance Report", Target: jsonComplianceReportPath}) + + return reportPaths, nil +} + func WriteCustomReports(scanReport reporting.ScanReport, projectName, projectID string) ([]piperutils.Path, error) { utils := piperutils.Files{} reportPaths := []piperutils.Path{} diff --git a/pkg/checkmarx/reporting_test.go b/pkg/checkmarx/reporting_test.go new file mode 100644 index 000000000..e2d185253 --- /dev/null +++ b/pkg/checkmarx/reporting_test.go @@ -0,0 +1,150 @@ +package checkmarx + +import ( + "encoding/xml" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreateJSONReport(t *testing.T) { + data := ` + + + + ` + + var xmlResult DetailedResult + xml.Unmarshal([]byte(data), &xmlResult) + resultMap := map[string]interface{}{} + resultMap["InitiatorName"] = xmlResult.InitiatorName + resultMap["Owner"] = xmlResult.Owner + resultMap["ScanId"] = xmlResult.ScanID + resultMap["ProjectId"] = xmlResult.ProjectID + resultMap["ProjectName"] = xmlResult.ProjectName + resultMap["Team"] = xmlResult.Team + resultMap["TeamFullPathOnReportDate"] = xmlResult.TeamFullPathOnReportDate + resultMap["ScanStart"] = xmlResult.ScanStart + resultMap["ScanTime"] = xmlResult.ScanTime + resultMap["LinesOfCodeScanned"] = xmlResult.LinesOfCodeScanned + resultMap["FilesScanned"] = xmlResult.FilesScanned + resultMap["CheckmarxVersion"] = xmlResult.CheckmarxVersion + resultMap["ScanType"] = xmlResult.ScanType + resultMap["Preset"] = xmlResult.Preset + resultMap["DeepLink"] = xmlResult.DeepLink + resultMap["ReportCreationTime"] = xmlResult.ReportCreationTime + resultMap["High"] = map[string]int{} + resultMap["Medium"] = map[string]int{} + resultMap["Low"] = map[string]int{} + resultMap["Information"] = map[string]int{} + submap := map[string]int{} + submap["Issues"] = 10 + submap["NotFalsePositive"] = 10 + resultMap["High"] = submap + + submap = map[string]int{} + submap["Issues"] = 4 + submap["NotFalsePositive"] = 0 + resultMap["Medium"] = submap + + submap = map[string]int{} + submap["Issues"] = 2 + submap["NotFalsePositive"] = 2 + resultMap["Low"] = submap + + submap = map[string]int{} + submap["Issues"] = 5 + submap["NotFalsePositive"] = 5 + resultMap["Information"] = submap + + reportingData := CreateJSONReport(resultMap) + assert.Equal(t, int64(1000005), reportingData.ScanID) + assert.Equal(t, "Project 1", reportingData.ProjectName) + assert.Equal(t, int64(2), reportingData.ProjectID) + assert.Equal(t, "CxServer", reportingData.TeamName) + assert.Equal(t, "checkmarx", reportingData.ToolName) + assert.Equal(t, "CxServer", reportingData.TeamPath) + assert.Equal(t, "http://WIN2K12-TEMP/CxWebClient/ViewerMain.aspx?scanid=1000005&projectid=2", reportingData.DeepLink) + assert.Equal(t, "Checkmarx Default", reportingData.Preset) + assert.Equal(t, "8.6.0", reportingData.CheckmarxVersion) + assert.Equal(t, "Incremental", reportingData.ScanType) + assert.Equal(t, 10, reportingData.HighTotal) + assert.Equal(t, 0, reportingData.HighAudited) + assert.Equal(t, 4, reportingData.MediumTotal) + assert.Equal(t, 4, reportingData.MediumAudited) + assert.Equal(t, 2, reportingData.LowTotal) + assert.Equal(t, 0, reportingData.LowAudited) + assert.Equal(t, 5, reportingData.InformationTotal) + assert.Equal(t, 0, reportingData.InformationAudited) +} + +func TestJsonReportWithNoLowVulnData(t *testing.T) { + data := ` + + + + ` + + var xmlResult DetailedResult + xml.Unmarshal([]byte(data), &xmlResult) + resultMap := map[string]interface{}{} + resultMap["InitiatorName"] = xmlResult.InitiatorName + resultMap["Owner"] = xmlResult.Owner + resultMap["ScanId"] = xmlResult.ScanID + resultMap["ProjectId"] = xmlResult.ProjectID + resultMap["ProjectName"] = xmlResult.ProjectName + resultMap["Team"] = xmlResult.Team + resultMap["TeamFullPathOnReportDate"] = xmlResult.TeamFullPathOnReportDate + resultMap["ScanStart"] = xmlResult.ScanStart + resultMap["ScanTime"] = xmlResult.ScanTime + resultMap["LinesOfCodeScanned"] = xmlResult.LinesOfCodeScanned + resultMap["FilesScanned"] = xmlResult.FilesScanned + resultMap["CheckmarxVersion"] = xmlResult.CheckmarxVersion + resultMap["ScanType"] = xmlResult.ScanType + resultMap["Preset"] = xmlResult.Preset + resultMap["DeepLink"] = xmlResult.DeepLink + resultMap["ReportCreationTime"] = xmlResult.ReportCreationTime + resultMap["High"] = map[string]int{} + resultMap["Medium"] = map[string]int{} + resultMap["Low"] = map[string]int{} + resultMap["Information"] = map[string]int{} + submap := map[string]int{} + submap["Issues"] = 10 + submap["NotFalsePositive"] = 10 + resultMap["High"] = submap + + submap = map[string]int{} + submap["Issues"] = 4 + submap["NotFalsePositive"] = 4 + resultMap["Medium"] = submap + + submap = map[string]int{} + submap["Issues"] = 5 + submap["NotFalsePositive"] = 5 + resultMap["Information"] = submap + + submap = map[string]int{} + submap["Issues"] = 2 + submap["NotFalsePositive"] = 1 + resultMap["Information"] = submap + + reportingData := CreateJSONReport(resultMap) + assert.Equal(t, int64(1000005), reportingData.ScanID) + assert.Equal(t, "Project 1", reportingData.ProjectName) + assert.Equal(t, int64(2), reportingData.ProjectID) + assert.Equal(t, "CxServer", reportingData.TeamName) + assert.Equal(t, "checkmarx", reportingData.ToolName) + assert.Equal(t, "CxServer", reportingData.TeamPath) + assert.Equal(t, "http://WIN2K12-TEMP/CxWebClient/ViewerMain.aspx?scanid=1000005&projectid=2", reportingData.DeepLink) + assert.Equal(t, "Checkmarx Default", reportingData.Preset) + assert.Equal(t, "8.6.0", reportingData.CheckmarxVersion) + assert.Equal(t, "Incremental", reportingData.ScanType) + assert.Equal(t, 10, reportingData.HighTotal) + assert.Equal(t, 0, reportingData.HighAudited) + assert.Equal(t, 4, reportingData.MediumTotal) + assert.Equal(t, 0, reportingData.MediumAudited) + assert.Equal(t, 0, reportingData.LowTotal) + assert.Equal(t, 0, reportingData.LowAudited) + assert.Equal(t, 2, reportingData.InformationTotal) + assert.Equal(t, 1, reportingData.InformationAudited) +}