You've already forked sap-jenkins-library
							
							
				mirror of
				https://github.com/SAP/jenkins-library.git
				synced 2025-10-30 23:57:50 +02:00 
			
		
		
		
	Fortify JSON Report (#3212)
Co-authored-by: Sven Merk <33895725+nevskrem@users.noreply.github.com>
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user