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 
			
		
		
		
	feat(detectExecuteScan) execution of rapid scans (#4211)
Co-authored-by: akram8008 <900658008.akram@email.com> Co-authored-by: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> Co-authored-by: Andrei Kireev <andrei.kireev@sap.com> Co-authored-by: ffeldmann <f.feldmann@sap.com> Co-authored-by: sumeet patil <sumeet.patil@sap.com>
This commit is contained in:
		
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							cc3bc76943
						
					
				
				
					commit
					f4fbf0f1ed
				
			| @@ -14,20 +14,20 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	bd "github.com/SAP/jenkins-library/pkg/blackduck" | ||||
| 	"github.com/SAP/jenkins-library/pkg/command" | ||||
| 	piperGithub "github.com/SAP/jenkins-library/pkg/github" | ||||
| 	piperhttp "github.com/SAP/jenkins-library/pkg/http" | ||||
| 	"github.com/SAP/jenkins-library/pkg/maven" | ||||
| 	"github.com/SAP/jenkins-library/pkg/reporting" | ||||
| 	"github.com/SAP/jenkins-library/pkg/versioning" | ||||
| 	"github.com/pkg/errors" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/command" | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| 	"github.com/SAP/jenkins-library/pkg/maven" | ||||
| 	"github.com/SAP/jenkins-library/pkg/orchestrator" | ||||
| 	"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" | ||||
|  | ||||
| 	"github.com/google/go-github/v45/github" | ||||
| 	"github.com/pkg/errors" | ||||
| ) | ||||
|  | ||||
| type detectUtils interface { | ||||
| @@ -46,14 +46,16 @@ type detectUtils interface { | ||||
|  | ||||
| 	GetIssueService() *github.IssuesService | ||||
| 	GetSearchService() *github.SearchService | ||||
| 	GetProvider() orchestrator.OrchestratorSpecificConfigProviding | ||||
| } | ||||
|  | ||||
| type detectUtilsBundle struct { | ||||
| 	*command.Command | ||||
| 	*piperutils.Files | ||||
| 	*piperhttp.Client | ||||
| 	issues *github.IssuesService | ||||
| 	search *github.SearchService | ||||
| 	issues   *github.IssuesService | ||||
| 	search   *github.SearchService | ||||
| 	provider orchestrator.OrchestratorSpecificConfigProviding | ||||
| } | ||||
|  | ||||
| func (d *detectUtilsBundle) GetIssueService() *github.IssuesService { | ||||
| @@ -64,6 +66,10 @@ func (d *detectUtilsBundle) GetSearchService() *github.SearchService { | ||||
| 	return d.search | ||||
| } | ||||
|  | ||||
| func (d *detectUtilsBundle) GetProvider() orchestrator.OrchestratorSpecificConfigProviding { | ||||
| 	return d.provider | ||||
| } | ||||
|  | ||||
| type blackduckSystem struct { | ||||
| 	Client bd.Client | ||||
| } | ||||
| @@ -104,6 +110,15 @@ func newDetectUtils(client *github.Client) detectUtils { | ||||
| 	} | ||||
| 	utils.Stdout(log.Writer()) | ||||
| 	utils.Stderr(log.Writer()) | ||||
|  | ||||
| 	provider, err := orchestrator.NewOrchestratorSpecificConfigProvider() | ||||
| 	if err != nil { | ||||
| 		log.Entry().WithError(err).Warning(err) | ||||
| 		provider = &orchestrator.UnknownOrchestratorConfigProvider{} | ||||
| 	} | ||||
|  | ||||
| 	utils.provider = provider | ||||
|  | ||||
| 	return &utils | ||||
| } | ||||
|  | ||||
| @@ -159,8 +174,10 @@ func runDetect(ctx context.Context, config detectExecuteScanOptions, utils detec | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	blackduckSystem := newBlackduckSystem(config) | ||||
|  | ||||
| 	args := []string{"./detect.sh"} | ||||
| 	args, err = addDetectArgs(args, config, utils) | ||||
| 	args, err = addDetectArgs(args, config, utils, blackduckSystem) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -173,7 +190,6 @@ func runDetect(ctx context.Context, config detectExecuteScanOptions, utils detec | ||||
| 	utils.SetEnv(envs) | ||||
|  | ||||
| 	err = utils.RunShell("/bin/bash", script) | ||||
| 	blackduckSystem := newBlackduckSystem(config) | ||||
| 	reportingErr := postScanChecksAndReporting(ctx, config, influx, utils, blackduckSystem) | ||||
| 	if reportingErr != nil { | ||||
| 		if strings.Contains(reportingErr.Error(), "License Policy Violations found") { | ||||
| @@ -295,7 +311,7 @@ func getDetectScript(config detectExecuteScanOptions, utils detectUtils) error { | ||||
| 	return utils.DownloadFile("https://detect.synopsys.com/detect7.sh", "detect.sh", nil, nil) | ||||
| } | ||||
|  | ||||
| func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectUtils) ([]string, error) { | ||||
| func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectUtils, sys *blackduckSystem) ([]string, error) { | ||||
| 	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 | ||||
| @@ -390,6 +406,18 @@ func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectU | ||||
| 		args = append(args, fmt.Sprintf("\"--detect.maven.build.command='%v'\"", strings.Join(mavenArgs, " "))) | ||||
| 	} | ||||
|  | ||||
| 	// rapid scan on pull request | ||||
| 	if utils.GetProvider().IsPullRequest() { | ||||
| 		log.Entry().Debug("pull request detected") | ||||
| 		args = append(args, "--detect.blackduck.scan.mode='RAPID'") | ||||
| 		_, err := sys.Client.GetProjectVersion(config.ProjectName, config.Version) | ||||
| 		if err == nil { | ||||
| 			args = append(args, "--detect.blackduck.rapid.compare.mode='BOM_COMPARE_STRICT'") | ||||
| 		} | ||||
| 		args = append(args, "--detect.cleanup=false") | ||||
| 		args = append(args, "--detect.output.path='report'") | ||||
| 	} | ||||
|  | ||||
| 	return args, nil | ||||
| } | ||||
|  | ||||
| @@ -498,6 +526,33 @@ func isMajorVulnerability(v bd.Vulnerability) bool { | ||||
| } | ||||
|  | ||||
| func postScanChecksAndReporting(ctx context.Context, config detectExecuteScanOptions, influx *detectExecuteScanInflux, utils detectUtils, sys *blackduckSystem) error { | ||||
|  | ||||
| 	if utils.GetProvider().IsPullRequest() { | ||||
| 		issueNumber, err := strconv.Atoi(utils.GetProvider().GetPullRequestConfig().Key) | ||||
| 		if err != nil { | ||||
| 			log.Entry().Warning("Can not get issue number ", err) | ||||
| 			return nil | ||||
| 		} | ||||
| 		commentBody, err := reporting.RapidScanResult("./report") | ||||
| 		if err != nil { | ||||
| 			log.Entry().Warning("Couldn't read file of report of rapid scan, error: ", err) | ||||
| 			return nil | ||||
| 		} | ||||
| 		_, _, err = utils.GetIssueService().CreateComment(ctx, | ||||
| 			config.Owner, | ||||
| 			config.Repository, | ||||
| 			issueNumber, | ||||
| 			&github.IssueComment{ | ||||
| 				Body: &commentBody, | ||||
| 			}) | ||||
| 		if err != nil { | ||||
| 			log.Entry().Warning("Can send request to github ", err) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	errorsOccured := []string{} | ||||
| 	vulns, err := getVulnerabilitiesWithComponents(config, influx, sys) | ||||
| 	if err != nil { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| @@ -15,6 +16,7 @@ import ( | ||||
| 	piperGithub "github.com/SAP/jenkins-library/pkg/github" | ||||
| 	piperhttp "github.com/SAP/jenkins-library/pkg/http" | ||||
| 	"github.com/SAP/jenkins-library/pkg/mock" | ||||
| 	"github.com/SAP/jenkins-library/pkg/orchestrator" | ||||
|  | ||||
| 	"github.com/google/go-github/v45/github" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| @@ -25,7 +27,12 @@ type detectTestUtilsBundle struct { | ||||
| 	downloadedFiles map[string]string // src, dest | ||||
| 	*mock.ShellMockRunner | ||||
| 	*mock.FilesMock | ||||
| 	customEnv []string | ||||
| 	customEnv    []string | ||||
| 	orchestrator *orchestratorConfigProviderMock | ||||
| } | ||||
|  | ||||
| func (d *detectTestUtilsBundle) GetProvider() orchestrator.OrchestratorSpecificConfigProviding { | ||||
| 	return d.orchestrator | ||||
| } | ||||
|  | ||||
| func (d *detectTestUtilsBundle) GetIssueService() *github.IssuesService { | ||||
| @@ -36,6 +43,15 @@ func (d *detectTestUtilsBundle) GetSearchService() *github.SearchService { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type orchestratorConfigProviderMock struct { | ||||
| 	orchestrator.UnknownOrchestratorConfigProvider | ||||
| 	isPullRequest bool | ||||
| } | ||||
|  | ||||
| func (o *orchestratorConfigProviderMock) IsPullRequest() bool { | ||||
| 	return o.isPullRequest | ||||
| } | ||||
|  | ||||
| type httpMockClient struct { | ||||
| 	responseBodyForURL map[string]string | ||||
| 	errorMessageForURL map[string]string | ||||
| @@ -73,6 +89,8 @@ func newBlackduckMockSystem(config detectExecuteScanOptions) blackduckSystem { | ||||
| 			"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/vunlerable-bom-components?limit=999&offset=0":                  vulnerabilitiesContent, | ||||
| 			"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/components?filter=policyCategory%3Alicense&limit=999&offset=0": componentsContent, | ||||
| 			"https://my.blackduck.system/api/projects/5ca86e11/versions/a6c94786/policy-status":                                                 policyStatusContent, | ||||
| 			"https://my.blackduck.system/api/projects?q=name%3ARapid_scan_on_PRs":                                                               projectContentRapidScan, | ||||
| 			"https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf/versions?limit=100&offset=0":                        projectVersionContentRapid, | ||||
| 		}, | ||||
| 		header: map[string]http.Header{}, | ||||
| 	} | ||||
| @@ -193,6 +211,48 @@ const ( | ||||
| 			"severityLevels": [{"name":"BLOCKER", "value": 1}, {"name": "CRITICAL", "value": 1}] | ||||
| 		} | ||||
| 	}` | ||||
| 	projectContentRapidScan = `{ | ||||
| 		"totalCount": 1, | ||||
| 		"items": [ | ||||
| 			{ | ||||
| 				"name": "Rapid_scan_on_PRs", | ||||
| 				"_meta": { | ||||
| 					"href": "https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf", | ||||
| 					"links": [ | ||||
| 						{ | ||||
| 							"rel": "versions", | ||||
| 							"href": "https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf/versions" | ||||
| 						} | ||||
| 					] | ||||
| 				} | ||||
| 			} | ||||
| 		] | ||||
| 	}` | ||||
| 	projectVersionContentRapid = `{ | ||||
| 		"totalCount": 1, | ||||
| 		"items": [ | ||||
| 			{ | ||||
| 				"versionName": "1.0", | ||||
| 				"_meta": { | ||||
| 					"href": "https://my.blackduck.system/api/projects/654ggfdgf-1983-4e7b-97d4-eb1a0aeffbbf/versions/54357fds-0ee6-414f-9054-90d549c69c36", | ||||
| 					"links": [ | ||||
| 						{ | ||||
| 							"rel": "components", | ||||
| 							"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/654784382/components" | ||||
| 						}, | ||||
| 						{ | ||||
| 							"rel": "vulnerable-components", | ||||
| 							"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/654784382/vunlerable-bom-components" | ||||
| 						}, | ||||
| 						{ | ||||
| 							"rel": "policy-status", | ||||
| 							"href": "https://my.blackduck.system/api/projects/5ca86e11/versions/654784382/policy-status" | ||||
| 						} | ||||
| 					] | ||||
| 				} | ||||
| 			} | ||||
| 		] | ||||
| 	}` | ||||
| ) | ||||
|  | ||||
| func (c *detectTestUtilsBundle) RunExecutable(string, ...string) error { | ||||
| @@ -222,10 +282,11 @@ func (w *detectTestUtilsBundle) CreateIssue(ghCreateIssueOptions *piperGithub.Cr | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func newDetectTestUtilsBundle() *detectTestUtilsBundle { | ||||
| func newDetectTestUtilsBundle(isPullRequest bool) *detectTestUtilsBundle { | ||||
| 	utilsBundle := detectTestUtilsBundle{ | ||||
| 		ShellMockRunner: &mock.ShellMockRunner{}, | ||||
| 		FilesMock:       &mock.FilesMock{}, | ||||
| 		orchestrator:    &orchestratorConfigProviderMock{isPullRequest: isPullRequest}, | ||||
| 	} | ||||
| 	return &utilsBundle | ||||
| } | ||||
| @@ -235,7 +296,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 	t.Run("success case", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		ctx := context.Background() | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock := newDetectTestUtilsBundle(false) | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| 		err := runDetect(ctx, detectExecuteScanOptions{}, utilsMock, &detectExecuteScanInflux{}) | ||||
|  | ||||
| @@ -251,7 +312,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 	t.Run("success case detect 6", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		ctx := context.Background() | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock := newDetectTestUtilsBundle(false) | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| 		options := detectExecuteScanOptions{ | ||||
| 			CustomEnvironmentVariables: []string{"DETECT_LATEST_RELEASE_VERSION=6.8.0"}, | ||||
| @@ -270,7 +331,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 	t.Run("success case detect 6 from OS env", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		ctx := context.Background() | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock := newDetectTestUtilsBundle(false) | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| 		utilsMock.customEnv = []string{"DETECT_LATEST_RELEASE_VERSION=6.8.0"} | ||||
| 		err := runDetect(ctx, detectExecuteScanOptions{}, utilsMock, &detectExecuteScanInflux{}) | ||||
| @@ -287,7 +348,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 	t.Run("failure case", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		ctx := context.Background() | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock := newDetectTestUtilsBundle(false) | ||||
| 		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("")} | ||||
| 		utilsMock.ExitCode = 3 | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| @@ -300,7 +361,7 @@ func TestRunDetect(t *testing.T) { | ||||
| 	t.Run("maven parameters", func(t *testing.T) { | ||||
| 		t.Parallel() | ||||
| 		ctx := context.Background() | ||||
| 		utilsMock := newDetectTestUtilsBundle() | ||||
| 		utilsMock := newDetectTestUtilsBundle(false) | ||||
| 		utilsMock.CurrentDir = "root_folder" | ||||
| 		utilsMock.AddFile("detect.sh", []byte("")) | ||||
| 		err := runDetect(ctx, detectExecuteScanOptions{ | ||||
| @@ -322,9 +383,10 @@ func TestRunDetect(t *testing.T) { | ||||
| func TestAddDetectArgs(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	testData := []struct { | ||||
| 		args     []string | ||||
| 		options  detectExecuteScanOptions | ||||
| 		expected []string | ||||
| 		args          []string | ||||
| 		options       detectExecuteScanOptions | ||||
| 		isPullRequest bool | ||||
| 		expected      []string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			args: []string{"--testProp1=1"}, | ||||
| @@ -645,13 +707,76 @@ func TestAddDetectArgs(t *testing.T) { | ||||
| 				"--detect.source.path='.'", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			args: []string{"--testProp1=1"}, | ||||
| 			options: detectExecuteScanOptions{ | ||||
| 				ServerURL:         "https://server.url", | ||||
| 				Token:             "apiToken", | ||||
| 				ProjectName:       "Rapid_scan_on_PRs", | ||||
| 				Version:           "1.0", | ||||
| 				VersioningModel:   "major-minor", | ||||
| 				CodeLocation:      "", | ||||
| 				ScanPaths:         []string{"path1", "path2"}, | ||||
| 				MinScanInterval:   4, | ||||
| 				CustomScanVersion: "1.0", | ||||
| 			}, | ||||
| 			isPullRequest: true, | ||||
| 			expected: []string{ | ||||
| 				"--testProp1=1", | ||||
| 				"--detect.blackduck.signature.scanner.arguments='--min-scan-interval=4'", | ||||
| 				"--blackduck.url=https://server.url", | ||||
| 				"--blackduck.api.token=apiToken", | ||||
| 				"\"--detect.project.name='Rapid_scan_on_PRs'\"", | ||||
| 				"\"--detect.project.version.name='1.0'\"", | ||||
| 				"\"--detect.code.location.name='Rapid_scan_on_PRs/1.0'\"", | ||||
| 				"--detect.blackduck.signature.scanner.paths=path1,path2", | ||||
| 				"--detect.source.path='.'", | ||||
| 				"--detect.blackduck.scan.mode='RAPID'", | ||||
| 				"--detect.blackduck.rapid.compare.mode='BOM_COMPARE_STRICT'", | ||||
| 				"--detect.cleanup=false", | ||||
| 				"--detect.output.path='report'", | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			args: []string{"--testProp1=1"}, | ||||
| 			options: detectExecuteScanOptions{ | ||||
| 				ServerURL:         "https://server.url", | ||||
| 				Token:             "apiToken", | ||||
| 				ProjectName:       "Rapid_scan_on_PRs", | ||||
| 				Version:           "2.0", | ||||
| 				VersioningModel:   "major-minor", | ||||
| 				CodeLocation:      "", | ||||
| 				ScanPaths:         []string{"path1", "path2"}, | ||||
| 				MinScanInterval:   4, | ||||
| 				CustomScanVersion: "2.0", | ||||
| 			}, | ||||
| 			isPullRequest: true, | ||||
| 			expected: []string{ | ||||
| 				"--testProp1=1", | ||||
| 				"--detect.blackduck.signature.scanner.arguments='--min-scan-interval=4'", | ||||
| 				"--blackduck.url=https://server.url", | ||||
| 				"--blackduck.api.token=apiToken", | ||||
| 				"\"--detect.project.name='Rapid_scan_on_PRs'\"", | ||||
| 				"\"--detect.project.version.name='2.0'\"", | ||||
| 				"\"--detect.code.location.name='Rapid_scan_on_PRs/2.0'\"", | ||||
| 				"--detect.blackduck.signature.scanner.paths=path1,path2", | ||||
| 				"--detect.source.path='.'", | ||||
| 				"--detect.blackduck.scan.mode='RAPID'", | ||||
| 				"--detect.cleanup=false", | ||||
| 				"--detect.output.path='report'", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for k, v := range testData { | ||||
| 		v := v | ||||
| 		t.Run(fmt.Sprintf("run %v", k), func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
| 			got, err := addDetectArgs(v.args, v.options, newDetectTestUtilsBundle()) | ||||
|  | ||||
| 			config := detectExecuteScanOptions{Token: "token", ServerURL: "https://my.blackduck.system", ProjectName: v.options.ProjectName, Version: v.options.Version, CustomScanVersion: v.options.CustomScanVersion} | ||||
| 			sys := newBlackduckMockSystem(config) | ||||
|  | ||||
| 			got, err := addDetectArgs(v.args, v.options, newDetectTestUtilsBundle(v.isPullRequest), &sys) | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, v.expected, got) | ||||
| 		}) | ||||
| @@ -681,7 +806,7 @@ func TestPostScanChecksAndReporting(t *testing.T) { | ||||
| 	t.Run("Reporting after scan", func(t *testing.T) { | ||||
| 		ctx := context.Background() | ||||
| 		config := detectExecuteScanOptions{Token: "token", ServerURL: "https://my.blackduck.system", ProjectName: "SHC-PiperTest", Version: "", CustomScanVersion: "1.0"} | ||||
| 		utils := newDetectTestUtilsBundle() | ||||
| 		utils := newDetectTestUtilsBundle(false) | ||||
| 		sys := newBlackduckMockSystem(config) | ||||
| 		err := postScanChecksAndReporting(ctx, config, &detectExecuteScanInflux{}, utils, &sys) | ||||
|  | ||||
|   | ||||
| @@ -51,7 +51,7 @@ If you have configured your orchestrator to detect pull requests, then the `dete | ||||
|     ``` | ||||
|  | ||||
| 2. Enable detecExecuationScan in the orchestrator. | ||||
|    For example: | ||||
|   For example: | ||||
|  | ||||
|     ``` | ||||
|     @Library('piper-lib') _ | ||||
|   | ||||
							
								
								
									
										420
									
								
								pkg/reporting/pullRequestReport.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								pkg/reporting/pullRequestReport.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,420 @@ | ||||
| package reporting | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
|  | ||||
| 	"github.com/SAP/jenkins-library/pkg/log" | ||||
| ) | ||||
|  | ||||
| // Components - for parsing from file | ||||
| type Components []Component | ||||
|  | ||||
| type Component struct { | ||||
| 	ComponentName                  string                         `json:"componentName"` | ||||
| 	ComponentVersion               string                         `json:"versionName"` | ||||
| 	ComponentIdentifier            string                         `json:"componentIdentifier"` | ||||
| 	ViolatingPolicyNames           []string                       `json:"violatingPolicyNames"` | ||||
| 	PolicyViolationVulnerabilities []PolicyViolationVulnerability `json:"policyViolationVulnerabilities"` | ||||
| 	PolicyViolationLicenses        []PolicyViolationLicense       `json:"policyViolationLicenses"` | ||||
| 	WarningMessage                 string                         `json:"warningMessage"` | ||||
| 	ErrorMessage                   string                         `json:"errorMessage"` | ||||
| } | ||||
|  | ||||
| type PolicyViolationVulnerability struct { | ||||
| 	Name                 string   `json:"name"` | ||||
| 	ViolatingPolicyNames []string `json:"ViolatingPolicyNames"` | ||||
| 	WarningMessage       string   `json:"warningMessage"` | ||||
| 	ErrorMessage         string   `json:"errorMessage"` | ||||
| 	Meta                 Meta     `json:"_meta"` | ||||
| } | ||||
|  | ||||
| type PolicyViolationLicense struct { | ||||
| 	LicenseName          string   `json:"licenseName"` | ||||
| 	ViolatingPolicyNames []string `json:"violatingPolicyNames"` | ||||
| 	Meta                 Meta     `json:"_meta"` | ||||
| } | ||||
|  | ||||
| type Meta struct { | ||||
| 	Href string `json:"href"` | ||||
| } | ||||
|  | ||||
| // RapidScanReport - for commenting to pull requests | ||||
| type RapidScanReport struct { | ||||
| 	Success bool | ||||
|  | ||||
| 	ExecutedTime string | ||||
|  | ||||
| 	MainTableHeaders []string | ||||
| 	MainTableValues  [][]string | ||||
|  | ||||
| 	VulnerabilitiesTable []Vulnerabilities | ||||
| 	LicensesTable        []Licenses | ||||
| 	OtherViolationsTable []OtherViolations | ||||
| } | ||||
|  | ||||
| type Vulnerabilities struct { | ||||
| 	PolicyViolationName string | ||||
| 	Values              []Vulnerability | ||||
| } | ||||
|  | ||||
| type Vulnerability struct { | ||||
| 	VulnerabilityID    string | ||||
| 	VulnerabilityScore string | ||||
| 	ComponentName      string | ||||
| 	VulnerabilityHref  string | ||||
| } | ||||
|  | ||||
| type Licenses struct { | ||||
| 	PolicyViolationName string | ||||
| 	Values              []License | ||||
| } | ||||
|  | ||||
| type License struct { | ||||
| 	LicenseName   string | ||||
| 	ComponentName string | ||||
| 	LicenseHref   string | ||||
| } | ||||
|  | ||||
| type OtherViolations struct { | ||||
| 	PolicyViolationName string | ||||
| 	Values              []OtherViolation | ||||
| } | ||||
|  | ||||
| type OtherViolation struct { | ||||
| 	ComponentName string | ||||
| } | ||||
|  | ||||
| const rapidReportMdTemplate = ` | ||||
| ## {{if .Success}}:heavy_check_mark: OSS related checks passed successfully | ||||
|  ### :clipboard: OSS related checks executed by Black Duck - rapid scan passed successfully. | ||||
|  <a href="https://community.synopsys.com/s/document-item?bundleId=integrations-detect&topicId=downloadingandrunning%2Frapidscan.html&_LANG=enus"><h3>RAPID SCAN</h3> </a> | ||||
|  | ||||
| {{else}} :x: OSS related checks failed | ||||
|  ### :clipboard: Policies violated by added OSS components | ||||
|  <table> | ||||
|  <tr>{{range $s := .MainTableHeaders -}}<td><b>{{$s}}</b></td>{{- end}}</tr> | ||||
|  {{range $s := .MainTableValues -}}<tr>{{range $s1 := $s }}<td>{{$s1}}</td>{{- end}}</tr> | ||||
|  {{- end}} | ||||
|  </table> | ||||
|  | ||||
| {{range $index := .VulnerabilitiesTable -}} | ||||
| <details><summary> | ||||
| {{$len := len $index.Values}} | ||||
| {{if le $len 1}} <h3> {{$len}} Policy Violation of {{$index.PolicyViolationName}}</h3> | ||||
| {{else}}<h3> {{$len}} Policy Violations of {{$index.PolicyViolationName}} </h3> {{end}} | ||||
| </summary> | ||||
| 	<table> | ||||
| 		<tr><td><b>Vulnerability ID</b></td><td><b>Vulnerability Score</b></td><td><b>Component Name</b></td></tr> | ||||
| 		{{range $value := $index.Values -}} | ||||
| 			<tr> | ||||
| 			<td> <a href="{{$value.VulnerabilityHref}}"> {{$value.VulnerabilityID}} </a> </td><td>{{$value.VulnerabilityScore}}</td><td>{{$value.ComponentName}}</td> | ||||
| 			</tr> | ||||
| 		{{end -}} | ||||
| 	</table> | ||||
| </details> | ||||
| {{end -}} | ||||
| {{range $index := .LicensesTable -}} | ||||
| <details><summary> | ||||
| {{$len := len $index.Values}} | ||||
| {{if le $len 1}} <h3> {{$len}} Policy Violation of {{$index.PolicyViolationName}}</h3> | ||||
| {{else}}<h3> {{$len}} Policy Violations of {{$index.PolicyViolationName}} </h3> {{end}} | ||||
| </summary> | ||||
| 	<table> | ||||
| 		<tr><td><b>License Name</b></td><td><b>Component Name</b></td></tr> | ||||
| 		{{range $value := $index.Values -}} | ||||
| 			<tr><td> <a href="{{$value.LicenseHref}}"> {{$value.LicenseName}} </a> </td><td>{{$value.ComponentName}}</td></tr> | ||||
| 		{{end -}} | ||||
| 	</table> | ||||
| </details> | ||||
| {{end -}} | ||||
| {{range $index := .OtherViolationsTable -}} | ||||
| <details><summary> | ||||
| {{$len := len $index.Values}} | ||||
| {{if le $len 1}} <h3> {{$len}} Policy Violation of {{$index.PolicyViolationName}}</h3> | ||||
| {{else}}<h3> {{$len}} Policy Violations of {{$index.PolicyViolationName}} </h3> {{end}} | ||||
| </summary> | ||||
| 	<table> | ||||
| 		<tr><td><b>Component Name</b></td></tr> | ||||
| 		{{range $value := $index.Values -}} | ||||
| 			<tr><td>{{$value.ComponentName}}</td></tr> | ||||
| 		{{end -}} | ||||
| 	</table> | ||||
| </details> | ||||
| {{end -}} | ||||
| {{end}} | ||||
| ` | ||||
|  | ||||
| // RapidScanResult reads result of Rapid scan from generated file | ||||
| func RapidScanResult(dir string) (string, error) { | ||||
| 	components, removeDir, err := findAndReadJsonFile(dir) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	if components == nil { | ||||
| 		return "", errors.New("couldn't parse info from file") | ||||
| 	} | ||||
|  | ||||
| 	buf, err := createMarkdownReport(components) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	err = os.RemoveAll(removeDir) | ||||
| 	if err != nil { | ||||
| 		log.Entry().Error("Couldn't remove report file", err) | ||||
| 	} | ||||
|  | ||||
| 	return buf.String(), nil | ||||
| } | ||||
|  | ||||
| type Files []os.DirEntry | ||||
|  | ||||
| // findLastCreatedDir finds last created directory | ||||
| func findLastCreatedDir(directories []os.DirEntry) os.DirEntry { | ||||
| 	lastCreatedDir := directories[0] | ||||
| 	for _, dir := range directories { | ||||
| 		if dir.Name() > lastCreatedDir.Name() { | ||||
| 			lastCreatedDir = dir | ||||
| 		} | ||||
| 	} | ||||
| 	return lastCreatedDir | ||||
| } | ||||
|  | ||||
| // findAndReadJsonFile find file BlackDuck_DeveloperMode_Result.json generated by detectExecuteStep and read it | ||||
| func findAndReadJsonFile(dir string) (*Components, string, error) { | ||||
| 	var err error | ||||
| 	filePath := dir + "/runs" | ||||
| 	allFiles, err := os.ReadDir(filePath) | ||||
| 	if err != nil { | ||||
| 		return nil, "", err | ||||
| 	} | ||||
| 	if allFiles == nil { | ||||
| 		return nil, "", errors.New("no report files") | ||||
| 	} | ||||
| 	lastDir := findLastCreatedDir(allFiles) | ||||
| 	removeDir := filePath + "/" + lastDir.Name() | ||||
| 	filePath = filePath + "/" + lastDir.Name() + "/scan" | ||||
| 	files, err := os.ReadDir(filePath) | ||||
| 	if err != nil { | ||||
| 		return nil, "", err | ||||
| 	} | ||||
| 	if files == nil { | ||||
| 		return nil, "", errors.New("no report files") | ||||
| 	} | ||||
|  | ||||
| 	for _, file := range files { | ||||
| 		if !file.IsDir() && strings.HasSuffix(file.Name(), "BlackDuck_DeveloperMode_Result.json") { | ||||
| 			var result Components | ||||
| 			jsonFile, err := os.Open(filePath + "/" + file.Name()) | ||||
| 			if err != nil { | ||||
| 				return nil, "", err | ||||
| 			} | ||||
| 			fileBody, err := io.ReadAll(jsonFile) | ||||
| 			if err != nil { | ||||
| 				return nil, "", err | ||||
| 			} | ||||
| 			err = json.Unmarshal(fileBody, &result) | ||||
| 			if err != nil { | ||||
| 				return nil, "", err | ||||
| 			} | ||||
| 			err = jsonFile.Close() | ||||
| 			if err != nil { | ||||
| 				log.Entry().Error(fmt.Sprintf("Couldn't close %s", jsonFile.Name()), err) | ||||
| 			} | ||||
| 			return &result, removeDir, nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil, "", nil | ||||
| } | ||||
|  | ||||
| // createMarkdownReport creates markdown report to upload it as GitHub PR comment | ||||
| func createMarkdownReport(components *Components) (*bytes.Buffer, error) { | ||||
| 	// preparing report | ||||
| 	var scanReport RapidScanReport | ||||
| 	scanReport.Success = true | ||||
|  | ||||
| 	// getting reports to maps | ||||
| 	allPolicyViolationsMapUsed := make(map[string]bool) | ||||
| 	countPolicyViolationComponent := make(map[string]map[string]int) | ||||
| 	vulnerabilities := make(map[string][]Vulnerability) | ||||
| 	licenses := make(map[string][]License) | ||||
| 	otherViolations := make(map[string][]OtherViolation) | ||||
| 	componentNames := make([]string, len(*components)) | ||||
|  | ||||
| 	for idx, component := range *components { | ||||
| 		componentName := component.ComponentName + " " + component.ComponentVersion + " (" + component.ComponentIdentifier + ")" | ||||
| 		componentNames[idx] = componentName | ||||
|  | ||||
| 		// for others | ||||
| 		for _, policyViolationName := range component.ViolatingPolicyNames { | ||||
| 			if !allPolicyViolationsMapUsed[policyViolationName] { | ||||
| 				allPolicyViolationsMapUsed[policyViolationName] = true | ||||
| 				scanReport.MainTableHeaders = append(scanReport.MainTableHeaders, policyViolationName) | ||||
| 			} | ||||
| 			if countPolicyViolationComponent[policyViolationName] == nil { | ||||
| 				countPolicyViolationComponent[policyViolationName] = make(map[string]int) | ||||
| 			} | ||||
| 			msg := component.ErrorMessage + " " + component.WarningMessage | ||||
| 			if strings.Contains(msg, policyViolationName) { | ||||
| 				countPolicyViolationComponent[policyViolationName][componentName]++ | ||||
| 				otherViolations[policyViolationName] = append(otherViolations[policyViolationName], OtherViolation{ComponentName: componentName}) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// for Vulnerabilities | ||||
| 		for _, policyVulnerability := range component.PolicyViolationVulnerabilities { | ||||
| 			for _, policyViolationName := range policyVulnerability.ViolatingPolicyNames { | ||||
| 				if countPolicyViolationComponent[policyViolationName] == nil { | ||||
| 					countPolicyViolationComponent[policyViolationName] = make(map[string]int) | ||||
| 				} | ||||
| 				countPolicyViolationComponent[policyViolationName][componentName]++ | ||||
| 				vulnerabilities[policyViolationName] = append(vulnerabilities[policyViolationName], | ||||
| 					Vulnerability{ | ||||
| 						VulnerabilityID:    policyVulnerability.Name, | ||||
| 						VulnerabilityHref:  policyVulnerability.Meta.Href, | ||||
| 						VulnerabilityScore: getScore(policyVulnerability.ErrorMessage, "score") + " " + getScore(policyVulnerability.ErrorMessage, "severity"), | ||||
| 						ComponentName:      componentName, | ||||
| 					}) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// for Licenses | ||||
| 		for _, policyViolationLicense := range component.PolicyViolationLicenses { | ||||
| 			for _, policyViolationName := range policyViolationLicense.ViolatingPolicyNames { | ||||
| 				if countPolicyViolationComponent[policyViolationName] == nil { | ||||
| 					countPolicyViolationComponent[policyViolationName] = make(map[string]int) | ||||
| 				} | ||||
| 				countPolicyViolationComponent[policyViolationName][componentName]++ | ||||
| 				licenses[policyViolationName] = append(licenses[policyViolationName], | ||||
| 					License{ | ||||
| 						LicenseName:   policyViolationLicense.LicenseName, | ||||
| 						LicenseHref:   policyViolationLicense.Meta.Href + "/license-terms", | ||||
| 						ComponentName: componentName, | ||||
| 					}) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if scanReport.MainTableHeaders != nil && componentNames != nil { | ||||
| 		scanReport.Success = false | ||||
|  | ||||
| 		// MainTable sort & copy | ||||
| 		sort.Strings(scanReport.MainTableHeaders) | ||||
| 		sort.Strings(componentNames) | ||||
| 		scanReport.MainTableHeaders = append([]string{"Component name"}, scanReport.MainTableHeaders...) | ||||
| 		for i := range componentNames { | ||||
| 			scanReport.MainTableValues = append(scanReport.MainTableValues, []string{}) | ||||
| 			scanReport.MainTableValues[i] = append(scanReport.MainTableValues[i], componentNames[i]) | ||||
| 			for j := 1; j < len(scanReport.MainTableHeaders); j++ { | ||||
| 				policyV := scanReport.MainTableHeaders[j] | ||||
| 				comp := componentNames[i] | ||||
| 				count := strconv.Itoa(countPolicyViolationComponent[policyV][comp]) | ||||
| 				scanReport.MainTableValues[i] = append(scanReport.MainTableValues[i], count) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// VulnerabilitiesTable sort & copy | ||||
| 		for key := range vulnerabilities { | ||||
| 			item := vulnerabilities[key] | ||||
| 			sort.Slice(item, func(i, j int) bool { | ||||
| 				return scoreLogicSort(item[i].VulnerabilityScore, item[j].VulnerabilityScore) | ||||
| 			}) | ||||
| 			scanReport.VulnerabilitiesTable = append(scanReport.VulnerabilitiesTable, Vulnerabilities{ | ||||
| 				PolicyViolationName: key, | ||||
| 				Values:              item, | ||||
| 			}) | ||||
| 		} | ||||
| 		sort.Slice(scanReport.VulnerabilitiesTable, func(i, j int) bool { | ||||
| 			return scanReport.VulnerabilitiesTable[i].PolicyViolationName < scanReport.VulnerabilitiesTable[j].PolicyViolationName | ||||
| 		}) | ||||
|  | ||||
| 		// LicensesTable sort & copy | ||||
| 		for key := range licenses { | ||||
| 			item := licenses[key] | ||||
| 			sort.Slice(item, func(i, j int) bool { | ||||
| 				if item[i].LicenseName < item[j].LicenseName { | ||||
| 					return true | ||||
| 				} | ||||
| 				if item[i].LicenseName > item[j].LicenseName { | ||||
| 					return false | ||||
| 				} | ||||
| 				return item[i].ComponentName < item[j].ComponentName | ||||
| 			}) | ||||
| 			scanReport.LicensesTable = append(scanReport.LicensesTable, Licenses{ | ||||
| 				PolicyViolationName: key, | ||||
| 				Values:              item, | ||||
| 			}) | ||||
| 		} | ||||
| 		sort.Slice(scanReport.LicensesTable, func(i, j int) bool { | ||||
| 			return scanReport.LicensesTable[i].PolicyViolationName < scanReport.LicensesTable[j].PolicyViolationName | ||||
| 		}) | ||||
|  | ||||
| 		// OtherViolationsTable sort & copy | ||||
| 		for key := range otherViolations { | ||||
| 			item := otherViolations[key] | ||||
| 			sort.Slice(item, func(i, j int) bool { | ||||
| 				return item[i].ComponentName < item[j].ComponentName | ||||
| 			}) | ||||
| 			scanReport.OtherViolationsTable = append(scanReport.OtherViolationsTable, OtherViolations{ | ||||
| 				PolicyViolationName: key, | ||||
| 				Values:              item, | ||||
| 			}) | ||||
| 		} | ||||
| 		sort.Slice(scanReport.OtherViolationsTable, func(i, j int) bool { | ||||
| 			return scanReport.OtherViolationsTable[i].PolicyViolationName < scanReport.OtherViolationsTable[j].PolicyViolationName | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	tmpl, err := template.New("report").Parse(rapidReportMdTemplate) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.New("failed to create Markdown report template err:" + err.Error()) | ||||
| 	} | ||||
| 	buf := new(bytes.Buffer) | ||||
| 	err = tmpl.Execute(buf, scanReport) | ||||
| 	if err != nil { | ||||
| 		return nil, errors.New("failed to create Markdown report template err:" + err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	return buf, nil | ||||
| } | ||||
|  | ||||
| // getScore extracts score or severity from error message | ||||
| func getScore(message, key string) string { | ||||
| 	indx := strings.Index(message, key) | ||||
| 	if indx == -1 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	var result string | ||||
| 	var notFirstSpace bool | ||||
| 	for _, s := range message[indx+len(key):] { | ||||
| 		if s == ' ' && notFirstSpace { | ||||
| 			break | ||||
| 		} | ||||
| 		notFirstSpace = true | ||||
| 		result = result + string(s) | ||||
| 	} | ||||
| 	return strings.Trim(result, " ") | ||||
| } | ||||
|  | ||||
| // scoreLogicSort sorts two scores | ||||
| func scoreLogicSort(iStr, jStr string) bool { | ||||
| 	if strings.Contains(iStr, "10.0") { | ||||
| 		return true | ||||
| 	} else if strings.Contains(jStr, "10.0") { | ||||
| 		return false | ||||
| 	} | ||||
| 	if iStr >= jStr { | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										249
									
								
								pkg/reporting/pullRequestReport_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								pkg/reporting/pullRequestReport_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| package reporting | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| // Testing createMarkdownReport function | ||||
| func TestCreateMarkdownReport(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		testName       string | ||||
| 		components     *Components | ||||
| 		expectedErr    error | ||||
| 		expectedReport string | ||||
| 	}{ | ||||
|  | ||||
| 		{ | ||||
| 			testName: "Vulnerabilities were found", | ||||
| 			components: &Components{ | ||||
| 				{ | ||||
| 					ComponentName:       "qs -  QS Querystring", | ||||
| 					ComponentVersion:    "5.2.1", | ||||
| 					ComponentIdentifier: "npmjs:qs/5.2.1", | ||||
| 					ViolatingPolicyNames: []string{ | ||||
| 						"High Vulnerability Security Issue", | ||||
| 					}, | ||||
| 					PolicyViolationVulnerabilities: []PolicyViolationVulnerability{ | ||||
| 						{ | ||||
| 							Name:                 "CVE-2017-1000048", | ||||
| 							ViolatingPolicyNames: []string{"High Vulnerability Security Issue"}, | ||||
| 							WarningMessage:       "", | ||||
| 							ErrorMessage: "Component qs -  QS Querystring version 5.2.1 with ID npmjs:qs/5.2.1 violates policy" + | ||||
| 								" High Vulnerability Security Issue: found vulnerability CVE-2017-1000048 with severity HIGH and CVSS score 7.5", | ||||
| 							Meta: Meta{ | ||||
| 								Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2017-1000048", | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 					PolicyViolationLicenses: nil, | ||||
| 					WarningMessage:          "", | ||||
| 					ErrorMessage:            "", | ||||
| 				}, | ||||
| 				{ | ||||
| 					ComponentName:       "Lodash", | ||||
| 					ComponentVersion:    "4.17.10", | ||||
| 					ComponentIdentifier: "npmjs:lodash/4.17.10", | ||||
| 					ViolatingPolicyNames: []string{ | ||||
| 						"High Vulnerability Security Issue", | ||||
| 						"Test High Severity Vuln Filter", | ||||
| 						"OutdatedFOSSLibraries", | ||||
| 					}, | ||||
| 					PolicyViolationVulnerabilities: []PolicyViolationVulnerability{ | ||||
| 						{ | ||||
| 							Name: "CVE-2019-10744", | ||||
| 							ViolatingPolicyNames: []string{ | ||||
| 								"High Vulnerability Security Issue", | ||||
| 								"Test High Severity Vuln Filter", | ||||
| 							}, | ||||
| 							WarningMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test High Severity Vuln " + | ||||
| 								"Filter: found vulnerability CVE-2019-10744 with severity CRITICAL and CVSS score 9.1", | ||||
| 							ErrorMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy High Vulnerability " + | ||||
| 								"Security Issue: found vulnerability CVE-2019-10744 with severity CRITICAL and CVSS score 9.1", | ||||
| 							Meta: Meta{ | ||||
| 								Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2019-10744"}, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name: "CVE-2020-8203", | ||||
| 							ViolatingPolicyNames: []string{ | ||||
| 								"High Vulnerability Security Issue", | ||||
| 								"Test High Severity Vuln Filter", | ||||
| 							}, | ||||
| 							WarningMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test " + | ||||
| 								"High Severity Vuln Filter: found vulnerability CVE-2020-8203 with severity HIGH and CVSS score 7.4", | ||||
| 							ErrorMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test High Severity Vuln Filter: " + | ||||
| 								"found vulnerability CVE-2020-8203 with severity HIGH and CVSS score 7.4", | ||||
| 							Meta: Meta{ | ||||
| 								Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2020-8203", | ||||
| 							}, | ||||
| 						}, | ||||
| 						{ | ||||
| 							Name: "BDSA-2019-3842", | ||||
| 							ViolatingPolicyNames: []string{ | ||||
| 								"High Vulnerability Security Issue", | ||||
| 								"Test High Severity Vuln Filter", | ||||
| 							}, | ||||
| 							WarningMessage: "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy Test High Severity Vuln Filter: found vulnerability BDSA-2019-3842 with severity HIGH and CVSS score 7.1", | ||||
| 							ErrorMessage:   "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy High Vulnerability Security Issue: found vulnerability BDSA-2019-3842 with severity HIGH and CVSS score 7.1", | ||||
| 							Meta: Meta{ | ||||
| 								Href: "https://sap-staging.app.blackduck.com/api/vulnerabilities/BDSA-2019-3842", | ||||
| 							}, | ||||
| 						}, | ||||
| 					}, | ||||
| 					PolicyViolationLicenses: nil, | ||||
| 					WarningMessage:          "Component Lodash version 4.17.10 with ID npmjs:lodash/4.17.10 violates policy OutdatedFOSSLibraries", | ||||
| 					ErrorMessage:            "", | ||||
| 				}, | ||||
| 				{ | ||||
| 					ComponentName:       "Chalk", | ||||
| 					ComponentVersion:    "1.1.3", | ||||
| 					ComponentIdentifier: "npmjs:chalk/1.1.3", | ||||
| 					ViolatingPolicyNames: []string{ | ||||
| 						"OutdatedFOSSLibraries", | ||||
| 					}, | ||||
| 					PolicyViolationVulnerabilities: nil, | ||||
| 					PolicyViolationLicenses:        nil, | ||||
| 					WarningMessage:                 "Component Chalk version 1.1.3 with ID npmjs:chalk/1.1.3 violates policy OutdatedFOSSLibraries", | ||||
| 					ErrorMessage:                   "", | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedReport: "\n##  :x: OSS related checks failed\n ### :clipboard: Policies violated by added OSS components\n " + | ||||
| 				"<table>\n <tr><td><b>Component name</b></td><td><b>High Vulnerability Security Issue</b></td><td><b>OutdatedFOSSLibraries</b></td><td><b>" + | ||||
| 				"Test High Severity Vuln Filter</b></td></tr>\n <tr><td>Chalk 1.1.3 (npmjs:chalk/1.1.3)</td><td>0</td><td>1</td><td>0</td></tr><tr><td>Lodash " + | ||||
| 				"4.17.10 (npmjs:lodash/4.17.10)</td><td>3</td><td>1</td><td>3</td></tr><tr><td>qs -  QS Querystring 5.2.1 " + | ||||
| 				"(npmjs:qs/5.2.1)</td><td>1</td><td>0</td><td>0</td></tr>\n </table>\n\n<details><summary>\n\n<h3> 4 Policy " + | ||||
| 				"Violations of High Vulnerability Security Issue </h3> \n</summary>\n\t<table>\n\t\t<tr><td><b>Vulnerability ID</b></td><td><b>Vulnerability" + | ||||
| 				" Score</b></td><td><b>Component Name</b></td></tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2019-10744\"> CVE-2019-10744 </a> </td><td>9.1 CRITICAL</td><td>Lodash 4.17.10 " + | ||||
| 				"(npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2017-1000048\"> " + | ||||
| 				"CVE-2017-1000048 </a> </td><td>7.5 HIGH</td><td>qs -  QS Querystring 5.2.1 (npmjs:qs/5.2.1)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> " + | ||||
| 				"<a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2020-8203\"> CVE-2020-8203 </a> </td><td>7.4 HIGH</td><td>Lodash " + | ||||
| 				"4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/BDSA-2019-3842\"> " + | ||||
| 				"BDSA-2019-3842 </a> </td><td>7.1 HIGH</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t</table>\n</details>\n<details><summary>\n\n<h3> " + | ||||
| 				"3 Policy Violations of Test High Severity Vuln Filter </h3> \n</summary>\n\t<table>\n\t\t<tr><td><b>Vulnerability ID</b></td><td><b>Vulnerability " + | ||||
| 				"Score</b></td><td><b>Component Name</b></td></tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2019-10744\"> " + | ||||
| 				"CVE-2019-10744 </a> </td><td>9.1 CRITICAL</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> " + | ||||
| 				"<a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/CVE-2020-8203\"> CVE-2020-8203 </a> </td><td>7.4 " + | ||||
| 				"HIGH</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <a href=\"https://sap-staging.app.blackduck.com/api/vulnerabilities/BDSA-2019-3842\"> " + | ||||
| 				"BDSA-2019-3842 </a> </td><td>7.1 HIGH</td><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td>\n\t\t\t</tr>\n\t\t</table>\n</details>\n<details><summary>\n\n<h3> " + | ||||
| 				"2 Policy Violations of OutdatedFOSSLibraries </h3> \n</summary>\n\t<table>\n\t\t<tr><td><b>Component Name</b></td></tr>\n\t\t<tr><td>Chalk 1.1.3 " + | ||||
| 				"(npmjs:chalk/1.1.3)</td></tr>\n\t\t<tr><td>Lodash 4.17.10 (npmjs:lodash/4.17.10)</td></tr>\n\t\t</table>\n</details>\n\n", | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "No vulnerabilities && successful build", | ||||
| 			components: &Components{}, | ||||
| 			expectedReport: "\n## :heavy_check_mark: OSS related checks passed successfully\n ### :clipboard: OSS related checks executed by Black Duck " + | ||||
| 				"- rapid scan passed successfully.\n" + | ||||
| 				" <a href=\"https://community.synopsys.com/s/document-item?bundleId=integrations-detect&topicId=downloadingandrunning%2Frapidscan.html&_LANG=enus\">" + | ||||
| 				"<h3>RAPID SCAN</h3> </a>\n\n\n", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, c := range testCases { | ||||
| 		t.Run(c.testName, func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
|  | ||||
| 			buf, err := createMarkdownReport(c.components) | ||||
|  | ||||
| 			assert.Equal(t, c.expectedErr, err) | ||||
| 			assert.Equal(t, c.expectedReport, buf.String()) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Testing getScore function | ||||
| func TestGetScore(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		testName string | ||||
| 		message  string | ||||
| 		key      string | ||||
| 		expected string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			testName: "Score 7.5", | ||||
| 			message: "Component qs -  QS Querystring version 5.2.1 with ID npmjs:qs/5.2.1 violates policy High " + | ||||
| 				"Vulnerability Security Issue: found vulnerability CVE-2017-1000048 with severity HIGH and CVSS score 7.5", | ||||
| 			key:      "score", | ||||
| 			expected: "7.5", | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName: "CRITICAL severity", | ||||
| 			message: "Component minimist version 0.0.8 with ID npmjs:minimist/0.0.8 violates policy High " + | ||||
| 				"Vulnerability Security Issue: found vulnerability CVE-2021-44906 with severity CRITICAL and CVSS score 9.8", | ||||
| 			key:      "severity", | ||||
| 			expected: "CRITICAL", | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName: "No severity", | ||||
| 			message: "Component minimist version 0.0.8 with ID npmjs:minimist/0.0.8 violates policy High " + | ||||
| 				"Vulnerability Security Issue: found vulnerability CVE-2021-44906 with CVSS score 9.8", | ||||
| 			key:      "severity", | ||||
| 			expected: "", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, c := range testCases { | ||||
| 		t.Run(c.testName, func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
|  | ||||
| 			got := getScore(c.message, c.key) | ||||
| 			assert.Equal(t, c.expected, got) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Testing scoreLogicSort function | ||||
| func TestScoreLogicSort(t *testing.T) { | ||||
| 	t.Parallel() | ||||
|  | ||||
| 	testCases := []struct { | ||||
| 		testName   string | ||||
| 		leftScore  string | ||||
| 		rightScore string | ||||
| 		expected   bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			testName:   "left score is higher", | ||||
| 			leftScore:  "8.8 HIGH", | ||||
| 			rightScore: "8.1 HIGH", | ||||
| 			expected:   true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "right score is higher", | ||||
| 			leftScore:  "7.9 HIGH", | ||||
| 			rightScore: "9.3 CRITICAL", | ||||
| 			expected:   false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "left score equals 10.0", | ||||
| 			leftScore:  "10.0 CRITICAL", | ||||
| 			rightScore: "8.1 HIGH", | ||||
| 			expected:   true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "right score equals 10.0", | ||||
| 			leftScore:  "7.9 HIGH", | ||||
| 			rightScore: "10.0 CRITICAL", | ||||
| 			expected:   false, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "both scores equal 10.0", | ||||
| 			leftScore:  "10.0 CRITICAL", | ||||
| 			rightScore: "10.0 CRITICAL", | ||||
| 			expected:   true, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, c := range testCases { | ||||
| 		t.Run(c.testName, func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
|  | ||||
| 			got := scoreLogicSort(c.leftScore, c.rightScore) | ||||
| 			assert.Equal(t, c.expected, got) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user