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 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) 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 { if err != nil {
return errors.Wrap(err, "failed to analyze unaudited issues"), reports 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.projectVersionID = projectVersion.ID
influx.fortify_data.fields.violations = numberOfViolations 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) 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...) 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 { if numberOfViolations > 0 {
log.SetErrorCategory(log.ErrorCompliance) log.SetErrorCategory(log.ErrorCompliance)
return errors.New("fortify scan failed, the project is not compliant. For details check the archived report"), reports 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.Exploitable = input.exploitable
output.Suppressed = input.suppressed output.Suppressed = input.suppressed
output.Suspicious = input.suspicious output.Suspicious = input.suspicious
output.ProjectVersionID = input.projectVersionID
return output 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") log.Entry().Info("Analyzing unaudited issues")
reducedFilterSelectorSet := sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Folder"}, nil) reducedFilterSelectorSet := sys.ReduceIssueFilterSelectorSet(issueFilterSelectorSet, []string{"Folder"}, nil)
fetchedIssueGroups, err := sys.GetProjectIssuesByIDAndFilterSetGroupedBySelector(projectVersion.ID, "", filterSet.GUID, reducedFilterSelectorSet) fetchedIssueGroups, err := sys.GetProjectIssuesByIDAndFilterSetGroupedBySelector(projectVersion.ID, "", filterSet.GUID, reducedFilterSelectorSet)
@ -327,7 +342,7 @@ func analyseUnauditedIssues(config fortifyExecuteScanOptions, sys fortify.System
} }
overallViolations := 0 overallViolations := 0
for _, issueGroup := range fetchedIssueGroups { 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 { if err != nil {
return overallViolations, fetchedIssueGroups, errors.Wrap(err, "failed to get issue delta") 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 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 totalMinusAuditedDelta := 0
group := "" group := ""
total := 0 total := 0
@ -378,13 +393,13 @@ func getIssueDeltaFor(config fortifyExecuteScanOptions, sys fortify.System, issu
if err != nil { 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) 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 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 overallDelta := 0
overallIssues := 0 overallIssues := 0
overallIssuesAudited := 0 overallIssuesAudited := 0
@ -418,6 +433,7 @@ func getSpotIssueCount(config fortifyExecuteScanOptions, sys fortify.System, spo
overallIssuesAudited += audited overallIssuesAudited += audited
auditStatus[group] = fmt.Sprintf("%v total : %v audited %v", total, audited, flagOutput) 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 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.NoError(t, err)
assert.Equal(t, 13, issues) assert.Equal(t, 13, issues)
assert.Equal(t, 3, len(groups)) 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, 13, influx.fortify_data.fields.spotChecksTotal)
assert.Equal(t, 11, influx.fortify_data.fields.spotChecksAudited) assert.Equal(t, 11, influx.fortify_data.fields.spotChecksAudited)
assert.Equal(t, 1, influx.fortify_data.fields.spotChecksGap) assert.Equal(t, 1, influx.fortify_data.fields.spotChecksGap)
assert.Equal(t, 3, len(spotChecksCountByCategory))
} }
func TestTriggerFortifyScan(t *testing.T) { func TestTriggerFortifyScan(t *testing.T) {

View File

@ -1314,3 +1314,36 @@ func TestBase64EndodePlainToken(t *testing.T) {
assert.Equal(t, "OTUzODcwNDYtNWFjOC00NTcwLTg3NWQtYTVlYzhiZDhkM2Qy", encodedToken) 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 ( import (
"crypto/sha1" "crypto/sha1"
"encoding/json"
"fmt" "fmt"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"time" "time"
@ -16,19 +18,31 @@ import (
) )
type FortifyReportData struct { type FortifyReportData struct {
ProjectName string ToolName string `json:"toolName"`
ProjectVersion string ToolInstance string `json:"toolInstance"`
Violations int ProjectName string `json:"projectName"`
CorporateTotal int ProjectVersion string `json:"projectVersion"`
CorporateAudited int ProjectVersionID int64 `json:"projectVersionID"`
AuditAllTotal int Violations int `json:"violations"`
AuditAllAudited int CorporateTotal int `json:"corporateTotal"`
SpotChecksTotal int CorporateAudited int `json:"corporateAudited"`
SpotChecksAudited int AuditAllTotal int `json:"auditAllTotal"`
SpotChecksGap int AuditAllAudited int `json:"auditAllAudited"`
Suspicious int SpotChecksTotal int `json:"spotChecksTotal"`
Exploitable int SpotChecksAudited int `json:"spotChecksAudited"`
Suppressed int 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 { func CreateCustomReport(data FortifyReportData, issueGroups []*models.ProjectVersionIssueGroup) reporting.ScanReport {
@ -77,7 +91,45 @@ func CreateCustomReport(data FortifyReportData, issueGroups []*models.ProjectVer
return scanReport 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{} utils := piperutils.Files{}
reportPaths := []piperutils.Path{} 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, // 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 // since it is just an intermediary report used as input for later
// and there does not seem to be real benefit in archiving it. // and there does not seem to be real benefit in archiving it.
return reportPaths, nil return reportPaths, nil
} }