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:
parent
ea001341cb
commit
903f273012
@ -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")
|
||||
}
|
||||
|
@ -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: ""}
|
||||
}
|
||||
|
@ -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{}
|
||||
|
@ -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")
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
})
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user