1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00

Fortify JSON Report (#3212)

Co-authored-by: Sven Merk <33895725+nevskrem@users.noreply.github.com>
This commit is contained in:
sumeet patil 2021-10-29 10:03:01 +02:00 committed by GitHub
parent 88bd8f1303
commit 732845507d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 23 deletions

View File

@ -275,7 +275,9 @@ func verifyFFProjectCompliance(config fortifyExecuteScanOptions, sys fortify.Sys
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, issueGroups, err := analyseUnauditedIssues(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus)
spotChecksCountByCategory := []fortify.SpotChecksAuditCount{}
numberOfViolations, issueGroups, err := analyseUnauditedIssues(config, sys, projectVersion, filterSet, issueFilterSelectorSet, influx, auditStatus, &spotChecksCountByCategory)
if err != nil {
return errors.Wrap(err, "failed to analyze unaudited issues"), reports
}
@ -290,9 +292,21 @@ func verifyFFProjectCompliance(config fortifyExecuteScanOptions, sys fortify.Sys
influx.fortify_data.fields.projectVersionID = projectVersion.ID
influx.fortify_data.fields.violations = numberOfViolations
scanReport := fortify.CreateCustomReport(prepareReportData(influx), issueGroups)
fortifyReportingData := prepareReportData(influx)
scanReport := fortify.CreateCustomReport(fortifyReportingData, issueGroups)
paths, err := fortify.WriteCustomReports(scanReport, influx.fortify_data.fields.projectName, influx.fortify_data.fields.projectVersion)
if err != nil {
return errors.Wrap(err, "failed to write custom reports"), reports
}
reports = append(reports, paths...)
jsonReport := fortify.CreateJSONReport(fortifyReportingData, spotChecksCountByCategory, config.ServerURL)
paths, err = fortify.WriteJSONReport(jsonReport)
if err != nil {
return errors.Wrap(err, "failed to write json report"), reports
}
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"), reports
@ -315,10 +329,11 @@ func prepareReportData(influx *fortifyExecuteScanInflux) fortify.FortifyReportDa
output.Exploitable = input.exploitable
output.Suppressed = input.suppressed
output.Suspicious = input.suspicious
output.ProjectVersionID = input.projectVersionID
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) {
func analyseUnauditedIssues(config fortifyExecuteScanOptions, sys fortify.System, projectVersion *models.ProjectVersion, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string, spotChecksCountByCategory *[]fortify.SpotChecksAuditCount) (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)
@ -327,7 +342,7 @@ func analyseUnauditedIssues(config fortifyExecuteScanOptions, sys fortify.System
}
overallViolations := 0
for _, issueGroup := range fetchedIssueGroups {
issueDelta, err := getIssueDeltaFor(config, sys, issueGroup, projectVersion.ID, filterSet, issueFilterSelectorSet, influx, auditStatus)
issueDelta, err := getIssueDeltaFor(config, sys, issueGroup, projectVersion.ID, filterSet, issueFilterSelectorSet, influx, auditStatus, spotChecksCountByCategory)
if err != nil {
return overallViolations, fetchedIssueGroups, errors.Wrap(err, "failed to get issue delta")
}
@ -336,7 +351,7 @@ func analyseUnauditedIssues(config fortifyExecuteScanOptions, sys fortify.System
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) {
func getIssueDeltaFor(config fortifyExecuteScanOptions, sys fortify.System, issueGroup *models.ProjectVersionIssueGroup, projectVersionID int64, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string, spotChecksCountByCategory *[]fortify.SpotChecksAuditCount) (int, error) {
totalMinusAuditedDelta := 0
group := ""
total := 0
@ -378,13 +393,13 @@ func getIssueDeltaFor(config fortifyExecuteScanOptions, sys fortify.System, issu
if err != nil {
return totalMinusAuditedDelta, errors.Wrapf(err, "failed to fetch project version issue groups with filter %v, filter set %v and selector %v for project version ID %v", filter, filterSet, issueFilterSelectorSet, projectVersionID)
}
totalMinusAuditedDelta += getSpotIssueCount(config, sys, fetchedIssueGroups, projectVersionID, filterSet, reducedFilterSelectorSet, influx, auditStatus)
totalMinusAuditedDelta += getSpotIssueCount(config, sys, fetchedIssueGroups, projectVersionID, filterSet, reducedFilterSelectorSet, influx, auditStatus, spotChecksCountByCategory)
}
}
return totalMinusAuditedDelta, nil
}
func getSpotIssueCount(config fortifyExecuteScanOptions, sys fortify.System, spotCheckCategories []*models.ProjectVersionIssueGroup, projectVersionID int64, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string) int {
func getSpotIssueCount(config fortifyExecuteScanOptions, sys fortify.System, spotCheckCategories []*models.ProjectVersionIssueGroup, projectVersionID int64, filterSet *models.FilterSet, issueFilterSelectorSet *models.IssueFilterSelectorSet, influx *fortifyExecuteScanInflux, auditStatus map[string]string, spotChecksCountByCategory *[]fortify.SpotChecksAuditCount) int {
overallDelta := 0
overallIssues := 0
overallIssuesAudited := 0
@ -418,6 +433,7 @@ func getSpotIssueCount(config fortifyExecuteScanOptions, sys fortify.System, spo
overallIssuesAudited += audited
auditStatus[group] = fmt.Sprintf("%v total : %v audited %v", total, audited, flagOutput)
*spotChecksCountByCategory = append(*spotChecksCountByCategory, fortify.SpotChecksAuditCount{Audited: audited, Total: total, Type: group})
}
influx.fortify_data.fields.spotChecksTotal = overallIssues

View File

@ -491,7 +491,9 @@ func TestAnalyseUnauditedIssues(t *testing.T) {
},
},
}
issues, groups, err := analyseUnauditedIssues(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus)
spotChecksCountByCategory := []fortify.SpotChecksAuditCount{}
issues, groups, err := analyseUnauditedIssues(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus, &spotChecksCountByCategory)
assert.NoError(t, err)
assert.Equal(t, 13, issues)
assert.Equal(t, 3, len(groups))
@ -503,6 +505,7 @@ func TestAnalyseUnauditedIssues(t *testing.T) {
assert.Equal(t, 13, influx.fortify_data.fields.spotChecksTotal)
assert.Equal(t, 11, influx.fortify_data.fields.spotChecksAudited)
assert.Equal(t, 1, influx.fortify_data.fields.spotChecksGap)
assert.Equal(t, 3, len(spotChecksCountByCategory))
}
func TestTriggerFortifyScan(t *testing.T) {

View File

@ -1314,3 +1314,36 @@ func TestBase64EndodePlainToken(t *testing.T) {
assert.Equal(t, "OTUzODcwNDYtNWFjOC00NTcwLTg3NWQtYTVlYzhiZDhkM2Qy", encodedToken)
})
}
func TestCreateJSONReport(t *testing.T) {
t.Run("test success", func(t *testing.T) {
spotChecksCountByCategory := []SpotChecksAuditCount{}
spotChecksCountByCategory = append(spotChecksCountByCategory, SpotChecksAuditCount{Audited: 3, Total: 3, Type: "J2EE Misconfiguration: Missing Error Handling"})
spotChecksCountByCategory = append(spotChecksCountByCategory, SpotChecksAuditCount{Audited: 1, Total: 3, Type: "J2EE Bad Practices: Leftover Debug Code"})
fortifyReportData := FortifyReportData{CorporateAudited: 30, CorporateTotal: 30, AuditAllTotal: 1, AuditAllAudited: 1, ProjectVersionID: 4999}
jsonReport := CreateJSONReport(fortifyReportData, spotChecksCountByCategory, "https://fortify-test.com/ssc")
assert.Equal(t, true, jsonReport.AtleastOneSpotChecksCategoryAudited)
assert.Equal(t, 1, jsonReport.AuditAllAudited)
assert.Equal(t, 1, jsonReport.AuditAllTotal)
assert.Equal(t, 30, jsonReport.CorporateAudited)
assert.Equal(t, 30, jsonReport.CorporateTotal)
assert.Equal(t, "https://fortify-test.com/ssc/html/ssc/version/4999", jsonReport.URL)
assert.Equal(t, "https://fortify-test.com/ssc", jsonReport.ToolInstance)
})
t.Run("atleast one category spotchecks failed", func(t *testing.T) {
spotChecksCountByCategory := []SpotChecksAuditCount{}
spotChecksCountByCategory = append(spotChecksCountByCategory, SpotChecksAuditCount{Audited: 3, Total: 3, Type: "J2EE Misconfiguration: Missing Error Handling"})
spotChecksCountByCategory = append(spotChecksCountByCategory, SpotChecksAuditCount{Audited: 0, Total: 1, Type: "J2EE Bad Practices: Leftover Debug Code"})
fortifyReportData := FortifyReportData{CorporateAudited: 0, CorporateTotal: 0, AuditAllTotal: 0, AuditAllAudited: 0}
jsonReport := CreateJSONReport(fortifyReportData, spotChecksCountByCategory, "https://fortify-test.com/ssc")
assert.Equal(t, false, jsonReport.AtleastOneSpotChecksCategoryAudited)
})
t.Run("no spot checks audited", func(t *testing.T) {
spotChecksCountByCategory := []SpotChecksAuditCount{}
fortifyReportData := FortifyReportData{CorporateAudited: 0, CorporateTotal: 0, AuditAllTotal: 0, AuditAllAudited: 0}
jsonReport := CreateJSONReport(fortifyReportData, spotChecksCountByCategory, "https://fortify-test.com/ssc")
assert.Equal(t, true, jsonReport.AtleastOneSpotChecksCategoryAudited)
})
}

View File

@ -2,8 +2,10 @@ package fortify
import (
"crypto/sha1"
"encoding/json"
"fmt"
"path/filepath"
"strconv"
"strings"
"time"
@ -16,19 +18,31 @@ import (
)
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
ToolName string `json:"toolName"`
ToolInstance string `json:"toolInstance"`
ProjectName string `json:"projectName"`
ProjectVersion string `json:"projectVersion"`
ProjectVersionID int64 `json:"projectVersionID"`
Violations int `json:"violations"`
CorporateTotal int `json:"corporateTotal"`
CorporateAudited int `json:"corporateAudited"`
AuditAllTotal int `json:"auditAllTotal"`
AuditAllAudited int `json:"auditAllAudited"`
SpotChecksTotal int `json:"spotChecksTotal"`
SpotChecksAudited int `json:"spotChecksAudited"`
SpotChecksGap int `json:"spotChecksGap"`
Suspicious int `json:"suspicious"`
Exploitable int `json:"exploitable"`
Suppressed int `json:"suppressed"`
AtleastOneSpotChecksCategoryAudited bool `json:"atleastOneSpotChecksCategoryAudited"`
URL string `json:"url"`
SpotChecksCategories *[]SpotChecksAuditCount `json:"spotChecksCategories"`
}
type SpotChecksAuditCount struct {
Audited int `json:"spotChecksCategories"`
Total int `json:"total"`
Type string `json:"type"`
}
func CreateCustomReport(data FortifyReportData, issueGroups []*models.ProjectVersionIssueGroup) reporting.ScanReport {
@ -77,7 +91,45 @@ func CreateCustomReport(data FortifyReportData, issueGroups []*models.ProjectVer
return scanReport
}
func WriteCustomReports(scanReport reporting.ScanReport, projectName, projectVersion string) ([]piperutils.Path, error) {
func CreateJSONReport(reportData FortifyReportData, spotChecksCountByCategory []SpotChecksAuditCount, serverURL string) FortifyReportData {
reportData.AtleastOneSpotChecksCategoryAudited = true
for _, spotChecksElement := range spotChecksCountByCategory {
if spotChecksElement.Total > 0 && spotChecksElement.Audited == 0 {
reportData.AtleastOneSpotChecksCategoryAudited = false
break
}
}
reportData.SpotChecksCategories = &spotChecksCountByCategory
reportData.URL = serverURL + "/html/ssc/version/" + strconv.FormatInt(reportData.ProjectVersionID, 10)
reportData.ToolInstance = serverURL
reportData.ToolName = "fortify"
return reportData
}
func WriteJSONReport(jsonReport FortifyReportData) ([]piperutils.Path, error) {
utils := piperutils.Files{}
reportPaths := []piperutils.Path{}
// Standard JSON Report
jsonComplianceReportPath := filepath.Join(ReportsDirectory, "piper_fortify_report.json")
// Ensure reporting directory exists
if err := utils.MkdirAll(ReportsDirectory, 0777); err != nil {
return reportPaths, errors.Wrapf(err, "failed to create report directory")
}
file, _ := json.Marshal(jsonReport)
if err := utils.FileWrite(jsonComplianceReportPath, file, 0666); err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return reportPaths, errors.Wrapf(err, "failed to write fortify json compliance report")
}
reportPaths = append(reportPaths, piperutils.Path{Name: "Fortify JSON Compliance Report", Target: jsonComplianceReportPath})
return reportPaths, nil
}
func WriteCustomReports(scanReport reporting.ScanReport, projectName string, projectVersion string) ([]piperutils.Path, error) {
utils := piperutils.Files{}
reportPaths := []piperutils.Path{}
@ -109,7 +161,6 @@ func WriteCustomReports(scanReport reporting.ScanReport, projectName, projectVer
// 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
}