package cmd
import (
"archive/zip"
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"time"
"github.com/bmatcuk/doublestar"
"github.com/SAP/jenkins-library/pkg/checkmarx"
"github.com/stretchr/testify/assert"
"github.com/google/go-github/v45/github"
)
type fileInfo struct {
nam string // base name of the file
siz int64 // length in bytes for regular files; system-dependent for others
mod os.FileMode // file mode bits
modtime time.Time // modification time
dir bool // abbreviation for Mode().IsDir()
syss interface{} // underlying data source (can return nil)
}
func (f *fileInfo) IsDir() bool {
return f.dir
}
func (f *fileInfo) Name() string {
return f.nam
}
func (f *fileInfo) Size() int64 {
return f.siz
}
func (f *fileInfo) ModTime() time.Time {
return f.modtime
}
func (f *fileInfo) Mode() os.FileMode {
return f.mod
}
func (f *fileInfo) Sys() interface{} {
return f.syss
}
type systemMock struct {
response interface{}
isIncremental bool
isPublic bool
forceScan bool
createProject bool
previousPName string
getPresetsCalled bool
updateProjectConfigurationCalled bool
}
func (sys *systemMock) FilterPresetByName(_ []checkmarx.Preset, presetName string) checkmarx.Preset {
if presetName == "CX_Default" {
return checkmarx.Preset{ID: 16, Name: "CX_Default", OwnerName: "16"}
}
return checkmarx.Preset{ID: 10050, Name: "SAP_JS_Default", OwnerName: "16"}
}
func (sys *systemMock) FilterPresetByID([]checkmarx.Preset, int) checkmarx.Preset {
return checkmarx.Preset{ID: 10048, Name: "SAP_Default", OwnerName: "16"}
}
func (sys *systemMock) FilterProjectByName([]checkmarx.Project, string) checkmarx.Project {
return checkmarx.Project{ID: 1, Name: "Test", TeamID: "16", IsPublic: true}
}
func (sys *systemMock) GetProjectByID(projectID int) (checkmarx.Project, error) {
if projectID == 17 {
return checkmarx.Project{ID: 17, Name: "Test_PR-17", TeamID: "16", IsPublic: true}, nil
}
return checkmarx.Project{ID: 19, Name: "Test_PR-19", TeamID: "16", IsPublic: true}, nil
}
func (sys *systemMock) GetProjectsByNameAndTeam(projectName, teamID string) ([]checkmarx.Project, error) {
if !sys.createProject || sys.previousPName == projectName {
if strings.Contains(projectName, "PR-17") {
return []checkmarx.Project{{ID: 17, Name: projectName, TeamID: teamID, IsPublic: true}}, nil
}
return []checkmarx.Project{{ID: 19, Name: projectName, TeamID: teamID, IsPublic: true}}, nil
}
if projectName == "Test" {
return []checkmarx.Project{{ID: 1, Name: projectName, TeamID: teamID, IsPublic: true}}, nil
}
sys.previousPName = projectName
return []checkmarx.Project{}, fmt.Errorf("no project error")
}
func (sys *systemMock) FilterTeamByName(_ []checkmarx.Team, teamName string) (checkmarx.Team, error) {
if teamName == "OpenSource/Cracks/16" {
return checkmarx.Team{ID: json.RawMessage(`"16"`), FullName: "OpenSource/Cracks/16"}, nil
}
return checkmarx.Team{ID: json.RawMessage(`15`), FullName: "OpenSource/Cracks/15"}, nil
}
func (sys *systemMock) FilterTeamByID(_ []checkmarx.Team, teamID json.RawMessage) checkmarx.Team {
teamIDBytes, _ := teamID.MarshalJSON()
if bytes.Equal(teamIDBytes, []byte(`"16"`)) {
return checkmarx.Team{ID: json.RawMessage(`"16"`), FullName: "OpenSource/Cracks/16"}
}
return checkmarx.Team{ID: json.RawMessage(`15`), FullName: "OpenSource/Cracks/15"}
}
func (sys *systemMock) DownloadReport(int) ([]byte, error) {
return sys.response.([]byte), nil
}
func (sys *systemMock) GetReportStatus(int) (checkmarx.ReportStatusResponse, error) {
return checkmarx.ReportStatusResponse{Status: checkmarx.ReportStatus{ID: 2, Value: "Created"}}, nil
}
func (sys *systemMock) RequestNewReport(int, string) (checkmarx.Report, error) {
return checkmarx.Report{ReportID: 17}, nil
}
func (sys *systemMock) GetResults(int) checkmarx.ResultsStatistics {
return checkmarx.ResultsStatistics{}
}
func (sys *systemMock) GetScans(int) ([]checkmarx.ScanStatus, error) {
return []checkmarx.ScanStatus{{IsIncremental: true}, {IsIncremental: true}, {IsIncremental: true}, {IsIncremental: false}}, nil
}
func (sys *systemMock) GetScanStatusAndDetail(int) (string, checkmarx.ScanStatusDetail) {
return "Finished", checkmarx.ScanStatusDetail{Stage: "Step 1 of 25", Step: "Scan something"}
}
func (sys *systemMock) ScanProject(_ int, isIncrementalV, isPublicV, forceScanV bool) (checkmarx.Scan, error) {
sys.isIncremental = isIncrementalV
sys.isPublic = isPublicV
sys.forceScan = forceScanV
return checkmarx.Scan{ID: 16}, nil
}
func (sys *systemMock) UpdateProjectConfiguration(int, int, string) error {
sys.updateProjectConfigurationCalled = true
return nil
}
func (sys *systemMock) UpdateProjectExcludeSettings(int, string, string) error {
return nil
}
func (sys *systemMock) UploadProjectSourceCode(int, string) error {
return nil
}
func (sys *systemMock) CreateProject(string, string) (checkmarx.ProjectCreateResult, error) {
return checkmarx.ProjectCreateResult{ID: 20}, nil
}
func (sys *systemMock) CreateBranch(int, string) int {
return 18
}
func (sys *systemMock) GetShortDescription(int, int) (checkmarx.ShortDescription, error) {
return checkmarx.ShortDescription{Text: "dummyText"}, nil
}
func (sys *systemMock) GetPresets() []checkmarx.Preset {
sys.getPresetsCalled = true
return []checkmarx.Preset{{ID: 10078, Name: "SAP Java Default", OwnerName: "16"}, {ID: 10048, Name: "SAP JS Default", OwnerName: "16"}, {ID: 16, Name: "CX_Default", OwnerName: "16"}}
}
func (sys *systemMock) GetProjects() ([]checkmarx.Project, error) {
return []checkmarx.Project{{ID: 15, Name: "OtherTest", TeamID: "16"}, {ID: 1, Name: "Test", TeamID: "16"}}, nil
}
func (sys *systemMock) GetTeams() []checkmarx.Team {
return []checkmarx.Team{{ID: json.RawMessage(`"16"`), FullName: "OpenSource/Cracks/16"}, {ID: json.RawMessage(`15`), FullName: "OpenSource/Cracks/15"}}
}
type systemMockForExistingProject struct {
response interface{}
isIncremental bool
isPublic bool
forceScan bool
scanProjectCalled bool
}
func (sys *systemMockForExistingProject) FilterPresetByName([]checkmarx.Preset, string) checkmarx.Preset {
return checkmarx.Preset{ID: 10050, Name: "SAP_JS_Default", OwnerName: "16"}
}
func (sys *systemMockForExistingProject) FilterPresetByID([]checkmarx.Preset, int) checkmarx.Preset {
return checkmarx.Preset{ID: 10048, Name: "SAP_Default", OwnerName: "16"}
}
func (sys *systemMockForExistingProject) FilterProjectByName([]checkmarx.Project, string) checkmarx.Project {
return checkmarx.Project{ID: 1, Name: "TestExisting", TeamID: "16", IsPublic: true}
}
func (sys *systemMockForExistingProject) GetProjectByID(int) (checkmarx.Project, error) {
return checkmarx.Project{}, nil
}
func (sys *systemMockForExistingProject) GetProjectsByNameAndTeam(projectName, teamID string) ([]checkmarx.Project, error) {
return []checkmarx.Project{{ID: 19, Name: projectName, TeamID: teamID, IsPublic: true}}, nil
}
func (sys *systemMockForExistingProject) FilterTeamByName([]checkmarx.Team, string) (checkmarx.Team, error) {
return checkmarx.Team{ID: json.RawMessage(`"16"`), FullName: "OpenSource/Cracks/16"}, nil
}
func (sys *systemMockForExistingProject) FilterTeamByID([]checkmarx.Team, json.RawMessage) checkmarx.Team {
return checkmarx.Team{ID: json.RawMessage(`"15"`), FullName: "OpenSource/Cracks/15"}
}
func (sys *systemMockForExistingProject) DownloadReport(int) ([]byte, error) {
return sys.response.([]byte), nil
}
func (sys *systemMockForExistingProject) GetReportStatus(int) (checkmarx.ReportStatusResponse, error) {
return checkmarx.ReportStatusResponse{Status: checkmarx.ReportStatus{ID: 2, Value: "Created"}}, nil
}
func (sys *systemMockForExistingProject) RequestNewReport(int, string) (checkmarx.Report, error) {
return checkmarx.Report{ReportID: 17}, nil
}
func (sys *systemMockForExistingProject) GetResults(int) checkmarx.ResultsStatistics {
return checkmarx.ResultsStatistics{}
}
func (sys *systemMockForExistingProject) GetScans(int) ([]checkmarx.ScanStatus, error) {
return []checkmarx.ScanStatus{{IsIncremental: true}, {IsIncremental: true}, {IsIncremental: true}, {IsIncremental: false}}, nil
}
func (sys *systemMockForExistingProject) GetShortDescription(int, int) (checkmarx.ShortDescription, error) {
return checkmarx.ShortDescription{Text: "dummyText"}, nil
}
func (sys *systemMockForExistingProject) GetScanStatusAndDetail(int) (string, checkmarx.ScanStatusDetail) {
return "Finished", checkmarx.ScanStatusDetail{Stage: "", Step: ""}
}
func (sys *systemMockForExistingProject) ScanProject(_ int, isIncrementalV, isPublicV, forceScanV bool) (checkmarx.Scan, error) {
sys.scanProjectCalled = true
sys.isIncremental = isIncrementalV
sys.isPublic = isPublicV
sys.forceScan = forceScanV
return checkmarx.Scan{ID: 16}, nil
}
func (sys *systemMockForExistingProject) UpdateProjectConfiguration(int, int, string) error {
return nil
}
func (sys *systemMockForExistingProject) UpdateProjectExcludeSettings(int, string, string) error {
return nil
}
func (sys *systemMockForExistingProject) UploadProjectSourceCode(int, string) error {
return nil
}
func (sys *systemMockForExistingProject) CreateProject(string, string) (checkmarx.ProjectCreateResult, error) {
return checkmarx.ProjectCreateResult{}, fmt.Errorf("create project error")
}
func (sys *systemMockForExistingProject) CreateBranch(int, string) int {
return 0
}
func (sys *systemMockForExistingProject) GetPresets() []checkmarx.Preset {
return []checkmarx.Preset{{ID: 10078, Name: "SAP_Java_Default", OwnerName: "16"}, {ID: 10048, Name: "SAP_JS_Default", OwnerName: "16"}}
}
func (sys *systemMockForExistingProject) GetProjects() ([]checkmarx.Project, error) {
return []checkmarx.Project{{ID: 1, Name: "TestExisting", TeamID: "16"}}, nil
}
func (sys *systemMockForExistingProject) GetTeams() []checkmarx.Team {
return []checkmarx.Team{{ID: json.RawMessage(`"16"`), FullName: "OpenSource/Cracks/16"}, {ID: json.RawMessage(`"15"`), FullName: "OpenSource/Cracks/15"}}
}
type checkmarxExecuteScanUtilsMock struct {
errorOnFileInfoHeader bool
errorOnStat bool
errorOnOpen bool
errorOnWriteFile bool
errorOnPathMatch bool
workspace string
}
func newCheckmarxExecuteScanUtilsMock() *checkmarxExecuteScanUtilsMock {
return &checkmarxExecuteScanUtilsMock{}
}
func (c *checkmarxExecuteScanUtilsMock) GetWorkspace() string {
return c.workspace
}
func (c *checkmarxExecuteScanUtilsMock) PathMatch(pattern, name string) (bool, error) {
if c.errorOnPathMatch {
return false, fmt.Errorf("error on PathMatch")
}
return doublestar.PathMatch(pattern, name)
}
func (c *checkmarxExecuteScanUtilsMock) WriteFile(filename string, data []byte, perm os.FileMode) error {
if c.errorOnWriteFile {
return fmt.Errorf("error on WriteFile")
}
return ioutil.WriteFile(filename, data, perm)
}
func (c *checkmarxExecuteScanUtilsMock) MkdirAll(path string, perm os.FileMode) error {
return os.MkdirAll(path, perm)
}
func (c *checkmarxExecuteScanUtilsMock) FileInfoHeader(fi os.FileInfo) (*zip.FileHeader, error) {
if c.errorOnFileInfoHeader {
return nil, fmt.Errorf("error on FileInfoHeader")
}
return zip.FileInfoHeader(fi)
}
func (c *checkmarxExecuteScanUtilsMock) Stat(name string) (os.FileInfo, error) {
if c.errorOnStat {
return nil, fmt.Errorf("error on Stat")
}
return os.Stat(name)
}
func (c *checkmarxExecuteScanUtilsMock) Open(name string) (*os.File, error) {
if c.errorOnOpen {
return nil, fmt.Errorf("error on Open")
}
return os.Open(name)
}
func (c *checkmarxExecuteScanUtilsMock) GetIssueService() *github.IssuesService {
return nil
}
func (c *checkmarxExecuteScanUtilsMock) GetSearchService() *github.SearchService {
return nil
}
func TestFilterFileGlob(t *testing.T) {
t.Parallel()
tt := []struct {
input string
fInfo fileInfo
expected bool
}{
{input: filepath.Join("somepath", "node_modules", "someOther", "some.file"), fInfo: fileInfo{}, expected: true},
{input: filepath.Join("somepath", "non_modules", "someOther", "some.go"), fInfo: fileInfo{}, expected: false},
{input: filepath.Join(".xmake", "someOther", "some.go"), fInfo: fileInfo{}, expected: true},
{input: filepath.Join("another", "vendor", "some.html"), fInfo: fileInfo{}, expected: false},
{input: filepath.Join("another", "vendor", "some.pdf"), fInfo: fileInfo{}, expected: true},
{input: filepath.Join("another", "vendor", "some.test"), fInfo: fileInfo{}, expected: true},
{input: filepath.Join("some.test"), fInfo: fileInfo{}, expected: false},
{input: filepath.Join("a", "b", "c"), fInfo: fileInfo{dir: true}, expected: false},
}
for k, v := range tt {
result, err := isFileNotMatchingPattern([]string{"!**/node_modules/**", "!**/.xmake/**", "!**/*_test.go", "!**/vendor/**/*.go", "**/*.go", "**/*.html", "*.test"}, v.input, &v.fInfo, newCheckmarxExecuteScanUtilsMock())
assert.Equal(t, v.expected, result, fmt.Sprintf("wrong result for run %v", k))
assert.NoError(t, err, "no error expected in run %v", k)
}
}
func TestFilterFileGlob_errorOnPathMatch(t *testing.T) {
t.Parallel()
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.errorOnPathMatch = true
result, err := isFileNotMatchingPattern([]string{"!**/node_modules/**", "!**/.xmake/**", "!**/*_test.go", "!**/vendor/**/*.go", "**/*.go", "**/*.html", "*.test"}, filepath.Join("a", "b", "c"), &fileInfo{}, utilsMock)
assert.Equal(t, false, result, "wrong result")
assert.EqualError(t, err, "Pattern **/node_modules/** could not get executed: error on PathMatch")
}
func TestZipFolder(t *testing.T) {
t.Parallel()
t.Run("zip files successfully", func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
err := ioutil.WriteFile(filepath.Join(dir, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abcd.yaml"), []byte("abcd.yaml"), 0o700)
assert.NoError(t, err)
err = os.Mkdir(filepath.Join(dir, "somepath"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "somepath", "abcd.txt"), []byte("somepath/abcd.txt"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abcd_test.go"), []byte("abcd_test.go"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abc_test.go"), []byte("abc_test.go"), 0o700)
assert.NoError(t, err)
var zipFileMock bytes.Buffer
err = zipFolder(dir, &zipFileMock, []string{"!**/abc_test.txt", "!**/abcd.yaml", "**/abcd.*"}, newCheckmarxExecuteScanUtilsMock())
assert.NoError(t, err)
zipString := zipFileMock.String()
// assert.Equal(t, 724, zipFileMock.Len(), "Expected length of 724, but got %v", zipFileMock.Len())
assert.True(t, strings.Contains(zipString, "abcd.go"), "Expected 'abcd.go' contained")
assert.True(t, strings.Contains(zipString, filepath.Join("somepath", "abcd.txt")), "Expected 'somepath/abcd.txt' contained")
assert.False(t, strings.Contains(zipString, "abcd_test.go"), "Not expected 'abcd_test.go' contained")
assert.False(t, strings.Contains(zipString, "abcd.yaml"), "Not expected 'abcd.yaml' contained")
assert.False(t, strings.Contains(zipString, "abc_test.go"), "Not expected 'abc_test.go' contained")
})
t.Run("error on query file info header", func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
err := ioutil.WriteFile(filepath.Join(dir, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
err = os.Mkdir(filepath.Join(dir, "somepath"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "somepath", "abcd.txt"), []byte("somepath/abcd.txt"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abcd_test.go"), []byte("abcd_test.go"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abc_test.go"), []byte("abc_test.go"), 0o700)
assert.NoError(t, err)
var zipFileMock bytes.Buffer
mock := newCheckmarxExecuteScanUtilsMock()
mock.errorOnFileInfoHeader = true
err = zipFolder(dir, &zipFileMock, []string{"!**/abc_test.txt", "**/abcd.*"}, mock)
assert.EqualError(t, err, "error on FileInfoHeader")
})
t.Run("error on os stat", func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
err := ioutil.WriteFile(filepath.Join(dir, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
err = os.Mkdir(filepath.Join(dir, "somepath"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "somepath", "abcd.txt"), []byte("somepath/abcd.txt"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abcd_test.go"), []byte("abcd_test.go"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abc_test.go"), []byte("abc_test.go"), 0o700)
assert.NoError(t, err)
var zipFileMock bytes.Buffer
mock := newCheckmarxExecuteScanUtilsMock()
mock.errorOnStat = true
err = zipFolder(dir, &zipFileMock, []string{"!**/abc_test.txt", "**/abcd.*"}, mock)
assert.NoError(t, err)
})
t.Run("error on os Open", func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
err := ioutil.WriteFile(filepath.Join(dir, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
err = os.Mkdir(filepath.Join(dir, "somepath"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "somepath", "abcd.txt"), []byte("somepath/abcd.txt"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abcd_test.go"), []byte("abcd_test.go"), 0o700)
assert.NoError(t, err)
err = ioutil.WriteFile(filepath.Join(dir, "abc_test.go"), []byte("abc_test.go"), 0o700)
assert.NoError(t, err)
var zipFileMock bytes.Buffer
mock := newCheckmarxExecuteScanUtilsMock()
mock.errorOnOpen = true
err = zipFolder(dir, &zipFileMock, []string{"!**/abc_test.txt", "**/abcd.*"}, mock)
assert.EqualError(t, err, "error on Open")
})
}
func TestGetDetailedResults(t *testing.T) {
t.Parallel()
t.Run("success case", func(t *testing.T) {
t.Parallel()
sys := &systemMock{response: []byte(`
`)}
dir := t.TempDir()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdHigh: 100, VulnerabilityThresholdEnabled: true, VulnerabilityThresholdLowPerQuery: true, VulnerabilityThresholdLowPerQueryMax: 10, VulnerabilityThresholdLow: 10}
result, err := getDetailedResults(options, sys, filepath.Join(dir, "abc.xml"), 2635, newCheckmarxExecuteScanUtilsMock())
assert.NoError(t, err, "error occurred but none expected")
assert.Equal(t, "2", result["ProjectId"], "Project ID incorrect")
assert.Equal(t, "Project 1", result["ProjectName"], "Project name incorrect")
assert.Equal(t, 2, result["High"].(map[string]int)["Issues"], "Number of High issues incorrect")
assert.Equal(t, 2, result["High"].(map[string]int)["NotFalsePositive"], "Number of High NotFalsePositive issues incorrect")
assert.Equal(t, 1, result["Medium"].(map[string]int)["Issues"], "Number of Medium issues incorrect")
assert.Equal(t, 0, result["Medium"].(map[string]int)["NotFalsePositive"], "Number of Medium NotFalsePositive issues incorrect")
assert.Equal(t, 4, result["Low"].(map[string]int)["Issues"], "Number of Low issues incorrect")
assert.Equal(t, 0, result["Low"].(map[string]int)["NotFalsePositive"], "Number of Low NotFalsePositive issues incorrect")
assert.Equal(t, 2, result["LowPerQuery"].(map[string]map[string]int)["SQL_Injection"]["Issues"], "Number of Low issues (per query 1) incorrect")
assert.Equal(t, 1, result["LowPerQuery"].(map[string]map[string]int)["SQL_Injection"]["Urgent"], "Number of Low issues (per query 1) incorrect")
assert.Equal(t, 1, result["LowPerQuery"].(map[string]map[string]int)["SQL_Injection"]["ProposedNotExploitable"], "Number of Low issues (per query 1) incorrect")
assert.Equal(t, 2, result["LowPerQuery"].(map[string]map[string]int)["Code_Injection"]["Issues"], "Number of Low issues (per query 2) incorrect")
assert.Equal(t, 1, result["LowPerQuery"].(map[string]map[string]int)["Code_Injection"]["Confirmed"], "Number of Low issues (per query 2) incorrect")
assert.Equal(t, 1, result["LowPerQuery"].(map[string]map[string]int)["Code_Injection"]["NotExploitable"], "Number of Low issues (per query 2) incorrect")
})
t.Run("error on write file", func(t *testing.T) {
t.Parallel()
sys := &systemMock{response: []byte(`
`)}
dir := t.TempDir()
utils := newCheckmarxExecuteScanUtilsMock()
utils.errorOnWriteFile = true
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdHigh: 100, VulnerabilityThresholdEnabled: true, VulnerabilityThresholdLowPerQuery: true, VulnerabilityThresholdLowPerQueryMax: 10, VulnerabilityThresholdLow: 10}
_, err := getDetailedResults(options, sys, filepath.Join(dir, "abc.xml"), 2635, utils)
assert.EqualError(t, err, "failed to write file: error on WriteFile")
})
}
func TestRunScan(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMockForExistingProject{response: []byte(``)}
options := checkmarxExecuteScanOptions{ProjectName: "TestExisting", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "2", Incremental: true, FullScansScheduled: true, Preset: "10048", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.NoError(t, err, "error occurred but none expected")
assert.Equal(t, false, sys.isIncremental, "isIncremental has wrong value")
assert.Equal(t, true, sys.isPublic, "isPublic has wrong value")
assert.Equal(t, true, sys.forceScan, "forceScan has wrong value")
assert.Equal(t, true, sys.scanProjectCalled, "ScanProject was not invoked")
}
func TestRunScan_nonNumeralPreset(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMockForExistingProject{response: []byte(``)}
options := checkmarxExecuteScanOptions{ProjectName: "TestExisting", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "2", Incremental: true, FullScansScheduled: true, Preset: "SAP_JS_Default", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.NoError(t, err, "error occurred but none expected")
}
func TestRunOptimizedScan(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMockForExistingProject{response: []byte(``)}
options := checkmarxExecuteScanOptions{IsOptimizedAndScheduled: true, ProjectName: "TestExisting", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "1", Incremental: true, FullScansScheduled: true, Preset: "10048", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.NoError(t, err, "error occurred but none expected")
assert.Equal(t, false, sys.isIncremental, "isIncremental has wrong value")
assert.Equal(t, true, sys.isPublic, "isPublic has wrong value")
assert.Equal(t, true, sys.forceScan, "forceScan has wrong value")
assert.Equal(t, true, sys.scanProjectCalled, "ScanProject was not invoked")
}
func TestSetPresetForProjectWithIDProvided(t *testing.T) {
t.Parallel()
sys := &systemMock{}
err := setPresetForProject(sys, 12345, 16, "testProject", "CX_Default", "")
assert.NoError(t, err, "error occurred but none expected")
assert.Equal(t, false, sys.getPresetsCalled, "GetPresets was called")
assert.Equal(t, true, sys.updateProjectConfigurationCalled, "UpdateProjectConfiguration was not called")
}
func TestSetPresetForProjectWithNameProvided(t *testing.T) {
t.Parallel()
sys := &systemMock{}
presetID, _ := strconv.Atoi("CX_Default")
err := setPresetForProject(sys, 12345, presetID, "testProject", "CX_Default", "")
assert.NoError(t, err, "error occurred but none expected")
assert.Equal(t, true, sys.getPresetsCalled, "GetPresets was not called")
assert.Equal(t, true, sys.updateProjectConfigurationCalled, "UpdateProjectConfiguration was not called")
}
func TestVerifyOnly(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMockForExistingProject{response: []byte(``)}
options := checkmarxExecuteScanOptions{VerifyOnly: true, ProjectName: "TestExisting", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "2", Incremental: true, FullScansScheduled: true, Preset: "10048", TeamName: "OpenSource/Cracks/15", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err := runScan(ctx, options, sys, &influx, utilsMock)
assert.NoError(t, err, "error occurred but none expected")
assert.Equal(t, false, sys.scanProjectCalled, "ScanProject was invoked but shouldn't")
}
func TestVerifyOnly_errorOnWriteFileDoesNotBlock(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMockForExistingProject{response: []byte(``)}
options := checkmarxExecuteScanOptions{VerifyOnly: true, ProjectName: "TestExisting", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "2", Incremental: true, FullScansScheduled: true, Preset: "10048", TeamName: "OpenSource/Cracks/15", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
utilsMock.errorOnWriteFile = true
err := runScan(ctx, options, sys, &influx, utilsMock)
assert.EqualError(t, err, "scan, upload, and result validation returned an error: project TestExisting not compliant: failed to get detailed results: failed to write file: error on WriteFile")
}
func TestRunScanWOtherCycle(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMock{response: []byte(``), createProject: true}
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "123", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.NoError(t, err, "error occurred but none expected")
assert.Equal(t, true, sys.isIncremental, "isIncremental has wrong value")
assert.Equal(t, true, sys.isPublic, "isPublic has wrong value")
assert.Equal(t, true, sys.forceScan, "forceScan has wrong value")
}
func TestRunScanErrorInZip(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMock{response: []byte(``), createProject: true}
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "123", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
utilsMock.errorOnFileInfoHeader = true
err := runScan(ctx, options, sys, &influx, utilsMock)
assert.EqualError(t, err, "scan, upload, and result validation returned an error: failed to zip workspace files: failed to compact folder: error on FileInfoHeader")
}
func TestRunScanForPullRequest(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMock{response: []byte(``)}
options := checkmarxExecuteScanOptions{PullRequestName: "PR-19", ProjectName: "Test", VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "123", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true, AvoidDuplicateProjectScans: false}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.Equal(t, true, sys.isIncremental, "isIncremental has wrong value")
assert.Equal(t, true, sys.isPublic, "isPublic has wrong value")
assert.Equal(t, true, sys.forceScan, "forceScan has wrong value")
}
func TestRunScanForPullRequestProjectNew(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMock{response: []byte(``), createProject: true}
options := checkmarxExecuteScanOptions{PullRequestName: "PR-17", ProjectName: "Test", AvoidDuplicateProjectScans: true, VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "10048", TeamName: "OpenSource/Cracks/15", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.NoError(t, err, "Unexpected error caught")
assert.Equal(t, true, sys.isIncremental, "isIncremental has wrong value")
assert.Equal(t, true, sys.isPublic, "isPublic has wrong value")
assert.Equal(t, false, sys.forceScan, "forceScan has wrong value")
}
func TestRunScanForPullRequestProjectNew_nonNumeralPreset(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMock{response: []byte(``), createProject: true}
options := checkmarxExecuteScanOptions{PullRequestName: "PR-17", ProjectName: "Test", AvoidDuplicateProjectScans: true, VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "SAP_JS_Default", TeamName: "OpenSource/Cracks/15", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.NoError(t, err, "error occurred but none expected")
}
func TestRunScanHighViolationPercentage(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMock{response: []byte(`
`)}
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdResult: "FAILURE", VulnerabilityThresholdHigh: 100, FullScanCycle: "10", FullScansScheduled: true, Preset: "10048", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.Contains(t, fmt.Sprint(err), "the project is not compliant", "Expected different error")
}
func TestRunScanHighViolationAbsolute(t *testing.T) {
t.Parallel()
ctx := context.Background()
sys := &systemMock{response: []byte(`
`)}
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdResult: "FAILURE", VulnerabilityThresholdLow: 1, FullScanCycle: "10", FullScansScheduled: true, Preset: "10048", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true}
workspace := t.TempDir()
err := ioutil.WriteFile(filepath.Join(workspace, "abcd.go"), []byte("abcd.go"), 0o700)
assert.NoError(t, err)
options.FilterPattern = "**/abcd.go"
influx := checkmarxExecuteScanInflux{}
utilsMock := newCheckmarxExecuteScanUtilsMock()
utilsMock.workspace = workspace
err = runScan(ctx, options, sys, &influx, utilsMock)
assert.Contains(t, fmt.Sprint(err), "the project is not compliant", "Expected different error")
}
func TestEnforceThresholds(t *testing.T) {
t.Parallel()
results := map[string]interface{}{}
results["High"] = map[string]int{}
results["Medium"] = map[string]int{}
results["Low"] = map[string]int{}
results["High"].(map[string]int)["NotFalsePositive"] = 10
results["Medium"].(map[string]int)["NotFalsePositive"] = 10
results["Low"].(map[string]int)["NotFalsePositive"] = 10
results["Low"].(map[string]int)["NotExploitable"] = 0
results["Low"].(map[string]int)["Confirmed"] = 0
results["High"].(map[string]int)["Issues"] = 10
results["Medium"].(map[string]int)["Issues"] = 10
results["Low"].(map[string]int)["Issues"] = 10
lowPerQuery := map[string]map[string]int{}
submap := map[string]int{}
submap["Issues"] = 8
submap["Confirmed"] = 1
submap["NotExploitable"] = 0
lowPerQuery["Low_Query_Name_1"] = submap
submap = map[string]int{}
submap["Issues"] = 100
submap["Confirmed"] = 5
submap["NotExploitable"] = 5
lowPerQuery["Low_Query_Name_2"] = submap
results["LowPerQuery"] = lowPerQuery
t.Run("percentage low violation per query", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdHigh: 0, VulnerabilityThresholdMedium: 0, VulnerabilityThresholdLow: 20, VulnerabilityThresholdEnabled: true, VulnerabilityThresholdLowPerQuery: true, VulnerabilityThresholdLowPerQueryMax: 10}
insecure, _, _ := enforceThresholds(options, results)
assert.Equal(t, true, insecure, "Expected results to be insecure but where not")
})
t.Run("percentage low no violation per query", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdHigh: 0, VulnerabilityThresholdMedium: 0, VulnerabilityThresholdLow: 10, VulnerabilityThresholdEnabled: true, VulnerabilityThresholdLowPerQuery: true, VulnerabilityThresholdLowPerQueryMax: 10}
insecure, _, _ := enforceThresholds(options, results)
assert.Equal(t, false, insecure, "Expected results to be insecure but where not")
})
t.Run("percentage high violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdHigh: 100, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, true, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 1, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 2, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
t.Run("absolute high violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdHigh: 5, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, true, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 3, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 0, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
t.Run("percentage medium violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdMedium: 100, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, true, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 1, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 2, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
t.Run("absolute medium violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdMedium: 5, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, true, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 3, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 0, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
t.Run("percentage low violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdLow: 100, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, true, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 1, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 2, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
t.Run("absolute low violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdLow: 5, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, true, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 3, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 0, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
t.Run("percentage no violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdLow: 0, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, false, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 0, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 3, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
t.Run("absolute no violation", func(t *testing.T) {
t.Parallel()
options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdLow: 15, VulnerabilityThresholdMedium: 15, VulnerabilityThresholdHigh: 15, VulnerabilityThresholdEnabled: true}
insecure, insecureResults, neutralResults := enforceThresholds(options, results)
assert.Equal(t, false, insecure, "Expected results to be insecure but where not")
assert.Equal(t, 0, len(insecureResults), fmt.Sprintf("Unexpected number of results: %v", insecureResults))
assert.Equal(t, 3, len(neutralResults), fmt.Sprintf("Unexpected number of results: %v", neutralResults))
})
}
func TestLoadPreset(t *testing.T) {
t.Parallel()
sys := &systemMock{}
t.Run("resolve via name", func(t *testing.T) {
t.Parallel()
preset, err := loadPreset(sys, "SAP_JS_Default")
assert.NoError(t, err, "Expected success but failed")
assert.Equal(t, "SAP_JS_Default", preset.Name, "Expected result but got none")
})
t.Run("error case", func(t *testing.T) {
t.Parallel()
preset, err := loadPreset(sys, "")
assert.Contains(t, fmt.Sprint(err), "preset SAP_JS_Default not found", "Expected different error")
assert.Equal(t, 0, preset.ID, "Expected result but got none")
})
}
func TestPreventInfiniteLoop(t *testing.T) {
t.Parallel()
utils := checkmarxExecuteScanUtilsBundle{
workspace: "abc",
}
assert.Equal(t, "abc", utils.GetWorkspace(), "Wrong workspace has been loaded")
}