github.com/jaylevin/jenkins-library@v1.230.4/cmd/whitesourceExecuteScan_test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"testing"
     7  	"time"
     8  
     9  	piperGithub "github.com/SAP/jenkins-library/pkg/github"
    10  	"github.com/SAP/jenkins-library/pkg/mock"
    11  	"github.com/SAP/jenkins-library/pkg/piperenv"
    12  	"github.com/SAP/jenkins-library/pkg/piperutils"
    13  	"github.com/SAP/jenkins-library/pkg/reporting"
    14  	"github.com/SAP/jenkins-library/pkg/versioning"
    15  	ws "github.com/SAP/jenkins-library/pkg/whitesource"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  type whitesourceUtilsMock struct {
    20  	*ws.ScanUtilsMock
    21  	coordinates             versioning.Coordinates
    22  	usedBuildTool           string
    23  	usedBuildDescriptorFile string
    24  	usedOptions             versioning.Options
    25  }
    26  
    27  func (w *whitesourceUtilsMock) GetArtifactCoordinates(buildTool, buildDescriptorFile string,
    28  	options *versioning.Options) (versioning.Coordinates, error) {
    29  	w.usedBuildTool = buildTool
    30  	w.usedBuildDescriptorFile = buildDescriptorFile
    31  	w.usedOptions = *options
    32  	return w.coordinates, nil
    33  }
    34  
    35  func (w *whitesourceUtilsMock) CreateIssue(ghCreateIssueOptions *piperGithub.CreateIssueOptions) error {
    36  	return nil
    37  }
    38  
    39  const wsTimeNow = "2010-05-10 00:15:42"
    40  
    41  func (w *whitesourceUtilsMock) Now() time.Time {
    42  	now, _ := time.Parse("2006-01-02 15:04:05", wsTimeNow)
    43  	return now
    44  }
    45  
    46  func newWhitesourceUtilsMock() *whitesourceUtilsMock {
    47  	return &whitesourceUtilsMock{
    48  		ScanUtilsMock: &ws.ScanUtilsMock{
    49  			FilesMock:      &mock.FilesMock{},
    50  			ExecMockRunner: &mock.ExecMockRunner{},
    51  		},
    52  		coordinates: versioning.Coordinates{
    53  			GroupID:    "mock-group-id",
    54  			ArtifactID: "mock-artifact-id",
    55  			Version:    "1.0.42",
    56  		},
    57  	}
    58  }
    59  
    60  func TestNewWhitesourceUtils(t *testing.T) {
    61  	t.Parallel()
    62  	config := ScanOptions{}
    63  	utils := newWhitesourceUtils(&config)
    64  
    65  	assert.NotNil(t, utils.Client)
    66  	assert.NotNil(t, utils.Command)
    67  	assert.NotNil(t, utils.Files)
    68  }
    69  
    70  func TestRunWhitesourceExecuteScan(t *testing.T) {
    71  	t.Parallel()
    72  	t.Run("fails for invalid configured project token", func(t *testing.T) {
    73  		// init
    74  		config := ScanOptions{
    75  			BuildDescriptorFile: "my-mta.yml",
    76  			VersioningModel:     "major",
    77  			ProductName:         "mock-product",
    78  			ProjectToken:        "no-such-project-token",
    79  			AgentDownloadURL:    "https://whitesource.com/agent.jar",
    80  			AgentFileName:       "ua.jar",
    81  		}
    82  		utilsMock := newWhitesourceUtilsMock()
    83  		utilsMock.AddFile("wss-generated-file.config", []byte("key=value"))
    84  		systemMock := ws.NewSystemMock("ignored")
    85  		scan := newWhitesourceScan(&config)
    86  		cpe := whitesourceExecuteScanCommonPipelineEnvironment{}
    87  		influx := whitesourceExecuteScanInflux{}
    88  		// test
    89  		err := runWhitesourceExecuteScan(&config, scan, utilsMock, systemMock, &cpe, &influx)
    90  		// assert
    91  		assert.EqualError(t, err, "failed to resolve and aggregate project name: failed to get project by token: no project with token 'no-such-project-token' found in Whitesource")
    92  		assert.Equal(t, "", config.ProjectName)
    93  		assert.Equal(t, "", scan.AggregateProjectName)
    94  	})
    95  	t.Run("retrieves aggregate project name by configured token", func(t *testing.T) {
    96  		// init
    97  		config := ScanOptions{
    98  			BuildDescriptorFile:       "my-mta.yml",
    99  			VersioningModel:           "major",
   100  			AgentDownloadURL:          "https://whitesource.com/agent.jar",
   101  			VulnerabilityReportFormat: "pdf",
   102  			Reporting:                 true,
   103  			AgentFileName:             "ua.jar",
   104  			ProductName:               "mock-product",
   105  			ProjectToken:              "mock-project-token",
   106  		}
   107  		utilsMock := newWhitesourceUtilsMock()
   108  		utilsMock.AddFile("wss-generated-file.config", []byte("key=value"))
   109  		lastUpdatedDate := time.Now().Format(ws.DateTimeLayout)
   110  		systemMock := ws.NewSystemMock(lastUpdatedDate)
   111  		systemMock.Alerts = []ws.Alert{}
   112  		scan := newWhitesourceScan(&config)
   113  		cpe := whitesourceExecuteScanCommonPipelineEnvironment{}
   114  		influx := whitesourceExecuteScanInflux{}
   115  		// test
   116  		err := runWhitesourceExecuteScan(&config, scan, utilsMock, systemMock, &cpe, &influx)
   117  		// assert
   118  		assert.NoError(t, err)
   119  		// Retrieved project name is stored in scan.AggregateProjectName, but not in config.ProjectName
   120  		// in order to differentiate between aggregate-project scanning and multi-project scanning.
   121  		assert.Equal(t, "", config.ProjectName)
   122  		assert.Equal(t, "mock-project", scan.AggregateProjectName)
   123  		if assert.Len(t, utilsMock.DownloadedFiles, 1) {
   124  			assert.Equal(t, ws.DownloadedFile{
   125  				SourceURL: "https://whitesource.com/agent.jar",
   126  				FilePath:  "ua.jar",
   127  			}, utilsMock.DownloadedFiles[0])
   128  		}
   129  		if assert.Len(t, cpe.custom.whitesourceProjectNames, 1) {
   130  			assert.Equal(t, []string{"mock-project - 1"}, cpe.custom.whitesourceProjectNames)
   131  		}
   132  		assert.True(t, utilsMock.HasWrittenFile(filepath.Join(ws.ReportsDirectory, "mock-project - 1-vulnerability-report.pdf")))
   133  		assert.True(t, utilsMock.HasWrittenFile(filepath.Join(ws.ReportsDirectory, "mock-project - 1-vulnerability-report.pdf")))
   134  	})
   135  }
   136  
   137  func TestCheckAndReportScanResults(t *testing.T) {
   138  	t.Parallel()
   139  	t.Run("no reports requested", func(t *testing.T) {
   140  		// init
   141  		config := &ScanOptions{
   142  			ProductToken: "mock-product-token",
   143  			ProjectToken: "mock-project-token",
   144  			Version:      "1",
   145  		}
   146  		scan := newWhitesourceScan(config)
   147  		utils := newWhitesourceUtilsMock()
   148  		system := ws.NewSystemMock(time.Now().Format(ws.DateTimeLayout))
   149  		influx := whitesourceExecuteScanInflux{}
   150  		// test
   151  		_, err := checkAndReportScanResults(config, scan, utils, system, &influx)
   152  		// assert
   153  		assert.NoError(t, err)
   154  		vPath := filepath.Join(ws.ReportsDirectory, "mock-project-vulnerability-report.txt")
   155  		assert.False(t, utils.HasWrittenFile(vPath))
   156  		rPath := filepath.Join(ws.ReportsDirectory, "mock-project-risk-report.pdf")
   157  		assert.False(t, utils.HasWrittenFile(rPath))
   158  	})
   159  	t.Run("check vulnerabilities - invalid limit", func(t *testing.T) {
   160  		// init
   161  		config := &ScanOptions{
   162  			SecurityVulnerabilities: true,
   163  			CvssSeverityLimit:       "invalid",
   164  		}
   165  		scan := newWhitesourceScan(config)
   166  		utils := newWhitesourceUtilsMock()
   167  		system := ws.NewSystemMock(time.Now().Format(ws.DateTimeLayout))
   168  		influx := whitesourceExecuteScanInflux{}
   169  		// test
   170  		_, err := checkAndReportScanResults(config, scan, utils, system, &influx)
   171  		// assert
   172  		assert.EqualError(t, err, "failed to parse parameter cvssSeverityLimit (invalid) as floating point number: strconv.ParseFloat: parsing \"invalid\": invalid syntax")
   173  	})
   174  	t.Run("check vulnerabilities - limit not hit", func(t *testing.T) {
   175  		// init
   176  		config := &ScanOptions{
   177  			ProductToken:            "mock-product-token",
   178  			ProjectToken:            "mock-project-token",
   179  			Version:                 "1",
   180  			SecurityVulnerabilities: true,
   181  			CvssSeverityLimit:       "6.0",
   182  		}
   183  		scan := newWhitesourceScan(config)
   184  		utils := newWhitesourceUtilsMock()
   185  		system := ws.NewSystemMock(time.Now().Format(ws.DateTimeLayout))
   186  		influx := whitesourceExecuteScanInflux{}
   187  		// test
   188  		_, err := checkAndReportScanResults(config, scan, utils, system, &influx)
   189  		// assert
   190  		assert.NoError(t, err)
   191  	})
   192  	t.Run("check vulnerabilities - limit exceeded", func(t *testing.T) {
   193  		// init
   194  		config := &ScanOptions{
   195  			ProductToken:            "mock-product-token",
   196  			ProjectName:             "mock-project - 1",
   197  			ProjectToken:            "mock-project-token",
   198  			Version:                 "1",
   199  			SecurityVulnerabilities: true,
   200  			CvssSeverityLimit:       "4",
   201  		}
   202  		scan := newWhitesourceScan(config)
   203  		utils := newWhitesourceUtilsMock()
   204  		system := ws.NewSystemMock(time.Now().Format(ws.DateTimeLayout))
   205  		influx := whitesourceExecuteScanInflux{}
   206  		// test
   207  		_, err := checkAndReportScanResults(config, scan, utils, system, &influx)
   208  		// assert
   209  		assert.EqualError(t, err, "1 Open Source Software Security vulnerabilities with CVSS score greater or equal to 4.0 detected in project mock-project - 1")
   210  	})
   211  }
   212  
   213  func TestResolveProjectIdentifiers(t *testing.T) {
   214  	t.Parallel()
   215  	t.Run("success", func(t *testing.T) {
   216  		// init
   217  		config := ScanOptions{
   218  			BuildTool:           "mta",
   219  			BuildDescriptorFile: "my-mta.yml",
   220  			VersioningModel:     "major",
   221  			ProductName:         "mock-product",
   222  			M2Path:              "m2/path",
   223  			ProjectSettingsFile: "project-settings.xml",
   224  			GlobalSettingsFile:  "global-settings.xml",
   225  		}
   226  		utilsMock := newWhitesourceUtilsMock()
   227  		systemMock := ws.NewSystemMock("ignored")
   228  		scan := newWhitesourceScan(&config)
   229  		// test
   230  		err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
   231  		// assert
   232  		if assert.NoError(t, err) {
   233  			assert.Equal(t, "mock-group-id-mock-artifact-id", scan.AggregateProjectName)
   234  			assert.Equal(t, "1", config.Version)
   235  			assert.Equal(t, "mock-product-token", config.ProductToken)
   236  			assert.Equal(t, "mta", utilsMock.usedBuildTool)
   237  			assert.Equal(t, "my-mta.yml", utilsMock.usedBuildDescriptorFile)
   238  			assert.Equal(t, "project-settings.xml", utilsMock.usedOptions.ProjectSettingsFile)
   239  			assert.Equal(t, "global-settings.xml", utilsMock.usedOptions.GlobalSettingsFile)
   240  			assert.Equal(t, "m2/path", utilsMock.usedOptions.M2Path)
   241  		}
   242  	})
   243  	t.Run("success - with version from default", func(t *testing.T) {
   244  		// init
   245  		config := ScanOptions{
   246  			BuildTool:           "mta",
   247  			BuildDescriptorFile: "my-mta.yml",
   248  			Version:             "1.2.3-20200101",
   249  			VersioningModel:     "major",
   250  			ProductName:         "mock-product",
   251  			M2Path:              "m2/path",
   252  			ProjectSettingsFile: "project-settings.xml",
   253  			GlobalSettingsFile:  "global-settings.xml",
   254  		}
   255  		utilsMock := newWhitesourceUtilsMock()
   256  		systemMock := ws.NewSystemMock("ignored")
   257  		scan := newWhitesourceScan(&config)
   258  		// test
   259  		err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
   260  		// assert
   261  		if assert.NoError(t, err) {
   262  			assert.Equal(t, "mock-group-id-mock-artifact-id", scan.AggregateProjectName)
   263  			assert.Equal(t, "1", config.Version)
   264  			assert.Equal(t, "mock-product-token", config.ProductToken)
   265  			assert.Equal(t, "mta", utilsMock.usedBuildTool)
   266  			assert.Equal(t, "my-mta.yml", utilsMock.usedBuildDescriptorFile)
   267  			assert.Equal(t, "project-settings.xml", utilsMock.usedOptions.ProjectSettingsFile)
   268  			assert.Equal(t, "global-settings.xml", utilsMock.usedOptions.GlobalSettingsFile)
   269  			assert.Equal(t, "m2/path", utilsMock.usedOptions.M2Path)
   270  		}
   271  	})
   272  	t.Run("success - with custom scan version", func(t *testing.T) {
   273  		// init
   274  		config := ScanOptions{
   275  			BuildTool:           "mta",
   276  			BuildDescriptorFile: "my-mta.yml",
   277  			CustomScanVersion:   "2.3.4",
   278  			VersioningModel:     "major",
   279  			ProductName:         "mock-product",
   280  			M2Path:              "m2/path",
   281  			ProjectSettingsFile: "project-settings.xml",
   282  			GlobalSettingsFile:  "global-settings.xml",
   283  		}
   284  		utilsMock := newWhitesourceUtilsMock()
   285  		systemMock := ws.NewSystemMock("ignored")
   286  		scan := newWhitesourceScan(&config)
   287  		// test
   288  		err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
   289  		// assert
   290  		if assert.NoError(t, err) {
   291  			assert.Equal(t, "mock-group-id-mock-artifact-id", scan.AggregateProjectName)
   292  			assert.Equal(t, "2.3.4", config.Version)
   293  			assert.Equal(t, "mock-product-token", config.ProductToken)
   294  			assert.Equal(t, "mta", utilsMock.usedBuildTool)
   295  			assert.Equal(t, "my-mta.yml", utilsMock.usedBuildDescriptorFile)
   296  			assert.Equal(t, "project-settings.xml", utilsMock.usedOptions.ProjectSettingsFile)
   297  			assert.Equal(t, "global-settings.xml", utilsMock.usedOptions.GlobalSettingsFile)
   298  			assert.Equal(t, "m2/path", utilsMock.usedOptions.M2Path)
   299  		}
   300  	})
   301  	t.Run("retrieves token for configured project name", func(t *testing.T) {
   302  		// init
   303  		config := ScanOptions{
   304  			BuildTool:           "mta",
   305  			BuildDescriptorFile: "my-mta.yml",
   306  			VersioningModel:     "major",
   307  			ProductName:         "mock-product",
   308  			ProjectName:         "mock-project",
   309  		}
   310  		utilsMock := newWhitesourceUtilsMock()
   311  		systemMock := ws.NewSystemMock("ignored")
   312  		scan := newWhitesourceScan(&config)
   313  		// test
   314  		err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
   315  		// assert
   316  		if assert.NoError(t, err) {
   317  			assert.Equal(t, "mock-project", scan.AggregateProjectName)
   318  			assert.Equal(t, "1", config.Version)
   319  			assert.Equal(t, "mock-product-token", config.ProductToken)
   320  			assert.Equal(t, "mta", utilsMock.usedBuildTool)
   321  			assert.Equal(t, "my-mta.yml", utilsMock.usedBuildDescriptorFile)
   322  			assert.Equal(t, "mock-project-token", config.ProjectToken)
   323  		}
   324  	})
   325  	t.Run("product not found", func(t *testing.T) {
   326  		// init
   327  		config := ScanOptions{
   328  			BuildTool:       "mta",
   329  			VersioningModel: "major",
   330  			ProductName:     "does-not-exist",
   331  		}
   332  		utilsMock := newWhitesourceUtilsMock()
   333  		systemMock := ws.NewSystemMock("ignored")
   334  		scan := newWhitesourceScan(&config)
   335  		// test
   336  		err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
   337  		// assert
   338  		assert.EqualError(t, err, "error resolving product token: failed to get product by name: no product with name 'does-not-exist' found in Whitesource")
   339  	})
   340  	t.Run("product not found, created from pipeline", func(t *testing.T) {
   341  		// init
   342  		config := ScanOptions{
   343  			BuildTool:                            "mta",
   344  			CreateProductFromPipeline:            true,
   345  			EmailAddressesOfInitialProductAdmins: []string{"user1@domain.org", "user2@domain.org"},
   346  			VersioningModel:                      "major",
   347  			ProductName:                          "created-by-pipeline",
   348  		}
   349  		utilsMock := newWhitesourceUtilsMock()
   350  		systemMock := ws.NewSystemMock("ignored")
   351  		scan := newWhitesourceScan(&config)
   352  		// test
   353  		err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
   354  		// assert
   355  		assert.NoError(t, err)
   356  		assert.Len(t, systemMock.Products, 2)
   357  		assert.Equal(t, "created-by-pipeline", systemMock.Products[1].Name)
   358  		assert.Equal(t, "mock-product-token-1", config.ProductToken)
   359  	})
   360  }
   361  
   362  func TestCheckPolicyViolations(t *testing.T) {
   363  	t.Parallel()
   364  
   365  	t.Run("success - no violations", func(t *testing.T) {
   366  		config := ScanOptions{ProductName: "mock-product", Version: "1"}
   367  		scan := newWhitesourceScan(&config)
   368  		scan.AppendScannedProject("testProject1")
   369  		systemMock := ws.NewSystemMock("ignored")
   370  		systemMock.Alerts = []ws.Alert{}
   371  		utilsMock := newWhitesourceUtilsMock()
   372  		reportPaths := []piperutils.Path{
   373  			{Target: filepath.Join("whitesource", "report1.pdf")},
   374  			{Target: filepath.Join("whitesource", "report2.pdf")},
   375  		}
   376  		influx := whitesourceExecuteScanInflux{}
   377  
   378  		path, err := checkPolicyViolations(&config, scan, systemMock, utilsMock, reportPaths, &influx)
   379  		assert.NoError(t, err)
   380  		assert.Equal(t, filepath.Join(ws.ReportsDirectory, "whitesource-ip.json"), path.Target)
   381  
   382  		fileContent, _ := utilsMock.FileRead(path.Target)
   383  		content := string(fileContent)
   384  		assert.Contains(t, content, `"policyViolations":0`)
   385  		assert.Contains(t, content, `"reports":["report1.pdf","report2.pdf"]`)
   386  
   387  		exists, err := utilsMock.FileExists(filepath.Join(reporting.StepReportDirectory, "whitesourceExecuteScan_ip_2d3120020f3f46393a54575a7f6f5675ad536721.json"))
   388  		assert.True(t, exists)
   389  
   390  	})
   391  
   392  	t.Run("success - no reports", func(t *testing.T) {
   393  		config := ScanOptions{}
   394  		scan := newWhitesourceScan(&config)
   395  		scan.AppendScannedProject("testProject1")
   396  		systemMock := ws.NewSystemMock("ignored")
   397  		systemMock.Alerts = []ws.Alert{}
   398  		utilsMock := newWhitesourceUtilsMock()
   399  		reportPaths := []piperutils.Path{}
   400  		influx := whitesourceExecuteScanInflux{}
   401  
   402  		path, err := checkPolicyViolations(&config, scan, systemMock, utilsMock, reportPaths, &influx)
   403  		assert.NoError(t, err)
   404  
   405  		fileContent, _ := utilsMock.FileRead(path.Target)
   406  		content := string(fileContent)
   407  		assert.Contains(t, content, `reports":[]`)
   408  	})
   409  
   410  	t.Run("error - policy violations", func(t *testing.T) {
   411  		config := ScanOptions{}
   412  		scan := newWhitesourceScan(&config)
   413  		scan.AppendScannedProject("testProject1")
   414  		systemMock := ws.NewSystemMock("ignored")
   415  		systemMock.Alerts = []ws.Alert{
   416  			{Vulnerability: ws.Vulnerability{Name: "policyVul1"}},
   417  			{Vulnerability: ws.Vulnerability{Name: "policyVul2"}},
   418  		}
   419  		utilsMock := newWhitesourceUtilsMock()
   420  		reportPaths := []piperutils.Path{
   421  			{Target: "report1.pdf"},
   422  			{Target: "report2.pdf"},
   423  		}
   424  		influx := whitesourceExecuteScanInflux{}
   425  
   426  		path, err := checkPolicyViolations(&config, scan, systemMock, utilsMock, reportPaths, &influx)
   427  		assert.Contains(t, fmt.Sprint(err), "2 policy violation(s) found")
   428  
   429  		fileContent, _ := utilsMock.FileRead(path.Target)
   430  		content := string(fileContent)
   431  		assert.Contains(t, content, `"policyViolations":2`)
   432  		assert.Contains(t, content, `"reports":["report1.pdf","report2.pdf"]`)
   433  	})
   434  
   435  	t.Run("error - get alerts", func(t *testing.T) {
   436  		config := ScanOptions{}
   437  		scan := newWhitesourceScan(&config)
   438  		scan.AppendScannedProject("testProject1")
   439  		systemMock := ws.NewSystemMock("ignored")
   440  		systemMock.AlertError = fmt.Errorf("failed to read alerts")
   441  		utilsMock := newWhitesourceUtilsMock()
   442  		reportPaths := []piperutils.Path{}
   443  		influx := whitesourceExecuteScanInflux{}
   444  
   445  		_, err := checkPolicyViolations(&config, scan, systemMock, utilsMock, reportPaths, &influx)
   446  		assert.Contains(t, fmt.Sprint(err), "failed to retrieve project policy alerts from WhiteSource")
   447  	})
   448  
   449  	t.Run("error - write file", func(t *testing.T) {
   450  		config := ScanOptions{}
   451  		scan := newWhitesourceScan(&config)
   452  		scan.AppendScannedProject("testProject1")
   453  		systemMock := ws.NewSystemMock("ignored")
   454  		systemMock.Alerts = []ws.Alert{}
   455  		utilsMock := newWhitesourceUtilsMock()
   456  		utilsMock.FileWriteError = fmt.Errorf("failed to write file")
   457  		reportPaths := []piperutils.Path{}
   458  		influx := whitesourceExecuteScanInflux{}
   459  
   460  		_, err := checkPolicyViolations(&config, scan, systemMock, utilsMock, reportPaths, &influx)
   461  		assert.Contains(t, fmt.Sprint(err), "failed to write policy violation report:")
   462  	})
   463  
   464  	t.Run("failed to write json report", func(t *testing.T) {
   465  		config := ScanOptions{ProductName: "mock-product", Version: "1"}
   466  		scan := newWhitesourceScan(&config)
   467  		scan.AppendScannedProject("testProject1")
   468  		systemMock := ws.NewSystemMock("ignored")
   469  		systemMock.Alerts = []ws.Alert{}
   470  		utilsMock := newWhitesourceUtilsMock()
   471  		utilsMock.FileWriteErrors = map[string]error{
   472  			filepath.Join(reporting.StepReportDirectory, "whitesourceExecuteScan_ip_2d3120020f3f46393a54575a7f6f5675ad536721.json"): fmt.Errorf("write error"),
   473  		}
   474  		reportPaths := []piperutils.Path{}
   475  		influx := whitesourceExecuteScanInflux{}
   476  
   477  		_, err := checkPolicyViolations(&config, scan, systemMock, utilsMock, reportPaths, &influx)
   478  		assert.Contains(t, fmt.Sprint(err), "failed to write json report")
   479  	})
   480  }
   481  
   482  func TestCheckSecurityViolations(t *testing.T) {
   483  	t.Parallel()
   484  
   485  	t.Run("success - non-aggregated", func(t *testing.T) {
   486  		config := ScanOptions{
   487  			CvssSeverityLimit: "7",
   488  		}
   489  		scan := newWhitesourceScan(&config)
   490  		scan.AppendScannedProject("testProject1")
   491  		systemMock := ws.NewSystemMock("ignored")
   492  		systemMock.Alerts = []ws.Alert{
   493  			{Vulnerability: ws.Vulnerability{Name: "vul1", CVSS3Score: 6.0}},
   494  		}
   495  		utilsMock := newWhitesourceUtilsMock()
   496  		influx := whitesourceExecuteScanInflux{}
   497  
   498  		reportPaths, err := checkSecurityViolations(&config, scan, systemMock, utilsMock, &influx)
   499  		assert.NoError(t, err)
   500  		fileContent, err := utilsMock.FileRead(reportPaths[0].Target)
   501  		assert.NoError(t, err)
   502  		assert.True(t, len(fileContent) > 0)
   503  	})
   504  
   505  	t.Run("success - aggregated", func(t *testing.T) {
   506  		config := ScanOptions{
   507  			CvssSeverityLimit: "7",
   508  			ProjectToken:      "theProjectToken",
   509  		}
   510  		scan := newWhitesourceScan(&config)
   511  		systemMock := ws.NewSystemMock("ignored")
   512  		systemMock.Alerts = []ws.Alert{
   513  			{Vulnerability: ws.Vulnerability{Name: "vul1", CVSS3Score: 6.0}},
   514  		}
   515  		utilsMock := newWhitesourceUtilsMock()
   516  		influx := whitesourceExecuteScanInflux{}
   517  
   518  		reportPaths, err := checkSecurityViolations(&config, scan, systemMock, utilsMock, &influx)
   519  		assert.NoError(t, err)
   520  		assert.Equal(t, 0, len(reportPaths))
   521  	})
   522  
   523  	t.Run("error - wrong limit", func(t *testing.T) {
   524  		config := ScanOptions{CvssSeverityLimit: "x"}
   525  		scan := newWhitesourceScan(&config)
   526  		systemMock := ws.NewSystemMock("ignored")
   527  		utilsMock := newWhitesourceUtilsMock()
   528  		influx := whitesourceExecuteScanInflux{}
   529  
   530  		_, err := checkSecurityViolations(&config, scan, systemMock, utilsMock, &influx)
   531  		assert.Contains(t, fmt.Sprint(err), "failed to parse parameter cvssSeverityLimit")
   532  
   533  	})
   534  
   535  	t.Run("error - non-aggregated", func(t *testing.T) {
   536  		config := ScanOptions{
   537  			CvssSeverityLimit: "5",
   538  		}
   539  		scan := newWhitesourceScan(&config)
   540  		scan.AppendScannedProject("testProject1")
   541  		systemMock := ws.NewSystemMock("ignored")
   542  		systemMock.Alerts = []ws.Alert{
   543  			{Vulnerability: ws.Vulnerability{Name: "vul1", CVSS3Score: 6.0}},
   544  		}
   545  		utilsMock := newWhitesourceUtilsMock()
   546  		influx := whitesourceExecuteScanInflux{}
   547  
   548  		reportPaths, err := checkSecurityViolations(&config, scan, systemMock, utilsMock, &influx)
   549  		assert.Contains(t, fmt.Sprint(err), "1 Open Source Software Security vulnerabilities")
   550  		fileContent, err := utilsMock.FileRead(reportPaths[0].Target)
   551  		assert.NoError(t, err)
   552  		assert.True(t, len(fileContent) > 0)
   553  	})
   554  
   555  	t.Run("error - aggregated", func(t *testing.T) {
   556  		config := ScanOptions{
   557  			CvssSeverityLimit: "5",
   558  			ProjectToken:      "theProjectToken",
   559  		}
   560  		scan := newWhitesourceScan(&config)
   561  		systemMock := ws.NewSystemMock("ignored")
   562  		systemMock.Alerts = []ws.Alert{
   563  			{Vulnerability: ws.Vulnerability{Name: "vul1", CVSS3Score: 6.0}},
   564  		}
   565  		utilsMock := newWhitesourceUtilsMock()
   566  		influx := whitesourceExecuteScanInflux{}
   567  
   568  		reportPaths, err := checkSecurityViolations(&config, scan, systemMock, utilsMock, &influx)
   569  		assert.Contains(t, fmt.Sprint(err), "1 Open Source Software Security vulnerabilities")
   570  		assert.Equal(t, 0, len(reportPaths))
   571  	})
   572  }
   573  
   574  func TestCheckProjectSecurityViolations(t *testing.T) {
   575  	project := ws.Project{Name: "testProject - 1", Token: "testToken"}
   576  
   577  	t.Run("success - no alerts", func(t *testing.T) {
   578  		systemMock := ws.NewSystemMock("ignored")
   579  		systemMock.Alerts = []ws.Alert{}
   580  		influx := whitesourceExecuteScanInflux{}
   581  
   582  		severeVulnerabilities, alerts, err := checkProjectSecurityViolations(7.0, project, systemMock, &influx)
   583  		assert.NoError(t, err)
   584  		assert.Equal(t, 0, severeVulnerabilities)
   585  		assert.Equal(t, 0, len(alerts))
   586  	})
   587  
   588  	t.Run("error - some vulnerabilities", func(t *testing.T) {
   589  		systemMock := ws.NewSystemMock("ignored")
   590  		systemMock.Alerts = []ws.Alert{
   591  			{Vulnerability: ws.Vulnerability{CVSS3Score: 7}},
   592  			{Vulnerability: ws.Vulnerability{CVSS3Score: 6}},
   593  		}
   594  		influx := whitesourceExecuteScanInflux{}
   595  
   596  		severeVulnerabilities, alerts, err := checkProjectSecurityViolations(7.0, project, systemMock, &influx)
   597  		assert.Contains(t, fmt.Sprint(err), "1 Open Source Software Security vulnerabilities")
   598  		assert.Equal(t, 1, severeVulnerabilities)
   599  		assert.Equal(t, 2, len(alerts))
   600  	})
   601  
   602  	t.Run("error - WhiteSource failure", func(t *testing.T) {
   603  		systemMock := ws.NewSystemMock("ignored")
   604  		systemMock.AlertError = fmt.Errorf("failed to read alerts")
   605  		influx := whitesourceExecuteScanInflux{}
   606  
   607  		_, _, err := checkProjectSecurityViolations(7.0, project, systemMock, &influx)
   608  		assert.Contains(t, fmt.Sprint(err), "failed to retrieve project alerts from WhiteSource")
   609  	})
   610  
   611  }
   612  
   613  func TestAggregateVersionWideLibraries(t *testing.T) {
   614  	t.Parallel()
   615  	t.Run("happy path", func(t *testing.T) {
   616  		// init
   617  		config := &ScanOptions{
   618  			ProductToken: "mock-product-token",
   619  			Version:      "1",
   620  		}
   621  		utils := newWhitesourceUtilsMock()
   622  		system := ws.NewSystemMock("2010-05-30 00:15:00 +0100")
   623  		// test
   624  		err := aggregateVersionWideLibraries(config, utils, system)
   625  		// assert
   626  		resource := filepath.Join(ws.ReportsDirectory, "libraries-20100510-001542.csv")
   627  		if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(resource)) {
   628  			contents, _ := utils.FileRead(resource)
   629  			asString := string(contents)
   630  			assert.Equal(t, "Library Name, Project Name\nmock-library, mock-project\n", asString)
   631  			assert.NotEmpty(t, piperenv.GetParameter("", "whitesourceExecuteScan_reports.json"))
   632  		}
   633  	})
   634  }
   635  
   636  func TestAggregateVersionWideVulnerabilities(t *testing.T) {
   637  	t.Parallel()
   638  	t.Run("happy path", func(t *testing.T) {
   639  		// init
   640  		config := &ScanOptions{
   641  			ProductToken: "mock-product-token",
   642  			Version:      "1",
   643  		}
   644  		utils := newWhitesourceUtilsMock()
   645  		system := ws.NewSystemMock("2010-05-30 00:15:00 +0100")
   646  		// test
   647  		err := aggregateVersionWideVulnerabilities(config, utils, system)
   648  		// assert
   649  		resource := filepath.Join(ws.ReportsDirectory, "project-names-aggregated.txt")
   650  		assert.NoError(t, err)
   651  		if assert.True(t, utils.HasWrittenFile(resource)) {
   652  			contents, _ := utils.FileRead(resource)
   653  			asString := string(contents)
   654  			assert.Equal(t, "mock-project - 1\n", asString)
   655  		}
   656  		reportSheet := filepath.Join(ws.ReportsDirectory, "vulnerabilities-20100510-001542.xlsx")
   657  		sheetContents, err := utils.FileRead(reportSheet)
   658  		assert.NoError(t, err)
   659  		assert.NotEmpty(t, sheetContents)
   660  		assert.NotEmpty(t, piperenv.GetParameter("", "whitesourceExecuteScan_reports.json"))
   661  	})
   662  }
   663  
   664  func TestPersistScannedProjects(t *testing.T) {
   665  	t.Parallel()
   666  	t.Run("write 1 scanned projects", func(t *testing.T) {
   667  		// init
   668  		cpe := whitesourceExecuteScanCommonPipelineEnvironment{}
   669  		config := &ScanOptions{Version: "1"}
   670  		scan := newWhitesourceScan(config)
   671  		_ = scan.AppendScannedProject("project")
   672  		// test
   673  		persistScannedProjects(config, scan, &cpe)
   674  		// assert
   675  		assert.Equal(t, []string{"project - 1"}, cpe.custom.whitesourceProjectNames)
   676  	})
   677  	t.Run("write 2 scanned projects", func(t *testing.T) {
   678  		// init
   679  		cpe := whitesourceExecuteScanCommonPipelineEnvironment{}
   680  		config := &ScanOptions{Version: "1"}
   681  		scan := newWhitesourceScan(config)
   682  		_ = scan.AppendScannedProject("project-app")
   683  		_ = scan.AppendScannedProject("project-db")
   684  		// test
   685  		persistScannedProjects(config, scan, &cpe)
   686  		// assert
   687  		assert.Equal(t, []string{"project-app - 1", "project-db - 1"}, cpe.custom.whitesourceProjectNames)
   688  	})
   689  	t.Run("write no projects", func(t *testing.T) {
   690  		// init
   691  		cpe := whitesourceExecuteScanCommonPipelineEnvironment{}
   692  		config := &ScanOptions{Version: "1"}
   693  		scan := newWhitesourceScan(config)
   694  		// test
   695  		persistScannedProjects(config, scan, &cpe)
   696  		// assert
   697  		assert.Equal(t, []string{}, cpe.custom.whitesourceProjectNames)
   698  	})
   699  	t.Run("write aggregated project", func(t *testing.T) {
   700  		// init
   701  		cpe := whitesourceExecuteScanCommonPipelineEnvironment{}
   702  		config := &ScanOptions{ProjectName: "project", Version: "1"}
   703  		scan := newWhitesourceScan(config)
   704  		// test
   705  		persistScannedProjects(config, scan, &cpe)
   706  		// assert
   707  		assert.Equal(t, []string{"project - 1"}, cpe.custom.whitesourceProjectNames)
   708  	})
   709  }