2020-09-18 11:54:45 +02:00
package cmd
import (
2020-11-02 09:51:58 +02:00
"path/filepath"
"testing"
"time"
2020-09-18 11:54:45 +02:00
"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"
)
type whitesourceCoordinatesMock struct {
GroupID string
ArtifactID string
Version string
}
type whitesourceUtilsMock struct {
2020-10-20 09:49:26 +02:00
* ws . ScanUtilsMock
2020-09-29 12:44:31 +02:00
coordinates whitesourceCoordinatesMock
usedBuildTool string
usedBuildDescriptorFile string
usedOptions versioning . Options
2020-09-18 11:54:45 +02:00
}
2020-09-29 12:44:31 +02:00
func ( w * whitesourceUtilsMock ) GetArtifactCoordinates ( buildTool , buildDescriptorFile string ,
options * versioning . Options ) ( versioning . Coordinates , error ) {
w . usedBuildTool = buildTool
w . usedBuildDescriptorFile = buildDescriptorFile
w . usedOptions = * options
2020-09-18 11:54:45 +02:00
return w . coordinates , nil
}
2020-09-29 12:44:31 +02:00
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
}
2020-09-18 11:54:45 +02:00
func newWhitesourceUtilsMock ( ) * whitesourceUtilsMock {
return & whitesourceUtilsMock {
2020-10-20 09:49:26 +02:00
ScanUtilsMock : & ws . ScanUtilsMock {
FilesMock : & mock . FilesMock { } ,
ExecMockRunner : & mock . ExecMockRunner { } ,
} ,
2020-09-18 11:54:45 +02:00
coordinates : whitesourceCoordinatesMock {
GroupID : "mock-group-id" ,
ArtifactID : "mock-artifact-id" ,
Version : "1.0.42" ,
} ,
}
}
func TestResolveProjectIdentifiers ( t * testing . T ) {
2020-09-29 12:44:31 +02:00
t . Parallel ( )
2020-09-18 11:54:45 +02:00
t . Run ( "happy path" , func ( t * testing . T ) {
// init
config := ScanOptions {
2020-09-29 12:44:31 +02:00
BuildTool : "mta" ,
BuildDescriptorFile : "my-mta.yml" ,
VersioningModel : "major" ,
ProductName : "mock-product" ,
M2Path : "m2/path" ,
ProjectSettingsFile : "project-settings.xml" ,
GlobalSettingsFile : "global-settings.xml" ,
2020-09-18 11:54:45 +02:00
}
utilsMock := newWhitesourceUtilsMock ( )
2020-10-20 09:49:26 +02:00
systemMock := ws . NewSystemMock ( "ignored" )
2020-09-29 12:44:31 +02:00
scan := newWhitesourceScan ( & config )
2020-09-18 11:54:45 +02:00
// test
2020-09-29 12:44:31 +02:00
err := resolveProjectIdentifiers ( & config , scan , utilsMock , systemMock )
2020-09-18 11:54:45 +02:00
// assert
if assert . NoError ( t , err ) {
2020-10-20 09:49:26 +02:00
assert . Equal ( t , "mock-group-id-mock-artifact-id" , scan . AggregateProjectName )
2020-09-18 11:54:45 +02:00
assert . Equal ( t , "1" , config . ProductVersion )
assert . Equal ( t , "mock-product-token" , config . ProductToken )
2020-09-29 12:44:31 +02:00
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" ,
2020-10-20 09:49:26 +02:00
ProjectName : "mock-project" ,
2020-09-29 12:44:31 +02:00
}
utilsMock := newWhitesourceUtilsMock ( )
2020-10-20 09:49:26 +02:00
systemMock := ws . NewSystemMock ( "ignored" )
2020-09-29 12:44:31 +02:00
scan := newWhitesourceScan ( & config )
// test
err := resolveProjectIdentifiers ( & config , scan , utilsMock , systemMock )
// assert
if assert . NoError ( t , err ) {
2020-10-20 09:49:26 +02:00
assert . Equal ( t , "mock-project" , scan . AggregateProjectName )
2020-09-29 12:44:31 +02:00
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 )
2020-09-18 11:54:45 +02:00
}
} )
t . Run ( "product not found" , func ( t * testing . T ) {
// init
config := ScanOptions {
2020-09-29 12:44:31 +02:00
BuildTool : "mta" ,
2020-09-18 11:54:45 +02:00
VersioningModel : "major" ,
ProductName : "does-not-exist" ,
}
utilsMock := newWhitesourceUtilsMock ( )
2020-10-20 09:49:26 +02:00
systemMock := ws . NewSystemMock ( "ignored" )
2020-09-29 12:44:31 +02:00
scan := newWhitesourceScan ( & config )
2020-09-18 11:54:45 +02:00
// test
2020-09-29 12:44:31 +02:00
err := resolveProjectIdentifiers ( & config , scan , utilsMock , systemMock )
2020-09-18 11:54:45 +02:00
// assert
assert . EqualError ( t , err , "no product with name 'does-not-exist' found in Whitesource" )
} )
2020-10-29 10:21:01 +02:00
t . Run ( "product not found, created from pipeline" , func ( t * testing . T ) {
// init
config := ScanOptions {
BuildTool : "mta" ,
CreateProductFromPipeline : true ,
EmailAddressesOfInitialProductAdmins : [ ] string { "user1@domain.org" , "user2@domain.org" } ,
VersioningModel : "major" ,
ProductName : "created-by-pipeline" ,
}
utilsMock := newWhitesourceUtilsMock ( )
systemMock := ws . NewSystemMock ( "ignored" )
scan := newWhitesourceScan ( & config )
// test
err := resolveProjectIdentifiers ( & config , scan , utilsMock , systemMock )
// assert
assert . NoError ( t , err )
assert . Len ( t , systemMock . Products , 2 )
assert . Equal ( t , "created-by-pipeline" , systemMock . Products [ 1 ] . Name )
assert . Equal ( t , "mock-product-token-1" , config . ProductToken )
} )
2020-09-18 11:54:45 +02:00
}
2020-11-10 10:09:51 +02:00
func TestRunWhitesourceExecuteScan ( t * testing . T ) {
2020-09-18 11:54:45 +02:00
t . Parallel ( )
2020-11-10 10:09:51 +02:00
t . Run ( "fails for invalid configured project token" , func ( t * testing . T ) {
2020-09-18 11:54:45 +02:00
// init
2020-11-10 10:09:51 +02:00
config := ScanOptions {
ScanType : "unified-agent" ,
BuildDescriptorFile : "my-mta.yml" ,
VersioningModel : "major" ,
ProductName : "mock-product" ,
ProjectToken : "no-such-project-token" ,
AgentDownloadURL : "https://whitesource.com/agent.jar" ,
AgentFileName : "ua.jar" ,
2020-09-18 11:54:45 +02:00
}
2020-11-10 10:09:51 +02:00
utilsMock := newWhitesourceUtilsMock ( )
utilsMock . AddFile ( "wss-generated-file.config" , [ ] byte ( "key=value" ) )
systemMock := ws . NewSystemMock ( "ignored" )
scan := newWhitesourceScan ( & config )
cpe := whitesourceExecuteScanCommonPipelineEnvironment { }
2020-09-18 11:54:45 +02:00
// test
2020-11-10 10:09:51 +02:00
err := runWhitesourceExecuteScan ( & config , scan , utilsMock , systemMock , & cpe )
2020-09-18 11:54:45 +02:00
// assert
2020-11-10 10:09:51 +02:00
assert . EqualError ( t , err , "no project with token 'no-such-project-token' found in Whitesource" )
assert . Equal ( t , "" , config . ProjectName )
assert . Equal ( t , "" , scan . AggregateProjectName )
2020-09-18 11:54:45 +02:00
} )
2020-11-10 10:09:51 +02:00
t . Run ( "retrieves aggregate project name by configured token" , func ( t * testing . T ) {
2020-09-18 11:54:45 +02:00
// init
2020-11-10 10:09:51 +02:00
config := ScanOptions {
BuildDescriptorFile : "my-mta.yml" ,
VersioningModel : "major" ,
AgentDownloadURL : "https://whitesource.com/agent.jar" ,
ReportDirectoryName : "ws-reports" ,
VulnerabilityReportFormat : "pdf" ,
Reporting : true ,
AgentFileName : "ua.jar" ,
ProductName : "mock-product" ,
ProjectToken : "mock-project-token" ,
ScanType : "unified-agent" ,
2020-09-18 11:54:45 +02:00
}
2020-11-10 10:09:51 +02:00
utilsMock := newWhitesourceUtilsMock ( )
utilsMock . AddFile ( "wss-generated-file.config" , [ ] byte ( "key=value" ) )
lastUpdatedDate := time . Now ( ) . Format ( ws . DateTimeLayout )
2020-10-20 09:49:26 +02:00
systemMock := ws . NewSystemMock ( lastUpdatedDate )
2020-11-10 10:09:51 +02:00
scan := newWhitesourceScan ( & config )
cpe := whitesourceExecuteScanCommonPipelineEnvironment { }
2020-09-18 11:54:45 +02:00
// test
2020-11-10 10:09:51 +02:00
err := runWhitesourceExecuteScan ( & config , scan , utilsMock , systemMock , & cpe )
2020-09-18 11:54:45 +02:00
// assert
2020-11-10 10:09:51 +02:00
assert . NoError ( t , err )
// Retrieved project name is stored in scan.AggregateProjectName, but not in config.ProjectName
// in order to differentiate between aggregate-project scanning and multi-project scanning.
assert . Equal ( t , "" , config . ProjectName )
assert . Equal ( t , "mock-project" , scan . AggregateProjectName )
if assert . Len ( t , utilsMock . DownloadedFiles , 1 ) {
assert . Equal ( t , ws . DownloadedFile {
SourceURL : "https://whitesource.com/agent.jar" ,
FilePath : "ua.jar" ,
} , utilsMock . DownloadedFiles [ 0 ] )
2020-09-18 11:54:45 +02:00
}
2020-11-10 10:09:51 +02:00
if assert . Len ( t , cpe . custom . whitesourceProjectNames , 1 ) {
assert . Equal ( t , [ ] string { "mock-project - 1" } , cpe . custom . whitesourceProjectNames )
2020-09-18 11:54:45 +02:00
}
2020-11-10 10:09:51 +02:00
assert . True ( t , utilsMock . HasWrittenFile ( "ws-reports/mock-project - 1-vulnerability-report.pdf" ) )
assert . True ( t , utilsMock . HasWrittenFile ( "ws-reports/mock-project - 1-risk-report.pdf" ) )
2020-09-18 11:54:45 +02:00
} )
}
2020-10-20 09:49:26 +02:00
func TestPersistScannedProjects ( t * testing . T ) {
2020-09-29 12:44:31 +02:00
t . Parallel ( )
t . Run ( "write 1 scanned projects" , func ( t * testing . T ) {
// init
2020-11-02 09:51:58 +02:00
cpe := whitesourceExecuteScanCommonPipelineEnvironment { }
2020-09-29 12:44:31 +02:00
config := & ScanOptions { ProductVersion : "1" }
scan := newWhitesourceScan ( config )
2020-10-20 09:49:26 +02:00
_ = scan . AppendScannedProject ( "project" )
2020-09-29 12:44:31 +02:00
// test
2020-11-02 09:51:58 +02:00
persistScannedProjects ( config , scan , & cpe )
2020-09-29 12:44:31 +02:00
// assert
2020-11-02 09:51:58 +02:00
assert . Equal ( t , [ ] string { "project - 1" } , cpe . custom . whitesourceProjectNames )
2020-09-29 12:44:31 +02:00
} )
t . Run ( "write 2 scanned projects" , func ( t * testing . T ) {
// init
2020-11-02 09:51:58 +02:00
cpe := whitesourceExecuteScanCommonPipelineEnvironment { }
2020-09-29 12:44:31 +02:00
config := & ScanOptions { ProductVersion : "1" }
scan := newWhitesourceScan ( config )
2020-10-20 09:49:26 +02:00
_ = scan . AppendScannedProject ( "project-app" )
_ = scan . AppendScannedProject ( "project-db" )
2020-09-29 12:44:31 +02:00
// test
2020-11-02 09:51:58 +02:00
persistScannedProjects ( config , scan , & cpe )
2020-09-29 12:44:31 +02:00
// assert
2020-11-02 09:51:58 +02:00
assert . Equal ( t , [ ] string { "project-app - 1" , "project-db - 1" } , cpe . custom . whitesourceProjectNames )
2020-09-29 12:44:31 +02:00
} )
t . Run ( "write no projects" , func ( t * testing . T ) {
// init
2020-11-02 09:51:58 +02:00
cpe := whitesourceExecuteScanCommonPipelineEnvironment { }
2020-09-29 12:44:31 +02:00
config := & ScanOptions { ProductVersion : "1" }
scan := newWhitesourceScan ( config )
// test
2020-11-02 09:51:58 +02:00
persistScannedProjects ( config , scan , & cpe )
2020-09-29 12:44:31 +02:00
// assert
2020-11-02 09:51:58 +02:00
assert . Equal ( t , [ ] string { } , cpe . custom . whitesourceProjectNames )
2020-09-29 12:44:31 +02:00
} )
t . Run ( "write aggregated project" , func ( t * testing . T ) {
// init
2020-11-02 09:51:58 +02:00
cpe := whitesourceExecuteScanCommonPipelineEnvironment { }
2020-09-29 12:44:31 +02:00
config := & ScanOptions { ProjectName : "project" , ProductVersion : "1" }
scan := newWhitesourceScan ( config )
// test
2020-11-02 09:51:58 +02:00
persistScannedProjects ( config , scan , & cpe )
2020-09-29 12:44:31 +02:00
// assert
2020-11-02 09:51:58 +02:00
assert . Equal ( t , [ ] string { "project - 1" } , cpe . custom . whitesourceProjectNames )
2020-09-29 12:44:31 +02:00
} )
}
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 ( )
2020-10-20 09:49:26 +02:00
system := ws . NewSystemMock ( "2010-05-30 00:15:00 +0100" )
2020-09-29 12:44:31 +02:00
// 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 ( )
2020-10-20 09:49:26 +02:00
system := ws . NewSystemMock ( "2010-05-30 00:15:00 +0100" )
2020-09-29 12:44:31 +02:00
// 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 ( )
2020-10-29 10:21:01 +02:00
system := ws . NewSystemMock ( time . Now ( ) . Format ( ws . DateTimeLayout ) )
2020-09-29 12:44:31 +02:00
// test
err := checkAndReportScanResults ( config , scan , utils , system )
// assert
assert . NoError ( t , err )
2020-10-29 10:21:01 +02:00
vPath := filepath . Join ( "mock-reports" , "mock-project-vulnerability-report.txt" )
2020-09-29 12:44:31 +02:00
assert . False ( t , utils . HasWrittenFile ( vPath ) )
2020-10-29 10:21:01 +02:00
rPath := filepath . Join ( "mock-reports" , "mock-project-risk-report.pdf" )
2020-09-29 12:44:31 +02:00
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 ( )
2020-10-29 10:21:01 +02:00
system := ws . NewSystemMock ( time . Now ( ) . Format ( ws . DateTimeLayout ) )
2020-09-29 12:44:31 +02:00
// 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 ( )
2020-10-29 10:21:01 +02:00
system := ws . NewSystemMock ( time . Now ( ) . Format ( ws . DateTimeLayout ) )
2020-09-29 12:44:31 +02:00
// 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 ( )
2020-10-29 10:21:01 +02:00
system := ws . NewSystemMock ( time . Now ( ) . Format ( ws . DateTimeLayout ) )
2020-09-29 12:44:31 +02:00
// 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" )
} )
2020-09-18 11:54:45 +02:00
}