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
25 changes: 19 additions & 6 deletions internal/commands/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -1662,7 +1662,7 @@ func exportGlSastResults(targetFile string, results *wrappers.ScanResultsCollect
if err != nil {
return errors.Wrapf(err, "%s: failed to add scan to gl-sast report", failedListingResults)
}
convertCxResultToGlSastVulnerability(results, glSast, summary.BaseURI)
convertCxResultToGlSastVulnerability(results, glSast, summary)
resultsJSON, err := json.Marshal(glSast)
if err != nil {
return errors.Wrapf(err, "%s: failed to serialize gl-sast report ", failedListingResults)
Expand Down Expand Up @@ -2031,10 +2031,10 @@ func convertCxResultsToSarif(results *wrappers.ScanResultsCollection) *wrappers.
return sarif
}

func convertCxResultToGlSastVulnerability(results *wrappers.ScanResultsCollection, glSast *wrappers.GlSastResultsCollection, summaryBaseURI string) {
func convertCxResultToGlSastVulnerability(results *wrappers.ScanResultsCollection, glSast *wrappers.GlSastResultsCollection, summary *wrappers.ResultSummary) {
for _, result := range results.Results {
if strings.TrimSpace(result.Type) == commonParams.SastType {
glSast = parseGlSastVulnerability(result, glSast, summaryBaseURI)
glSast = parseGlSastVulnerability(result, glSast, summary)
}
}
}
Expand All @@ -2054,7 +2054,9 @@ func convertCxResultToGlScaFiles(results *wrappers.ScanResultsCollection, glScaR
}
}
}
func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSastResultsCollection, summaryBaseURI string) *wrappers.GlSastResultsCollection {
func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSastResultsCollection, summary *wrappers.ResultSummary) *wrappers.GlSastResultsCollection {
hostName := parseURI(summary.BaseURI)

queryName := result.ScanResultData.QueryName
fileName := result.ScanResultData.Nodes[0].FileName
lineNumber := strconv.FormatUint(uint64(result.ScanResultData.Nodes[0].Line), 10)
Expand All @@ -2063,13 +2065,14 @@ func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSa
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)
QueryDescriptionLink := fmt.Sprintf("%s/results/%s/%s/sast/description/%s/%s", hostName, summary.ScanID, summary.ProjectID, result.VulnerabilityDetails.CweID, result.ScanResultData.QueryID)

glSast.Vulnerabilities = append(glSast.Vulnerabilities, wrappers.GlVulnerabilities{
ID: ID,
Category: category,
Name: queryName,
Message: message,
Description: result.Description,
Description: result.Description + " \n" + QueryDescriptionLink,
CVE: ID,
Severity: cases.Title(language.English).String(result.Severity),
Confidence: cases.Title(language.English).String(result.Severity),
Expand All @@ -2083,7 +2086,7 @@ func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSa
{
Type: "cxOneScan",
Name: "CxOne Scan",
URL: summaryBaseURI,
URL: summary.BaseURI,
Value: result.ID,
},
},
Expand Down Expand Up @@ -2889,6 +2892,16 @@ type ScannerResponse struct {
ErrorCode string `json:"ErrorCode,omitempty"`
}

func parseURI(summaryBaseURI string) (hostName string) {
parsedURL, err := url.Parse(summaryBaseURI)
if err != nil {
return ""
}
hostName = fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host)

return hostName
}

func printWarningIfIgnorePolicyOmiited() {
fmt.Printf("\n Warning: The --ignore-policy flag was not implemented because you don’t have the required permission.\n Only users with 'override-policy-management' permission can use this flag. \n\n")
}
80 changes: 77 additions & 3 deletions internal/commands/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,7 @@ func TestRunGetResultsByScanIdSummaryConsoleFormat_ScsCompleted_ScsCompletedInRe
"Expected SCS summary:"+scsSummary)
secretDetectionSummary := secretDetectionLine
assert.Equal(t, strings.Contains(cleanString, secretDetectionSummary), true,
"Expected Secret Detection summary:"+secretDetectionSummary)
"Expected Secret Detection summary:"+secretDetectionLine)
scorecardSummary := "| Scorecard 0 0 0 1 0 Completed |"
assert.Equal(t, strings.Contains(cleanString, scorecardSummary), true,
"Expected Scorecard summary:"+scorecardSummary)
Expand Down Expand Up @@ -1130,7 +1130,7 @@ func TestRunGetResultsByScanIdSummaryConsoleFormat_ScsPartial_ScsPartialInReport
"Expected SCS summary:"+scsSummary)
secretDetectionSummary := secretDetectionLine
assert.Equal(t, strings.Contains(cleanString, secretDetectionSummary), true,
"Expected Secret Detection summary:"+secretDetectionSummary)
"Expected Secret Detection summary:"+secretDetectionLine)
scorecardSummary := " | Scorecard 0 0 0 0 0 Failed |"
assert.Equal(t, strings.Contains(cleanString, scorecardSummary), true,
"Expected Scorecard summary:"+scorecardSummary)
Expand All @@ -1157,7 +1157,7 @@ func TestRunGetResultsByScanIdSummaryConsoleFormat_ScsScorecardNotScanned_Scorec
"Expected SCS summary:"+scsSummary)
secretDetectionSummary := secretDetectionLine
assert.Equal(t, strings.Contains(stdoutString, secretDetectionSummary), true,
"Expected Secret Detection summary:"+secretDetectionSummary)
"Expected Secret Detection summary:"+secretDetectionLine)
scorecardSummary := "| Scorecard - - - - - - |"
assert.Equal(t, strings.Contains(stdoutString, scorecardSummary), true,
"Expected Scorecard summary:"+scorecardSummary)
Expand Down Expand Up @@ -1697,3 +1697,77 @@ func TestIgnorePolicyWithPermission(t *testing.T) {
output := buf.String()
assert.Assert(t, !strings.Contains(output, "Warning: The --ignore-policy flag was not implemented because you don’t have the required permission."), "'Ignore Policy flag omitted because you dont have permission' should not be present in the output")
}

func TestParseGlSastVulnerability_QueryDescriptionLink_Succeed(t *testing.T) {
mockResult := createMockScanResult("q1234", "c5678")
glSast := &wrappers.GlSastResultsCollection{}
summary := &wrappers.ResultSummary{
BaseURI: "https://example.com/overview",
ScanID: "scanID",
ProjectID: "projectID",
}
expectedURL := "https://example.com/results/scanID/projectID/sast/description/c5678/q1234"

glSast = parseGlSastVulnerability(mockResult, glSast, summary)

assert.Assert(t, len(glSast.Vulnerabilities) > 0)

actualURL := extractURLFromDescription(glSast.Vulnerabilities[0].Description)

assert.Equal(t, actualURL, expectedURL, "QueryDescriptionLink URL does not match expected format")
}

func TestParseGlSastVulnerability_QueryDescriptionLink_Negative(t *testing.T) {
mockResult := createMockScanResult("", "")
glSast := &wrappers.GlSastResultsCollection{}
summary := &wrappers.ResultSummary{
BaseURI: "invalid-url",
ScanID: "scanID",
ProjectID: "projectID",
}
expectedPattern := "/results/scanID/projectID/sast/description//"

glSast = parseGlSastVulnerability(mockResult, glSast, summary)

assert.Assert(t, len(glSast.Vulnerabilities) > 0)
vuln := glSast.Vulnerabilities[0]

assert.Assert(t, strings.Contains(vuln.Description, expectedPattern),
"URL should contain pattern with empty values")

actualURL := extractURLFromDescription(vuln.Description)
assert.Assert(t, actualURL != "", "Extracted URL should not be empty")
}

func createMockScanResult(queryID, cweID string) *wrappers.ScanResult {
return &wrappers.ScanResult{
Type: "sast",
ScanResultData: wrappers.ScanResultData{
QueryName: "TestQuery",
QueryID: queryID,
Nodes: []*wrappers.ScanResultNode{
{
FileName: "file.go",
Line: 42,
Length: 1,
},
},
},
VulnerabilityDetails: wrappers.VulnerabilityDetails{
CweID: cweID,
},
ID: "vuln-1",
Description: "desc-",
Severity: "high",
}
}

func extractURLFromDescription(description string) string {
parts := strings.Split(description, "http")
if len(parts) == 1 {
return "http" + strings.Split(parts[0], " ")[0]
} else if len(parts) > 1 {
return "http" + strings.Split(parts[1], " ")[0]
}
return ""
}
2 changes: 1 addition & 1 deletion internal/wrappers/results-gl-sast.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const (
AnalyzerURL = "https://checkmarx.com/"
VendorName = "Checkmarx"
SastSchema = "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/sast-report-format.json"
SastSchemaVersion = "15.0"
SastSchemaVersion = "15.0.0"
)

type GlSastResultsCollection struct {
Expand Down
52 changes: 52 additions & 0 deletions test/integration/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -725,3 +725,55 @@ func TestResultsIncludePublishedAtJsonOutput(t *testing.T) {
}
}
}

func TestCreateQueryDescriptionLinkInGlSASTReport(t *testing.T) {
scanID, _ := getRootScan(t)
_ = executeCmdNilAssertion(
t, "Results show generating gl-sast report should pass",
"results", "show",
flag(params.ScanIDFlag), scanID,
flag(params.TargetFormatFlag), printer.FormatGLSast,
flag(params.TargetPathFlag), resultsDirectory,
flag(params.TargetFlag), fileName,
)

defer os.RemoveAll(resultsDirectory)
reportPath := fmt.Sprintf("%s%s.%s-%s", resultsDirectory, fileName, printer.FormatGLSast, fileExtention)
file, err := os.ReadFile(reportPath)
assert.NilError(t, err, "error reading gl-sast file")

var glReport wrappers.GlSastResultsCollection
err = json.Unmarshal(file, &glReport)
assert.NilError(t, err, "error unmarshalling gl-sast file")

link, found := findQueryDescriptionLink(glReport)
assert.Assert(t, found, "Should find at least one QueryDescriptionLink")

t.Logf("Found QueryDescriptionLink: %s", link)
}

func findQueryDescriptionLink(glReport wrappers.GlSastResultsCollection) (string, bool) {
for _, vulnerability := range glReport.Vulnerabilities {
if !strings.Contains(vulnerability.Description, "/results/") {
continue
}

if httpIndex := strings.Index(vulnerability.Description, "http"); httpIndex != -1 {
urlStart := httpIndex
urlEnd := len(vulnerability.Description)

for _, terminator := range []string{" ", "\n", "\t", ")", "]", "}"} {
if idx := strings.Index(vulnerability.Description[urlStart:], terminator); idx != -1 {
urlEnd = urlStart + idx
break
}
}

link := vulnerability.Description[urlStart:urlEnd]
if link != "" {
return link, true
}
}
}
return "", false
}
Loading