package cmd import ( "bytes" "fmt" "io/ioutil" "os" "path/filepath" "strings" "testing" "time" "github.com/SAP/jenkins-library/pkg/checkmarx" "github.com/stretchr/testify/assert" ) 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 (fi fileInfo) IsDir() bool { return fi.dir } func (fi fileInfo) Name() string { return fi.nam } func (fi fileInfo) Size() int64 { return fi.siz } func (fi fileInfo) ModTime() time.Time { return fi.modtime } func (fi fileInfo) Mode() os.FileMode { return fi.mod } func (fi fileInfo) Sys() interface{} { return fi.syss } type systemMock struct { response interface{} isIncremental bool isPublic bool forceScan bool createProject bool previousPName string } func (sys *systemMock) FilterPresetByName(presets []checkmarx.Preset, presetName string) checkmarx.Preset { return checkmarx.Preset{ID: 10050, Name: "SAP_JS_Default", OwnerName: "16"} } func (sys *systemMock) FilterPresetByID(presets []checkmarx.Preset, presetID int) checkmarx.Preset { return checkmarx.Preset{ID: 10048, Name: "SAP_Default", OwnerName: "16"} } func (sys *systemMock) FilterProjectByName(projects []checkmarx.Project, projectName 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(teams []checkmarx.Team, teamName string) checkmarx.Team { return checkmarx.Team{ID: "16", FullName: "OpenSource/Cracks/16"} } func (sys *systemMock) FilterTeamByID(teams []checkmarx.Team, teamID string) checkmarx.Team { return checkmarx.Team{ID: "15", FullName: "OpenSource/Cracks/15"} } func (sys *systemMock) DownloadReport(reportID int) ([]byte, error) { return sys.response.([]byte), nil } func (sys *systemMock) GetReportStatus(reportID int) (checkmarx.ReportStatusResponse, error) { return checkmarx.ReportStatusResponse{Status: checkmarx.ReportStatus{ID: 2, Value: "Created"}}, nil } func (sys *systemMock) RequestNewReport(scanID int, reportType string) (checkmarx.Report, error) { return checkmarx.Report{ReportID: 17}, nil } func (sys *systemMock) GetResults(scanID int) checkmarx.ResultsStatistics { return checkmarx.ResultsStatistics{} } func (sys *systemMock) GetScans(projectID int) ([]checkmarx.ScanStatus, error) { return []checkmarx.ScanStatus{{IsIncremental: true}, {IsIncremental: true}, {IsIncremental: true}, {IsIncremental: false}}, nil } func (sys *systemMock) GetScanStatusAndDetail(scanID int) (string, checkmarx.ScanStatusDetail) { return "Finished", checkmarx.ScanStatusDetail{Stage: "Step 1 of 25", Step: "Scan something"} } func (sys *systemMock) ScanProject(projectID 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(projectID int, presetID int, engineConfigurationID string) error { return nil } func (sys *systemMock) UpdateProjectExcludeSettings(projectID int, excludeFolders string, excludeFiles string) error { return nil } func (sys *systemMock) UploadProjectSourceCode(projectID int, zipFile string) error { return nil } func (sys *systemMock) CreateProject(projectName string, teamID string) (checkmarx.ProjectCreateResult, error) { return checkmarx.ProjectCreateResult{ID: 20}, nil } func (sys *systemMock) CreateBranch(projectID int, branchName string) int { return 18 } func (sys *systemMock) GetPresets() []checkmarx.Preset { return []checkmarx.Preset{{ID: 10078, Name: "SAP Java Default", OwnerName: "16"}, {ID: 10048, Name: "SAP JS 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: "16", FullName: "OpenSource/Cracks/16"}, {ID: "15", FullName: "OpenSource/Cracks/15"}} } type systemMockForExistingProject struct { response interface{} isIncremental bool isPublic bool forceScan bool scanProjectCalled bool } func (sys *systemMockForExistingProject) FilterPresetByName(presets []checkmarx.Preset, presetName string) checkmarx.Preset { return checkmarx.Preset{ID: 10050, Name: "SAP_JS_Default", OwnerName: "16"} } func (sys *systemMockForExistingProject) FilterPresetByID(presets []checkmarx.Preset, presetID int) checkmarx.Preset { return checkmarx.Preset{ID: 10048, Name: "SAP_Default", OwnerName: "16"} } func (sys *systemMockForExistingProject) FilterProjectByName(projects []checkmarx.Project, projectName string) checkmarx.Project { return checkmarx.Project{ID: 1, Name: "TestExisting", TeamID: "16", IsPublic: true} } func (sys *systemMockForExistingProject) GetProjectByID(projectID 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(teams []checkmarx.Team, teamName string) checkmarx.Team { return checkmarx.Team{ID: "16", FullName: "OpenSource/Cracks/16"} } func (sys *systemMockForExistingProject) FilterTeamByID(teams []checkmarx.Team, teamID string) checkmarx.Team { return checkmarx.Team{ID: "15", FullName: "OpenSource/Cracks/15"} } func (sys *systemMockForExistingProject) DownloadReport(reportID int) ([]byte, error) { return sys.response.([]byte), nil } func (sys *systemMockForExistingProject) GetReportStatus(reportID int) (checkmarx.ReportStatusResponse, error) { return checkmarx.ReportStatusResponse{Status: checkmarx.ReportStatus{ID: 2, Value: "Created"}}, nil } func (sys *systemMockForExistingProject) RequestNewReport(scanID int, reportType string) (checkmarx.Report, error) { return checkmarx.Report{ReportID: 17}, nil } func (sys *systemMockForExistingProject) GetResults(scanID int) checkmarx.ResultsStatistics { return checkmarx.ResultsStatistics{} } func (sys *systemMockForExistingProject) GetScans(projectID int) ([]checkmarx.ScanStatus, error) { return []checkmarx.ScanStatus{{IsIncremental: true}, {IsIncremental: true}, {IsIncremental: true}, {IsIncremental: false}}, nil } func (sys *systemMockForExistingProject) GetScanStatusAndDetail(scanID int) (string, checkmarx.ScanStatusDetail) { return "Finished", checkmarx.ScanStatusDetail{Stage: "", Step: ""} } func (sys *systemMockForExistingProject) ScanProject(projectID 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(projectID int, presetID int, engineConfigurationID string) error { return nil } func (sys *systemMockForExistingProject) UpdateProjectExcludeSettings(projectID int, excludeFolders string, excludeFiles string) error { return nil } func (sys *systemMockForExistingProject) UploadProjectSourceCode(projectID int, zipFile string) error { return nil } func (sys *systemMockForExistingProject) CreateProject(projectName string, teamID string) (checkmarx.ProjectCreateResult, error) { return checkmarx.ProjectCreateResult{}, fmt.Errorf("create project error") } func (sys *systemMockForExistingProject) CreateBranch(projectID int, branchName 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: "16", FullName: "OpenSource/Cracks/16"}, {ID: "15", FullName: "OpenSource/Cracks/15"}} } func TestFilterFileGlob(t *testing.T) { tt := []struct { input string fInfo fileInfo expected bool }{ {input: "somepath/node_modules/someOther/some.file", fInfo: fileInfo{}, expected: true}, {input: "somepath/non_modules/someOther/some.go", fInfo: fileInfo{}, expected: false}, {input: ".xmake/someOther/some.go", fInfo: fileInfo{}, expected: true}, {input: "another/vendor/some.html", fInfo: fileInfo{}, expected: false}, {input: "another/vendor/some.pdf", fInfo: fileInfo{}, expected: true}, {input: "another/vendor/some.test", fInfo: fileInfo{}, expected: true}, {input: "some.test", fInfo: fileInfo{}, expected: false}, {input: "a/b/c", fInfo: fileInfo{dir: true}, expected: false}, } for k, v := range tt { assert.Equal(t, v.expected, filterFileGlob([]string{"!**/node_modules/**", "!**/.xmake/**", "!**/*_test.go", "!**/vendor/**/*.go", "**/*.go", "**/*.html", "*.test"}, v.input, v.fInfo), fmt.Sprintf("wrong long name for run %v", k)) } } func TestZipFolder(t *testing.T) { t.Run("zip files", func(t *testing.T) { dir, err := ioutil.TempDir("", "test zip files") if err != nil { t.Fatal("Failed to create temporary directory") } // clean up tmp dir defer os.RemoveAll(dir) ioutil.WriteFile(filepath.Join(dir, "abcd.go"), []byte{byte(1), byte(2), byte(3)}, 0700) ioutil.WriteFile(filepath.Join(dir, "somepath", "abcd.txt"), []byte{byte(1), byte(2), byte(3)}, 0700) ioutil.WriteFile(filepath.Join(dir, "abcd_test.go"), []byte{byte(1), byte(2), byte(3)}, 0700) var zipFileMock bytes.Buffer zipFolder(dir, &zipFileMock, []string{"!abc_test.go", "**/abcd.txt", "**/abc.go"}) got := zipFileMock.Len() want := 164 if got != want { t.Errorf("Zipping test failed expected %v but got %v", want, got) } }) } func TestGetDetailedResults(t *testing.T) { t.Run("success case", func(t *testing.T) { sys := &systemMock{response: []byte(` `)} dir, err := ioutil.TempDir("", "test detailed results") if err != nil { t.Fatal("Failed to create temporary directory") } // clean up tmp dir defer os.RemoveAll(dir) result, err := getDetailedResults(sys, filepath.Join(dir, "abc.xml"), 2635) assert.NoError(t, err, "error occured 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") }) } func TestRunScan(t *testing.T) { 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, err := ioutil.TempDir("", "workspace1") if err != nil { t.Fatal("Failed to create temporary workspace directory") } // clean up tmp dir defer os.RemoveAll(workspace) influx := checkmarxExecuteScanInflux{} err = runScan(options, sys, workspace, &influx) assert.NoError(t, err, "error occured 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 TestVerifyOnly(t *testing.T) { sys := &systemMockForExistingProject{response: []byte(``)} options := checkmarxExecuteScanOptions{VerifyOnly: true, ProjectName: "TestExisting", VulnerabilityThresholdUnit: "absolute", FullScanCycle: "2", Incremental: true, FullScansScheduled: true, Preset: "10048", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true} workspace, err := ioutil.TempDir("", "workspace1") if err != nil { t.Fatal("Failed to create temporary workspace directory") } // clean up tmp dir defer os.RemoveAll(workspace) influx := checkmarxExecuteScanInflux{} err = runScan(options, sys, workspace, &influx) assert.NoError(t, err, "error occured but none expected") assert.Equal(t, false, sys.scanProjectCalled, "ScanProject was invoked but shouldn't") } func TestRunScanWOtherCycle(t *testing.T) { sys := &systemMock{response: []byte(``), createProject: true} options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "SAP_JS_Default", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true} workspace, err := ioutil.TempDir("", "workspace2") if err != nil { t.Fatal("Failed to create temporary workspace directory") } // clean up tmp dir defer os.RemoveAll(workspace) influx := checkmarxExecuteScanInflux{} err = runScan(options, sys, workspace, &influx) assert.NoError(t, err, "error occured 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 TestRunScanForPullRequest(t *testing.T) { sys := &systemMock{response: []byte(``)} options := checkmarxExecuteScanOptions{PullRequestName: "PR-19", ProjectName: "Test", VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "SAP_JS_Default", TeamID: "16", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true, AvoidDuplicateProjectScans: false} workspace, err := ioutil.TempDir("", "workspace3") if err != nil { t.Fatal("Failed to create temporary workspace directory") } // clean up tmp dir defer os.RemoveAll(workspace) influx := checkmarxExecuteScanInflux{} err = runScan(options, sys, workspace, &influx) 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) { sys := &systemMock{response: []byte(``), createProject: true} options := checkmarxExecuteScanOptions{PullRequestName: "PR-17", ProjectName: "Test", VulnerabilityThresholdUnit: "percentage", FullScanCycle: "3", Incremental: true, FullScansScheduled: true, Preset: "10048", TeamName: "OpenSource/Cracks/15", VulnerabilityThresholdEnabled: true, GeneratePdfReport: true} workspace, err := ioutil.TempDir("", "workspace4") if err != nil { t.Fatal("Failed to create temporary workspace directory") } // clean up tmp dir defer os.RemoveAll(workspace) influx := checkmarxExecuteScanInflux{} err = runScan(options, sys, workspace, &influx) 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 TestRunScanHighViolationPercentage(t *testing.T) { 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, err := ioutil.TempDir("", "workspace5") if err != nil { t.Fatal("Failed to create temporary workspace directory") } // clean up tmp dir defer os.RemoveAll(workspace) influx := checkmarxExecuteScanInflux{} err = runScan(options, sys, workspace, &influx) assert.Contains(t, fmt.Sprint(err), "the project is not compliant", "Expected different error") } func TestRunScanHighViolationAbsolute(t *testing.T) { 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, err := ioutil.TempDir("", "workspace6") if err != nil { t.Fatal("Failed to create temporary workspace directory") } // clean up tmp dir defer os.RemoveAll(workspace) influx := checkmarxExecuteScanInflux{} err = runScan(options, sys, workspace, &influx) assert.Contains(t, fmt.Sprint(err), "the project is not compliant", "Expected different error") } func TestEnforceThresholds(t *testing.T) { 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 t.Run("percentage high violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdHigh: 100, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, true, insecure, "Expected results to be insecure but where not") }) t.Run("absolute high violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdHigh: 5, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, true, insecure, "Expected results to be insecure but where not") }) t.Run("percentage medium violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdMedium: 100, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, true, insecure, "Expected results to be insecure but where not") }) t.Run("absolute medium violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdMedium: 5, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, true, insecure, "Expected results to be insecure but where not") }) t.Run("percentage low violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdLow: 100, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, true, insecure, "Expected results to be insecure but where not") }) t.Run("absolute low violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdLow: 5, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, true, insecure, "Expected results to be insecure but where not") }) t.Run("percentage no violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "percentage", VulnerabilityThresholdLow: 0, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, false, insecure, "Expected results to be insecure but where not") }) t.Run("absolute no violation", func(t *testing.T) { options := checkmarxExecuteScanOptions{VulnerabilityThresholdUnit: "absolute", VulnerabilityThresholdLow: 15, VulnerabilityThresholdMedium: 15, VulnerabilityThresholdHigh: 15, VulnerabilityThresholdEnabled: true} insecure := enforceThresholds(options, results) assert.Equal(t, false, insecure, "Expected results to be insecure but where not") }) } func TestLoadPreset(t *testing.T) { sys := &systemMock{} t.Run("resolve via code", func(t *testing.T) { preset, err := loadPreset(sys, "10048") assert.NoError(t, err, "Expected success but failed") assert.Equal(t, 10048, preset.ID, "Expected result but got none") }) t.Run("resolve via name", func(t *testing.T) { 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) { 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") }) }