github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/protecode/report.go (about)

     1  package protecode
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding/json"
     6  	"fmt"
     7  	"path/filepath"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/pkg/errors"
    12  
    13  	"github.com/SAP/jenkins-library/pkg/log"
    14  	"github.com/SAP/jenkins-library/pkg/piperutils"
    15  	"github.com/SAP/jenkins-library/pkg/reporting"
    16  )
    17  
    18  // ReportData is representing the data of the step report JSON
    19  type ReportData struct {
    20  	Target                      string `json:"target,omitempty"`
    21  	Mandatory                   bool   `json:"mandatory,omitempty"`
    22  	ProductID                   string `json:"productID,omitempty"`
    23  	ServerURL                   string `json:"serverUrl,omitempty"`
    24  	FailOnSevereVulnerabilities bool   `json:"failOnSevereVulnerabilities,omitempty"`
    25  	ExcludeCVEs                 string `json:"excludeCVEs,omitempty"`
    26  	Count                       string `json:"count,omitempty"`
    27  	Cvss2GreaterOrEqualSeven    string `json:"cvss2GreaterOrEqualSeven,omitempty"`
    28  	Cvss3GreaterOrEqualSeven    string `json:"cvss3GreaterOrEqualSeven,omitempty"`
    29  	ExcludedVulnerabilities     string `json:"excludedVulnerabilities,omitempty"`
    30  	TriagedVulnerabilities      string `json:"triagedVulnerabilities,omitempty"`
    31  	HistoricalVulnerabilities   string `json:"historicalVulnerabilities,omitempty"`
    32  	Vulnerabilities             []Vuln `json:"Vulnerabilities,omitempty"`
    33  }
    34  
    35  // WriteReport ...
    36  func WriteReport(data ReportData, reportPath string, reportFileName string, result map[string]int, fileUtils piperutils.FileUtils) error {
    37  	data.Mandatory = true
    38  	data.Count = fmt.Sprintf("%v", result["count"])
    39  	data.Cvss2GreaterOrEqualSeven = fmt.Sprintf("%v", result["cvss2GreaterOrEqualSeven"])
    40  	data.Cvss3GreaterOrEqualSeven = fmt.Sprintf("%v", result["cvss3GreaterOrEqualSeven"])
    41  	data.ExcludedVulnerabilities = fmt.Sprintf("%v", result["excluded_vulnerabilities"])
    42  	data.TriagedVulnerabilities = fmt.Sprintf("%v", result["triaged_vulnerabilities"])
    43  	data.HistoricalVulnerabilities = fmt.Sprintf("%v", result["historical_vulnerabilities"])
    44  
    45  	log.Entry().Infof("Protecode scan info, %v of which %v had a CVSS v2 score >= 7.0 and %v had a CVSS v3 score >= 7.0.\n %v vulnerabilities were excluded via configuration (%v) and %v vulnerabilities were triaged via the webUI.\nIn addition %v historical vulnerabilities were spotted. \n\n Vulnerabilities: %v",
    46  		data.Count, data.Cvss2GreaterOrEqualSeven, data.Cvss3GreaterOrEqualSeven,
    47  		data.ExcludedVulnerabilities, data.ExcludeCVEs, data.TriagedVulnerabilities,
    48  		data.HistoricalVulnerabilities, data.Vulnerabilities)
    49  	return writeJSON(reportPath, reportFileName, data, fileUtils)
    50  }
    51  
    52  func writeJSON(path, name string, data interface{}, fileUtils piperutils.FileUtils) error {
    53  	jsonData, err := json.Marshal(data)
    54  	if err != nil {
    55  		return err
    56  	}
    57  	return fileUtils.FileWrite(filepath.Join(path, name), jsonData, 0644)
    58  }
    59  
    60  func CreateCustomReport(productName string, productID int, data map[string]int, vulns []Vuln) reporting.ScanReport {
    61  	scanReport := reporting.ScanReport{
    62  		ReportTitle: "Protecode Vulnerability Report",
    63  		Subheaders: []reporting.Subheader{
    64  			{Description: "Product name", Details: productName},
    65  			{Description: "Product ID", Details: fmt.Sprint(productID)},
    66  		},
    67  		Overview: []reporting.OverviewRow{
    68  			{Description: "Vulnerabilities", Details: fmt.Sprint(data["vulnerabilities"])},
    69  			{Description: "Major Vulnerabilities", Details: fmt.Sprint(data["major_vulnerabilities"])},
    70  			{Description: "Minor Vulnerabilities", Details: fmt.Sprint(data["minor_vulnerabilities"])},
    71  			{Description: "Triaged Vulnerabilities", Details: fmt.Sprint(data["triaged_vulnerabilities"])},
    72  			{Description: "Excluded Vulnerabilities", Details: fmt.Sprint(data["excluded_vulnerabilities"])},
    73  		},
    74  		ReportTime: time.Now(),
    75  	}
    76  
    77  	detailTable := reporting.ScanDetailTable{
    78  		NoRowsMessage: "No findings detected",
    79  		Headers: []string{
    80  			"Issue CVE",
    81  			"CVSS Score",
    82  			"CVSS v3 Score",
    83  		},
    84  		WithCounter:   true,
    85  		CounterHeader: "Entry #",
    86  	}
    87  
    88  	for _, vuln := range vulns {
    89  		row := reporting.ScanRow{}
    90  		row.AddColumn(fmt.Sprint(*&vuln.Cve), 0)
    91  		row.AddColumn(fmt.Sprint(*&vuln.Cvss), 0)
    92  		row.AddColumn(fmt.Sprint(*&vuln.Cvss3Score), 0)
    93  
    94  		detailTable.Rows = append(detailTable.Rows, row)
    95  	}
    96  	scanReport.DetailTable = detailTable
    97  
    98  	return scanReport
    99  }
   100  
   101  func WriteCustomReports(scanReport reporting.ScanReport, projectName, projectID string, fileUtils piperutils.FileUtils) ([]piperutils.Path, error) {
   102  	reportPaths := []piperutils.Path{}
   103  
   104  	// ignore templating errors since template is in our hands and issues will be detected with the automated tests
   105  	htmlReport, _ := scanReport.ToHTML()
   106  	htmlReportPath := filepath.Join(ReportsDirectory, "piper_protecode_report.html")
   107  	// Ensure reporting directory exists
   108  	if err := fileUtils.MkdirAll(ReportsDirectory, 0777); err != nil {
   109  		return reportPaths, errors.Wrapf(err, "failed to create report directory")
   110  	}
   111  	if err := fileUtils.FileWrite(htmlReportPath, htmlReport, 0666); err != nil {
   112  		log.SetErrorCategory(log.ErrorConfiguration)
   113  		return reportPaths, errors.Wrapf(err, "failed to write html report")
   114  	}
   115  	reportPaths = append(reportPaths, piperutils.Path{Name: "Protecode Vulnerability Report", Target: htmlReportPath})
   116  
   117  	// JSON reports are used by step pipelineCreateSummary in order to e.g. prepare an issue creation in GitHub
   118  	// ignore JSON errors since structure is in our hands
   119  	jsonReport, _ := scanReport.ToJSON()
   120  	if exists, _ := fileUtils.DirExists(reporting.StepReportDirectory); !exists {
   121  		err := fileUtils.MkdirAll(reporting.StepReportDirectory, 0777)
   122  		if err != nil {
   123  			return reportPaths, errors.Wrap(err, "failed to create reporting directory")
   124  		}
   125  	}
   126  	if err := fileUtils.FileWrite(filepath.Join(reporting.StepReportDirectory, fmt.Sprintf("protecodeExecuteScan_osvm_%v.json", reportShaProtecode([]string{projectName, projectID}))), jsonReport, 0666); err != nil {
   127  		return reportPaths, errors.Wrapf(err, "failed to write json report")
   128  	}
   129  	// we do not add the json report to the overall list of reports for now,
   130  	// since it is just an intermediary report used as input for later
   131  	// and there does not seem to be real benefit in archiving it.
   132  
   133  	return reportPaths, nil
   134  }
   135  
   136  func reportShaProtecode(parts []string) string {
   137  	reportShaData := []byte(strings.Join(parts, ","))
   138  	return fmt.Sprintf("%x", sha1.Sum(reportShaData))
   139  }