From 5933079e0fa151913d460abc00948f630a574bb2 Mon Sep 17 00:00:00 2001 From: Margarita Date: Thu, 26 Oct 2023 11:43:39 +0300 Subject: [PATCH] AST-25406 Add gl-sast report (#606) * AST-25406 Add gl-sast report * Add changes due to code review * Remove row --- internal/commands/result.go | 107 ++++++++++++++++++++- internal/commands/result_test.go | 6 ++ internal/commands/util/printer/printer.go | 1 + internal/wrappers/results-gl-sast.go | 111 ++++++++++++++++++++++ test/integration/result_test.go | 3 +- 5 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 internal/wrappers/results-gl-sast.go diff --git a/internal/commands/result.go b/internal/commands/result.go index 68e53ee3e..1082c9878 100644 --- a/internal/commands/result.go +++ b/internal/commands/result.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" "text/template" "time" @@ -81,6 +82,7 @@ var resultsFormats = []string{ printer.FormatJSON, printer.FormatSarif, printer.FormatSonar, + printer.FormatGL, } var summaryFormats = []string{ printer.FormatSummaryConsole, @@ -851,6 +853,10 @@ func createReport(format, jsonRpt := createTargetName(targetFile, targetPath, printer.FormatJSON) return exportJSONResults(jsonRpt, results) } + if printer.IsFormat(format, printer.FormatGL) { + jsonRpt := createTargetName("gl-sast-report", targetPath, printer.FormatJSON) + return exportGlSastResults(jsonRpt, results, summary) + } if printer.IsFormat(format, printer.FormatSummaryConsole) { return writeConsoleSummary(summary) } @@ -988,7 +994,28 @@ func exportSarifResults(targetFile string, results *wrappers.ScanResultsCollecti _ = f.Close() return nil } - +func exportGlSastResults(targetFile string, results *wrappers.ScanResultsCollection, summary *wrappers.ResultSummary) error { + var err error + var resultsJSON []byte + log.Println("Creating gl-sast Report: ", targetFile) + var glSastResults = convertCxResultsToGLSast(results) + glSastResults = addStatus(summary, glSastResults) + resultsJSON, err = json.Marshal(glSastResults) + if err != nil { + return errors.Wrapf(err, "%s: failed to serialize results response ", failedGettingAll) + } + f, err := os.Create(targetFile) + if err != nil { + return errors.Wrapf(err, "%s: failed to create target file ", failedGettingAll) + } + _, _ = fmt.Fprintln(f, string(resultsJSON)) + defer f.Close() + return nil +} +func addStatus(summary *wrappers.ResultSummary, glSastResults *wrappers.GlSastResultsCollection) *wrappers.GlSastResultsCollection { + glSastResults.Scan.Status = summary.Status + return glSastResults +} func exportSonarResults(targetFile string, results *wrappers.ScanResultsCollection) error { var err error var resultsJSON []byte @@ -1221,6 +1248,84 @@ func convertCxResultsToSarif(results *wrappers.ScanResultsCollection) *wrappers. sarif.Runs = append(sarif.Runs, createSarifRun(results)) return sarif } +func convertCxResultsToGLSast(results *wrappers.ScanResultsCollection) *wrappers.GlSastResultsCollection { + var glSast = new(wrappers.GlSastResultsCollection) + glSast.Scan = wrappers.ScanGlReport{} + glSast = setConstValueGlReport(glSast) + glVulnra := convertCxResultToGlVulnerability(results, glSast) + glSast.Vulnerabilities = glVulnra + return glSast +} + +func convertCxResultToGlVulnerability(results *wrappers.ScanResultsCollection, glSast *wrappers.GlSastResultsCollection) []wrappers.GlVulnerabilities { + for _, result := range results.Results { + engineType := strings.TrimSpace(result.Type) + if engineType == commonParams.SastType { + glSast = parseGlSastVulnerability(result, glSast) + } + } + return glSast.Vulnerabilities +} + +func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSastResultsCollection) *wrappers.GlSastResultsCollection { + queryName := result.ScanResultData.QueryName + fileName := result.ScanResultData.Nodes[0].FileName + lineNumber := strconv.FormatUint(uint64(result.ScanResultData.Nodes[0].Line), 10) + startLine := result.ScanResultData.Nodes[0].Line + endLine := result.ScanResultData.Nodes[0].Line + result.ScanResultData.Nodes[0].Length + ID := fmt.Sprintf("%s:%s:%s", queryName, fileName, lineNumber) + category := fmt.Sprintf("%s-%s", wrappers.VendorName, result.Type) + message := fmt.Sprintf("%s@%s:%s", queryName, fileName, lineNumber) + + glSast.Vulnerabilities = append(glSast.Vulnerabilities, wrappers.GlVulnerabilities{ + ID: ID, + Category: category, + Name: queryName, + Message: message, + Description: result.Description, + CVE: ID, + Severity: result.Severity, + Confidence: result.Severity, + Solution: "", + Scanner: wrappers.GlScanner{ + ID: category, + Name: category, + }, + Links: nil, + Tracking: wrappers.Tracking{Items: wrappers.Item{ + Signatures: wrappers.Signature{ + Algorithm: result.Type + "-Algorithm ", + Value: "NA"}, + File: fileName, + EndLine: endLine, + StartLine: startLine}, + }, + Flags: wrappers.Flag{ + Type: "", + Origin: result.Type, + Description: result.Description, + }, + Location: wrappers.Location{ + File: fileName, + StartLine: startLine, + EndLine: endLine, + Class: fileName, + }, + }) + return glSast +} + +func setConstValueGlReport(glSast *wrappers.GlSastResultsCollection) *wrappers.GlSastResultsCollection { + glSast.Schema = "https://gitlab.com/gitlab-org/gitlab/-/blob/8a42b7e8ab41ec2920f02fb4b36f244bbbb4bfb8/lib/gitlab/ci/parsers/security/validators/schemas/14.1.2/sast-report-format.json" + glSast.Version = "14.1.2" + glSast.Scan.Analyzer.URL = wrappers.AnalyzerURL + glSast.Scan.Analyzer.Name = wrappers.VendorName + glSast.Scan.Analyzer.Vendor.Name = wrappers.VendorName + glSast.Scan.Analyzer.ID = wrappers.AnalyzerID + glSast.Scan.Scanner.ID = wrappers.AnalyzerID + glSast.Scan.Scanner.Name = wrappers.VendorName + return glSast +} func convertCxResultsToSonar(results *wrappers.ScanResultsCollection) *wrappers.ScanResultsSonar { var sonar = new(wrappers.ScanResultsSonar) diff --git a/internal/commands/result_test.go b/internal/commands/result_test.go index 427128edd..4a8158938 100644 --- a/internal/commands/result_test.go +++ b/internal/commands/result_test.go @@ -317,3 +317,9 @@ func TestSBOMReportXMLWithProxy(t *testing.T) { // Remove generated json file os.Remove(fmt.Sprintf("%s.%s", fileName+"_"+printer.FormatSbom, printer.FormatXML)) } + +func TestRunGetResultsByScanIdGLFormat(t *testing.T) { + execCmdNilAssertion(t, "results", "show", "--scan-id", "MOCK", "--report-format", "gl-sast") + // Run test for gl-sast report type + os.Remove(fmt.Sprintf("%s.%s", fileName, printer.FormatGL)) +} diff --git a/internal/commands/util/printer/printer.go b/internal/commands/util/printer/printer.go index 82768b6d4..f6c83a5f7 100644 --- a/internal/commands/util/printer/printer.go +++ b/internal/commands/util/printer/printer.go @@ -27,6 +27,7 @@ const ( FormatSummaryMarkdown = "markdown" FormatSbom = "sbom" FormatXML = "xml" + FormatGL = "gl-sast" ) func Print(w io.Writer, view interface{}, format string) error { diff --git a/internal/wrappers/results-gl-sast.go b/internal/wrappers/results-gl-sast.go new file mode 100644 index 000000000..c9c3ad2ee --- /dev/null +++ b/internal/wrappers/results-gl-sast.go @@ -0,0 +1,111 @@ +package wrappers + +const ( + AnalyzerName = "CxOne" + AnalyzerID = AnalyzerName + "-SAST" + AnalyzerURL = "https://checkmarx.company.com/" + VendorName = "Checkmarx" +) + +type GlSastResultsCollection struct { + Scan ScanGlReport `json:"scan"` + Schema string `json:"schema"` + Version string `json:"version"` + Vulnerabilities []GlVulnerabilities `json:"vulnerabilities"` +} +type GlVulnerabilities struct { + ID string `json:"id"` + Category string `json:"category"` + Name string `json:"name"` + Message string `json:"message"` + Description string `json:"description"` + CVE string `json:"cve"` + Severity string `json:"severity"` + Confidence string `json:"confidence"` + Solution string `json:"solution"` + Scanner GlScanner `json:"scanner"` + Identifiers []Identifier `json:"identifiers"` + Links []string `json:"links"` + Tracking Tracking `json:"tracking"` + Flags Flag `json:"flags"` + Location Location `json:"location"` +} +type Identifier struct { + Type string `json:"type"` + Name string `json:"name"` + URL string `json:"url"` + Value string `json:"value"` +} +type Flag struct { + Type string `json:"type"` + Origin string `json:"origin"` + Description string `json:"description"` +} +type Location struct { + File string `json:"file"` + StartLine uint `json:"start_line"` + EndLine uint `json:"end_line"` + Class string `json:"class"` +} + +type Tracking struct { + Items Item `json:"items"` +} +type Item struct { + Signatures Signature `json:"signatures"` + File string `json:"file"` + EndLine uint `json:"end_line"` + StartLine uint `json:"start_line"` +} +type Signature struct { + Algorithm string `json:"algorithm"` + Value string `json:"value"` +} +type ScanGlReport struct { + EndTime string `json:"end_time"` + Analyzer Analyzer `json:"analyzer"` + Scanner GlScanner `json:"scanner"` + StartTime string `json:"start_time"` + Status string `json:"status"` + Type string `json:"type"` +} + +type Analyzer struct { + ID string `json:"id"` + Name string `json:"name"` + URL string `json:"url"` + Vendor Vendor `json:"vendor"` + Version string `json:"version"` +} +type GlScanner struct { + ID string `json:"id"` + Name string `json:"name"` +} +type Vendor struct { + Name string `json:"name"` +} +type GLSastIdentifiers struct { + Type string `json:"type"` + Name string `json:"name"` + URL string `json:"url"` + Value string `json:"value"` +} +type GlSastTracking struct { + Items []GlSastTrackingItems `json:"items"` +} + +type GlSastTrackingItems struct { + Signatures GlSastTrackingItemsSignatures `json:"signatures"` + File string `json:"file"` + EndLine string `json:"end_line"` + StartLine string `json:"start_line"` +} +type GlSastTrackingItemsSignatures struct { + Algorithm string `json:"algorithm"` + Value string `json:"value"` +} +type GlSastFlags struct { + Type string `json:"type"` + Origin string `json:"origin"` + Description string `json:"description"` +} diff --git a/test/integration/result_test.go b/test/integration/result_test.go index 8f67de847..b78e53e07 100644 --- a/test/integration/result_test.go +++ b/test/integration/result_test.go @@ -41,6 +41,7 @@ func TestResultListJson(t *testing.T) { printer.FormatSummaryJSON, printer.FormatPDF, printer.FormatSummaryMarkdown, + printer.FormatGL, }, ",", ), flag(params.TargetFlag), fileName, @@ -60,7 +61,7 @@ func TestResultListJson(t *testing.T) { // assert all files were created func assertResultFilesCreated(t *testing.T) { - extensions := []string{printer.FormatJSON, printer.FormatSarif, printer.FormatHTML, printer.FormatJSON, printer.FormatPDF, printer.FormatMarkdown} + extensions := []string{printer.FormatJSON, printer.FormatSarif, printer.FormatHTML, printer.FormatJSON, printer.FormatPDF, printer.FormatMarkdown, printer.FormatGL} for _, e := range extensions { _, err := os.Stat(fmt.Sprintf("%s%s.%s", resultsDirectory, fileName, e))