1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-12 10:55:20 +02:00
sap-jenkins-library/cmd/whitesourceExecuteScan_test.go
Kevin Hudemann 6c2f8365c1
WhiteSource: Remove performance-tests from default maven excludes (#2176)
This change fixes an issue with the default maven excludes where the step would fail when performance tests are present in the project, but are not a module in the root pom. In addition, it provides a small fix of a method where a wrong path was passed to an error.
2020-10-15 08:08:00 +02:00

1124 lines
36 KiB
Go

package cmd
import (
"encoding/json"
"fmt"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/SAP/jenkins-library/pkg/versioning"
ws "github.com/SAP/jenkins-library/pkg/whitesource"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"net/http"
"os"
"path/filepath"
"testing"
"time"
)
type whitesourceSystemMock struct {
productName string
products []ws.Product
projects []ws.Project
alerts []ws.Alert
libraries []ws.Library
riskReport []byte
vulnerabilityReport []byte
}
func (m *whitesourceSystemMock) GetProductByName(productName string) (ws.Product, error) {
for _, product := range m.products {
if product.Name == productName {
return product, nil
}
}
return ws.Product{}, fmt.Errorf("no product with name '%s' found in Whitesource", productName)
}
func (m *whitesourceSystemMock) GetProjectsMetaInfo(productToken string) ([]ws.Project, error) {
return m.projects, nil
}
func (m *whitesourceSystemMock) GetProjectToken(productToken, projectName string) (string, error) {
return "mock-project-token", nil
}
func (m *whitesourceSystemMock) GetProjectByToken(projectToken string) (ws.Project, error) {
for _, project := range m.projects {
if project.Token == projectToken {
return project, nil
}
}
return ws.Project{}, fmt.Errorf("no project with token '%s' found in Whitesource", projectToken)
}
func (m *whitesourceSystemMock) GetProjectRiskReport(projectToken string) ([]byte, error) {
return m.riskReport, nil
}
func (m *whitesourceSystemMock) GetProjectVulnerabilityReport(projectToken string, format string) ([]byte, error) {
_, err := m.GetProjectByToken(projectToken)
if err != nil {
return nil, err
}
if m.vulnerabilityReport == nil {
return nil, fmt.Errorf("no report available")
}
return m.vulnerabilityReport, nil
}
func (m *whitesourceSystemMock) GetProjectAlerts(projectToken string) ([]ws.Alert, error) {
return m.alerts, nil
}
func (m *whitesourceSystemMock) GetProjectLibraryLocations(projectToken string) ([]ws.Library, error) {
return m.libraries, nil
}
var mockLibrary = ws.Library{
Name: "mock-library",
Filename: "mock-library-file",
Version: "mock-library-version",
Project: "mock-project - 1",
}
func newWhitesourceSystemMock(lastUpdateDate string) *whitesourceSystemMock {
return &whitesourceSystemMock{
productName: "mock-product",
products: []ws.Product{
{
Name: "mock-product",
Token: "mock-product-token",
CreationDate: "last-thursday",
LastUpdateDate: lastUpdateDate,
},
},
projects: []ws.Project{
{
ID: 42,
Name: "mock-project - 1",
PluginName: "mock-plugin-name",
Token: "mock-project-token",
UploadedBy: "MrBean",
CreationDate: "last-thursday",
LastUpdateDate: lastUpdateDate,
},
},
alerts: []ws.Alert{
{
Vulnerability: ws.Vulnerability{
Name: "something severe",
Score: 5,
},
Library: mockLibrary,
Project: "mock-project - 1",
CreationDate: "last-thursday",
},
},
libraries: []ws.Library{mockLibrary},
riskReport: []byte("mock-risk-report"),
vulnerabilityReport: []byte("mock-vulnerability-report"),
}
}
type whitesourceCoordinatesMock struct {
GroupID string
ArtifactID string
Version string
}
type downloadedFile struct {
sourceURL string
filePath string
}
type npmInstall struct {
currentDir string
packageJSON []string
}
type whitesourceUtilsMock struct {
*mock.FilesMock
*mock.ExecMockRunner
coordinates whitesourceCoordinatesMock
usedBuildTool string
usedBuildDescriptorFile string
usedOptions versioning.Options
downloadedFiles []downloadedFile
npmInstalledModules []npmInstall
}
func (w *whitesourceUtilsMock) DownloadFile(url, filename string, _ http.Header, _ []*http.Cookie) error {
w.downloadedFiles = append(w.downloadedFiles, downloadedFile{sourceURL: url, filePath: filename})
return nil
}
func (w *whitesourceUtilsMock) FileOpen(name string, flag int, perm os.FileMode) (wsFile, error) {
return w.Open(name, flag, perm)
}
func (w *whitesourceUtilsMock) RemoveAll(path string) error {
// TODO: Implement in FS Mock
return nil
}
func (w *whitesourceUtilsMock) GetArtifactCoordinates(buildTool, buildDescriptorFile string,
options *versioning.Options) (versioning.Coordinates, error) {
w.usedBuildTool = buildTool
w.usedBuildDescriptorFile = buildDescriptorFile
w.usedOptions = *options
return w.coordinates, nil
}
func (w *whitesourceUtilsMock) FindPackageJSONFiles(_ *ScanOptions) ([]string, error) {
matches, _ := w.Glob("**/package.json")
return matches, nil
}
func (w *whitesourceUtilsMock) InstallAllNPMDependencies(_ *ScanOptions, packageJSONs []string) error {
w.npmInstalledModules = append(w.npmInstalledModules, npmInstall{
currentDir: w.CurrentDir,
packageJSON: packageJSONs,
})
return nil
}
const wsTimeNow = "2010-05-10 00:15:42"
func (w *whitesourceUtilsMock) Now() time.Time {
now, _ := time.Parse("2006-01-02 15:04:05", wsTimeNow)
return now
}
func newWhitesourceUtilsMock() *whitesourceUtilsMock {
return &whitesourceUtilsMock{
FilesMock: &mock.FilesMock{},
ExecMockRunner: &mock.ExecMockRunner{},
coordinates: whitesourceCoordinatesMock{
GroupID: "mock-group-id",
ArtifactID: "mock-artifact-id",
Version: "1.0.42",
},
}
}
func TestResolveProjectIdentifiers(t *testing.T) {
t.Parallel()
t.Run("happy path", func(t *testing.T) {
// init
config := ScanOptions{
BuildTool: "mta",
BuildDescriptorFile: "my-mta.yml",
VersioningModel: "major",
ProductName: "mock-product",
M2Path: "m2/path",
ProjectSettingsFile: "project-settings.xml",
GlobalSettingsFile: "global-settings.xml",
}
utilsMock := newWhitesourceUtilsMock()
systemMock := newWhitesourceSystemMock("ignored")
scan := newWhitesourceScan(&config)
// test
err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
// assert
if assert.NoError(t, err) {
assert.Equal(t, "mock-group-id-mock-artifact-id", scan.aggregateProjectName)
assert.Equal(t, "1", config.ProductVersion)
assert.Equal(t, "mock-product-token", config.ProductToken)
assert.Equal(t, "mta", utilsMock.usedBuildTool)
assert.Equal(t, "my-mta.yml", utilsMock.usedBuildDescriptorFile)
assert.Equal(t, "project-settings.xml", utilsMock.usedOptions.ProjectSettingsFile)
assert.Equal(t, "global-settings.xml", utilsMock.usedOptions.GlobalSettingsFile)
assert.Equal(t, "m2/path", utilsMock.usedOptions.M2Path)
}
})
t.Run("retrieves token for configured project name", func(t *testing.T) {
// init
config := ScanOptions{
BuildTool: "mta",
BuildDescriptorFile: "my-mta.yml",
VersioningModel: "major",
ProductName: "mock-product",
ProjectName: "mock-project - 1",
}
utilsMock := newWhitesourceUtilsMock()
systemMock := newWhitesourceSystemMock("ignored")
scan := newWhitesourceScan(&config)
// test
err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
// assert
if assert.NoError(t, err) {
assert.Equal(t, "mock-project - 1", scan.aggregateProjectName)
assert.Equal(t, "1", config.ProductVersion)
assert.Equal(t, "mock-product-token", config.ProductToken)
assert.Equal(t, "mta", utilsMock.usedBuildTool)
assert.Equal(t, "my-mta.yml", utilsMock.usedBuildDescriptorFile)
assert.Equal(t, "mock-project-token", config.ProjectToken)
}
})
t.Run("product not found", func(t *testing.T) {
// init
config := ScanOptions{
BuildTool: "mta",
VersioningModel: "major",
ProductName: "does-not-exist",
}
utilsMock := newWhitesourceUtilsMock()
systemMock := newWhitesourceSystemMock("ignored")
scan := newWhitesourceScan(&config)
// test
err := resolveProjectIdentifiers(&config, scan, utilsMock, systemMock)
// assert
assert.EqualError(t, err, "no product with name 'does-not-exist' found in Whitesource")
})
}
func TestExecuteScanUA(t *testing.T) {
t.Parallel()
t.Run("happy path UA", func(t *testing.T) {
// init
config := ScanOptions{
ScanType: "unified-agent",
OrgToken: "org-token",
UserToken: "user-token",
ProductName: "mock-product",
ProjectName: "mock-project",
ProductVersion: "product-version",
AgentDownloadURL: "https://download.ua.org/agent.jar",
AgentFileName: "unified-agent.jar",
ConfigFilePath: "ua.cfg",
M2Path: ".pipeline/m2",
}
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("wss-generated-file.config", []byte("key=value"))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// many assert
require.NoError(t, err)
content, err := utilsMock.FileRead("ua.cfg")
require.NoError(t, err)
contentAsString := string(content)
assert.Contains(t, contentAsString, "key=value\n")
assert.Contains(t, contentAsString, "gradle.aggregateModules=true\n")
assert.Contains(t, contentAsString, "maven.aggregateModules=true\n")
assert.Contains(t, contentAsString, "maven.m2RepositoryPath=.pipeline/m2\n")
assert.Contains(t, contentAsString, "excludes=")
require.Len(t, utilsMock.Calls, 4)
fmt.Printf("calls: %v\n", utilsMock.Calls)
expectedCall := mock.ExecCall{
Exec: "java",
Params: []string{
"-jar",
config.AgentFileName,
"-d", ".",
"-c", config.ConfigFilePath,
"-apiKey", config.OrgToken,
"-userKey", config.UserToken,
"-project", config.ProjectName,
"-product", config.ProductName,
"-productVersion", config.ProductVersion,
},
}
assert.Equal(t, expectedCall, utilsMock.Calls[3])
})
t.Run("UA is downloaded", func(t *testing.T) {
// init
config := ScanOptions{
ScanType: "unified-agent",
AgentDownloadURL: "https://download.ua.org/agent.jar",
AgentFileName: "unified-agent.jar",
}
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("wss-generated-file.config", []byte("dummy"))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// many assert
require.NoError(t, err)
require.Len(t, utilsMock.downloadedFiles, 1)
assert.Equal(t, "https://download.ua.org/agent.jar", utilsMock.downloadedFiles[0].sourceURL)
assert.Equal(t, "unified-agent.jar", utilsMock.downloadedFiles[0].filePath)
})
t.Run("UA is NOT downloaded", func(t *testing.T) {
// init
config := ScanOptions{
ScanType: "unified-agent",
AgentDownloadURL: "https://download.ua.org/agent.jar",
AgentFileName: "unified-agent.jar",
}
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("wss-generated-file.config", []byte("dummy"))
utilsMock.AddFile("unified-agent.jar", []byte("dummy"))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// many assert
require.NoError(t, err)
assert.Len(t, utilsMock.downloadedFiles, 0)
})
}
func TestExecuteScanNPM(t *testing.T) {
config := ScanOptions{
ScanType: "npm",
OrgToken: "org-token",
UserToken: "user-token",
ProductName: "mock-product",
ProjectName: "mock-project",
ProductVersion: "product-version",
}
t.Parallel()
t.Run("happy path NPM", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("package.json", []byte(`{"name":"my-module-name"}`))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
require.NoError(t, err)
expectedCalls := []mock.ExecCall{
{
Exec: "npm",
Params: []string{
"ls",
},
},
{
Exec: "npx",
Params: []string{
"whitesource",
"run",
},
},
}
assert.Equal(t, expectedCalls, utilsMock.Calls)
assert.True(t, utilsMock.HasWrittenFile(whiteSourceConfig))
assert.True(t, utilsMock.HasRemovedFile(whiteSourceConfig))
})
t.Run("no NPM modules", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
assert.EqualError(t, err, "found no NPM modules to scan. Configured excludes: []")
assert.Len(t, utilsMock.Calls, 0)
assert.False(t, utilsMock.HasWrittenFile(whiteSourceConfig))
})
t.Run("package.json needs name", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("package.json", []byte(`{"key":"value"}`))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
assert.EqualError(t, err, "failed to scan NPM module 'package.json': the file 'package.json' must configure a name")
})
t.Run("npm ls fails", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("package.json", []byte(`{"name":"my-module-name"}`))
utilsMock.AddFile(filepath.Join("app", "package.json"), []byte(`{"name":"my-app-module-name"}`))
utilsMock.AddFile("package-lock.json", []byte("dummy"))
utilsMock.ShouldFailOnCommand = make(map[string]error)
utilsMock.ShouldFailOnCommand["npm ls"] = fmt.Errorf("mock failure")
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
assert.NoError(t, err)
expectedNpmInstalls := []npmInstall{
{currentDir: "app", packageJSON: []string{"package.json"}},
{currentDir: "", packageJSON: []string{"package.json"}},
}
assert.Equal(t, expectedNpmInstalls, utilsMock.npmInstalledModules)
assert.True(t, utilsMock.HasRemovedFile("package-lock.json"))
})
}
func TestExecuteScanMaven(t *testing.T) {
t.Parallel()
t.Run("maven modules are aggregated", func(t *testing.T) {
// init
const pomXML = `<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>my-artifact-id</artifactId>
<packaging>jar</packaging>
</project>
`
config := ScanOptions{
ScanType: "maven",
OrgToken: "org-token",
UserToken: "user-token",
ProductName: "mock-product",
ProjectName: "mock-project",
ProductVersion: "product-version",
}
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("pom.xml", []byte(pomXML))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
require.NoError(t, err)
expectedCalls := []mock.ExecCall{
{
Exec: "mvn",
Params: []string{
"--file",
"pom.xml",
"-Dorg.whitesource.orgToken=org-token",
"-Dorg.whitesource.product=mock-product",
"-Dorg.whitesource.checkPolicies=true",
"-Dorg.whitesource.failOnError=true",
"-Dorg.whitesource.aggregateProjectName=mock-project",
"-Dorg.whitesource.aggregateModules=true",
"-Dorg.whitesource.userKey=user-token",
"-Dorg.whitesource.productVersion=product-version",
"-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn",
"--batch-mode",
"org.whitesource:whitesource-maven-plugin:19.5.1:update",
},
},
}
assert.Equal(t, expectedCalls, utilsMock.Calls)
})
t.Run("maven modules are separate projects", func(t *testing.T) {
// init
const rootPomXML = `<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>my-artifact-id</artifactId>
<packaging>jar</packaging>
<modules>
<module>sub</module>
</modules>
</project>
`
const modulePomXML = `<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>my-artifact-id-sub</artifactId>
<packaging>jar</packaging>
</project>
`
config := ScanOptions{
ScanType: "maven",
OrgToken: "org-token",
UserToken: "user-token",
ProductName: "mock-product",
ProductVersion: "product-version",
}
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("pom.xml", []byte(rootPomXML))
utilsMock.AddFile(filepath.Join("sub", "pom.xml"), []byte(modulePomXML))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
require.NoError(t, err)
expectedCalls := []mock.ExecCall{
{
Exec: "mvn",
Params: []string{
"--file",
"pom.xml",
"-Dorg.whitesource.orgToken=org-token",
"-Dorg.whitesource.product=mock-product",
"-Dorg.whitesource.checkPolicies=true",
"-Dorg.whitesource.failOnError=true",
"-Dorg.whitesource.userKey=user-token",
"-Dorg.whitesource.productVersion=product-version",
"-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn",
"--batch-mode",
"org.whitesource:whitesource-maven-plugin:19.5.1:update",
},
},
}
assert.Equal(t, expectedCalls, utilsMock.Calls)
require.Len(t, scan.scannedProjects, 2)
_, existsRoot := scan.scannedProjects["my-artifact-id - product-version"]
_, existsModule := scan.scannedProjects["my-artifact-id-sub - product-version"]
assert.True(t, existsRoot)
assert.True(t, existsModule)
})
t.Run("pom.xml does not exist", func(t *testing.T) {
// init
config := ScanOptions{
ScanType: "maven",
OrgToken: "org-token",
UserToken: "user-token",
ProductName: "mock-product",
ProductVersion: "product-version",
}
utilsMock := newWhitesourceUtilsMock()
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
assert.EqualError(t, err,
"for scanning with type 'maven', the file 'pom.xml' must exist in the project root")
assert.Len(t, utilsMock.Calls, 0)
})
}
func TestExecuteScanMTA(t *testing.T) {
const pomXML = `<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>my-artifact-id</artifactId>
<packaging>jar</packaging>
</project>
`
config := ScanOptions{
BuildTool: "mta",
OrgToken: "org-token",
UserToken: "user-token",
ProductName: "mock-product",
ProjectName: "mock-project",
ProductVersion: "product-version",
}
t.Parallel()
t.Run("happy path MTA", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("pom.xml", []byte(pomXML))
utilsMock.AddFile("package.json", []byte(`{"name":"my-module-name"}`))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
require.NoError(t, err)
expectedCalls := []mock.ExecCall{
{
Exec: "mvn",
Params: []string{
"--file",
"pom.xml",
"-Dorg.whitesource.orgToken=org-token",
"-Dorg.whitesource.product=mock-product",
"-Dorg.whitesource.checkPolicies=true",
"-Dorg.whitesource.failOnError=true",
"-Dorg.whitesource.aggregateProjectName=mock-project",
"-Dorg.whitesource.aggregateModules=true",
"-Dorg.whitesource.userKey=user-token",
"-Dorg.whitesource.productVersion=product-version",
"-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn",
"--batch-mode",
"org.whitesource:whitesource-maven-plugin:19.5.1:update",
},
},
{
Exec: "npm",
Params: []string{
"ls",
},
},
{
Exec: "npx",
Params: []string{
"whitesource",
"run",
},
},
}
assert.Equal(t, expectedCalls, utilsMock.Calls)
assert.True(t, utilsMock.HasWrittenFile(whiteSourceConfig))
assert.True(t, utilsMock.HasRemovedFile(whiteSourceConfig))
assert.Equal(t, expectedCalls, utilsMock.Calls)
})
t.Run("MTA with only maven modules", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("pom.xml", []byte(pomXML))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
require.NoError(t, err)
expectedCalls := []mock.ExecCall{
{
Exec: "mvn",
Params: []string{
"--file",
"pom.xml",
"-Dorg.whitesource.orgToken=org-token",
"-Dorg.whitesource.product=mock-product",
"-Dorg.whitesource.checkPolicies=true",
"-Dorg.whitesource.failOnError=true",
"-Dorg.whitesource.aggregateProjectName=mock-project",
"-Dorg.whitesource.aggregateModules=true",
"-Dorg.whitesource.userKey=user-token",
"-Dorg.whitesource.productVersion=product-version",
"-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn",
"--batch-mode",
"org.whitesource:whitesource-maven-plugin:19.5.1:update",
},
},
}
assert.Equal(t, expectedCalls, utilsMock.Calls)
assert.False(t, utilsMock.HasWrittenFile(whiteSourceConfig))
assert.Equal(t, expectedCalls, utilsMock.Calls)
})
t.Run("MTA with only NPM modules", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
utilsMock.AddFile("package.json", []byte(`{"name":"my-module-name"}`))
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
require.NoError(t, err)
expectedCalls := []mock.ExecCall{
{
Exec: "npm",
Params: []string{
"ls",
},
},
{
Exec: "npx",
Params: []string{
"whitesource",
"run",
},
},
}
assert.Equal(t, expectedCalls, utilsMock.Calls)
assert.True(t, utilsMock.HasWrittenFile(whiteSourceConfig))
assert.True(t, utilsMock.HasRemovedFile(whiteSourceConfig))
assert.Equal(t, expectedCalls, utilsMock.Calls)
})
t.Run("MTA with neither Maven nor NPM modules results in error", func(t *testing.T) {
// init
utilsMock := newWhitesourceUtilsMock()
scan := newWhitesourceScan(&config)
// test
err := executeScan(&config, scan, utilsMock)
// assert
assert.EqualError(t, err, "neither Maven nor NPM modules found, no scan performed")
})
}
func TestBlockUntilProjectIsUpdated(t *testing.T) {
t.Parallel()
t.Run("already new enough", func(t *testing.T) {
// init
nowString := "2010-05-30 00:15:00 +0100"
now, err := time.Parse(whitesourceDateTimeLayout, nowString)
if err != nil {
t.Fatalf(err.Error())
}
lastUpdatedDate := "2010-05-30 00:15:01 +0100"
systemMock := newWhitesourceSystemMock(lastUpdatedDate)
// test
err = blockUntilProjectIsUpdated(systemMock.projects[0].Token, systemMock, now, 2*time.Second, 1*time.Second, 2*time.Second)
// assert
assert.NoError(t, err)
})
t.Run("timeout while polling", func(t *testing.T) {
// init
nowString := "2010-05-30 00:15:00 +0100"
now, err := time.Parse(whitesourceDateTimeLayout, nowString)
if err != nil {
t.Fatalf(err.Error())
}
lastUpdatedDate := "2010-05-30 00:07:00 +0100"
systemMock := newWhitesourceSystemMock(lastUpdatedDate)
// test
err = blockUntilProjectIsUpdated(systemMock.projects[0].Token, systemMock, now, 2*time.Second, 1*time.Second, 1*time.Second)
// assert
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "timeout while waiting")
}
})
t.Run("timeout while polling, no update time", func(t *testing.T) {
// init
nowString := "2010-05-30 00:15:00 +0100"
now, err := time.Parse(whitesourceDateTimeLayout, nowString)
if err != nil {
t.Fatalf(err.Error())
}
systemMock := newWhitesourceSystemMock("")
// test
err = blockUntilProjectIsUpdated(systemMock.projects[0].Token, systemMock, now, 2*time.Second, 1*time.Second, 1*time.Second)
// assert
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "timeout while waiting")
}
})
}
func TestDownloadReports(t *testing.T) {
t.Parallel()
t.Run("happy path", func(t *testing.T) {
// init
config := &ScanOptions{
ProjectToken: "mock-project-token",
ProjectName: "mock-project",
ReportDirectoryName: "report-dir",
VulnerabilityReportFormat: "txt",
}
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock("2010-05-30 00:15:00 +0100")
scan := newWhitesourceScan(config)
// test
paths, err := downloadReports(config, scan, utils, system)
// assert
if assert.NoError(t, err) && assert.Len(t, paths, 2) {
vPath := filepath.Join("report-dir", "mock-project-vulnerability-report.txt")
assert.True(t, utils.HasWrittenFile(vPath))
vContent, _ := utils.FileRead(vPath)
assert.Equal(t, []byte("mock-vulnerability-report"), vContent)
rPath := filepath.Join("report-dir", "mock-project-risk-report.pdf")
assert.True(t, utils.HasWrittenFile(rPath))
rContent, _ := utils.FileRead(rPath)
assert.Equal(t, []byte("mock-risk-report"), rContent)
}
})
t.Run("invalid project token", func(t *testing.T) {
// init
config := &ScanOptions{
ProjectToken: "<invalid>",
ProjectName: "mock-project",
}
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock("2010-05-30 00:15:00 +0100")
scan := newWhitesourceScan(config)
// test
paths, err := downloadReports(config, scan, utils, system)
// assert
assert.EqualError(t, err, "no project with token '<invalid>' found in Whitesource")
assert.Nil(t, paths)
})
t.Run("multiple scanned projects", func(t *testing.T) {
// init
config := &ScanOptions{
ReportDirectoryName: "report-dir",
VulnerabilityReportFormat: "txt",
}
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock("2010-05-30 00:15:00 +0100")
scan := newWhitesourceScan(config)
scan.init()
scan.scannedProjects["mock-project"] = ws.Project{
Name: "mock-project",
Token: "mock-project-token",
}
// test
paths, err := downloadReports(config, scan, utils, system)
// assert
if assert.NoError(t, err) && assert.Len(t, paths, 2) {
vPath := filepath.Join("report-dir", "mock-project-vulnerability-report.txt")
assert.True(t, utils.HasWrittenFile(vPath))
vContent, _ := utils.FileRead(vPath)
assert.Equal(t, []byte("mock-vulnerability-report"), vContent)
rPath := filepath.Join("report-dir", "mock-project-risk-report.pdf")
assert.True(t, utils.HasWrittenFile(rPath))
rContent, _ := utils.FileRead(rPath)
assert.Equal(t, []byte("mock-risk-report"), rContent)
}
})
}
func TestWriteWhitesourceConfigJSON(t *testing.T) {
config := &ScanOptions{
OrgToken: "org-token",
UserToken: "user-token",
ProductName: "mock-product",
ProjectName: "mock-project",
ProductToken: "mock-product-token",
ProductVersion: "42",
}
expected := make(map[string]interface{})
expected["apiKey"] = "org-token"
expected["userKey"] = "user-token"
expected["checkPolicies"] = true
expected["productName"] = "mock-product"
expected["projectName"] = "mock-project"
expected["productToken"] = "mock-product-token"
expected["productVer"] = "42"
expected["devDep"] = true
expected["ignoreNpmLsErrors"] = true
t.Parallel()
t.Run("write config from scratch", func(t *testing.T) {
// init
utils := newWhitesourceUtilsMock()
// test
err := writeWhitesourceConfigJSON(config, utils, true, true)
// assert
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(whiteSourceConfig)) {
contents, _ := utils.FileRead(whiteSourceConfig)
actual := make(map[string]interface{})
_ = json.Unmarshal(contents, &actual)
assert.Equal(t, expected, actual)
}
})
t.Run("extend and merge config", func(t *testing.T) {
// init
initial := make(map[string]interface{})
initial["checkPolicies"] = false
initial["productName"] = "mock-product"
initial["productVer"] = "41"
initial["unknown"] = "preserved"
encoded, _ := json.Marshal(initial)
utils := newWhitesourceUtilsMock()
utils.AddFile(whiteSourceConfig, encoded)
// test
err := writeWhitesourceConfigJSON(config, utils, true, true)
// assert
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(whiteSourceConfig)) {
contents, _ := utils.FileRead(whiteSourceConfig)
actual := make(map[string]interface{})
_ = json.Unmarshal(contents, &actual)
mergedExpected := expected
mergedExpected["unknown"] = "preserved"
assert.Equal(t, mergedExpected, actual)
}
})
t.Run("extend and merge config, omit productToken", func(t *testing.T) {
// init
initial := make(map[string]interface{})
initial["checkPolicies"] = false
initial["productName"] = "mock-product"
initial["productVer"] = "41"
initial["unknown"] = "preserved"
initial["projectToken"] = "mock-project-token"
encoded, _ := json.Marshal(initial)
utils := newWhitesourceUtilsMock()
utils.AddFile(whiteSourceConfig, encoded)
// test
err := writeWhitesourceConfigJSON(config, utils, true, true)
// assert
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(whiteSourceConfig)) {
contents, _ := utils.FileRead(whiteSourceConfig)
actual := make(map[string]interface{})
_ = json.Unmarshal(contents, &actual)
mergedExpected := expected
mergedExpected["unknown"] = "preserved"
mergedExpected["projectToken"] = "mock-project-token"
delete(mergedExpected, "productToken")
assert.Equal(t, mergedExpected, actual)
}
})
}
func TestPersisScannedProjects(t *testing.T) {
resource := filepath.Join(".pipeline", "commonPipelineEnvironment", "custom", "whitesourceProjectNames")
t.Parallel()
t.Run("write 1 scanned projects", func(t *testing.T) {
// init
config := &ScanOptions{ProductVersion: "1"}
utils := newWhitesourceUtilsMock()
scan := newWhitesourceScan(config)
_ = scan.appendScannedProject("project")
// test
err := persistScannedProjects(config, scan, utils)
// assert
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(resource)) {
contents, _ := utils.FileRead(resource)
assert.Equal(t, "project - 1", string(contents))
}
})
t.Run("write 2 scanned projects", func(t *testing.T) {
// init
config := &ScanOptions{ProductVersion: "1"}
utils := newWhitesourceUtilsMock()
scan := newWhitesourceScan(config)
_ = scan.appendScannedProject("project-app")
_ = scan.appendScannedProject("project-db")
// test
err := persistScannedProjects(config, scan, utils)
// assert
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(resource)) {
contents, _ := utils.FileRead(resource)
assert.Equal(t, "project-app - 1,project-db - 1", string(contents))
}
})
t.Run("write no projects", func(t *testing.T) {
// init
config := &ScanOptions{ProductVersion: "1"}
utils := newWhitesourceUtilsMock()
scan := newWhitesourceScan(config)
// test
err := persistScannedProjects(config, scan, utils)
// assert
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(resource)) {
contents, _ := utils.FileRead(resource)
assert.Equal(t, "", string(contents))
}
})
t.Run("write aggregated project", func(t *testing.T) {
// init
config := &ScanOptions{ProjectName: "project", ProductVersion: "1"}
utils := newWhitesourceUtilsMock()
scan := newWhitesourceScan(config)
// test
err := persistScannedProjects(config, scan, utils)
// assert
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(resource)) {
contents, _ := utils.FileRead(resource)
assert.Equal(t, "project - 1", string(contents))
}
})
}
func TestAggregateVersionWideLibraries(t *testing.T) {
t.Parallel()
t.Run("happy path", func(t *testing.T) {
// init
config := &ScanOptions{
ProductToken: "mock-product-token",
ProductVersion: "1",
ReportDirectoryName: "mock-reports",
}
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock("2010-05-30 00:15:00 +0100")
// test
err := aggregateVersionWideLibraries(config, utils, system)
// assert
resource := filepath.Join("mock-reports", "libraries-20100510-001542.csv")
if assert.NoError(t, err) && assert.True(t, utils.HasWrittenFile(resource)) {
contents, _ := utils.FileRead(resource)
asString := string(contents)
assert.Equal(t, "Library Name, Project Name\nmock-library, mock-project\n", asString)
}
})
}
func TestAggregateVersionWideVulnerabilities(t *testing.T) {
t.Parallel()
t.Run("happy path", func(t *testing.T) {
// init
config := &ScanOptions{
ProductToken: "mock-product-token",
ProductVersion: "1",
ReportDirectoryName: "mock-reports",
}
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock("2010-05-30 00:15:00 +0100")
// test
err := aggregateVersionWideVulnerabilities(config, utils, system)
// assert
resource := filepath.Join("mock-reports", "project-names-aggregated.txt")
assert.NoError(t, err)
if assert.True(t, utils.HasWrittenFile(resource)) {
contents, _ := utils.FileRead(resource)
asString := string(contents)
assert.Equal(t, "mock-project - 1\n", asString)
}
reportSheet := filepath.Join("mock-reports", "vulnerabilities-20100510-001542.xlsx")
sheetContents, err := utils.FileRead(reportSheet)
assert.NoError(t, err)
assert.NotEmpty(t, sheetContents)
})
}
func TestCheckAndReportScanResults(t *testing.T) {
t.Parallel()
t.Run("no reports requested", func(t *testing.T) {
// init
config := &ScanOptions{
ProductToken: "mock-product-token",
ProjectToken: "mock-project-token",
ProductVersion: "1",
ReportDirectoryName: "mock-reports",
}
scan := newWhitesourceScan(config)
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock(time.Now().Format(whitesourceDateTimeLayout))
// test
err := checkAndReportScanResults(config, scan, utils, system)
// assert
assert.NoError(t, err)
vPath := filepath.Join("report-dir", "mock-project-vulnerability-report.txt")
assert.False(t, utils.HasWrittenFile(vPath))
rPath := filepath.Join("report-dir", "mock-project-risk-report.pdf")
assert.False(t, utils.HasWrittenFile(rPath))
})
t.Run("check vulnerabilities - invalid limit", func(t *testing.T) {
// init
config := &ScanOptions{
SecurityVulnerabilities: true,
CvssSeverityLimit: "invalid",
}
scan := newWhitesourceScan(config)
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock(time.Now().Format(whitesourceDateTimeLayout))
// test
err := checkAndReportScanResults(config, scan, utils, system)
// assert
assert.EqualError(t, err, "failed to parse parameter cvssSeverityLimit (invalid) as floating point number: strconv.ParseFloat: parsing \"invalid\": invalid syntax")
})
t.Run("check vulnerabilities - limit not hit", func(t *testing.T) {
// init
config := &ScanOptions{
ProductToken: "mock-product-token",
ProjectToken: "mock-project-token",
ProductVersion: "1",
ReportDirectoryName: "mock-reports",
SecurityVulnerabilities: true,
CvssSeverityLimit: "6.0",
}
scan := newWhitesourceScan(config)
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock(time.Now().Format(whitesourceDateTimeLayout))
// test
err := checkAndReportScanResults(config, scan, utils, system)
// assert
assert.NoError(t, err)
})
t.Run("check vulnerabilities - limit exceeded", func(t *testing.T) {
// init
config := &ScanOptions{
ProductToken: "mock-product-token",
ProjectName: "mock-project - 1",
ProjectToken: "mock-project-token",
ProductVersion: "1",
ReportDirectoryName: "mock-reports",
SecurityVulnerabilities: true,
CvssSeverityLimit: "4",
}
scan := newWhitesourceScan(config)
utils := newWhitesourceUtilsMock()
system := newWhitesourceSystemMock(time.Now().Format(whitesourceDateTimeLayout))
// test
err := checkAndReportScanResults(config, scan, utils, system)
// assert
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")
})
}