github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/fortifyExecuteScan_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package cmd 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "fmt" 11 "io" 12 "net/http" 13 "os" 14 "path/filepath" 15 "reflect" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/SAP/jenkins-library/pkg/mock" 21 22 "github.com/SAP/jenkins-library/pkg/fortify" 23 "github.com/SAP/jenkins-library/pkg/log" 24 "github.com/SAP/jenkins-library/pkg/piperutils" 25 "github.com/SAP/jenkins-library/pkg/versioning" 26 27 "github.com/google/go-github/v45/github" 28 "github.com/stretchr/testify/assert" 29 30 "github.com/piper-validation/fortify-client-go/models" 31 ) 32 33 const author string = "johnDoe178" 34 35 type fortifyTestUtilsBundle struct { 36 *execRunnerMock 37 *mock.FilesMock 38 getArtifactShouldFail bool 39 } 40 41 func (f *fortifyTestUtilsBundle) DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error { 42 panic("not expected to be called in tests") 43 } 44 45 func (f *fortifyTestUtilsBundle) GetArtifact(buildTool, buildDescriptorFile string, options *versioning.Options) (versioning.Artifact, error) { 46 if f.getArtifactShouldFail { 47 return nil, fmt.Errorf("build tool '%v' not supported", buildTool) 48 } 49 return artifactMock{Coordinates: newCoordinatesMock()}, nil 50 } 51 52 func (f *fortifyTestUtilsBundle) GetIssueService() *github.IssuesService { 53 return nil 54 } 55 56 func (cf *fortifyTestUtilsBundle) GetSearchService() *github.SearchService { 57 return nil 58 } 59 60 func newFortifyTestUtilsBundle() fortifyTestUtilsBundle { 61 utilsBundle := fortifyTestUtilsBundle{ 62 execRunnerMock: &execRunnerMock{}, 63 FilesMock: &mock.FilesMock{}, 64 } 65 return utilsBundle 66 } 67 func mockExecinPath(exec string) (string, error) { 68 executable_list := []string{"fortifyupdate", "sourceanalyzer"} 69 for _, exec := range executable_list { 70 if exec == "fortifyupdate" || exec == "sourceanalyzer" { 71 return "/" + exec, nil 72 } else { 73 err_string := fmt.Sprintf("ERROR , command not found: %s. Please configure a supported docker image or install Fortify SCA on the system.", exec) 74 return "", errors.New(err_string) 75 } 76 } 77 return "", nil 78 } 79 80 func failMockExecinPathfortifyupdate(exec string) (string, error) { 81 if exec == "fortifyupdate" { 82 return "", errors.New("Command not found: fortifyupdate. Please configure a supported docker image or install Fortify SCA on the system.") 83 } 84 return "/fortifyupdate", nil 85 } 86 func failMockExecinPathsourceanalyzer(exec string) (string, error) { 87 if exec == "sourceanalyzer" { 88 return "", errors.New("Command not found: sourceanalyzer. Please configure a supported docker image or install Fortify SCA on the system.") 89 } 90 return "/sourceanalyzer", nil 91 } 92 93 type artifactMock struct { 94 Coordinates versioning.Coordinates 95 } 96 97 func newCoordinatesMock() versioning.Coordinates { 98 return versioning.Coordinates{ 99 GroupID: "a", 100 ArtifactID: "b", 101 Version: "1.0.0", 102 } 103 } 104 105 func (a artifactMock) VersioningScheme() string { 106 return "full" 107 } 108 109 func (a artifactMock) GetVersion() (string, error) { 110 return a.Coordinates.Version, nil 111 } 112 113 func (a artifactMock) SetVersion(v string) error { 114 a.Coordinates.Version = v 115 return nil 116 } 117 118 func (a artifactMock) GetCoordinates() (versioning.Coordinates, error) { 119 return a.Coordinates, nil 120 } 121 122 type fortifyMock struct { 123 Successive bool 124 getArtifactsOfProjectVersionIdx int 125 getArtifactsOfProjectVersionTime time.Time 126 } 127 128 func (f *fortifyMock) GetProjectByName(name string, autoCreate bool, projectVersion string) (*models.Project, error) { 129 return &models.Project{Name: &name, ID: 64}, nil 130 } 131 132 func (f *fortifyMock) GetProjectVersionDetailsByProjectIDAndVersionName(id int64, name string, autoCreate bool, projectName string) (*models.ProjectVersion, error) { 133 return &models.ProjectVersion{ID: id, Name: &name, Project: &models.Project{Name: &projectName}}, nil 134 } 135 136 func (f *fortifyMock) GetProjectVersionAttributesByProjectVersionID(id int64) ([]*models.Attribute, error) { 137 return []*models.Attribute{}, nil 138 } 139 140 func (f *fortifyMock) SetProjectVersionAttributesByProjectVersionID(id int64, attributes []*models.Attribute) ([]*models.Attribute, error) { 141 return attributes, nil 142 } 143 144 func (f *fortifyMock) CreateProjectVersionIfNotExist(projectName, projectVersionName, description string) (*models.ProjectVersion, error) { 145 return &models.ProjectVersion{ID: 4711, Name: &projectVersionName, Project: &models.Project{Name: &projectName}}, nil 146 } 147 148 func (f *fortifyMock) LookupOrCreateProjectVersionDetailsForPullRequest(projectID int64, masterProjectVersion *models.ProjectVersion, pullRequestName string) (*models.ProjectVersion, error) { 149 return &models.ProjectVersion{ID: 4712, Name: &pullRequestName, Project: masterProjectVersion.Project}, nil 150 } 151 152 func (f *fortifyMock) CreateProjectVersion(version *models.ProjectVersion) (*models.ProjectVersion, error) { 153 return version, nil 154 } 155 156 func (f *fortifyMock) ProjectVersionCopyFromPartial(sourceID, targetID int64) error { 157 return nil 158 } 159 160 func (f *fortifyMock) ProjectVersionCopyCurrentState(sourceID, targetID int64) error { 161 return nil 162 } 163 164 func (f *fortifyMock) ProjectVersionCopyPermissions(sourceID, targetID int64) error { 165 return nil 166 } 167 168 func (f *fortifyMock) CommitProjectVersion(id int64) (*models.ProjectVersion, error) { 169 name := "Committed" 170 return &models.ProjectVersion{ID: id, Name: &name}, nil 171 } 172 173 func (f *fortifyMock) MergeProjectVersionStateOfPRIntoMaster(downloadEndpoint, uploadEndpoint string, masterProjectID, masterProjectVersionID int64, pullRequestName string) error { 174 return nil 175 } 176 177 func (f *fortifyMock) GetArtifactsOfProjectVersion(id int64) ([]*models.Artifact, error) { 178 switch id { 179 case 4711: 180 return []*models.Artifact{{ 181 Status: "PROCESSED", 182 UploadDate: toFortifyTime(time.Now()), 183 }}, nil 184 case 4712: 185 return []*models.Artifact{{ 186 Status: "ERROR_PROCESSING", 187 UploadDate: toFortifyTime(time.Now()), 188 }}, nil 189 case 4713: 190 return []*models.Artifact{{ 191 Status: "REQUIRE_AUTH", 192 UploadDate: toFortifyTime(time.Now()), 193 }}, nil 194 case 4714: 195 return []*models.Artifact{{ 196 Status: "PROCESSING", 197 UploadDate: toFortifyTime(time.Now()), 198 }}, nil 199 case 4715: 200 return []*models.Artifact{{ 201 Status: "PROCESSED", 202 Embed: &models.EmbeddedScans{ 203 Scans: []*models.Scan{{BuildLabel: "/commit/test"}}, 204 }, 205 UploadDate: toFortifyTime(time.Now()), 206 }}, nil 207 case 4716: 208 var status string 209 if f.getArtifactsOfProjectVersionIdx == 0 { 210 f.getArtifactsOfProjectVersionTime = time.Now().Add(-2 * time.Minute) 211 } 212 if f.getArtifactsOfProjectVersionIdx < 2 { 213 status = "PROCESSING" 214 } else { 215 f.getArtifactsOfProjectVersionTime = time.Now() 216 status = "PROCESSED" 217 } 218 f.getArtifactsOfProjectVersionIdx++ 219 220 return []*models.Artifact{{ 221 Status: status, 222 UploadDate: toFortifyTime(f.getArtifactsOfProjectVersionTime), 223 }}, nil 224 case 4718: 225 return []*models.Artifact{ 226 { 227 Status: "PROCESSED", 228 UploadDate: toFortifyTime(time.Now()), 229 }, 230 { 231 Status: "ERROR_PROCESSING", 232 UploadDate: toFortifyTime(time.Now().Add(-2 * time.Minute)), 233 }, 234 }, nil 235 236 default: 237 return []*models.Artifact{}, nil 238 } 239 } 240 241 func (f *fortifyMock) GetFilterSetOfProjectVersionByTitle(id int64, title string) (*models.FilterSet, error) { 242 return &models.FilterSet{}, nil 243 } 244 245 func (f *fortifyMock) GetIssueFilterSelectorOfProjectVersionByName(id int64, names []string, options []string) (*models.IssueFilterSelectorSet, error) { 246 return &models.IssueFilterSelectorSet{}, nil 247 } 248 249 func (f *fortifyMock) GetFilterSetByDisplayName(issueFilterSelectorSet *models.IssueFilterSelectorSet, name string) *models.IssueFilterSelector { 250 if issueFilterSelectorSet.FilterBySet != nil { 251 for _, filter := range issueFilterSelectorSet.FilterBySet { 252 if filter.DisplayName == name { 253 return filter 254 } 255 } 256 } 257 return &models.IssueFilterSelector{DisplayName: name} 258 } 259 260 func (f *fortifyMock) GetProjectIssuesByIDAndFilterSetGroupedBySelector(id int64, filter, filterSetGUID string, issueFilterSelectorSet *models.IssueFilterSelectorSet) ([]*models.ProjectVersionIssueGroup, error) { 261 if filter == "ET1:abcd" { 262 group := "HTTP Verb tampering" 263 total := int32(4) 264 audited := int32(3) 265 group2 := "Password in code" 266 total2 := int32(4) 267 audited2 := int32(4) 268 group3 := "Memory leak" 269 total3 := int32(5) 270 audited3 := int32(4) 271 return []*models.ProjectVersionIssueGroup{ 272 {ID: &group, TotalCount: &total, AuditedCount: &audited}, 273 {ID: &group2, TotalCount: &total2, AuditedCount: &audited2}, 274 {ID: &group3, TotalCount: &total3, AuditedCount: &audited3}, 275 }, nil 276 } 277 if issueFilterSelectorSet != nil && issueFilterSelectorSet.FilterBySet != nil && len(issueFilterSelectorSet.FilterBySet) > 0 && issueFilterSelectorSet.FilterBySet[0].GUID == "3" { 278 groupName := "Suspicious" 279 groupName2 := "Exploitable" 280 group := "3" 281 total := int32(4) 282 audited := int32(0) 283 group2 := "4" 284 total2 := int32(5) 285 audited2 := int32(0) 286 return []*models.ProjectVersionIssueGroup{ 287 {ID: &group, CleanName: &groupName, TotalCount: &total, AuditedCount: &audited}, 288 {ID: &group2, CleanName: &groupName2, TotalCount: &total2, AuditedCount: &audited2}, 289 }, nil 290 } 291 group := "Audit All" 292 total := int32(15) 293 audited := int32(12) 294 group2 := "Corporate Security Requirements" 295 total2 := int32(20) 296 audited2 := int32(11) 297 group3 := "Spot Checks of Each Category" 298 total3 := int32(5) 299 audited3 := int32(4) 300 return []*models.ProjectVersionIssueGroup{ 301 {ID: &group, CleanName: &group, TotalCount: &total, AuditedCount: &audited}, 302 {ID: &group2, CleanName: &group2, TotalCount: &total2, AuditedCount: &audited2}, 303 {ID: &group3, CleanName: &group3, TotalCount: &total3, AuditedCount: &audited3}, 304 }, nil 305 } 306 307 func (f *fortifyMock) ReduceIssueFilterSelectorSet(issueFilterSelectorSet *models.IssueFilterSelectorSet, names []string, options []string) *models.IssueFilterSelectorSet { 308 return issueFilterSelectorSet 309 } 310 311 func (f *fortifyMock) GetIssueStatisticsOfProjectVersion(id int64) ([]*models.IssueStatistics, error) { 312 suppressed := int32(6) 313 return []*models.IssueStatistics{{SuppressedCount: &suppressed}}, nil 314 } 315 316 func (f *fortifyMock) GenerateQGateReport(projectID, projectVersionID, reportTemplateID int64, projectName, projectVersionName, reportFormat string) (*models.SavedReport, error) { 317 if !f.Successive { 318 f.Successive = true 319 return &models.SavedReport{Status: "PROCESSING"}, nil 320 } 321 f.Successive = false 322 return &models.SavedReport{Status: "PROCESS_COMPLETE"}, nil 323 } 324 325 func (f *fortifyMock) GetReportDetails(id int64) (*models.SavedReport, error) { 326 return &models.SavedReport{Status: "PROCESS_COMPLETE"}, nil 327 } 328 329 func (f *fortifyMock) GetAllIssueDetails(projectVersionId int64) ([]*models.ProjectVersionIssue, error) { 330 exploitable := "Exploitable" 331 friority := "High" 332 hascomments := true 333 return []*models.ProjectVersionIssue{{ID: 1111, Audited: true, PrimaryTag: &exploitable, HasComments: &hascomments, Friority: &friority}, {ID: 1112, Audited: true, PrimaryTag: &exploitable, HasComments: &hascomments, Friority: &friority}}, nil 334 } 335 336 func (f *fortifyMock) GetIssueDetails(projectVersionId int64, issueInstanceId string) ([]*models.ProjectVersionIssue, error) { 337 exploitable := "Exploitable" 338 friority := "High" 339 hascomments := true 340 return []*models.ProjectVersionIssue{{ID: 1111, Audited: true, PrimaryTag: &exploitable, HasComments: &hascomments, Friority: &friority}}, nil 341 } 342 343 func (f *fortifyMock) GetIssueComments(parentId int64) ([]*models.IssueAuditComment, error) { 344 comment := "Dummy" 345 return []*models.IssueAuditComment{{Comment: &comment}}, nil 346 } 347 348 func (f *fortifyMock) UploadResultFile(endpoint, file string, projectVersionID int64) error { 349 return nil 350 } 351 352 func (f *fortifyMock) DownloadReportFile(endpoint string, reportID int64) ([]byte, error) { 353 return []byte("abcd"), nil 354 } 355 356 func (f *fortifyMock) DownloadResultFile(endpoint string, projectVersionID int64) ([]byte, error) { 357 return []byte("defg"), nil 358 } 359 360 type pullRequestServiceMock struct{} 361 362 func (prService pullRequestServiceMock) ListPullRequestsWithCommit(ctx context.Context, owner, repo, sha string, opts *github.PullRequestListOptions) ([]*github.PullRequest, *github.Response, error) { 363 authorString := author 364 user := github.User{Login: &authorString} 365 if owner == "A" { 366 result := 17 367 return []*github.PullRequest{{Number: &result, User: &user}}, &github.Response{}, nil 368 } else if owner == "C" { 369 return []*github.PullRequest{{User: &user}}, &github.Response{}, errors.New("Test error") 370 } else if owner == "E" { 371 return []*github.PullRequest{{User: nil}}, &github.Response{}, errors.New("Test error") 372 } 373 return []*github.PullRequest{}, &github.Response{}, nil 374 } 375 376 type execRunnerMock struct { 377 numExecutions int 378 current *execution 379 executions []*execution 380 } 381 382 type execution struct { 383 dirValue string 384 envValue []string 385 outWriter io.Writer 386 errWriter io.Writer 387 executable string 388 parameters []string 389 } 390 391 func (er *execRunnerMock) newExecution() *execution { 392 newExecution := &execution{} 393 er.executions = append(er.executions, newExecution) 394 return newExecution 395 } 396 397 func (er *execRunnerMock) currentExecution() *execution { 398 if nil == er.current { 399 er.numExecutions = 0 400 er.current = er.newExecution() 401 } 402 return er.current 403 } 404 405 func (er *execRunnerMock) SetDir(d string) { 406 er.currentExecution().dirValue = d 407 } 408 409 func (er *execRunnerMock) SetEnv(e []string) { 410 er.currentExecution().envValue = e 411 } 412 413 func (er *execRunnerMock) Stdout(out io.Writer) { 414 er.currentExecution().outWriter = out 415 } 416 417 func (er *execRunnerMock) Stderr(err io.Writer) { 418 er.currentExecution().errWriter = err 419 } 420 421 func (er *execRunnerMock) RunExecutable(e string, p ...string) error { 422 er.numExecutions++ 423 er.currentExecution().executable = e 424 if len(p) > 0 && piperutils.ContainsString(p, "--failTranslate") { 425 return errors.New("Translate failed") 426 } 427 er.currentExecution().parameters = p 428 classpathPip := "/usr/lib/python35.zip;/usr/lib/python3.5;/usr/lib/python3.5/plat-x86_64-linux-gnu;/usr/lib/python3.5/lib-dynload;/home/piper/.local/lib/python3.5/site-packages;/usr/local/lib/python3.5/dist-packages;/usr/lib/python3/dist-packages;./lib" 429 classpathMaven := "some.jar;someother.jar" 430 if e == "python2" { 431 if p[1] == "invalid" { 432 return errors.New("Invalid command") 433 } 434 _, err := er.currentExecution().outWriter.Write([]byte(classpathPip)) 435 if err != nil { 436 return err 437 } 438 } else if e == "mvn" { 439 path := strings.ReplaceAll(p[2], "-Dmdep.outputFile=", "") 440 err := os.WriteFile(path, []byte(classpathMaven), 0o644) 441 if err != nil { 442 return err 443 } 444 } 445 er.current = er.newExecution() 446 return nil 447 } 448 449 func TestDetermineArtifact(t *testing.T) { 450 t.Run("Cannot get artifact without build tool", func(t *testing.T) { 451 utilsMock := newFortifyTestUtilsBundle() 452 utilsMock.getArtifactShouldFail = true 453 454 _, err := determineArtifact(fortifyExecuteScanOptions{}, &utilsMock) 455 assert.EqualError(t, err, "Unable to get artifact from descriptor : build tool '' not supported") 456 }) 457 } 458 459 func TestFailFortifyexecinPath(t *testing.T) { 460 t.Run("Testing if fortifyupdate in $PATH or not", func(t *testing.T) { 461 ff := fortifyMock{} 462 ctx := context.Background() 463 utils := newFortifyTestUtilsBundle() 464 influx := fortifyExecuteScanInflux{} 465 auditStatus := map[string]string{} 466 execInPath = failMockExecinPathfortifyupdate 467 config := fortifyExecuteScanOptions{SpotCheckMinimum: 4, MustAuditIssueGroups: "Audit All, Corporate Security Requirements", SpotAuditIssueGroups: "Spot Checks of Each Category"} 468 _, err := runFortifyScan(ctx, config, &ff, &utils, nil, &influx, auditStatus) 469 assert.EqualError(t, err, "Command not found: fortifyupdate. Please configure a supported docker image or install Fortify SCA on the system.") 470 471 }) 472 t.Run("Testing if sourceanalyzer in $PATH or not", func(t *testing.T) { 473 ff := fortifyMock{} 474 ctx := context.Background() 475 utils := newFortifyTestUtilsBundle() 476 influx := fortifyExecuteScanInflux{} 477 auditStatus := map[string]string{} 478 execInPath = failMockExecinPathsourceanalyzer 479 config := fortifyExecuteScanOptions{SpotCheckMinimum: 4, MustAuditIssueGroups: "Audit All, Corporate Security Requirements", SpotAuditIssueGroups: "Spot Checks of Each Category"} 480 _, err := runFortifyScan(ctx, config, &ff, &utils, nil, &influx, auditStatus) 481 assert.EqualError(t, err, "Command not found: sourceanalyzer. Please configure a supported docker image or install Fortify SCA on the system.") 482 483 }) 484 } 485 486 func TestExecutions(t *testing.T) { 487 type parameterTestData struct { 488 nameOfRun string 489 config fortifyExecuteScanOptions 490 expectedReportsLength int 491 expectedReports []string 492 } 493 494 testData := []parameterTestData{ 495 { 496 nameOfRun: "golang scan and verify", 497 config: fortifyExecuteScanOptions{BuildTool: "golang", BuildDescriptorFile: "go.mod"}, 498 expectedReportsLength: 2, 499 expectedReports: []string{"target/fortify-scan.*", "target/*.fpr"}, 500 }, 501 { 502 nameOfRun: "golang verify only", 503 config: fortifyExecuteScanOptions{BuildTool: "golang", BuildDescriptorFile: "go.mod", VerifyOnly: true}, 504 expectedReportsLength: 0, 505 }, 506 { 507 nameOfRun: "maven scan and verify", 508 config: fortifyExecuteScanOptions{BuildTool: "maven", BuildDescriptorFile: "pom.xml", UpdateRulePack: true, Reporting: true, UploadResults: true}, 509 expectedReportsLength: 2, 510 expectedReports: []string{"target/fortify-scan.*", "target/*.fpr"}, 511 }, 512 } 513 514 for _, data := range testData { 515 t.Run(data.nameOfRun, func(t *testing.T) { 516 ctx := context.Background() 517 ff := fortifyMock{} 518 utils := newFortifyTestUtilsBundle() 519 influx := fortifyExecuteScanInflux{} 520 auditStatus := map[string]string{} 521 execInPath = mockExecinPath 522 reports, _ := runFortifyScan(ctx, data.config, &ff, &utils, nil, &influx, auditStatus) 523 if len(data.expectedReports) != data.expectedReportsLength { 524 assert.Fail(t, fmt.Sprintf("Wrong number of reports detected, expected %v, actual %v", data.expectedReportsLength, len(data.expectedReports))) 525 } 526 if len(data.expectedReports) > 0 { 527 for _, expectedPath := range data.expectedReports { 528 found := false 529 for _, actualPath := range reports { 530 if actualPath.Target == expectedPath { 531 found = true 532 } 533 } 534 if !found { 535 assert.Failf(t, "Expected path %s not found", expectedPath) 536 } 537 } 538 } 539 }) 540 } 541 } 542 543 func TestAnalyseSuspiciousExploitable(t *testing.T) { 544 config := fortifyExecuteScanOptions{SpotCheckMinimum: 4, MustAuditIssueGroups: "Audit All, Corporate Security Requirements", SpotAuditIssueGroups: "Spot Checks of Each Category"} 545 ff := fortifyMock{} 546 influx := fortifyExecuteScanInflux{} 547 name := "test" 548 selectorGUID := "3" 549 selectorName := "Analysis" 550 selectorEntityType := "CUSTOMTAG" 551 projectVersion := models.ProjectVersion{ID: 4711, Name: &name} 552 auditStatus := map[string]string{} 553 selectorSet := models.IssueFilterSelectorSet{ 554 FilterBySet: []*models.IssueFilterSelector{ 555 { 556 GUID: selectorGUID, 557 DisplayName: selectorName, 558 EntityType: selectorEntityType, 559 }, 560 }, 561 GroupBySet: []*models.IssueSelector{ 562 { 563 GUID: &selectorGUID, 564 DisplayName: &selectorName, 565 EntityType: &selectorEntityType, 566 }, 567 }, 568 } 569 issues, groups := analyseSuspiciousExploitable(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus) 570 assert.Equal(t, 9, issues) 571 assert.Equal(t, 2, len(groups)) 572 573 assert.Equal(t, 4, influx.fortify_data.fields.suspicious) 574 assert.Equal(t, 5, influx.fortify_data.fields.exploitable) 575 assert.Equal(t, 6, influx.fortify_data.fields.suppressed) 576 } 577 578 func TestAnalyseUnauditedIssues(t *testing.T) { 579 config := fortifyExecuteScanOptions{SpotCheckMinimumUnit: "number", SpotCheckMinimum: 4, MustAuditIssueGroups: "Audit All, Corporate Security Requirements", SpotAuditIssueGroups: "Spot Checks of Each Category"} 580 ff := fortifyMock{} 581 influx := fortifyExecuteScanInflux{} 582 name := "test" 583 projectVersion := models.ProjectVersion{ID: 4711, Name: &name} 584 auditStatus := map[string]string{} 585 selectorSet := models.IssueFilterSelectorSet{ 586 FilterBySet: []*models.IssueFilterSelector{ 587 { 588 GUID: "1", 589 DisplayName: "Folder", 590 EntityType: "ET1", 591 SelectorOptions: []*models.SelectorOption{ 592 { 593 Value: "abcd", 594 }, 595 }, 596 }, 597 { 598 GUID: "2", 599 DisplayName: "Category", 600 EntityType: "ET2", 601 SelectorOptions: []*models.SelectorOption{ 602 { 603 Value: "abcd", 604 }, 605 }, 606 }, 607 { 608 GUID: "3", 609 DisplayName: "Analysis", 610 EntityType: "ET3", 611 SelectorOptions: []*models.SelectorOption{ 612 { 613 Value: "abcd", 614 }, 615 }, 616 }, 617 }, 618 } 619 620 spotChecksCountByCategory := []fortify.SpotChecksAuditCount{} 621 issues, groups, err := analyseUnauditedIssues(config, &ff, &projectVersion, &models.FilterSet{}, &selectorSet, &influx, auditStatus, &spotChecksCountByCategory) 622 assert.NoError(t, err) 623 assert.Equal(t, 13, issues) 624 assert.Equal(t, 3, len(groups)) 625 626 assert.Equal(t, 15, influx.fortify_data.fields.auditAllTotal) 627 assert.Equal(t, 12, influx.fortify_data.fields.auditAllAudited) 628 assert.Equal(t, 20, influx.fortify_data.fields.corporateTotal) 629 assert.Equal(t, 11, influx.fortify_data.fields.corporateAudited) 630 assert.Equal(t, 13, influx.fortify_data.fields.spotChecksTotal) 631 assert.Equal(t, 11, influx.fortify_data.fields.spotChecksAudited) 632 assert.Equal(t, 1, influx.fortify_data.fields.spotChecksGap) 633 assert.Equal(t, 3, len(spotChecksCountByCategory)) 634 } 635 636 func TestAnalyseUnauditedIssuesWithWrongConfig(t *testing.T) { 637 config := fortifyExecuteScanOptions{SpotCheckMinimumUnit: "float"} 638 spotChecksCountByCategory := []fortify.SpotChecksAuditCount{} 639 ff := fortifyMock{} 640 auditStatus := map[string]string{} 641 _, _, err := analyseUnauditedIssues(config, &ff, &models.ProjectVersion{}, &models.FilterSet{}, &models.IssueFilterSelectorSet{}, &fortifyExecuteScanInflux{}, auditStatus, &spotChecksCountByCategory) 642 assert.Error(t, err) 643 assert.Equal(t, "Invalid spotCheckMinimumUnit. Please set it as 'percentage' or 'number'.", err.Error()) 644 } 645 646 func TestTriggerFortifyScan(t *testing.T) { 647 t.Run("maven", func(t *testing.T) { 648 dir := t.TempDir() 649 oldCWD, _ := os.Getwd() 650 _ = os.Chdir(dir) 651 // clean up tmp dir 652 defer func() { 653 _ = os.Chdir(oldCWD) 654 }() 655 656 utils := newFortifyTestUtilsBundle() 657 config := fortifyExecuteScanOptions{ 658 BuildTool: "maven", 659 AutodetectClasspath: true, 660 BuildDescriptorFile: "./pom.xml", 661 AdditionalScanParameters: []string{"-Dtest=property"}, 662 Memory: "-Xmx4G -Xms2G", 663 Src: []string{"**/*.xml", "**/*.html", "**/*.jsp", "**/*.js", "src/main/resources/**/*", "src/main/java/**/*"}, 664 } 665 triggerFortifyScan(config, &utils, "test", "testLabel", "my.group-myartifact") 666 667 assert.Equal(t, 3, utils.numExecutions) 668 669 assert.Equal(t, "mvn", utils.executions[0].executable) 670 assert.Equal(t, []string{"--file", "./pom.xml", "-Dmdep.outputFile=fortify-execute-scan-cp.txt", "-Dfortify", "-DincludeScope=compile", "-DskipTests", "-Dmaven.javadoc.skip=true", "--fail-at-end", "-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn", "--batch-mode", "dependency:build-classpath", "package"}, utils.executions[0].parameters) 671 672 assert.Equal(t, "sourceanalyzer", utils.executions[1].executable) 673 assert.True(t, reflect.DeepEqual([]string{"-verbose", "-64", "-b", "test", "-Xmx4G", "-Xms2G", "-cp", "some.jar;someother.jar", "-exclude", "**/src/test/**/*", "**/*.xml", "**/*.html", "**/*.jsp", "**/*.js", "src/main/resources/**/*", "src/main/java/**/*"}, utils.executions[1].parameters) || reflect.DeepEqual([]string{"-verbose", "-64", "-b", "test", "-Xmx4G", "-Xms2G", "-cp", "some.jar;someother.jar", "-exclude", "**/src/test/**/*", "**/*.xml", "**/*.html", "**/*.jsp", "**/*.js", "src/main/resources/**/*", "src/main/java/**/*"}, utils.executions[1].parameters)) 674 675 assert.Equal(t, "sourceanalyzer", utils.executions[2].executable) 676 assert.Equal(t, []string{"-verbose", "-64", "-b", "test", "-scan", "-Xmx4G", "-Xms2G", "-Dtest=property", "-build-label", "testLabel", "-build-project", "my.group-myartifact", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, utils.executions[2].parameters) 677 }) 678 679 t.Run("pip", func(t *testing.T) { 680 dir := t.TempDir() 681 oldCWD, _ := os.Getwd() 682 _ = os.Chdir(dir) 683 // clean up tmp dir 684 defer func() { 685 _ = os.Chdir(oldCWD) 686 }() 687 688 utils := newFortifyTestUtilsBundle() 689 config := fortifyExecuteScanOptions{BuildTool: "pip", PythonVersion: "python2", AutodetectClasspath: true, BuildDescriptorFile: "./setup.py", PythonRequirementsFile: "./requirements.txt", PythonInstallCommand: "pip2 install --user", Memory: "-Xmx4G -Xms2G"} 690 triggerFortifyScan(config, &utils, "test", "testLabel", "") 691 692 assert.Equal(t, 5, utils.numExecutions) 693 694 assert.Equal(t, "python2", utils.executions[0].executable) 695 separator := getSeparator() 696 template := fmt.Sprintf("import sys;p=sys.path;p.remove('');print('%v'.join(p))", separator) 697 assert.Equal(t, []string{"-c", template}, utils.executions[0].parameters) 698 699 assert.Equal(t, "pip2", utils.executions[1].executable) 700 assert.Equal(t, []string{"install", "--user", "-r", "./requirements.txt", ""}, utils.executions[1].parameters) 701 702 assert.Equal(t, "pip2", utils.executions[2].executable) 703 assert.Equal(t, []string{"install", "--user"}, utils.executions[2].parameters) 704 705 assert.Equal(t, "sourceanalyzer", utils.executions[3].executable) 706 assert.Equal(t, []string{"-verbose", "-64", "-b", "test", "-Xmx4G", "-Xms2G", "-python-path", "/usr/lib/python35.zip;/usr/lib/python3.5;/usr/lib/python3.5/plat-x86_64-linux-gnu;/usr/lib/python3.5/lib-dynload;/home/piper/.local/lib/python3.5/site-packages;/usr/local/lib/python3.5/dist-packages;/usr/lib/python3/dist-packages;./lib", "-python-version", "2", "-exclude", fmt.Sprintf("./**/tests/**/*%s./**/setup.py", separator), "./**/*"}, utils.executions[3].parameters) 707 708 assert.Equal(t, "sourceanalyzer", utils.executions[4].executable) 709 assert.Equal(t, []string{"-verbose", "-64", "-b", "test", "-scan", "-Xmx4G", "-Xms2G", "-build-label", "testLabel", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, utils.executions[4].parameters) 710 }) 711 712 t.Run("invalid buildTool", func(t *testing.T) { 713 dir := t.TempDir() 714 oldCWD, _ := os.Getwd() 715 _ = os.Chdir(dir) 716 // clean up tmp dir 717 defer func() { 718 _ = os.Chdir(oldCWD) 719 }() 720 721 utils := newFortifyTestUtilsBundle() 722 config := fortifyExecuteScanOptions{ 723 BuildTool: "docker", 724 AutodetectClasspath: true, 725 } 726 err := triggerFortifyScan(config, &utils, "test", "testLabel", "my.group-myartifact") 727 728 assert.Error(t, err) 729 assert.Equal(t, "buildTool 'docker' is not supported by this step", err.Error()) 730 }) 731 } 732 733 func TestGetMinSpotChecksPerCategory(t *testing.T) { 734 testExpectedGetMinSpotChecksPerCategory := func(spotChecksMinUnit string, spotChecksMax int, spotChecksMin int, issuesPerCategory int, spotChecksMinCalculatedExpected int) { 735 testName := fmt.Sprintf("Test GetMinSpotChecksPerCategory for SpotCheckMinimumUnit: %v, SpotCheckMaximum: %v, SpotCheckMinimum: %v, issuesPerCategory: %v", spotChecksMinUnit, spotChecksMax, spotChecksMin, issuesPerCategory) 736 t.Run(testName, func(t *testing.T) { 737 config := fortifyExecuteScanOptions{SpotCheckMinimumUnit: spotChecksMinUnit, SpotCheckMaximum: spotChecksMax, SpotCheckMinimum: spotChecksMin} 738 spotCheckMin := getMinSpotChecksPerCategory(config, issuesPerCategory) 739 assert.Equal(t, spotChecksMinCalculatedExpected, spotCheckMin) 740 }) 741 } 742 743 testExpectedGetMinSpotChecksPerCategory("percentage", 0, 1, 10, 1) 744 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 10, 3, 1) 745 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 10, 8, 1) 746 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 10, 10, 1) 747 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 10, 24, 3) 748 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 10, 26, 3) 749 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 10, 100, 10) 750 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 10, 200, 10) 751 testExpectedGetMinSpotChecksPerCategory("percentage", 10, 50, 10, 5) 752 testExpectedGetMinSpotChecksPerCategory("percentage", 0, 50, 100, 50) 753 testExpectedGetMinSpotChecksPerCategory("percentage", -10, 50, 100, 50) 754 755 testExpectedGetMinSpotChecksPerCategory("number", 0, 1, 10, 1) 756 testExpectedGetMinSpotChecksPerCategory("number", 5, 10, 100, 5) 757 } 758 759 func TestGenerateAndDownloadQGateReport(t *testing.T) { 760 ffMock := fortifyMock{Successive: false} 761 config := fortifyExecuteScanOptions{ReportTemplateID: 18, ReportType: "PDF"} 762 name := "test" 763 projectVersion := models.ProjectVersion{ID: 4711, Name: &name} 764 project := models.Project{ID: 815, Name: &name} 765 projectVersion.Project = &project 766 767 t.Run("success", func(t *testing.T) { 768 data, err := generateAndDownloadQGateReport(config, &ffMock, &project, &projectVersion) 769 assert.NoError(t, err) 770 assert.Equal(t, []byte("abcd"), data) 771 }) 772 } 773 774 var ( 775 defaultPollingDelay = 10 * time.Second 776 defaultPollingTimeout = 0 * time.Minute 777 ) 778 779 func verifyScanResultsFinishedUploadingDefaults(config fortifyExecuteScanOptions, sys fortify.System, projectVersionID int64) error { 780 return verifyScanResultsFinishedUploading(config, sys, projectVersionID, "", &models.FilterSet{}, 781 defaultPollingDelay, defaultPollingTimeout) 782 } 783 784 func TestVerifyScanResultsFinishedUploading(t *testing.T) { 785 t.Parallel() 786 787 t.Run("error no recent upload detected", func(t *testing.T) { 788 ffMock := fortifyMock{} 789 config := fortifyExecuteScanOptions{DeltaMinutes: -1} 790 err := verifyScanResultsFinishedUploadingDefaults(config, &ffMock, 4711) 791 assert.EqualError(t, err, "no recent upload detected on Project Version") 792 }) 793 794 config := fortifyExecuteScanOptions{DeltaMinutes: 20} 795 t.Run("success", func(t *testing.T) { 796 ffMock := fortifyMock{} 797 err := verifyScanResultsFinishedUploadingDefaults(config, &ffMock, 4711) 798 assert.NoError(t, err) 799 }) 800 801 t.Run("error processing", func(t *testing.T) { 802 ffMock := fortifyMock{} 803 err := verifyScanResultsFinishedUploadingDefaults(config, &ffMock, 4712) 804 assert.EqualError(t, err, "There are artifacts that failed processing for Project Version 4712\n/html/ssc/index.jsp#!/version/4712/artifacts?filterSet=") 805 }) 806 807 t.Run("error required auth", func(t *testing.T) { 808 ffMock := fortifyMock{} 809 err := verifyScanResultsFinishedUploadingDefaults(config, &ffMock, 4713) 810 assert.EqualError(t, err, "There are artifacts that require manual approval for Project Version 4713, please visit Fortify SSC and approve them for processing\n/html/ssc/index.jsp#!/version/4713/artifacts?filterSet=") 811 }) 812 813 t.Run("error polling timeout", func(t *testing.T) { 814 ffMock := fortifyMock{} 815 err := verifyScanResultsFinishedUploadingDefaults(config, &ffMock, 4714) 816 assert.EqualError(t, err, "terminating after 0s since artifact for Project Version 4714 is still in status PROCESSING") 817 }) 818 819 t.Run("success build label", func(t *testing.T) { 820 ffMock := fortifyMock{} 821 err := verifyScanResultsFinishedUploading(config, &ffMock, 4715, "/commit/test", &models.FilterSet{}, 822 10*time.Second, time.Duration(config.PollingMinutes)*time.Minute) 823 assert.NoError(t, err) 824 }) 825 826 t.Run("failure after polling", func(t *testing.T) { 827 config := fortifyExecuteScanOptions{DeltaMinutes: 1} 828 ffMock := fortifyMock{} 829 const pollingDelay = 1 * time.Second 830 const timeout = 1 * time.Second 831 err := verifyScanResultsFinishedUploading(config, &ffMock, 4716, "", &models.FilterSet{}, pollingDelay, timeout) 832 assert.EqualError(t, err, "terminating after 1s since artifact for Project Version 4716 is still in status PROCESSING") 833 }) 834 835 t.Run("success after polling", func(t *testing.T) { 836 config := fortifyExecuteScanOptions{DeltaMinutes: 1} 837 ffMock := fortifyMock{} 838 const pollingDelay = 500 * time.Millisecond 839 const timeout = 1 * time.Second 840 err := verifyScanResultsFinishedUploading(config, &ffMock, 4716, "", &models.FilterSet{}, pollingDelay, timeout) 841 assert.NoError(t, err) 842 }) 843 844 t.Run("error no artifacts", func(t *testing.T) { 845 ffMock := fortifyMock{} 846 err := verifyScanResultsFinishedUploadingDefaults(config, &ffMock, 4717) 847 assert.EqualError(t, err, "no uploaded artifacts for assessment detected for project version with ID 4717") 848 }) 849 850 t.Run("warn old artifacts have errors", func(t *testing.T) { 851 ffMock := fortifyMock{} 852 853 logBuffer := new(bytes.Buffer) 854 logOutput := log.Entry().Logger.Out 855 log.Entry().Logger.Out = logBuffer 856 defer func() { log.Entry().Logger.Out = logOutput }() 857 858 err := verifyScanResultsFinishedUploadingDefaults(config, &ffMock, 4718) 859 assert.NoError(t, err) 860 assert.Contains(t, logBuffer.String(), "Previous uploads detected that failed processing") 861 }) 862 } 863 864 func TestCalculateTimeDifferenceToLastUpload(t *testing.T) { 865 diffSeconds := calculateTimeDifferenceToLastUpload(models.Iso8601MilliDateTime(time.Now().UTC()), 1234) 866 867 assert.Equal(t, true, diffSeconds < 1) 868 } 869 870 func TestExecuteTemplatedCommand(t *testing.T) { 871 utils := newFortifyTestUtilsBundle() 872 template := []string{"{{.Executable}}", "-c", "{{.Param}}"} 873 context := map[string]string{"Executable": "test.cmd", "Param": "abcd"} 874 executeTemplatedCommand(&utils, template, context) 875 876 assert.Equal(t, "test.cmd", utils.executions[0].executable) 877 assert.Equal(t, []string{"-c", "abcd"}, utils.executions[0].parameters) 878 } 879 880 func TestDeterminePullRequestMerge(t *testing.T) { 881 config := fortifyExecuteScanOptions{CommitMessage: "Merge pull request #2462 from branch f-test", PullRequestMessageRegex: `(?m).*Merge pull request #(\d+) from.*`, PullRequestMessageRegexGroup: 1} 882 883 t.Run("success", func(t *testing.T) { 884 match, authorString := determinePullRequestMerge(config) 885 assert.Equal(t, "2462", match, "Expected different result") 886 assert.Equal(t, "", authorString, "Expected different result") 887 }) 888 889 t.Run("no match", func(t *testing.T) { 890 config.CommitMessage = "Some test commit" 891 match, authorString := determinePullRequestMerge(config) 892 assert.Equal(t, "0", match, "Expected different result") 893 assert.Equal(t, "", authorString, "Expected different result") 894 }) 895 } 896 897 func TestDeterminePullRequestMergeGithub(t *testing.T) { 898 prServiceMock := pullRequestServiceMock{} 899 900 t.Run("success", func(t *testing.T) { 901 match, authorString, err := determinePullRequestMergeGithub(nil, fortifyExecuteScanOptions{Owner: "A"}, prServiceMock) 902 assert.NoError(t, err) 903 assert.Equal(t, "17", match, "Expected different result") 904 assert.Equal(t, author, authorString, "Expected different result") 905 }) 906 907 t.Run("no match", func(t *testing.T) { 908 match, authorString, err := determinePullRequestMergeGithub(nil, fortifyExecuteScanOptions{Owner: "B"}, prServiceMock) 909 assert.NoError(t, err) 910 assert.Equal(t, "0", match, "Expected different result") 911 assert.Equal(t, "", authorString, "Expected different result") 912 }) 913 914 t.Run("error", func(t *testing.T) { 915 match, authorString, err := determinePullRequestMergeGithub(nil, fortifyExecuteScanOptions{Owner: "E"}, prServiceMock) 916 assert.EqualError(t, err, "Test error") 917 assert.Equal(t, "0", match, "Expected different result") 918 assert.Equal(t, "", authorString, "Expected different result") 919 }) 920 } 921 922 func TestTranslateProject(t *testing.T) { 923 t.Run("python", func(t *testing.T) { 924 utils := newFortifyTestUtilsBundle() 925 config := fortifyExecuteScanOptions{BuildTool: "pip", Memory: "-Xmx4G", Translate: `[{"pythonPath":"./some/path","src":"./**/*","exclude":"./tests/**/*"}]`} 926 translateProject(&config, &utils, "/commit/7267658798797", "") 927 assert.Equal(t, "sourceanalyzer", utils.executions[0].executable, "Expected different executable") 928 assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx4G", "-python-path", "./some/path", "-exclude", "./tests/**/*", "./**/*"}, utils.executions[0].parameters, "Expected different parameters") 929 }) 930 931 t.Run("asp", func(t *testing.T) { 932 utils := newFortifyTestUtilsBundle() 933 config := fortifyExecuteScanOptions{BuildTool: "windows", Memory: "-Xmx6G", Translate: `[{"aspnetcore":"true","dotNetCoreVersion":"3.5","exclude":"./tests/**/*","libDirs":"tmp/","src":"./**/*"}]`} 934 translateProject(&config, &utils, "/commit/7267658798797", "") 935 assert.Equal(t, "sourceanalyzer", utils.executions[0].executable, "Expected different executable") 936 assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx6G", "-aspnetcore", "-dotnet-core-version", "3.5", "-libdirs", "tmp/", "-exclude", "./tests/**/*", "./**/*"}, utils.executions[0].parameters, "Expected different parameters") 937 }) 938 939 t.Run("java", func(t *testing.T) { 940 utils := newFortifyTestUtilsBundle() 941 config := fortifyExecuteScanOptions{BuildTool: "maven", Memory: "-Xmx2G", Translate: `[{"classpath":"./classes/*.jar","extdirs":"tmp/","jdk":"1.8.0-21","source":"1.8","sourcepath":"src/ext/","src":"./**/*"}]`} 942 translateProject(&config, &utils, "/commit/7267658798797", "") 943 assert.Equal(t, "sourceanalyzer", utils.executions[0].executable, "Expected different executable") 944 assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx2G", "-cp", "./classes/*.jar", "-extdirs", "tmp/", "-source", "1.8", "-jdk", "1.8.0-21", "-sourcepath", "src/ext/", "./**/*"}, utils.executions[0].parameters, "Expected different parameters") 945 }) 946 947 t.Run("auto classpath", func(t *testing.T) { 948 utils := newFortifyTestUtilsBundle() 949 config := fortifyExecuteScanOptions{BuildTool: "maven", Memory: "-Xmx2G", Translate: `[{"classpath":"./classes/*.jar", "extdirs":"tmp/","jdk":"1.8.0-21","source":"1.8","sourcepath":"src/ext/","src":"./**/*"}]`} 950 translateProject(&config, &utils, "/commit/7267658798797", "./WEB-INF/lib/*.jar") 951 assert.Equal(t, "sourceanalyzer", utils.executions[0].executable, "Expected different executable") 952 assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-Xmx2G", "-cp", "./WEB-INF/lib/*.jar", "-extdirs", "tmp/", "-source", "1.8", "-jdk", "1.8.0-21", "-sourcepath", "src/ext/", "./**/*"}, utils.executions[0].parameters, "Expected different parameters") 953 }) 954 955 t.Run("failure propagated", func(t *testing.T) { 956 utils := newFortifyTestUtilsBundle() 957 config := fortifyExecuteScanOptions{BuildTool: "maven", Memory: "-Xmx2G", Translate: `[{"classpath":"./classes/*.jar", "extdirs":"tmp/","jdk":"1.8.0-21","source":"1.8","sourcepath":"src/ext/","src":"./**/*"}]`} 958 err := translateProject(&config, &utils, "--failTranslate", "./WEB-INF/lib/*.jar") 959 assert.Error(t, err) 960 assert.Equal(t, "failed to execute sourceanalyzer translate command with options [-verbose -64 -b --failTranslate -Xmx2G -cp ./WEB-INF/lib/*.jar -extdirs tmp/ -source 1.8 -jdk 1.8.0-21 -sourcepath src/ext/ ./**/*]: Translate failed", err.Error()) 961 }) 962 } 963 964 func TestScanProject(t *testing.T) { 965 config := fortifyExecuteScanOptions{Memory: "-Xmx4G"} 966 967 t.Run("normal", func(t *testing.T) { 968 utils := newFortifyTestUtilsBundle() 969 scanProject(&config, &utils, "/commit/7267658798797", "label", "my.group-myartifact") 970 assert.Equal(t, "sourceanalyzer", utils.executions[0].executable, "Expected different executable") 971 assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-scan", "-Xmx4G", "-build-label", "label", "-build-project", "my.group-myartifact", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, utils.executions[0].parameters, "Expected different parameters") 972 }) 973 974 t.Run("quick", func(t *testing.T) { 975 utils := newFortifyTestUtilsBundle() 976 config.QuickScan = true 977 scanProject(&config, &utils, "/commit/7267658798797", "", "") 978 assert.Equal(t, "sourceanalyzer", utils.executions[0].executable, "Expected different executable") 979 assert.Equal(t, []string{"-verbose", "-64", "-b", "/commit/7267658798797", "-scan", "-Xmx4G", "-quick", "-logfile", "target/fortify-scan.log", "-f", "target/result.fpr"}, utils.executions[0].parameters, "Expected different parameters") 980 }) 981 } 982 983 func TestAutoresolveClasspath(t *testing.T) { 984 t.Run("success pip", func(t *testing.T) { 985 utils := newFortifyTestUtilsBundle() 986 dir := t.TempDir() 987 file := filepath.Join(dir, "cp.txt") 988 989 result, err := autoresolvePipClasspath("python2", []string{"-c", "import sys;p=sys.path;p.remove('');print(';'.join(p))"}, file, &utils) 990 assert.NoError(t, err) 991 assert.Equal(t, "python2", utils.executions[0].executable, "Expected different executable") 992 assert.Equal(t, []string{"-c", "import sys;p=sys.path;p.remove('');print(';'.join(p))"}, utils.executions[0].parameters, "Expected different parameters") 993 assert.Equal(t, "/usr/lib/python35.zip;/usr/lib/python3.5;/usr/lib/python3.5/plat-x86_64-linux-gnu;/usr/lib/python3.5/lib-dynload;/home/piper/.local/lib/python3.5/site-packages;/usr/local/lib/python3.5/dist-packages;/usr/lib/python3/dist-packages;./lib", result, "Expected different result") 994 }) 995 996 t.Run("error pip file", func(t *testing.T) { 997 utils := newFortifyTestUtilsBundle() 998 999 _, err := autoresolvePipClasspath("python2", []string{"-c", "import sys;p=sys.path;p.remove('');print(';'.join(p))"}, "../.", &utils) 1000 assert.Error(t, err) 1001 }) 1002 1003 t.Run("error pip command", func(t *testing.T) { 1004 utils := newFortifyTestUtilsBundle() 1005 dir := t.TempDir() 1006 file := filepath.Join(dir, "cp.txt") 1007 1008 _, err := autoresolvePipClasspath("python2", []string{"-c", "invalid"}, file, &utils) 1009 assert.Error(t, err) 1010 assert.Equal(t, "failed to run classpath autodetection command python2 with parameters [-c invalid]: Invalid command", err.Error()) 1011 }) 1012 1013 t.Run("success maven", func(t *testing.T) { 1014 utils := newFortifyTestUtilsBundle() 1015 dir := t.TempDir() 1016 file := filepath.Join(dir, "cp.txt") 1017 1018 result, err := autoresolveMavenClasspath(fortifyExecuteScanOptions{BuildDescriptorFile: "pom.xml"}, file, &utils) 1019 assert.NoError(t, err) 1020 assert.Equal(t, "mvn", utils.executions[0].executable, "Expected different executable") 1021 assert.Equal(t, []string{"--file", "pom.xml", fmt.Sprintf("-Dmdep.outputFile=%v", file), "-Dfortify", "-DincludeScope=compile", "-DskipTests", "-Dmaven.javadoc.skip=true", "--fail-at-end", "-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn", "--batch-mode", "dependency:build-classpath", "package"}, utils.executions[0].parameters, "Expected different parameters") 1022 assert.Equal(t, "some.jar;someother.jar", result, "Expected different result") 1023 }) 1024 } 1025 1026 func TestPopulateMavenTranslate(t *testing.T) { 1027 t.Run("src without translate", func(t *testing.T) { 1028 config := fortifyExecuteScanOptions{Src: []string{"./**/*"}} 1029 translate, err := populateMavenGradleTranslate(&config, "") 1030 assert.NoError(t, err) 1031 assert.Equal(t, `[{"classpath":"","exclude":"**/src/test/**/*","src":"./**/*"}]`, translate) 1032 }) 1033 1034 t.Run("exclude without translate", func(t *testing.T) { 1035 config := fortifyExecuteScanOptions{Exclude: []string{"./**/*"}} 1036 translate, err := populateMavenGradleTranslate(&config, "") 1037 assert.NoError(t, err) 1038 assert.Equal(t, `[{"classpath":"","exclude":"./**/*","src":"**/*.xml:**/*.html:**/*.jsp:**/*.js:**/src/main/resources/**/*:**/src/main/java/**/*:**/src/gen/java/cds/**/*:**/target/main/java/**/*:**/target/main/resources/**/*:**/target/generated-sources/**/*"}]`, translate) 1039 }) 1040 1041 t.Run("with translate", func(t *testing.T) { 1042 config := fortifyExecuteScanOptions{Translate: `[{"classpath":""}]`, Src: []string{"./**/*"}, Exclude: []string{"./**/*"}} 1043 translate, err := populateMavenGradleTranslate(&config, "ignored/path") 1044 assert.NoError(t, err) 1045 assert.Equal(t, `[{"classpath":""}]`, translate) 1046 }) 1047 } 1048 1049 func TestPopulatePipTranslate(t *testing.T) { 1050 t.Run("PythonAdditionalPath without translate", func(t *testing.T) { 1051 config := fortifyExecuteScanOptions{PythonVersion: "python2", PythonAdditionalPath: []string{"./lib", "."}} 1052 translate, err := populatePipTranslate(&config, "") 1053 separator := getSeparator() 1054 expected := fmt.Sprintf(`[{"exclude":"./**/tests/**/*%v./**/setup.py","pythonPath":"%v./lib%v.","pythonVersion":"2","src":"./**/*"}]`, 1055 separator, separator, separator) 1056 assert.NoError(t, err) 1057 assert.Equal(t, expected, translate) 1058 }) 1059 1060 t.Run("Invalid python version", func(t *testing.T) { 1061 config := fortifyExecuteScanOptions{PythonVersion: "python4", PythonAdditionalPath: []string{"./lib", "."}} 1062 _, err := populatePipTranslate(&config, "") 1063 assert.Error(t, err) 1064 }) 1065 1066 t.Run("Src without translate", func(t *testing.T) { 1067 config := fortifyExecuteScanOptions{PythonVersion: "python3", Src: []string{"./**/*.py"}} 1068 translate, err := populatePipTranslate(&config, "") 1069 separator := getSeparator() 1070 expected := fmt.Sprintf( 1071 `[{"exclude":"./**/tests/**/*%v./**/setup.py","pythonPath":"%v","pythonVersion":"3","src":"./**/*.py"}]`, 1072 separator, separator) 1073 assert.NoError(t, err) 1074 assert.Equal(t, expected, translate) 1075 }) 1076 1077 t.Run("Exclude without translate", func(t *testing.T) { 1078 config := fortifyExecuteScanOptions{PythonVersion: "python3", Exclude: []string{"./**/tests/**/*"}} 1079 translate, err := populatePipTranslate(&config, "") 1080 separator := getSeparator() 1081 expected := fmt.Sprintf( 1082 `[{"exclude":"./**/tests/**/*","pythonPath":"%v","pythonVersion":"3","src":"./**/*"}]`, 1083 separator) 1084 assert.NoError(t, err) 1085 assert.Equal(t, expected, translate) 1086 }) 1087 1088 t.Run("with translate", func(t *testing.T) { 1089 config := fortifyExecuteScanOptions{ 1090 Translate: `[{"pythonPath":""}]`, 1091 Src: []string{"./**/*"}, 1092 PythonAdditionalPath: []string{"./lib", "."}, 1093 } 1094 translate, err := populatePipTranslate(&config, "ignored/path") 1095 assert.NoError(t, err) 1096 assert.Equal(t, `[{"pythonPath":""}]`, translate, "Expected different parameters") 1097 }) 1098 } 1099 1100 func TestRemoveDuplicates(t *testing.T) { 1101 testData := []struct { 1102 name string 1103 input string 1104 expected string 1105 separator string 1106 }{ 1107 {"empty", "", "", "x"}, 1108 {"no duplicates", ":a::b::", "a:b", ":"}, 1109 {"duplicates", "::a:b:a:b::a", "a:b", ":"}, 1110 {"long separator", "..a.b....ab..a.b", "a.b..ab", ".."}, 1111 {"no separator", "abc", "abc", ""}, 1112 } 1113 for _, data := range testData { 1114 t.Run(data.name, func(t *testing.T) { 1115 assert.Equal(t, data.expected, removeDuplicates(data.input, data.separator)) 1116 }) 1117 } 1118 } 1119 1120 func toFortifyTime(time time.Time) models.Iso8601MilliDateTime { 1121 return models.Iso8601MilliDateTime(time.UTC()) 1122 } 1123 1124 func TestGetProxyParams(t *testing.T) { 1125 t.Run("Valid Proxy URL", func(t *testing.T) { 1126 proxyPort, proxyHost := getProxyParams("http://testproxy.com:8080") 1127 assert.Equal(t, "8080", proxyPort) 1128 assert.Equal(t, "testproxy.com", proxyHost) 1129 }) 1130 1131 t.Run("Invalid Proxy URL", func(t *testing.T) { 1132 proxyPort, proxyHost := getProxyParams("testproxy.com:8080") 1133 assert.Equal(t, "", proxyPort) 1134 assert.Equal(t, "", proxyHost) 1135 }) 1136 }