1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-18 05:18:24 +02:00

feat(checkmarxExecuteScan): added API to get description, incorporated to SARIF file (#3814)

This commit is contained in:
xgoffin 2022-06-01 15:48:56 +02:00 committed by GitHub
parent ea001341cb
commit 903f273012
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 7 deletions

View File

@ -325,7 +325,7 @@ func verifyCxProjectCompliance(config checkmarxExecuteScanOptions, sys checkmarx
// generate sarif report
if config.ConvertToSarif {
log.Entry().Info("Calling conversion to SARIF function.")
sarif, err := checkmarx.ConvertCxxmlToSarif(xmlReportName)
sarif, err := checkmarx.ConvertCxxmlToSarif(sys, xmlReportName, scanID)
if err != nil {
return fmt.Errorf("failed to generate SARIF")
}

View File

@ -144,6 +144,9 @@ func (sys *systemMock) CreateProject(string, string) (checkmarx.ProjectCreateRes
func (sys *systemMock) CreateBranch(int, string) int {
return 18
}
func (sys *systemMock) GetShortDescription(int, int) (checkmarx.ShortDescription, error) {
return checkmarx.ShortDescription{Text: "dummyText"}, nil
}
func (sys *systemMock) GetPresets() []checkmarx.Preset {
sys.getPresetsCalled = true
return []checkmarx.Preset{{ID: 10078, Name: "SAP Java Default", OwnerName: "16"}, {ID: 10048, Name: "SAP JS Default", OwnerName: "16"}, {ID: 16, Name: "CX_Default", OwnerName: "16"}}
@ -199,6 +202,9 @@ func (sys *systemMockForExistingProject) GetResults(int) checkmarx.ResultsStatis
func (sys *systemMockForExistingProject) GetScans(int) ([]checkmarx.ScanStatus, error) {
return []checkmarx.ScanStatus{{IsIncremental: true}, {IsIncremental: true}, {IsIncremental: true}, {IsIncremental: false}}, nil
}
func (sys *systemMockForExistingProject) GetShortDescription(int, int) (checkmarx.ShortDescription, error) {
return checkmarx.ShortDescription{Text: "dummyText"}, nil
}
func (sys *systemMockForExistingProject) GetScanStatusAndDetail(int) (string, checkmarx.ScanStatusDetail) {
return "Finished", checkmarx.ScanStatusDetail{Stage: "", Step: ""}
}

View File

@ -161,6 +161,10 @@ type SourceSettingsLink struct {
URI string `json:"uri"`
}
type ShortDescription struct {
Text string `json:"shortDescription"`
}
//DetailedResult - DetailedResult Structure
type DetailedResult struct {
XMLName xml.Name `xml:"CxXMLResults"`
@ -231,6 +235,7 @@ type System interface {
GetProjectByID(projectID int) (Project, error)
GetProjectsByNameAndTeam(projectName, teamID string) ([]Project, error)
GetProjects() ([]Project, error)
GetShortDescription(scanID int, pathID int) (ShortDescription, error)
GetTeams() []Team
}
@ -652,6 +657,20 @@ func (sys *SystemInstance) GetReportStatus(reportID int) (ReportStatusResponse,
return response, nil
}
// GetShortDescription returns the short description for an issue with a scanID and pathID
func (sys *SystemInstance) GetShortDescription(scanID int, pathID int) (ShortDescription, error) {
var shortDescription ShortDescription
data, err := sendRequest(sys, http.MethodGet, fmt.Sprintf("/sast/scans/%v/results/%v/shortDescription", scanID, pathID), nil, nil)
if err != nil {
sys.logger.Errorf("Failed to get short description for scanID %v and pathID %v: %s", scanID, pathID, err)
return shortDescription, err
}
json.Unmarshal(data, &shortDescription)
return shortDescription, nil
}
// DownloadReport downloads the report addressed by reportID and returns the XML contents
func (sys *SystemInstance) DownloadReport(reportID int) ([]byte, error) {
header := http.Header{}

View File

@ -605,3 +605,20 @@ func TestGetProjectByName(t *testing.T) {
assert.Equal(t, "Project1_PR-18", result[0].Name, "Result incorrect")
})
}
func TestGetShortDescription(t *testing.T) {
logger := log.Entry().WithField("package", "SAP/jenkins-library/pkg/checkmarx_test")
opts := piperHttp.ClientOptions{}
t.Run("test success", func(t *testing.T) {
myTestClient := senderMock{responseBody: `{"shortDescription":"This is a dummy short description."}`, httpStatusCode: 200}
sys := SystemInstance{serverURL: "https://cx.server.com", client: &myTestClient, logger: logger}
myTestClient.SetOptions(opts)
shortDescription, err := sys.GetShortDescription(11037, 1)
assert.NoError(t, err)
assert.Equal(t, "https://cx.server.com/cxrestapi/sast/scans/11037/results/1/shortDescription", myTestClient.urlCalled, "Called url incorrect")
assert.Equal(t, "GET", myTestClient.httpMethod, "HTTP method incorrect")
assert.Equal(t, "This is a dummy short description.", shortDescription.Text, "Description incorrect")
})
}

View File

@ -114,7 +114,7 @@ type Line struct {
}
// ConvertCxxmlToSarif is the entrypoint for the Parse function
func ConvertCxxmlToSarif(xmlReportName string) (format.SARIF, error) {
func ConvertCxxmlToSarif(sys System, xmlReportName string, scanID int) (format.SARIF, error) {
var sarif format.SARIF
log.Entry().Debug("Reading audit file.")
data, err := ioutil.ReadFile(xmlReportName)
@ -128,11 +128,11 @@ func ConvertCxxmlToSarif(xmlReportName string) (format.SARIF, error) {
}
log.Entry().Debug("Calling Parse.")
return Parse(data)
return Parse(sys, data, scanID)
}
// Parse function
func Parse(data []byte) (format.SARIF, error) {
func Parse(sys System, data []byte, scanID int) (format.SARIF, error) {
reader := bytes.NewReader(data)
decoder := xml.NewDecoder(reader)
@ -153,18 +153,33 @@ func Parse(data []byte) (format.SARIF, error) {
baseURL := "https://" + strings.Split(cxxml.DeepLink, "/")[2] + "/CxWebClient/ScanQueryDescription.aspx?"
cweIdsForTaxonomies := make(map[string]int) //use a map to avoid duplicates
cweCounter := 0
var apiDescription string
//CxXML files contain a CxXMLResults > Query object, which represents a broken rule or type of vuln
//This Query object contains a list of Result objects, each representing an occurence
//Each Result object contains a ResultPath, which represents the exact location of the occurence (the "Snippet")
log.Entry().Debug("[SARIF] Now handling results.")
for i := 0; i < len(cxxml.Query); i++ {
descriptionFetched := false
//add cweid to array
cweIdsForTaxonomies[cxxml.Query[i].CweID] = cweCounter
cweCounter = cweCounter + 1
for j := 0; j < len(cxxml.Query[i].Result); j++ {
result := *new(format.Results)
// For rules later, fetch description
if !descriptionFetched {
if sys != nil {
apiShortDescription, err := sys.GetShortDescription(scanID, cxxml.Query[i].Result[j].Path.PathID)
if err != nil {
log.Entry().Error(err)
} else {
descriptionFetched = true
apiDescription = apiShortDescription.Text
}
}
}
//General
result.RuleID = "checkmarx-" + cxxml.Query[i].ID
result.RuleIndex = cweIdsForTaxonomies[cxxml.Query[i].CweID]
@ -294,7 +309,10 @@ func Parse(data []byte) (format.SARIF, error) {
rule.Help.Text = rule.HelpURI
rule.ShortDescription = new(format.Message)
rule.ShortDescription.Text = cxxml.Query[i].Name
if cxxml.Query[i].Categories != "" {
if apiDescription != "" {
rule.FullDescription = new(format.Message)
rule.FullDescription.Text = apiDescription
} else if cxxml.Query[i].Categories != "" {
rule.FullDescription = new(format.Message)
rule.FullDescription.Text = cxxml.Query[i].Categories
}

View File

@ -3,6 +3,8 @@ package checkmarx
import (
"testing"
piperHttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/stretchr/testify/assert"
)
@ -107,16 +109,34 @@ func TestParse(t *testing.T) {
`
t.Run("Valid config", func(t *testing.T) {
sarif, err := Parse([]byte(testCxxml))
opts := piperHttp.ClientOptions{}
logger := log.Entry().WithField("package", "SAP/jenkins-library/pkg/checkmarx_test")
myTestClient := senderMock{responseBody: `{"shortDescription":"This is a dummy short description."}`, httpStatusCode: 200}
sys := SystemInstance{serverURL: "https://cx.server.com", client: &myTestClient, logger: logger}
myTestClient.SetOptions(opts)
sarif, err := Parse(&sys, []byte(testCxxml), 11037)
assert.NoError(t, err, "error")
assert.Equal(t, len(sarif.Runs[0].Results), 3)
assert.Equal(t, len(sarif.Runs[0].Tool.Driver.Rules), 2)
assert.Equal(t, sarif.Runs[0].Results[2].Properties.ToolState, "Confirmed")
assert.Equal(t, sarif.Runs[0].Results[2].Properties.ToolAuditMessage, "Changed status to Confirmed \n Dummy comment")
assert.Equal(t, "This is a dummy short description.", sarif.Runs[0].Tool.Driver.Rules[0].FullDescription.Text)
})
t.Run("Missing sys", func(t *testing.T) {
sarif, err := Parse(nil, []byte(testCxxml), 11037)
assert.NoError(t, err, "error")
assert.Equal(t, len(sarif.Runs[0].Results), 3)
assert.Equal(t, len(sarif.Runs[0].Tool.Driver.Rules), 2)
assert.Equal(t, sarif.Runs[0].Results[2].Properties.ToolState, "Confirmed")
assert.Equal(t, sarif.Runs[0].Results[2].Properties.ToolAuditMessage, "Changed status to Confirmed \n Dummy comment")
assert.Equal(t, "Dummy Categories", sarif.Runs[0].Tool.Driver.Rules[0].FullDescription.Text)
})
t.Run("Missing data", func(t *testing.T) {
_, err := Parse([]byte{})
_, err := Parse(nil, []byte{}, 11037)
assert.Error(t, err, "EOF")
})