Skip to content

Commit c8c5d78

Browse files
Fix bug AST-106030 and add Description link to AST-106749 gl-sast report
* Include the description link for the GL-SAST report - (AST-106749) * Fix Bug (AST-106030)
1 parent ba50ebd commit c8c5d78

File tree

4 files changed

+149
-10
lines changed

4 files changed

+149
-10
lines changed

internal/commands/result.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,7 +1662,7 @@ func exportGlSastResults(targetFile string, results *wrappers.ScanResultsCollect
16621662
if err != nil {
16631663
return errors.Wrapf(err, "%s: failed to add scan to gl-sast report", failedListingResults)
16641664
}
1665-
convertCxResultToGlSastVulnerability(results, glSast, summary.BaseURI)
1665+
convertCxResultToGlSastVulnerability(results, glSast, summary)
16661666
resultsJSON, err := json.Marshal(glSast)
16671667
if err != nil {
16681668
return errors.Wrapf(err, "%s: failed to serialize gl-sast report ", failedListingResults)
@@ -2031,10 +2031,10 @@ func convertCxResultsToSarif(results *wrappers.ScanResultsCollection) *wrappers.
20312031
return sarif
20322032
}
20332033

2034-
func convertCxResultToGlSastVulnerability(results *wrappers.ScanResultsCollection, glSast *wrappers.GlSastResultsCollection, summaryBaseURI string) {
2034+
func convertCxResultToGlSastVulnerability(results *wrappers.ScanResultsCollection, glSast *wrappers.GlSastResultsCollection, summary *wrappers.ResultSummary) {
20352035
for _, result := range results.Results {
20362036
if strings.TrimSpace(result.Type) == commonParams.SastType {
2037-
glSast = parseGlSastVulnerability(result, glSast, summaryBaseURI)
2037+
glSast = parseGlSastVulnerability(result, glSast, summary)
20382038
}
20392039
}
20402040
}
@@ -2054,7 +2054,9 @@ func convertCxResultToGlScaFiles(results *wrappers.ScanResultsCollection, glScaR
20542054
}
20552055
}
20562056
}
2057-
func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSastResultsCollection, summaryBaseURI string) *wrappers.GlSastResultsCollection {
2057+
func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSastResultsCollection, summary *wrappers.ResultSummary) *wrappers.GlSastResultsCollection {
2058+
hostName := parseURI(summary.BaseURI)
2059+
20582060
queryName := result.ScanResultData.QueryName
20592061
fileName := result.ScanResultData.Nodes[0].FileName
20602062
lineNumber := strconv.FormatUint(uint64(result.ScanResultData.Nodes[0].Line), 10)
@@ -2063,13 +2065,14 @@ func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSa
20632065
ID := fmt.Sprintf("%s:%s:%s", queryName, fileName, lineNumber)
20642066
category := fmt.Sprintf("%s-%s", wrappers.VendorName, result.Type)
20652067
message := fmt.Sprintf("%s@%s:%s", queryName, fileName, lineNumber)
2068+
QueryDescriptionLink := fmt.Sprintf("%s/results/%s/%s/sast/description/%s/%s", hostName, summary.ScanID, summary.ProjectID, result.VulnerabilityDetails.CweID, result.ScanResultData.QueryID)
20662069

20672070
glSast.Vulnerabilities = append(glSast.Vulnerabilities, wrappers.GlVulnerabilities{
20682071
ID: ID,
20692072
Category: category,
20702073
Name: queryName,
20712074
Message: message,
2072-
Description: result.Description,
2075+
Description: result.Description + " \n" + QueryDescriptionLink,
20732076
CVE: ID,
20742077
Severity: cases.Title(language.English).String(result.Severity),
20752078
Confidence: cases.Title(language.English).String(result.Severity),
@@ -2083,7 +2086,7 @@ func parseGlSastVulnerability(result *wrappers.ScanResult, glSast *wrappers.GlSa
20832086
{
20842087
Type: "cxOneScan",
20852088
Name: "CxOne Scan",
2086-
URL: summaryBaseURI,
2089+
URL: summary.BaseURI,
20872090
Value: result.ID,
20882091
},
20892092
},
@@ -2889,6 +2892,16 @@ type ScannerResponse struct {
28892892
ErrorCode string `json:"ErrorCode,omitempty"`
28902893
}
28912894

2895+
func parseURI(summaryBaseURI string) (hostName string) {
2896+
parsedURL, err := url.Parse(summaryBaseURI)
2897+
if err != nil {
2898+
return ""
2899+
}
2900+
hostName = fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host)
2901+
2902+
return hostName
2903+
}
2904+
28922905
func printWarningIfIgnorePolicyOmiited() {
28932906
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")
28942907
}

internal/commands/result_test.go

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,7 @@ func TestRunGetResultsByScanIdSummaryConsoleFormat_ScsCompleted_ScsCompletedInRe
10951095
"Expected SCS summary:"+scsSummary)
10961096
secretDetectionSummary := secretDetectionLine
10971097
assert.Equal(t, strings.Contains(cleanString, secretDetectionSummary), true,
1098-
"Expected Secret Detection summary:"+secretDetectionSummary)
1098+
"Expected Secret Detection summary:"+secretDetectionLine)
10991099
scorecardSummary := "| Scorecard 0 0 0 1 0 Completed |"
11001100
assert.Equal(t, strings.Contains(cleanString, scorecardSummary), true,
11011101
"Expected Scorecard summary:"+scorecardSummary)
@@ -1130,7 +1130,7 @@ func TestRunGetResultsByScanIdSummaryConsoleFormat_ScsPartial_ScsPartialInReport
11301130
"Expected SCS summary:"+scsSummary)
11311131
secretDetectionSummary := secretDetectionLine
11321132
assert.Equal(t, strings.Contains(cleanString, secretDetectionSummary), true,
1133-
"Expected Secret Detection summary:"+secretDetectionSummary)
1133+
"Expected Secret Detection summary:"+secretDetectionLine)
11341134
scorecardSummary := " | Scorecard 0 0 0 0 0 Failed |"
11351135
assert.Equal(t, strings.Contains(cleanString, scorecardSummary), true,
11361136
"Expected Scorecard summary:"+scorecardSummary)
@@ -1157,7 +1157,7 @@ func TestRunGetResultsByScanIdSummaryConsoleFormat_ScsScorecardNotScanned_Scorec
11571157
"Expected SCS summary:"+scsSummary)
11581158
secretDetectionSummary := secretDetectionLine
11591159
assert.Equal(t, strings.Contains(stdoutString, secretDetectionSummary), true,
1160-
"Expected Secret Detection summary:"+secretDetectionSummary)
1160+
"Expected Secret Detection summary:"+secretDetectionLine)
11611161
scorecardSummary := "| Scorecard - - - - - - |"
11621162
assert.Equal(t, strings.Contains(stdoutString, scorecardSummary), true,
11631163
"Expected Scorecard summary:"+scorecardSummary)
@@ -1697,3 +1697,77 @@ func TestIgnorePolicyWithPermission(t *testing.T) {
16971697
output := buf.String()
16981698
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")
16991699
}
1700+
1701+
func TestParseGlSastVulnerability_QueryDescriptionLink_Succeed(t *testing.T) {
1702+
mockResult := createMockScanResult("q1234", "c5678")
1703+
glSast := &wrappers.GlSastResultsCollection{}
1704+
summary := &wrappers.ResultSummary{
1705+
BaseURI: "https://example.com/overview",
1706+
ScanID: "scanID",
1707+
ProjectID: "projectID",
1708+
}
1709+
expectedURL := "https://example.com/results/scanID/projectID/sast/description/c5678/q1234"
1710+
1711+
glSast = parseGlSastVulnerability(mockResult, glSast, summary)
1712+
1713+
assert.Assert(t, len(glSast.Vulnerabilities) > 0)
1714+
1715+
actualURL := extractURLFromDescription(glSast.Vulnerabilities[0].Description)
1716+
1717+
assert.Equal(t, actualURL, expectedURL, "QueryDescriptionLink URL does not match expected format")
1718+
}
1719+
1720+
func TestParseGlSastVulnerability_QueryDescriptionLink_Negative(t *testing.T) {
1721+
mockResult := createMockScanResult("", "")
1722+
glSast := &wrappers.GlSastResultsCollection{}
1723+
summary := &wrappers.ResultSummary{
1724+
BaseURI: "invalid-url",
1725+
ScanID: "scanID",
1726+
ProjectID: "projectID",
1727+
}
1728+
expectedPattern := "/results/scanID/projectID/sast/description//"
1729+
1730+
glSast = parseGlSastVulnerability(mockResult, glSast, summary)
1731+
1732+
assert.Assert(t, len(glSast.Vulnerabilities) > 0)
1733+
vuln := glSast.Vulnerabilities[0]
1734+
1735+
assert.Assert(t, strings.Contains(vuln.Description, expectedPattern),
1736+
"URL should contain pattern with empty values")
1737+
1738+
actualURL := extractURLFromDescription(vuln.Description)
1739+
assert.Assert(t, actualURL != "", "Extracted URL should not be empty")
1740+
}
1741+
1742+
func createMockScanResult(queryID, cweID string) *wrappers.ScanResult {
1743+
return &wrappers.ScanResult{
1744+
Type: "sast",
1745+
ScanResultData: wrappers.ScanResultData{
1746+
QueryName: "TestQuery",
1747+
QueryID: queryID,
1748+
Nodes: []*wrappers.ScanResultNode{
1749+
{
1750+
FileName: "file.go",
1751+
Line: 42,
1752+
Length: 1,
1753+
},
1754+
},
1755+
},
1756+
VulnerabilityDetails: wrappers.VulnerabilityDetails{
1757+
CweID: cweID,
1758+
},
1759+
ID: "vuln-1",
1760+
Description: "desc-",
1761+
Severity: "high",
1762+
}
1763+
}
1764+
1765+
func extractURLFromDescription(description string) string {
1766+
parts := strings.Split(description, "http")
1767+
if len(parts) == 1 {
1768+
return "http" + strings.Split(parts[0], " ")[0]
1769+
} else if len(parts) > 1 {
1770+
return "http" + strings.Split(parts[1], " ")[0]
1771+
}
1772+
return ""
1773+
}

internal/wrappers/results-gl-sast.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const (
66
AnalyzerURL = "https://checkmarx.com/"
77
VendorName = "Checkmarx"
88
SastSchema = "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/sast-report-format.json"
9-
SastSchemaVersion = "15.0"
9+
SastSchemaVersion = "15.0.0"
1010
)
1111

1212
type GlSastResultsCollection struct {

test/integration/result_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,3 +725,55 @@ func TestResultsIncludePublishedAtJsonOutput(t *testing.T) {
725725
}
726726
}
727727
}
728+
729+
func TestCreateQueryDescriptionLinkInGlSASTReport(t *testing.T) {
730+
scanID, _ := getRootScan(t)
731+
_ = executeCmdNilAssertion(
732+
t, "Results show generating gl-sast report should pass",
733+
"results", "show",
734+
flag(params.ScanIDFlag), scanID,
735+
flag(params.TargetFormatFlag), printer.FormatGLSast,
736+
flag(params.TargetPathFlag), resultsDirectory,
737+
flag(params.TargetFlag), fileName,
738+
)
739+
740+
defer os.RemoveAll(resultsDirectory)
741+
reportPath := fmt.Sprintf("%s%s.%s-%s", resultsDirectory, fileName, printer.FormatGLSast, fileExtention)
742+
file, err := os.ReadFile(reportPath)
743+
assert.NilError(t, err, "error reading gl-sast file")
744+
745+
var glReport wrappers.GlSastResultsCollection
746+
err = json.Unmarshal(file, &glReport)
747+
assert.NilError(t, err, "error unmarshalling gl-sast file")
748+
749+
link, found := findQueryDescriptionLink(glReport)
750+
assert.Assert(t, found, "Should find at least one QueryDescriptionLink")
751+
752+
t.Logf("Found QueryDescriptionLink: %s", link)
753+
}
754+
755+
func findQueryDescriptionLink(glReport wrappers.GlSastResultsCollection) (string, bool) {
756+
for _, vulnerability := range glReport.Vulnerabilities {
757+
if !strings.Contains(vulnerability.Description, "/results/") {
758+
continue
759+
}
760+
761+
if httpIndex := strings.Index(vulnerability.Description, "http"); httpIndex != -1 {
762+
urlStart := httpIndex
763+
urlEnd := len(vulnerability.Description)
764+
765+
for _, terminator := range []string{" ", "\n", "\t", ")", "]", "}"} {
766+
if idx := strings.Index(vulnerability.Description[urlStart:], terminator); idx != -1 {
767+
urlEnd = urlStart + idx
768+
break
769+
}
770+
}
771+
772+
link := vulnerability.Description[urlStart:urlEnd]
773+
if link != "" {
774+
return link, true
775+
}
776+
}
777+
}
778+
return "", false
779+
}

0 commit comments

Comments
 (0)