mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-01-18 05:18:24 +02:00
feat(fortifyExecuteScan): HTML report for Fortify (#2879)
* Tune test * Fix report implementation * Fix tests * Fix values * Fix code and test * Report writing fix * Commit generated sources * Update cmd/fortifyExecuteScan.go Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> * Externalize report generation * Fix fmt * Fix fmt 2 Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com>
This commit is contained in:
parent
d1c8abc6b3
commit
a43f46465a
@ -174,7 +174,9 @@ func runFortifyScan(config fortifyExecuteScanOptions, sys fortify.System, utils
|
||||
|
||||
if config.VerifyOnly {
|
||||
log.Entry().Infof("Starting audit status check on project %v with version %v and project version ID %v", fortifyProjectName, fortifyProjectVersion, projectVersion.ID)
|
||||
return reports, verifyFFProjectCompliance(config, sys, project, projectVersion, filterSet, influx, auditStatus)
|
||||
err, paths := verifyFFProjectCompliance(config, sys, project, projectVersion, filterSet, influx, auditStatus)
|
||||
reports = append(reports, paths...)
|
||||
return reports, err
|
||||
}
|
||||
|
||||
log.Entry().Infof("Scanning and uploading to project %v with version %v and projectVersionId %v", fortifyProjectName, fortifyProjectVersion, projectVersion.ID)
|
||||
@ -196,10 +198,12 @@ func runFortifyScan(config fortifyExecuteScanOptions, sys fortify.System, utils
|
||||
}
|
||||
}
|
||||
|
||||
triggerFortifyScan(config, utils, buildID, buildLabel, fortifyProjectName)
|
||||
|
||||
err = triggerFortifyScan(config, utils, buildID, buildLabel, fortifyProjectName)
|
||||
reports = append(reports, piperutils.Path{Target: fmt.Sprintf("%vtarget/fortify-scan.*", config.ModulePath)})
|
||||
reports = append(reports, piperutils.Path{Target: fmt.Sprintf("%vtarget/*.fpr", config.ModulePath)})
|
||||
if err != nil {
|
||||
return reports, errors.Wrapf(err, "failed to scan project")
|
||||
}
|
||||
|
||||
var message string
|
||||
if config.UploadResults {
|
||||
@ -228,7 +232,9 @@ func runFortifyScan(config fortifyExecuteScanOptions, sys fortify.System, utils
|
||||
return reports, err
|
||||
}
|
||||
|
||||
return reports, verifyFFProjectCompliance(config, sys, project, projectVersion, filterSet, influx, auditStatus)
|
||||
err, paths := verifyFFProjectCompliance(config, sys, project, projectVersion, filterSet, influx, auditStatus)
|
||||
reports = append(reports, paths...)
|
||||
return reports, err
|
||||
}
|
||||
|
||||
func classifyErrorOnLookup(err error) {
|
||||
@ -237,7 +243,8 @@ func classifyErrorOnLookup(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func verifyFFProjectCompliance(config fortifyExecuteScanOptions, sys fortify.System, project *models.Project, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) error {
|
||||
func verifyFFProjectCompliance(config fortifyExecuteScanOptions, sys fortify.System, project *models.Project, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) (error, []piperutils.Path) {
|
||||
reports := []piperutils.Path{}
|
||||
// Generate report
|
||||
if config.Reporting {
|
||||
resultURL := []byte(fmt.Sprintf("https://fortify.tools.sap/ssc/html/ssc/version/%v/fix/null/", projectVersion.ID))
|
||||
@ -245,7 +252,7 @@ func verifyFFProjectCompliance(config fortifyExecuteScanOptions, sys fortify.Sys
|
||||
|
||||
data, err := generateAndDownloadQGateReport(config, sys, project, projectVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return err, reports
|
||||
}
|
||||
ioutil.WriteFile(fmt.Sprintf("%vtarget/%v-%v.%v", config.ModulePath, *project.Name, *projectVersion.Name, config.ReportType), data, 0700)
|
||||
}
|
||||
@ -253,43 +260,67 @@ func verifyFFProjectCompliance(config fortifyExecuteScanOptions, sys fortify.Sys
|
||||
// Perform audit compliance checks
|
||||
issueFilterSelectorSet, err := sys.GetIssueFilterSelectorOfProjectVersionByName(projectVersion.ID, []string{"Analysis", "Folder", "Category"}, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to fetch project version issue filter selector for project version ID %v", projectVersion.ID)
|
||||
return errors.Wrapf(err, "failed to fetch project version issue filter selector for project version ID %v", projectVersion.ID), reports
|
||||
}
|
||||
log.Entry().Debugf("initial filter selector set: %v", issueFilterSelectorSet)
|
||||
numberOfViolations, err := analyseUnauditedIssues(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus)
|
||||
numberOfViolations, issueGroups, err := analyseUnauditedIssues(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to analyze unaudited issues")
|
||||
return errors.Wrap(err, "failed to analyze unaudited issues"), reports
|
||||
}
|
||||
numberOfViolations += analyseSuspiciousExploitable(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus)
|
||||
numberOfSuspiciousExplotable, issueGroupsSuspiciousExploitable := analyseSuspiciousExploitable(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus)
|
||||
numberOfViolations += numberOfSuspiciousExplotable
|
||||
issueGroups = append(issueGroups, issueGroupsSuspiciousExploitable...)
|
||||
|
||||
log.Entry().Infof("Counted %v violations, details: %v", numberOfViolations, auditStatus)
|
||||
|
||||
influx.fortify_data.fields.projectName = *project.Name
|
||||
influx.fortify_data.fields.projectVersion = *projectVersion.Name
|
||||
influx.fortify_data.fields.violations = numberOfViolations
|
||||
|
||||
scanReport := fortify.CreateCustomReport(prepareReportData(influx), issueGroups)
|
||||
paths, err := fortify.WriteCustomReports(scanReport, influx.fortify_data.fields.projectName, influx.fortify_data.fields.projectVersion)
|
||||
reports = append(reports, paths...)
|
||||
if numberOfViolations > 0 {
|
||||
log.SetErrorCategory(log.ErrorCompliance)
|
||||
return errors.New("fortify scan failed, the project is not compliant. For details check the archived report")
|
||||
return errors.New("fortify scan failed, the project is not compliant. For details check the archived report"), reports
|
||||
}
|
||||
return nil
|
||||
return nil, reports
|
||||
}
|
||||
|
||||
func analyseUnauditedIssues(config fortifyExecuteScanOptions, sys fortify.System, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) (int, error) {
|
||||
func prepareReportData(influx *fortifyExecuteScanInflux) fortify.FortifyReportData {
|
||||
input := influx.fortify_data.fields
|
||||
output := fortify.FortifyReportData{}
|
||||
output.ProjectName = input.projectName
|
||||
output.ProjectVersion = input.projectVersion
|
||||
output.AuditAllAudited = input.auditAllAudited
|
||||
output.AuditAllTotal = input.auditAllTotal
|
||||
output.CorporateAudited = input.corporateAudited
|
||||
output.CorporateTotal = input.corporateTotal
|
||||
output.SpotChecksAudited = input.spotChecksAudited
|
||||
output.SpotChecksGap = input.spotChecksGap
|
||||
output.SpotChecksTotal = input.spotChecksTotal
|
||||
output.Exploitable = input.exploitable
|
||||
output.Suppressed = input.suppressed
|
||||
output.Suspicious = input.suspicious
|
||||
return output
|
||||
}
|
||||
|
||||
func analyseUnauditedIssues(config fortifyExecuteScanOptions, sys fortify.System, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) (int, []*models.ProjectVersionIssueGroup, error) {
|
||||
log.Entry().Info("Analyzing unaudited issues")
|
||||
reducedFilterSelectorSet := sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Folder"}, nil)
|
||||
fetchedIssueGroups, err := sys.GetProjectIssuesByIDAndFilterSetGroupedBySelector(projectVersion.ID, "", filterSet.GUID, reducedFilterSelectorSet)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "failed to fetch project version issue groups with filter set %v and selector %v for project version ID %v", filterSet, issueFilterSelectorSet, projectVersion.ID)
|
||||
return 0, fetchedIssueGroups, errors.Wrapf(err, "failed to fetch project version issue groups with filter set %v and selector %v for project version ID %v", filterSet, issueFilterSelectorSet, projectVersion.ID)
|
||||
}
|
||||
overallViolations := 0
|
||||
for _, issueGroup := range fetchedIssueGroups {
|
||||
issueDelta, err := getIssueDeltaFor(config, sys, issueGroup, projectVersion.ID, filterSet, issueFilterSelectorSet, influx, auditStatus)
|
||||
if err != nil {
|
||||
return overallViolations, errors.Wrap(err, "failed to get issue delata")
|
||||
return overallViolations, fetchedIssueGroups, errors.Wrap(err, "failed to get issue delta")
|
||||
}
|
||||
overallViolations += issueDelta
|
||||
}
|
||||
return overallViolations, nil
|
||||
return overallViolations, fetchedIssueGroups, nil
|
||||
}
|
||||
|
||||
func getIssueDeltaFor(config fortifyExecuteScanOptions, sys fortify.System, issueGroup *models.ProjectVersionIssueGroup, projectVersionID int64, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) (int, error) {
|
||||
@ -383,7 +414,7 @@ func getSpotIssueCount(config fortifyExecuteScanOptions, sys fortify.System, spo
|
||||
return overallDelta
|
||||
}
|
||||
|
||||
func analyseSuspiciousExploitable(config fortifyExecuteScanOptions, sys fortify.System, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) int {
|
||||
func analyseSuspiciousExploitable(config fortifyExecuteScanOptions, sys fortify.System, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) (int, []*models.ProjectVersionIssueGroup) {
|
||||
log.Entry().Info("Analyzing suspicious and exploitable issues")
|
||||
reducedFilterSelectorSet := sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Analysis"}, []string{})
|
||||
fetchedGroups, err := sys.GetProjectIssuesByIDAndFilterSetGroupedBySelector(projectVersion.ID, "", filterSet.GUID, reducedFilterSelectorSet)
|
||||
@ -418,7 +449,7 @@ func analyseSuspiciousExploitable(config fortifyExecuteScanOptions, sys fortify.
|
||||
influx.fortify_data.fields.exploitable = exploitableCount
|
||||
influx.fortify_data.fields.suppressed = int(suppressedCount)
|
||||
|
||||
return result
|
||||
return result, fetchedGroups
|
||||
}
|
||||
|
||||
func logIssueURL(config fortifyExecuteScanOptions, projectVersionID int64, folderSelector, analysisSelector *models.IssueFilterSelector) {
|
||||
|
@ -217,6 +217,8 @@ func (f *fortifyMock) GetProjectIssuesByIDAndFilterSetGroupedBySelector(id int64
|
||||
}, nil
|
||||
}
|
||||
if issueFilterSelectorSet != nil && issueFilterSelectorSet.FilterBySet != nil && len(issueFilterSelectorSet.FilterBySet) > 0 && issueFilterSelectorSet.FilterBySet[0].GUID == "3" {
|
||||
groupName := "Suspicious"
|
||||
groupName2 := "Exploitable"
|
||||
group := "3"
|
||||
total := int32(4)
|
||||
audited := int32(0)
|
||||
@ -224,8 +226,8 @@ func (f *fortifyMock) GetProjectIssuesByIDAndFilterSetGroupedBySelector(id int64
|
||||
total2 := int32(5)
|
||||
audited2 := int32(0)
|
||||
return []*models.ProjectVersionIssueGroup{
|
||||
{ID: &group, TotalCount: &total, AuditedCount: &audited},
|
||||
{ID: &group2, TotalCount: &total2, AuditedCount: &audited2},
|
||||
{ID: &group, CleanName: &groupName, TotalCount: &total, AuditedCount: &audited},
|
||||
{ID: &group2, CleanName: &groupName2, TotalCount: &total2, AuditedCount: &audited2},
|
||||
}, nil
|
||||
}
|
||||
group := "Audit All"
|
||||
@ -238,9 +240,9 @@ func (f *fortifyMock) GetProjectIssuesByIDAndFilterSetGroupedBySelector(id int64
|
||||
total3 := int32(5)
|
||||
audited3 := int32(4)
|
||||
return []*models.ProjectVersionIssueGroup{
|
||||
{ID: &group, TotalCount: &total, AuditedCount: &audited},
|
||||
{ID: &group2, TotalCount: &total2, AuditedCount: &audited2},
|
||||
{ID: &group3, TotalCount: &total3, AuditedCount: &audited3},
|
||||
{ID: &group, CleanName: &group, TotalCount: &total, AuditedCount: &audited},
|
||||
{ID: &group2, CleanName: &group2, TotalCount: &total2, AuditedCount: &audited2},
|
||||
{ID: &group3, CleanName: &group3, TotalCount: &total3, AuditedCount: &audited3},
|
||||
}, nil
|
||||
}
|
||||
func (f *fortifyMock) ReduceIssueFilterSelectorSet(issueFilterSelectorSet *models.IssueFilterSelectorSet, names []string, options []string) *models.IssueFilterSelectorSet {
|
||||
@ -432,8 +434,9 @@ func TestAnalyseSuspiciousExploitable(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
issues := analyseSuspiciousExploitable(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus)
|
||||
issues, groups := analyseSuspiciousExploitable(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus)
|
||||
assert.Equal(t, 9, issues)
|
||||
assert.Equal(t, 2, len(groups))
|
||||
|
||||
assert.Equal(t, 4, influx.fortify_data.fields.suspicious)
|
||||
assert.Equal(t, 5, influx.fortify_data.fields.exploitable)
|
||||
@ -481,9 +484,10 @@ func TestAnalyseUnauditedIssues(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
issues, err := analyseUnauditedIssues(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus)
|
||||
issues, groups, err := analyseUnauditedIssues(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 13, issues)
|
||||
assert.Equal(t, 3, len(groups))
|
||||
|
||||
assert.Equal(t, 15, influx.fortify_data.fields.auditAllTotal)
|
||||
assert.Equal(t, 12, influx.fortify_data.fields.auditAllAudited)
|
||||
|
@ -37,6 +37,9 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ReportsDirectory defines the subfolder for the Fortify reports which are generated
|
||||
const ReportsDirectory = "fortify"
|
||||
|
||||
// System is the interface abstraction of a specific SystemInstance
|
||||
type System interface {
|
||||
GetProjectByName(name string, autoCreate bool, projectVersion string) (*models.Project, error)
|
||||
|
@ -126,7 +126,7 @@ func TestGetProjectByName(t *testing.T) {
|
||||
"project":{"id":815,"name":"autocreate","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -134,8 +134,8 @@ func TestGetProjectByName(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null},
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/815/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/815/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/815/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/815/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
if req.URL.Path == "/projectVersions/10172" {
|
||||
@ -195,7 +195,7 @@ func TestGetProjectVersionDetailsByProjectIDAndVersionName(t *testing.T) {
|
||||
"project":{"id":4711,"name":"python-test","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -203,8 +203,8 @@ func TestGetProjectVersionDetailsByProjectIDAndVersionName(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null}],
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
if req.URL.Path == "/projects/777/versions" {
|
||||
@ -227,7 +227,7 @@ func TestGetProjectVersionDetailsByProjectIDAndVersionName(t *testing.T) {
|
||||
"project":{"id":815,"name":"autocreate","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -235,8 +235,8 @@ func TestGetProjectVersionDetailsByProjectIDAndVersionName(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null},
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/815/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/815/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/815/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/815/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
if req.URL.Path == "/projectVersions/0" {
|
||||
@ -247,7 +247,7 @@ func TestGetProjectVersionDetailsByProjectIDAndVersionName(t *testing.T) {
|
||||
"project":{"id":815,"name":"autocreate","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -255,8 +255,8 @@ func TestGetProjectVersionDetailsByProjectIDAndVersionName(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null},
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/815/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/815/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/815/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/815/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
})
|
||||
@ -295,7 +295,7 @@ func TestGetProjectVersionAttributesByProjectVersionID(t *testing.T) {
|
||||
header := rw.Header()
|
||||
header.Add("Content-type", "application/json")
|
||||
rw.Write([]byte(
|
||||
`{"data": [{"_href": "https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/4711/attributes/4712","attributeDefinitionId": 31,
|
||||
`{"data": [{"_href": "https://fortify/ssc/api/v1/projectVersions/4711/attributes/4712","attributeDefinitionId": 31,
|
||||
"values": null,"guid": "gdgfdgfdgfdgfd","id": 4712,"value": "abcd"}],"count": 8,"responseCode": 200}`))
|
||||
return
|
||||
}
|
||||
@ -433,7 +433,7 @@ func TestProjectVersionCopyFromPartial(t *testing.T) {
|
||||
"project":{"id":4711,"name":"python-test","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -441,8 +441,8 @@ func TestProjectVersionCopyFromPartial(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null}],
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
})
|
||||
@ -472,7 +472,7 @@ func TestProjectVersionCopyCurrentState(t *testing.T) {
|
||||
"project":{"id":4711,"name":"python-test","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -480,8 +480,8 @@ func TestProjectVersionCopyCurrentState(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null}],
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
})
|
||||
@ -1102,7 +1102,7 @@ func TestLookupOrCreateProjectVersionDetailsForPullRequest(t *testing.T) {
|
||||
header := rw.Header()
|
||||
header.Add("Content-type", "application/json")
|
||||
rw.Write([]byte(
|
||||
`{"data": [{"_href": "https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/4711/attributes/4712","attributeDefinitionId": 31,
|
||||
`{"data": [{"_href": "https://fortify/ssc/api/v1/projectVersions/4711/attributes/4712","attributeDefinitionId": 31,
|
||||
"values": null,"guid": "gdgfdgfdgfdgfd","id": 4712,"value": "abcd"}],"count": 8,"responseCode": 200}`))
|
||||
return
|
||||
}
|
||||
@ -1126,7 +1126,7 @@ func TestLookupOrCreateProjectVersionDetailsForPullRequest(t *testing.T) {
|
||||
"project":{"id":4711,"name":"python-test","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -1134,8 +1134,8 @@ func TestLookupOrCreateProjectVersionDetailsForPullRequest(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null}],
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
if req.URL.Path == "/projectVersions/10172" {
|
||||
@ -1152,7 +1152,7 @@ func TestLookupOrCreateProjectVersionDetailsForPullRequest(t *testing.T) {
|
||||
"project":{"id":4711,"name":"python-test","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"0","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -1160,8 +1160,8 @@ func TestLookupOrCreateProjectVersionDetailsForPullRequest(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null}],
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
return
|
||||
}
|
||||
if req.URL.Path == "/projectVersions/10172/authEntities" {
|
||||
@ -1234,7 +1234,7 @@ func TestMergeProjectVersionStateOfPRIntoMaster(t *testing.T) {
|
||||
"project":{"id":4711,"name":"product.some.com","description":"","creationDate":"2018-12-03T06:29:38.197+0000","createdBy":"someUser",
|
||||
"issueTemplateId":"dasdasdasdsadasdasdasdasdas"},"sourceBasePath":null,"mode":"BASIC","masterAttrGuid":"sddasdasda","obfuscatedId":null,
|
||||
"id":10172,"customTagValuesAutoApply":null,"issueTemplateId":"dasdasdasdsadasdasdasdasdas","loadProperties":null,"predictionPolicy":null,
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify.mo.sap.corp/ssc/api/v1/projectVersions/10172",
|
||||
"bugTrackerPluginId":null,"owner":"admin","_href":"https://fortify/ssc/api/v1/projectVersions/10172",
|
||||
"committed":true,"bugTrackerEnabled":false,"active":true,"snapshotOutOfDate":false,"issueTemplateModifiedTime":1578411924701,
|
||||
"securityGroup":null,"creationDate":"2018-02-09T16:59:41.297+0000","refreshRequired":false,"issueTemplateName":"someTemplate",
|
||||
"migrationVersion":null,"createdBy":"admin","name":"PR-815","siteId":null,"staleIssueTemplate":false,"autoPredict":null,
|
||||
@ -1242,8 +1242,8 @@ func TestMergeProjectVersionStateOfPRIntoMaster(t *testing.T) {
|
||||
"lastFprUploadDate":"2018-02-09T16:59:53.497+0000","extraMessage":null,"analysisUploadEnabled":true,"batchBugSubmissionExists":false,
|
||||
"hasCustomIssues":false,"metricEvaluationDate":"2018-03-10T00:02:45.553+0000","deltaPeriod":7,"issueCountDelta":0,"percentAuditedDelta":0.0,
|
||||
"criticalPriorityIssueCountDelta":0,"percentCriticalPriorityIssuesAuditedDelta":0.0},"assignedIssuesCount":0,"status":null}],
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify.mo.sap.corp/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
"count":1,"responseCode":200,"links":{"last":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"},
|
||||
"first":{"href":"https://fortify/ssc/api/v1/projects/4711/versions?start=0"}}}`))
|
||||
getPRProjectVersionCalled = true
|
||||
return
|
||||
}
|
||||
|
117
pkg/fortify/reporting.go
Normal file
117
pkg/fortify/reporting.go
Normal file
@ -0,0 +1,117 @@
|
||||
package fortify
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/SAP/jenkins-library/pkg/piperutils"
|
||||
"github.com/SAP/jenkins-library/pkg/reporting"
|
||||
"github.com/piper-validation/fortify-client-go/models"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type FortifyReportData struct {
|
||||
ProjectName string
|
||||
ProjectVersion string
|
||||
Violations int
|
||||
CorporateTotal int
|
||||
CorporateAudited int
|
||||
AuditAllTotal int
|
||||
AuditAllAudited int
|
||||
SpotChecksTotal int
|
||||
SpotChecksAudited int
|
||||
SpotChecksGap int
|
||||
Suspicious int
|
||||
Exploitable int
|
||||
Suppressed int
|
||||
}
|
||||
|
||||
func CreateCustomReport(data FortifyReportData, issueGroups []*models.ProjectVersionIssueGroup) reporting.ScanReport {
|
||||
|
||||
scanReport := reporting.ScanReport{
|
||||
Title: "Fortify SAST Report",
|
||||
Subheaders: []reporting.Subheader{
|
||||
{Description: "Fortify project name", Details: data.ProjectName},
|
||||
{Description: "Fortify project version", Details: data.ProjectVersion},
|
||||
},
|
||||
Overview: []reporting.OverviewRow{
|
||||
{Description: "Total number of compliance violations", Details: fmt.Sprint(data.Violations)},
|
||||
{Description: "Total number of issues suppressed", Details: fmt.Sprint(data.Suppressed)},
|
||||
{Description: "Unaudited corporate issues", Details: fmt.Sprint(data.CorporateTotal - data.CorporateAudited)},
|
||||
{Description: "Unaudited audit all issues", Details: fmt.Sprint(data.AuditAllTotal - data.AuditAllAudited)},
|
||||
{Description: "Unaudited spot check issues", Details: fmt.Sprint(data.SpotChecksTotal - data.SpotChecksAudited)},
|
||||
{Description: "Total number of issues", Details: fmt.Sprint(data.Suspicious)},
|
||||
{Description: "Total number of exploitable issues", Details: fmt.Sprint(data.Exploitable)},
|
||||
},
|
||||
ReportTime: time.Now(),
|
||||
}
|
||||
|
||||
detailTable := reporting.ScanDetailTable{
|
||||
NoRowsMessage: "No findings detected",
|
||||
Headers: []string{
|
||||
"Issue group",
|
||||
"Total count",
|
||||
"Audited count",
|
||||
},
|
||||
WithCounter: true,
|
||||
CounterHeader: "Entry #",
|
||||
}
|
||||
|
||||
for _, group := range issueGroups {
|
||||
row := reporting.ScanRow{}
|
||||
row.AddColumn(fmt.Sprint(*group.CleanName), 0)
|
||||
row.AddColumn(fmt.Sprint(*group.TotalCount), 0)
|
||||
row.AddColumn(fmt.Sprint(*group.AuditedCount), 0)
|
||||
|
||||
detailTable.Rows = append(detailTable.Rows, row)
|
||||
}
|
||||
scanReport.DetailTable = detailTable
|
||||
|
||||
return scanReport
|
||||
}
|
||||
|
||||
func WriteCustomReports(scanReport reporting.ScanReport, projectName, projectVersion string) ([]piperutils.Path, error) {
|
||||
utils := piperutils.Files{}
|
||||
reportPaths := []piperutils.Path{}
|
||||
|
||||
// ignore templating errors since template is in our hands and issues will be detected with the automated tests
|
||||
htmlReport, _ := scanReport.ToHTML()
|
||||
htmlReportPath := filepath.Join(ReportsDirectory, "piper_fortify_report.html")
|
||||
// Ensure reporting directory exists
|
||||
if err := utils.MkdirAll(ReportsDirectory, 0777); err != nil {
|
||||
return reportPaths, errors.Wrapf(err, "failed to create report directory")
|
||||
}
|
||||
if err := utils.FileWrite(htmlReportPath, htmlReport, 0666); err != nil {
|
||||
log.SetErrorCategory(log.ErrorConfiguration)
|
||||
return reportPaths, errors.Wrapf(err, "failed to write html report")
|
||||
}
|
||||
reportPaths = append(reportPaths, piperutils.Path{Name: "Fortify Report", Target: htmlReportPath})
|
||||
|
||||
// JSON reports are used by step pipelineCreateSummary in order to e.g. prepare an issue creation in GitHub
|
||||
// ignore JSON errors since structure is in our hands
|
||||
jsonReport, _ := scanReport.ToJSON()
|
||||
if exists, _ := utils.DirExists(reporting.StepReportDirectory); !exists {
|
||||
err := utils.MkdirAll(reporting.StepReportDirectory, 0777)
|
||||
if err != nil {
|
||||
return reportPaths, errors.Wrap(err, "failed to create reporting directory")
|
||||
}
|
||||
}
|
||||
if err := utils.FileWrite(filepath.Join(reporting.StepReportDirectory, fmt.Sprintf("fortifyExecuteScan_sast_%v.json", reportShaFortify([]string{projectName, projectVersion}))), jsonReport, 0666); err != nil {
|
||||
return reportPaths, errors.Wrapf(err, "failed to write json report")
|
||||
}
|
||||
// we do not add the json report to the overall list of reports for now,
|
||||
// since it is just an intermediary report used as input for later
|
||||
// and there does not seem to be real benefit in archiving it.
|
||||
|
||||
return reportPaths, nil
|
||||
}
|
||||
|
||||
func reportShaFortify(parts []string) string {
|
||||
reportShaData := []byte(strings.Join(parts, ","))
|
||||
return fmt.Sprintf("%x", sha1.Sum(reportShaData))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user