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 
			
		
		
		
	detect : Create html and json report upon scan completion (#3042)
* changes to detectExec before master merge * changes for detectExecuteScan * self generated code added * fix syntax errors and update docu * added unit tests for fail and Group * fix failOn bug * add Groups as string array * add Groups as string array * tests and validation for groups, failOn * Updated docs and added more tests * documentation md files should not be changed * Handle merge conflicts from PR 1845 * fix merge errors * remove duplicate groups, merge error * adding buildCode and buildTool as params * switching build options * building maven modules * parameter correction * parameter correction * gnerate with new build parameter * adding comments * removing piper lib master and modifying goUtils to download 1.5.7 release * first cleaning then installing * multi module maven built * multi module maven built removing unwanted code * multi module maven built moving inside switch * testing * modifying the default use case to also call maven build * modifying the default use case to also call maven build wih -- * corrected maven build command * corrected maven build command with %v * skipping test runs * testing for MTA project with single pom * adding absolute path to m2 path * clean up * adding switch for mta and maven and removing env from containers * commiting changes for new detect step * correting log message * code clean up * unit tests changes to detectExecute * basic tests for new change * restoring piperGoUtils to download correct piper binary * code clean up * code clean up * add basic reporting * write html and json reports * fix syntax errors and tests * sort values in report by vuln * add more unit tests Co-authored-by: Keshav <anil.keshav@sap.com> Co-authored-by: Oliver Nocon <33484802+OliverNocon@users.noreply.github.com> Co-authored-by: Sven Merk <33895725+nevskrem@users.noreply.github.com>
This commit is contained in:
		| @@ -7,14 +7,19 @@ import ( | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	bd "github.com/SAP/jenkins-library/pkg/blackduck" | ||||
| 	piperhttp "github.com/SAP/jenkins-library/pkg/http" | ||||
| 	"github.com/SAP/jenkins-library/pkg/maven" | ||||
| 	"github.com/pkg/errors" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/command" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperutils" | ||||
| 	"github.com/SAP/jenkins-library/pkg/reporting" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"github.com/SAP/jenkins-library/pkg/toolrecord" | ||||
| 	"github.com/SAP/jenkins-library/pkg/versioning" | ||||
| @@ -25,6 +30,7 @@ type detectUtils interface { | ||||
| 	FileExists(filename string) (bool, error) | ||||
| 	FileRemove(filename string) error | ||||
| 	Copy(src, dest string) (int64, error) | ||||
| 	DirExists(dest string) (bool, error) | ||||
| 	FileRead(path string) ([]byte, error) | ||||
| 	FileWrite(path string, content []byte, perm os.FileMode) error | ||||
| 	MkdirAll(path string, perm os.FileMode) error | ||||
| @@ -47,6 +53,10 @@ type detectUtilsBundle struct { | ||||
| 	*piperhttp.Client | ||||
| } | ||||
|  | ||||
| type blackduckSystem struct { | ||||
| 	Client bd.Client | ||||
| } | ||||
|  | ||||
| func newDetectUtils() detectUtils { | ||||
| 	utils := detectUtilsBundle{ | ||||
| 		Command: &command.Command{ | ||||
| @@ -67,9 +77,17 @@ func newDetectUtils() detectUtils { | ||||
| 	return &utils | ||||
| } | ||||
|  | ||||
| func detectExecuteScan(config detectExecuteScanOptions, _ *telemetry.CustomData) { | ||||
| func newBlackduckSystem(config detectExecuteScanOptions) *blackduckSystem { | ||||
| 	sys := blackduckSystem{ | ||||
| 		Client: bd.NewClient(config.Token, config.ServerURL, &piperhttp.Client{}), | ||||
| 	} | ||||
| 	return &sys | ||||
| } | ||||
|  | ||||
| func detectExecuteScan(config detectExecuteScanOptions, _ *telemetry.CustomData, influx *detectExecuteScanInflux) { | ||||
| 	influx.step_data.fields.detect = false | ||||
| 	utils := newDetectUtils() | ||||
| 	err := runDetect(config, utils) | ||||
| 	err := runDetect(config, utils, influx) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		log.Entry(). | ||||
| @@ -77,6 +95,7 @@ func detectExecuteScan(config detectExecuteScanOptions, _ *telemetry.CustomData) | ||||
| 			Fatal("failed to execute detect scan") | ||||
| 	} | ||||
|  | ||||
| 	influx.step_data.fields.detect = true | ||||
| 	// create Toolrecord file | ||||
| 	toolRecordFileName, err := createToolRecordDetect("./", config) | ||||
| 	if err != nil { | ||||
| @@ -85,7 +104,7 @@ func detectExecuteScan(config detectExecuteScanOptions, _ *telemetry.CustomData) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runDetect(config detectExecuteScanOptions, utils detectUtils) error { | ||||
| func runDetect(config detectExecuteScanOptions, utils detectUtils, influx *detectExecuteScanInflux) error { | ||||
| 	// detect execution details, see https://synopsys.atlassian.net/wiki/spaces/INTDOCS/pages/88440888/Sample+Synopsys+Detect+Scan+Configuration+Scenarios+for+Black+Duck | ||||
| 	err := getDetectScript(config, utils) | ||||
| 	if err != nil { | ||||
| @@ -127,6 +146,10 @@ func runDetect(config detectExecuteScanOptions, utils detectUtils) error { | ||||
| 	utils.SetEnv(envs) | ||||
|  | ||||
| 	err = utils.RunShell("/bin/bash", script) | ||||
| 	reportingErr := postScanChecksAndReporting(config, influx, utils, newBlackduckSystem(config)) | ||||
| 	if reportingErr != nil { | ||||
| 		log.Entry().Warnf("Failed to generate reports: %v", reportingErr) | ||||
| 	} | ||||
| 	if err == nil && piperutils.ContainsString(config.FailOn, "BLOCKER") { | ||||
| 		violations := struct { | ||||
| 			PolicyViolations int      `json:"policyViolations"` | ||||
| @@ -163,12 +186,7 @@ func getDetectScript(config detectExecuteScanOptions, utils detectUtils) error { | ||||
| } | ||||
|  | ||||
| func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectUtils) ([]string, error) { | ||||
| 	detectVersionName := config.CustomScanVersion | ||||
| 	if len(detectVersionName) > 0 { | ||||
| 		log.Entry().Infof("Using custom version: %v", detectVersionName) | ||||
| 	} else { | ||||
| 		detectVersionName = versioning.ApplyVersioningModel(config.VersioningModel, config.Version) | ||||
| 	} | ||||
| 	detectVersionName := getVersionName(config) | ||||
| 	//Split on spaces, the scanPropeties, so that each property is available as a single string | ||||
| 	//instead of all properties being part of a single string | ||||
| 	config.ScanProperties = piperutils.SplitAndTrim(config.ScanProperties, " ") | ||||
| @@ -259,6 +277,171 @@ func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectU | ||||
| 	return args, nil | ||||
| } | ||||
|  | ||||
| func getVersionName(config detectExecuteScanOptions) string { | ||||
| 	detectVersionName := config.CustomScanVersion | ||||
| 	if len(detectVersionName) > 0 { | ||||
| 		log.Entry().Infof("Using custom version: %v", detectVersionName) | ||||
| 	} else { | ||||
| 		detectVersionName = versioning.ApplyVersioningModel(config.VersioningModel, config.Version) | ||||
| 	} | ||||
| 	return detectVersionName | ||||
| } | ||||
|  | ||||
| func postScanChecksAndReporting(config detectExecuteScanOptions, influx *detectExecuteScanInflux, utils detectUtils, sys *blackduckSystem) error { | ||||
| 	vulns, _, err := getVulnsAndComponents(config, influx, sys) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	scanReport := createVulnerabilityReport(config, vulns, influx) | ||||
| 	paths, err := writeVulnerabilityReports(scanReport, config, utils) | ||||
| 	piperutils.PersistReportsAndLinks("detectExecuteScan", "", paths, nil) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrapf(err, "failed to check and report scan results") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getVulnsAndComponents(config detectExecuteScanOptions, influx *detectExecuteScanInflux, sys *blackduckSystem) (*bd.Vulnerabilities, *bd.Components, error) { | ||||
| 	detectVersionName := getVersionName(config) | ||||
| 	vulns, err := sys.Client.GetVulnerabilities(config.ProjectName, detectVersionName) | ||||
| 	if err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	majorVulns := 0 | ||||
| 	activeVulns := 0 | ||||
| 	for _, vuln := range vulns.Items { | ||||
| 		if isActiveVulnerability(vuln) { | ||||
| 			activeVulns++ | ||||
| 			if isMajorVulnerability(vuln) { | ||||
| 				majorVulns++ | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	influx.detect_data.fields.vulnerabilities = activeVulns | ||||
| 	influx.detect_data.fields.major_vulnerabilities = majorVulns | ||||
| 	influx.detect_data.fields.minor_vulnerabilities = activeVulns - majorVulns | ||||
|  | ||||
| 	components, err := sys.Client.GetComponents(config.ProjectName, detectVersionName) | ||||
| 	if err != nil { | ||||
| 		return vulns, nil, err | ||||
| 	} | ||||
| 	influx.detect_data.fields.components = components.TotalCount | ||||
|  | ||||
| 	return vulns, components, nil | ||||
| } | ||||
|  | ||||
| func createVulnerabilityReport(config detectExecuteScanOptions, vulns *bd.Vulnerabilities, influx *detectExecuteScanInflux) reporting.ScanReport { | ||||
| 	scanReport := reporting.ScanReport{ | ||||
| 		Title: "BlackDuck Security Vulnerability Report", | ||||
| 		Subheaders: []reporting.Subheader{ | ||||
| 			{Description: "BlackDuck Project Name ", Details: config.ProjectName}, | ||||
| 			{Description: "BlackDuck Project Version ", Details: getVersionName(config)}, | ||||
| 		}, | ||||
| 		Overview: []reporting.OverviewRow{ | ||||
| 			{Description: "Total number of vulnerabilities ", Details: fmt.Sprint(influx.detect_data.fields.vulnerabilities)}, | ||||
| 			{Description: "Total number of Critical/High vulnerabilties ", Details: fmt.Sprint(influx.detect_data.fields.major_vulnerabilities)}, | ||||
| 		}, | ||||
| 		SuccessfulScan: influx.detect_data.fields.major_vulnerabilities == 0, | ||||
| 		ReportTime:     time.Now(), | ||||
| 	} | ||||
|  | ||||
| 	detailTable := reporting.ScanDetailTable{ | ||||
| 		NoRowsMessage: "No publicly known vulnerabilities detected", | ||||
| 		Headers: []string{ | ||||
| 			"Vulnerability Name", | ||||
| 			"Severity", | ||||
| 			"Overall Score", | ||||
| 			"Base Score", | ||||
| 			"Component Name", | ||||
| 			"Component Version", | ||||
| 			"Description", | ||||
| 			"Status", | ||||
| 		}, | ||||
| 		WithCounter:   true, | ||||
| 		CounterHeader: "Entry#", | ||||
| 	} | ||||
|  | ||||
| 	vulnItems := vulns.Items | ||||
| 	sort.Slice(vulnItems, func(i, j int) bool { | ||||
| 		return vulnItems[i].OverallScore > vulnItems[j].OverallScore | ||||
| 	}) | ||||
|  | ||||
| 	for _, vuln := range vulnItems { | ||||
| 		row := reporting.ScanRow{} | ||||
| 		row.AddColumn(vuln.VulnerabilityWithRemediation.VulnerabilityName, 0) | ||||
| 		row.AddColumn(vuln.VulnerabilityWithRemediation.Severity, 0) | ||||
|  | ||||
| 		var scoreStyle reporting.ColumnStyle = reporting.Yellow | ||||
| 		if isMajorVulnerability(vuln) { | ||||
| 			scoreStyle = reporting.Red | ||||
| 		} | ||||
| 		if !isActiveVulnerability(vuln) { | ||||
| 			scoreStyle = reporting.Grey | ||||
| 		} | ||||
| 		row.AddColumn(vuln.VulnerabilityWithRemediation.OverallScore, scoreStyle) | ||||
| 		row.AddColumn(vuln.VulnerabilityWithRemediation.BaseScore, 0) | ||||
| 		row.AddColumn(vuln.Name, 0) | ||||
| 		row.AddColumn(vuln.Version, 0) | ||||
| 		row.AddColumn(vuln.VulnerabilityWithRemediation.Description, 0) | ||||
| 		row.AddColumn(vuln.VulnerabilityWithRemediation.RemediationStatus, 0) | ||||
|  | ||||
| 		detailTable.Rows = append(detailTable.Rows, row) | ||||
| 	} | ||||
|  | ||||
| 	scanReport.DetailTable = detailTable | ||||
| 	return scanReport | ||||
| } | ||||
|  | ||||
| func writeVulnerabilityReports(scanReport reporting.ScanReport, config detectExecuteScanOptions, utils detectUtils) ([]piperutils.Path, error) { | ||||
| 	reportPaths := []piperutils.Path{} | ||||
|  | ||||
| 	htmlReport, _ := scanReport.ToHTML() | ||||
| 	htmlReportPath := "piper_detect_vulnerability_report.html" | ||||
| 	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: "BlackDuck Vulnerability Report", Target: htmlReportPath}) | ||||
|  | ||||
| 	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("detectExecuteScan_oss_%v.json", fmt.Sprintf("%v", time.Now()))), jsonReport, 0666); err != nil { | ||||
| 		return reportPaths, errors.Wrapf(err, "failed to write json report") | ||||
| 	} | ||||
|  | ||||
| 	return reportPaths, nil | ||||
| } | ||||
|  | ||||
| func isActiveVulnerability(v bd.Vulnerability) bool { | ||||
| 	switch v.VulnerabilityWithRemediation.RemediationStatus { | ||||
| 	case "NEW": | ||||
| 		return true | ||||
| 	case "REMEDIATION_REQUIRED": | ||||
| 		return true | ||||
| 	case "NEEDS_REVIEW": | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func isMajorVulnerability(v bd.Vulnerability) bool { | ||||
| 	switch v.VulnerabilityWithRemediation.Severity { | ||||
| 	case "CRITICAL": | ||||
| 		return true | ||||
| 	case "HIGH": | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // create toolrecord file for detect | ||||
| // | ||||
| // | ||||
|   | ||||
| @@ -5,10 +5,12 @@ package cmd | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/config" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/piperenv" | ||||
| 	"github.com/SAP/jenkins-library/pkg/splunk" | ||||
| 	"github.com/SAP/jenkins-library/pkg/telemetry" | ||||
| 	"github.com/spf13/cobra" | ||||
| @@ -41,6 +43,55 @@ type detectExecuteScanOptions struct { | ||||
| 	CustomEnvironmentVariables []string `json:"customEnvironmentVariables,omitempty"` | ||||
| } | ||||
|  | ||||
| type detectExecuteScanInflux struct { | ||||
| 	step_data struct { | ||||
| 		fields struct { | ||||
| 			detect bool | ||||
| 		} | ||||
| 		tags struct { | ||||
| 		} | ||||
| 	} | ||||
| 	detect_data struct { | ||||
| 		fields struct { | ||||
| 			vulnerabilities       int | ||||
| 			major_vulnerabilities int | ||||
| 			minor_vulnerabilities int | ||||
| 			components            int | ||||
| 			policy_violations     int | ||||
| 		} | ||||
| 		tags struct { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (i *detectExecuteScanInflux) persist(path, resourceName string) { | ||||
| 	measurementContent := []struct { | ||||
| 		measurement string | ||||
| 		valType     string | ||||
| 		name        string | ||||
| 		value       interface{} | ||||
| 	}{ | ||||
| 		{valType: config.InfluxField, measurement: "step_data", name: "detect", value: i.step_data.fields.detect}, | ||||
| 		{valType: config.InfluxField, measurement: "detect_data", name: "vulnerabilities", value: i.detect_data.fields.vulnerabilities}, | ||||
| 		{valType: config.InfluxField, measurement: "detect_data", name: "major_vulnerabilities", value: i.detect_data.fields.major_vulnerabilities}, | ||||
| 		{valType: config.InfluxField, measurement: "detect_data", name: "minor_vulnerabilities", value: i.detect_data.fields.minor_vulnerabilities}, | ||||
| 		{valType: config.InfluxField, measurement: "detect_data", name: "components", value: i.detect_data.fields.components}, | ||||
| 		{valType: config.InfluxField, measurement: "detect_data", name: "policy_violations", value: i.detect_data.fields.policy_violations}, | ||||
| 	} | ||||
|  | ||||
| 	errCount := 0 | ||||
| 	for _, metric := range measurementContent { | ||||
| 		err := piperenv.SetResourceParameter(path, resourceName, filepath.Join(metric.measurement, fmt.Sprintf("%vs", metric.valType), metric.name), metric.value) | ||||
| 		if err != nil { | ||||
| 			log.Entry().WithError(err).Error("Error persisting influx environment.") | ||||
| 			errCount++ | ||||
| 		} | ||||
| 	} | ||||
| 	if errCount > 0 { | ||||
| 		log.Entry().Fatal("failed to persist Influx environment") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // DetectExecuteScanCommand Executes Synopsys Detect scan | ||||
| func DetectExecuteScanCommand() *cobra.Command { | ||||
| 	const STEP_NAME = "detectExecuteScan" | ||||
| @@ -48,6 +99,7 @@ func DetectExecuteScanCommand() *cobra.Command { | ||||
| 	metadata := detectExecuteScanMetadata() | ||||
| 	var stepConfig detectExecuteScanOptions | ||||
| 	var startTime time.Time | ||||
| 	var influx detectExecuteScanInflux | ||||
| 	var logCollector *log.CollectorHook | ||||
|  | ||||
| 	var createDetectExecuteScanCmd = &cobra.Command{ | ||||
| @@ -91,6 +143,7 @@ Please configure your BlackDuck server Url using the serverUrl parameter and the | ||||
| 			telemetryData.ErrorCode = "1" | ||||
| 			handler := func() { | ||||
| 				config.RemoveVaultSecretFiles() | ||||
| 				influx.persist(GeneralConfig.EnvRootPath, "influx") | ||||
| 				telemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds()) | ||||
| 				telemetryData.ErrorCategory = log.GetErrorCategory().String() | ||||
| 				telemetry.Send(&telemetryData) | ||||
| @@ -108,7 +161,7 @@ Please configure your BlackDuck server Url using the serverUrl parameter and the | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.Index, | ||||
| 					GeneralConfig.HookConfig.SplunkConfig.SendLogs) | ||||
| 			} | ||||
| 			detectExecuteScan(stepConfig, &telemetryData) | ||||
| 			detectExecuteScan(stepConfig, &telemetryData, &influx) | ||||
| 			telemetryData.ErrorCode = "0" | ||||
| 			log.Entry().Info("SUCCESS") | ||||
| 		}, | ||||
| @@ -404,6 +457,18 @@ func detectExecuteScanMetadata() config.StepData { | ||||
| 			Containers: []config.Container{ | ||||
| 				{Name: "openjdk", Image: "openjdk:11", WorkingDir: "/root", Options: []config.Option{{Name: "-u", Value: "0"}}}, | ||||
| 			}, | ||||
| 			Outputs: config.StepOutputs{ | ||||
| 				Resources: []config.StepResources{ | ||||
| 					{ | ||||
| 						Name: "influx", | ||||
| 						Type: "influx", | ||||
| 						Parameters: []map[string]interface{}{ | ||||
| 							{"Name": "step_data"}, {"fields": []map[string]string{{"name": "detect"}}}, | ||||
| 							{"Name": "detect_data"}, {"fields": []map[string]string{{"name": "vulnerabilities"}, {"name": "major_vulnerabilities"}, {"name": "minor_vulnerabilities"}, {"name": "components"}, {"name": "policy_violations"}}}, | ||||
| 						}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return theMetaData | ||||
|   | ||||
| @@ -1,12 +1,16 @@ | ||||
| package cmd | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
|  | ||||
| 	bd "github.com/SAP/jenkins-library/pkg/blackduck" | ||||
| 	piperhttp "github.com/SAP/jenkins-library/pkg/http" | ||||
| 	"github.com/SAP/jenkins-library/pkg/mock" | ||||
|  | ||||
| @@ -20,6 +24,128 @@ type detectTestUtilsBundle struct { | ||||
| 	*mock.FilesMock | ||||
| } | ||||
|  | ||||
| type httpMockClient struct { | ||||
| 	responseBodyForURL map[string]string | ||||
| 	errorMessageForURL map[string]string | ||||
| 	header             map[string]http.Header | ||||
| } | ||||
|  | ||||
| func (c *httpMockClient) SetOptions(opts piperhttp.ClientOptions) {} | ||||
| func (c *httpMockClient) SendRequest(method, url string, body io.Reader, header http.Header, cookies []*http.Cookie) (*http.Response, error) { | ||||
| 	c.header[url] = header | ||||
| 	response := http.Response{ | ||||
| 		StatusCode: 200, | ||||
| 		Body:       ioutil.NopCloser(bytes.NewReader([]byte(""))), | ||||
| 	} | ||||
|  | ||||
| 	if c.errorMessageForURL[url] != "" { | ||||
| 		response.StatusCode = 400 | ||||
| 		return &response, fmt.Errorf(c.errorMessageForURL[url]) | ||||
| 	} | ||||
|  | ||||
| 	if c.responseBodyForURL[url] != "" { | ||||
| 		response.Body = ioutil.NopCloser(bytes.NewReader([]byte(c.responseBodyForURL[url]))) | ||||
| 		return &response, nil | ||||
| 	} | ||||
|  | ||||
| 	return &response, nil | ||||
| } | ||||
|  | ||||
| func newBlackduckMockSystem(config detectExecuteScanOptions) blackduckSystem { | ||||
| 	myTestClient := httpMockClient{ | ||||
| 		responseBodyForURL: map[string]string{ | ||||
| 			"https://my.blackduck.system/api/tokens/authenticate":                                                              authContent, | ||||
| 			"https://my.blackduck.system/api/projects?q=name%3ASHC-PiperTest":                                                  projectContent, | ||||
| 			"https://my.blackduck.system/api/projects/5ca86e11-1983-4e7b-97d4-eb1a0aeffbbf/versions":                           projectVersionContent, | ||||
| 			"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/components?limit=999&offset=0":                componentsContent, | ||||
| 			"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/vunlerable-bom-components?limit=999&offset=0": vulnerabilitiesContent, | ||||
| 		}, | ||||
| 		header: map[string]http.Header{}, | ||||
| 	} | ||||
| 	sys := blackduckSystem{ | ||||
| 		Client: bd.NewClient(config.Token, config.ServerURL, &myTestClient), | ||||
| 	} | ||||
| 	return sys | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	authContent = `{ | ||||
| 		"bearerToken":"bearerTestToken", | ||||
| 		"expiresInMilliseconds":7199997 | ||||
| 	}` | ||||
| 	projectContent = `{ | ||||
| 		"totalCount": 1, | ||||
| 		"items": [ | ||||
| 			{ | ||||
| 				"name": "SHC-PiperTest", | ||||
| 				"_meta": { | ||||
| 					"href": "https://my.blackduck.system/api/projects/5ca86e11-1983-4e7b-97d4-eb1a0aeffbbf", | ||||
| 					"links": [ | ||||
| 						{ | ||||
| 							"rel": "versions", | ||||
| 							"href": "https://my.blackduck.system/api/projects/5ca86e11-1983-4e7b-97d4-eb1a0aeffbbf/versions" | ||||
| 						} | ||||
| 					] | ||||
| 				} | ||||
| 			} | ||||
| 		] | ||||
| 	}` | ||||
| 	projectVersionContent = `{ | ||||
| 		"totalCount": 1, | ||||
| 		"items": [ | ||||
| 			{ | ||||
| 				"versionName": "1.0", | ||||
| 				"_meta": { | ||||
| 					"href": "https://my.blackduck.system/api/projects/5ca86e11-1983-4e7b-97d4-eb1a0aeffbbf/versions/a6c94786-0ee6-414f-9054-90d549c69c36", | ||||
| 					"links": [ | ||||
| 						{ | ||||
| 							"rel": "components", | ||||
| 							"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/components" | ||||
| 						}, | ||||
| 						{ | ||||
| 							"rel": "vulnerable-components", | ||||
| 							"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/vunlerable-bom-components" | ||||
| 						}, | ||||
| 						{ | ||||
| 							"rel": "policy-status", | ||||
| 							"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/policy-status" | ||||
| 						} | ||||
| 					] | ||||
| 				} | ||||
| 			} | ||||
| 		] | ||||
| 	}` | ||||
| 	componentsContent = `{ | ||||
| 		"totalCount": 2, | ||||
| 		"items" : [ | ||||
| 			{ | ||||
| 				"componentName": "Spring Framework", | ||||
| 				"componentVersionName": "5.3.9" | ||||
| 			}, { | ||||
| 				"componentName": "Apache Tomcat", | ||||
| 				"componentVersionName": "9.0.52" | ||||
| 			} | ||||
| 		] | ||||
| 	}` | ||||
| 	vulnerabilitiesContent = `{ | ||||
| 		"totalCount": 1, | ||||
| 		"items": [ | ||||
| 			{ | ||||
| 				"componentName": "Spring Framework", | ||||
| 				"componentVersionName": "5.3.2", | ||||
| 				"vulnerabilityWithRemediation" : { | ||||
| 					"vulnerabilityName" : "BDSA-2019-2021", | ||||
| 					"baseScore" : 7.5, | ||||
| 					"overallScore" : 7.5, | ||||
| 					"severity" : "HIGH", | ||||
| 					"remediationStatus" : "IGNORED", | ||||
| 					"description" : "description" | ||||
| 				} | ||||
| 			} | ||||
| 		] | ||||
| 	}` | ||||
| ) | ||||
|  | ||||
| func (c *detectTestUtilsBundle) RunExecutable(string, ...string) error { | ||||
| 	panic("not expected to be called in test") | ||||
| } | ||||
| @@ -55,7 +181,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| 		err := runDetect(detectExecuteScanOptions{}, utilsMock) | ||||
| 		err := runDetect(detectExecuteScanOptions{}, utilsMock, &detectExecuteScanInflux{}) | ||||
|  | ||||
| 		assert.Equal(t, utilsMock.downloadedFiles["https://detect.synopsys.com/detect.sh"], "detect.sh") | ||||
| 		assert.True(t, utilsMock.HasRemovedFile("detect.sh")) | ||||
| @@ -71,7 +197,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| 		utilsMock.AddFile("my_BlackDuck_RiskReport.pdf", []byte("")) | ||||
| 		err := runDetect(detectExecuteScanOptions{FailOn: []string{"BLOCKER"}}, utilsMock) | ||||
| 		err := runDetect(detectExecuteScanOptions{FailOn: []string{"BLOCKER"}}, utilsMock, &detectExecuteScanInflux{}) | ||||
|  | ||||
| 		assert.Equal(t, utilsMock.downloadedFiles["https://detect.synopsys.com/detect.sh"], "detect.sh") | ||||
| 		assert.True(t, utilsMock.HasRemovedFile("detect.sh")) | ||||
| @@ -91,7 +217,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock.ShouldFailOnCommand = map[string]error{"./detect.sh --blackduck.url= --blackduck.api.token= \"--detect.project.name=''\" \"--detect.project.version.name=''\" \"--detect.code.location.name=''\" --detect.source.path='.'": fmt.Errorf("Test Error")} | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| 		err := runDetect(detectExecuteScanOptions{}, utilsMock) | ||||
| 		err := runDetect(detectExecuteScanOptions{}, utilsMock, &detectExecuteScanInflux{}) | ||||
| 		assert.EqualError(t, err, "Test Error") | ||||
| 		assert.True(t, utilsMock.HasRemovedFile("detect.sh")) | ||||
| 	}) | ||||
| @@ -105,7 +231,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 			M2Path:              ".pipeline/local_repo", | ||||
| 			ProjectSettingsFile: "project-settings.xml", | ||||
| 			GlobalSettingsFile:  "global-settings.xml", | ||||
| 		}, utilsMock) | ||||
| 		}, utilsMock, &detectExecuteScanInflux{}) | ||||
|  | ||||
| 		assert.NoError(t, err) | ||||
| 		assert.Equal(t, ".", utilsMock.Dir, "Wrong execution directory used") | ||||
| @@ -431,3 +557,69 @@ func TestAddDetectArgs(t *testing.T) { | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestPostScanChecksAndReporting(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	t.Run("Reporting after scan", func(t *testing.T) { | ||||
| 		config := detectExecuteScanOptions{Token: "token", ServerURL: "https://my.blackduck.system", ProjectName: "SHC-PiperTest", Version: "", CustomScanVersion: "1.0"} | ||||
| 		utils := newDetectTestUtilsBundle() | ||||
| 		sys := newBlackduckMockSystem(config) | ||||
| 		err := postScanChecksAndReporting(config, &detectExecuteScanInflux{}, utils, &sys) | ||||
|  | ||||
| 		assert.NoError(t, err) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestIsMajorVulnerability(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	t.Run("Case True", func(t *testing.T) { | ||||
| 		vr := bd.VulnerabilityWithRemediation{ | ||||
| 			OverallScore: 7.5, | ||||
| 			Severity:     "HIGH", | ||||
| 		} | ||||
| 		v := bd.Vulnerability{ | ||||
| 			Name:                         "", | ||||
| 			VulnerabilityWithRemediation: vr, | ||||
| 		} | ||||
| 		assert.True(t, isMajorVulnerability(v)) | ||||
| 	}) | ||||
| 	t.Run("Case False", func(t *testing.T) { | ||||
| 		vr := bd.VulnerabilityWithRemediation{ | ||||
| 			OverallScore: 7.5, | ||||
| 			Severity:     "MEDIUM", | ||||
| 		} | ||||
| 		v := bd.Vulnerability{ | ||||
| 			Name:                         "", | ||||
| 			VulnerabilityWithRemediation: vr, | ||||
| 		} | ||||
| 		assert.False(t, isMajorVulnerability(v)) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestIstActiveVulnerability(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	t.Run("Case true", func(t *testing.T) { | ||||
| 		vr := bd.VulnerabilityWithRemediation{ | ||||
| 			OverallScore:      7.5, | ||||
| 			Severity:          "HIGH", | ||||
| 			RemediationStatus: "NEW", | ||||
| 		} | ||||
| 		v := bd.Vulnerability{ | ||||
| 			Name:                         "", | ||||
| 			VulnerabilityWithRemediation: vr, | ||||
| 		} | ||||
| 		assert.True(t, isActiveVulnerability(v)) | ||||
| 	}) | ||||
| 	t.Run("Case False", func(t *testing.T) { | ||||
| 		vr := bd.VulnerabilityWithRemediation{ | ||||
| 			OverallScore:      7.5, | ||||
| 			Severity:          "HIGH", | ||||
| 			RemediationStatus: "IGNORED", | ||||
| 		} | ||||
| 		v := bd.Vulnerability{ | ||||
| 			Name:                         "", | ||||
| 			VulnerabilityWithRemediation: vr, | ||||
| 		} | ||||
| 		assert.False(t, isActiveVulnerability(v)) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -303,6 +303,27 @@ spec: | ||||
|           - PARAMETERS | ||||
|           - STAGES | ||||
|           - STEPS | ||||
|   outputs: | ||||
|     resources: | ||||
|       - name: influx | ||||
|         type: influx | ||||
|         params: | ||||
|           - name: step_data | ||||
|             fields: | ||||
|               - name: detect | ||||
|                 type: bool | ||||
|           - name: detect_data | ||||
|             fields: | ||||
|               - name: vulnerabilities | ||||
|                 type: int | ||||
|               - name: major_vulnerabilities | ||||
|                 type: int | ||||
|               - name: minor_vulnerabilities | ||||
|                 type: int | ||||
|               - name: components | ||||
|                 type: int | ||||
|               - name: policy_violations | ||||
|                 type: int | ||||
|   containers: | ||||
|     - name: openjdk | ||||
|       image: openjdk:11 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user