github.com/SAP/jenkins-library@v1.362.0/cmd/checkmarxOneExecuteScan.go (about) 1 package cmd 2 3 import ( 4 "archive/zip" 5 "context" 6 "fmt" 7 "io" 8 "math" 9 "os" 10 "path/filepath" 11 "regexp" 12 "sort" 13 "strconv" 14 "strings" 15 "time" 16 17 checkmarxOne "github.com/SAP/jenkins-library/pkg/checkmarxone" 18 piperGithub "github.com/SAP/jenkins-library/pkg/github" 19 piperHttp "github.com/SAP/jenkins-library/pkg/http" 20 "github.com/SAP/jenkins-library/pkg/log" 21 "github.com/SAP/jenkins-library/pkg/piperutils" 22 "github.com/SAP/jenkins-library/pkg/reporting" 23 "github.com/SAP/jenkins-library/pkg/telemetry" 24 "github.com/SAP/jenkins-library/pkg/toolrecord" 25 "github.com/bmatcuk/doublestar" 26 "github.com/google/go-github/v45/github" 27 "github.com/pkg/errors" 28 ) 29 30 type checkmarxOneExecuteScanUtils interface { 31 FileInfoHeader(fi os.FileInfo) (*zip.FileHeader, error) 32 Stat(name string) (os.FileInfo, error) 33 Open(name string) (*os.File, error) 34 WriteFile(filename string, data []byte, perm os.FileMode) error 35 MkdirAll(path string, perm os.FileMode) error 36 PathMatch(pattern, name string) (bool, error) 37 GetWorkspace() string 38 GetIssueService() *github.IssuesService 39 GetSearchService() *github.SearchService 40 } 41 42 type checkmarxOneExecuteScanHelper struct { 43 ctx context.Context 44 config checkmarxOneExecuteScanOptions 45 sys checkmarxOne.System 46 influx *checkmarxOneExecuteScanInflux 47 utils checkmarxOneExecuteScanUtils 48 Project *checkmarxOne.Project 49 Group *checkmarxOne.Group 50 App *checkmarxOne.Application 51 reports []piperutils.Path 52 } 53 54 type checkmarxOneExecuteScanUtilsBundle struct { 55 workspace string 56 issues *github.IssuesService 57 search *github.SearchService 58 } 59 60 func checkmarxOneExecuteScan(config checkmarxOneExecuteScanOptions, _ *telemetry.CustomData, influx *checkmarxOneExecuteScanInflux) { 61 // TODO: Setup connection with Splunk, influxDB? 62 cx1sh, err := Authenticate(config, influx) 63 if err != nil { 64 log.Entry().WithError(err).Fatalf("failed to create Cx1 client: %s", err) 65 } 66 67 err = runStep(config, influx, &cx1sh) 68 if err != nil { 69 log.Entry().WithError(err).Fatalf("Failed to run CheckmarxOne scan.") 70 } 71 influx.step_data.fields.checkmarxOne = true 72 } 73 74 func runStep(config checkmarxOneExecuteScanOptions, influx *checkmarxOneExecuteScanInflux, cx1sh *checkmarxOneExecuteScanHelper) error { 75 err := error(nil) 76 cx1sh.Project, err = cx1sh.GetProjectByName() 77 if err != nil && err.Error() != "project not found" { 78 return fmt.Errorf("failed to get project: %s", err) 79 } 80 81 cx1sh.Group, err = cx1sh.GetGroup() // used when creating a project and when generating a SARIF report 82 if err != nil { 83 log.Entry().WithError(err).Warnf("failed to get group") 84 } 85 86 if cx1sh.Project == nil { 87 cx1sh.App, err = cx1sh.GetApplication() // read application name from piper config (optional) and get ID from CxONE API 88 if err != nil { 89 log.Entry().WithError(err).Warnf("Failed to get application - will attempt to create the project on the Tenant level") 90 } 91 cx1sh.Project, err = cx1sh.CreateProject() // requires groups, repoUrl, mainBranch, origin, tags, criticality 92 if err != nil { 93 return fmt.Errorf("failed to create project: %s", err) 94 } 95 } else { 96 cx1sh.Project, err = cx1sh.GetProjectByID(cx1sh.Project.ProjectID) 97 if err != nil { 98 return fmt.Errorf("failed to get project by ID: %s", err) 99 } else { 100 if len(cx1sh.Project.Applications) > 0 { 101 appId := cx1sh.Project.Applications[0] 102 cx1sh.App, err = cx1sh.GetApplicationByID(cx1sh.Project.Applications[0]) 103 if err != nil { 104 return fmt.Errorf("failed to retrieve information for project's assigned application %v", appId) 105 } 106 } 107 } 108 } 109 110 err = cx1sh.SetProjectPreset() 111 if err != nil { 112 return fmt.Errorf("failed to set preset: %s", err) 113 } 114 115 scans, err := cx1sh.GetLastScans(10) 116 if err != nil { 117 log.Entry().WithError(err).Warnf("failed to get last 10 scans") 118 } 119 120 if config.VerifyOnly { 121 if len(scans) > 0 { 122 results, err := cx1sh.ParseResults(&scans[0]) // incl report-gen 123 if err != nil { 124 return fmt.Errorf("failed to get scan results: %s", err) 125 } 126 127 err = cx1sh.CheckCompliance(&scans[0], &results) 128 if err != nil { 129 log.SetErrorCategory(log.ErrorCompliance) 130 return fmt.Errorf("project %v not compliant: %s", cx1sh.Project.Name, err) 131 } 132 133 return nil 134 } else { 135 log.Entry().Warnf("Cannot load scans for project %v, verification only mode aborted", cx1sh.Project.Name) 136 } 137 } 138 139 incremental, err := cx1sh.IncrementalOrFull(scans) // requires: scan list 140 if err != nil { 141 return fmt.Errorf("failed to determine incremental or full scan configuration: %s", err) 142 } 143 144 if config.Incremental { 145 log.Entry().Warnf("If you change your file filter pattern it is recommended to run a Full scan instead of an incremental, to ensure full code coverage.") 146 } 147 148 zipFile, err := cx1sh.ZipFiles() 149 if err != nil { 150 return fmt.Errorf("failed to create zip file: %s", err) 151 } 152 153 uploadLink, err := cx1sh.UploadScanContent(zipFile) // POST /api/uploads + PUT /{uploadLink} 154 if err != nil { 155 return fmt.Errorf("failed to get upload URL: %s", err) 156 } 157 158 // TODO : The step structure should allow to enable different scanners: SAST, KICKS, SCA 159 scan, err := cx1sh.CreateScanRequest(incremental, uploadLink) 160 if err != nil { 161 return fmt.Errorf("failed to create scan: %s", err) 162 } 163 164 // TODO: how to provide other scan parameters like engineConfiguration? 165 // TODO: potential to persist file exclusions for git? 166 err = cx1sh.PollScanStatus(scan) 167 if err != nil { 168 return fmt.Errorf("failed while polling scan status: %s", err) 169 } 170 171 results, err := cx1sh.ParseResults(scan) // incl report-gen 172 if err != nil { 173 return fmt.Errorf("failed to get scan results: %s", err) 174 } 175 err = cx1sh.CheckCompliance(scan, &results) 176 if err != nil { 177 log.SetErrorCategory(log.ErrorCompliance) 178 return fmt.Errorf("project %v not compliant: %s", cx1sh.Project.Name, err) 179 } 180 // TODO: upload logs to Splunk, influxDB? 181 return nil 182 183 } 184 185 func Authenticate(config checkmarxOneExecuteScanOptions, influx *checkmarxOneExecuteScanInflux) (checkmarxOneExecuteScanHelper, error) { 186 client := &piperHttp.Client{} 187 188 ctx, ghClient, err := piperGithub.NewClientBuilder(config.GithubToken, config.GithubAPIURL).Build() 189 if err != nil { 190 log.Entry().WithError(err).Warning("Failed to get GitHub client") 191 } 192 sys, err := checkmarxOne.NewSystemInstance(client, config.ServerURL, config.IamURL, config.Tenant, config.APIKey, config.ClientID, config.ClientSecret) 193 if err != nil { 194 return checkmarxOneExecuteScanHelper{}, fmt.Errorf("failed to create Checkmarx One client talking to URLs %v and %v with tenant %v: %s", config.ServerURL, config.IamURL, config.Tenant, err) 195 } 196 influx.step_data.fields.checkmarxOne = false 197 198 utils := newcheckmarxOneExecuteScanUtilsBundle("./", ghClient) 199 200 return checkmarxOneExecuteScanHelper{ctx, config, sys, influx, utils, nil, nil, nil, []piperutils.Path{}}, nil 201 } 202 203 func (c *checkmarxOneExecuteScanHelper) GetProjectByName() (*checkmarxOne.Project, error) { 204 if len(c.config.ProjectName) == 0 { 205 log.Entry().Fatalf("No project name set in the configuration") 206 } 207 208 // get the Project, if it exists 209 projects, err := c.sys.GetProjectsByName(c.config.ProjectName) 210 if err != nil { 211 return nil, fmt.Errorf("error when trying to load project: %s", err) 212 } 213 214 for _, p := range projects { 215 if p.Name == c.config.ProjectName { 216 return &p, nil 217 } 218 } 219 return nil, fmt.Errorf("project not found") 220 } 221 222 func (c *checkmarxOneExecuteScanHelper) GetProjectByID(projectId string) (*checkmarxOne.Project, error) { 223 project, err := c.sys.GetProjectByID(projectId) 224 return &project, err 225 } 226 227 func (c *checkmarxOneExecuteScanHelper) GetGroup() (*checkmarxOne.Group, error) { 228 if len(c.config.GroupName) > 0 { 229 group, err := c.sys.GetGroupByName(c.config.GroupName) 230 if err != nil { 231 return nil, fmt.Errorf("Failed to get Checkmarx One group by Name %v: %s", c.config.GroupName, err) 232 } 233 return &group, nil 234 } 235 return nil, fmt.Errorf("No group name specified in configuration") 236 } 237 238 func (c *checkmarxOneExecuteScanHelper) GetApplication() (*checkmarxOne.Application, error) { 239 if len(c.config.ApplicationName) > 0 { 240 app, err := c.sys.GetApplicationByName(c.config.ApplicationName) 241 if err != nil { 242 return nil, fmt.Errorf("Failed to get Checkmarx One application by Name %v: %s", c.config.ApplicationName, err) 243 } 244 245 return &app, nil 246 } 247 return nil, fmt.Errorf("No application name specified in configuration") 248 } 249 250 func (c *checkmarxOneExecuteScanHelper) GetApplicationByID(applicationId string) (*checkmarxOne.Application, error) { 251 app, err := c.sys.GetApplicationByID(applicationId) 252 if err != nil { 253 return nil, fmt.Errorf("Failed to get Checkmarx One application by Name %v: %s", c.config.ApplicationName, err) 254 } 255 256 return &app, nil 257 } 258 259 func (c *checkmarxOneExecuteScanHelper) CreateProject() (*checkmarxOne.Project, error) { 260 if len(c.config.Preset) == 0 { 261 return nil, fmt.Errorf("Preset is required to create a project") 262 } 263 264 var project checkmarxOne.Project 265 var err error 266 var groupIDs []string = []string{} 267 if c.Group != nil { 268 groupIDs = []string{c.Group.GroupID} 269 } 270 271 if c.App != nil { 272 project, err = c.sys.CreateProjectInApplication(c.config.ProjectName, c.App.ApplicationID, groupIDs) 273 } else { 274 project, err = c.sys.CreateProject(c.config.ProjectName, groupIDs) 275 } 276 277 if err != nil { 278 return nil, fmt.Errorf("Error when trying to create project: %s", err) 279 } 280 log.Entry().Infof("Project %v created", project.ProjectID) 281 282 // new project, set the defaults per pipeline config 283 err = c.sys.SetProjectPreset(project.ProjectID, c.config.Preset, true) 284 if err != nil { 285 return nil, fmt.Errorf("Unable to set preset for project %v to %v: %s", project.ProjectID, c.config.Preset, err) 286 } 287 log.Entry().Infof("Project preset updated to %v", c.config.Preset) 288 289 if len(c.config.LanguageMode) != 0 { 290 err = c.sys.SetProjectLanguageMode(project.ProjectID, c.config.LanguageMode, true) 291 if err != nil { 292 293 return nil, fmt.Errorf("Unable to set languageMode for project %v to %v: %s", project.ProjectID, c.config.LanguageMode, err) 294 } 295 log.Entry().Infof("Project languageMode updated to %v", c.config.LanguageMode) 296 } 297 298 return &project, nil 299 } 300 301 func (c *checkmarxOneExecuteScanHelper) SetProjectPreset() error { 302 projectConf, err := c.sys.GetProjectConfiguration(c.Project.ProjectID) 303 304 if err != nil { 305 return fmt.Errorf("Failed to retrieve current project configuration: %s", err) 306 } 307 308 currentPreset := "" 309 currentLanguageMode := "multi" // piper default 310 for _, conf := range projectConf { 311 if conf.Key == "scan.config.sast.presetName" { 312 currentPreset = conf.Value 313 } 314 if conf.Key == "scan.config.sast.languageMode" { 315 currentLanguageMode = conf.Value 316 } 317 } 318 319 if c.config.LanguageMode == "" || strings.EqualFold(c.config.LanguageMode, "multi") { // default multi if blank 320 if currentLanguageMode != "multi" { 321 log.Entry().Info("Pipeline yaml requests multi-language scan - updating project configuration") 322 c.sys.SetProjectLanguageMode(c.Project.ProjectID, "multi", true) 323 324 if c.config.Incremental { 325 log.Entry().Warn("Pipeline yaml requests incremental scan, but switching from 'primary' to 'multi' language mode requires a full scan - switching from incremental to full") 326 c.config.Incremental = false 327 } 328 } 329 } else { // primary language mode 330 if currentLanguageMode != "primary" { 331 log.Entry().Info("Pipeline yaml requests primary-language scan - updating project configuration") 332 c.sys.SetProjectLanguageMode(c.Project.ProjectID, "primary", true) 333 // no need to switch incremental to full here (multi-language scan includes single-language scan coverage) 334 } 335 } 336 337 if c.config.Preset == "" { 338 if currentPreset == "" { 339 return fmt.Errorf("must specify the preset in either the pipeline yaml or in the CheckmarxOne project configuration") 340 } else { 341 log.Entry().Infof("Pipeline yaml does not specify a preset, will use project configuration (%v).", currentPreset) 342 } 343 c.config.Preset = currentPreset 344 } else if currentPreset != c.config.Preset { 345 log.Entry().Infof("Project configured preset (%v) does not match pipeline yaml (%v) - updating project configuration.", currentPreset, c.config.Preset) 346 c.sys.SetProjectPreset(c.Project.ProjectID, c.config.Preset, true) 347 348 if c.config.Incremental { 349 log.Entry().Warn("Changing project settings requires a full scan to take effect - switching from incremental to full") 350 c.config.Incremental = false 351 } 352 } else { 353 log.Entry().Infof("Project is already configured to use pipeline preset %v", currentPreset) 354 } 355 return nil 356 } 357 358 func (c *checkmarxOneExecuteScanHelper) GetLastScans(count int) ([]checkmarxOne.Scan, error) { 359 scans, err := c.sys.GetLastScansByStatus(c.Project.ProjectID, count, []string{"Completed"}) 360 if err != nil { 361 return []checkmarxOne.Scan{}, fmt.Errorf("Failed to get last %d Completed scans for project %v: %s", count, c.Project.ProjectID, err) 362 } 363 return scans, nil 364 } 365 366 func (c *checkmarxOneExecuteScanHelper) IncrementalOrFull(scans []checkmarxOne.Scan) (bool, error) { 367 incremental := c.config.Incremental 368 fullScanCycle, err := strconv.Atoi(c.config.FullScanCycle) 369 if err != nil { 370 log.SetErrorCategory(log.ErrorConfiguration) 371 return false, fmt.Errorf("invalid configuration value for fullScanCycle %v, must be a positive int", c.config.FullScanCycle) 372 } 373 374 coherentIncrementalScans := c.getNumCoherentIncrementalScans(scans) 375 376 if c.config.IsOptimizedAndScheduled { 377 incremental = false 378 } else if incremental && c.config.FullScansScheduled && fullScanCycle > 0 && (coherentIncrementalScans+1) >= fullScanCycle { 379 incremental = false 380 } 381 382 return incremental, nil 383 } 384 385 func (c *checkmarxOneExecuteScanHelper) ZipFiles() (*os.File, error) { 386 zipFile, err := c.zipWorkspaceFiles(c.config.FilterPattern, c.utils) 387 if err != nil { 388 return nil, fmt.Errorf("Failed to zip workspace files") 389 } 390 return zipFile, nil 391 } 392 393 func (c *checkmarxOneExecuteScanHelper) UploadScanContent(zipFile *os.File) (string, error) { 394 uploadUri, err := c.sys.UploadProjectSourceCode(c.Project.ProjectID, zipFile.Name()) 395 if err != nil { 396 return "", fmt.Errorf("Failed to upload source code for project %v: %s", c.Project.ProjectID, err) 397 } 398 399 log.Entry().Debugf("Source code uploaded for project %v", c.Project.Name) 400 err = os.Remove(zipFile.Name()) 401 if err != nil { 402 log.Entry().WithError(err).Warnf("Failed to delete zipped source code for project %v", c.Project.Name) 403 } 404 return uploadUri, nil 405 } 406 407 func (c *checkmarxOneExecuteScanHelper) CreateScanRequest(incremental bool, uploadLink string) (*checkmarxOne.Scan, error) { 408 sastConfig := checkmarxOne.ScanConfiguration{} 409 sastConfig.ScanType = "sast" 410 411 sastConfig.Values = make(map[string]string, 0) 412 sastConfig.Values["incremental"] = strconv.FormatBool(incremental) 413 sastConfig.Values["presetName"] = c.config.Preset // always set, either coming from config or coming from Cx1 configuration 414 sastConfigString := fmt.Sprintf("incremental %v, preset %v", strconv.FormatBool(incremental), c.config.Preset) 415 416 if len(c.config.LanguageMode) > 0 { 417 sastConfig.Values["languageMode"] = c.config.LanguageMode 418 sastConfigString = sastConfigString + fmt.Sprintf(", languageMode %v", c.config.LanguageMode) 419 } 420 421 branch := c.config.Branch 422 if len(branch) == 0 && len(c.config.GitBranch) > 0 { 423 branch = c.config.GitBranch 424 } 425 if len(c.config.PullRequestName) > 0 { 426 branch = fmt.Sprintf("%v-%v", c.config.PullRequestName, c.config.Branch) 427 } 428 429 sastConfigString = fmt.Sprintf("Cx1 Branch name %v, ", branch) + sastConfigString 430 431 log.Entry().Infof("Will run a scan with the following configuration: %v", sastConfigString) 432 433 configs := []checkmarxOne.ScanConfiguration{sastConfig} 434 // add more engines 435 436 scan, err := c.sys.ScanProjectZip(c.Project.ProjectID, uploadLink, branch, configs) 437 438 if err != nil { 439 return nil, fmt.Errorf("Failed to run scan on project %v: %s", c.Project.Name, err) 440 } 441 442 log.Entry().Debugf("Scanning project %v: %v ", c.Project.Name, scan.ScanID) 443 444 return &scan, nil 445 } 446 447 func (c *checkmarxOneExecuteScanHelper) PollScanStatus(scan *checkmarxOne.Scan) error { 448 statusDetails := "Scan phase: New" 449 pastStatusDetails := statusDetails 450 log.Entry().Info(statusDetails) 451 status := "New" 452 for { 453 scan_refresh, err := c.sys.GetScan(scan.ScanID) 454 455 if err != nil { 456 return fmt.Errorf("Error while polling scan %v: %s", scan.ScanID, err) 457 } 458 459 status = scan_refresh.Status 460 workflow, err := c.sys.GetScanWorkflow(scan.ScanID) 461 if err != nil { 462 return fmt.Errorf("Error while getting workflow for scan %v: %s", scan.ScanID, err) 463 } 464 465 statusDetails = workflow[len(workflow)-1].Info 466 467 if pastStatusDetails != statusDetails { 468 log.Entry().Info(statusDetails) 469 pastStatusDetails = statusDetails 470 } 471 472 if status == "Completed" || status == "Canceled" || status == "Failed" { 473 break 474 } 475 476 if pastStatusDetails != statusDetails { 477 log.Entry().Info(statusDetails) 478 pastStatusDetails = statusDetails 479 } 480 481 log.Entry().Debug("Polling for status: sleeping...") 482 483 time.Sleep(10 * time.Second) 484 } 485 if status == "Canceled" { 486 log.SetErrorCategory(log.ErrorCustom) 487 return fmt.Errorf("Scan %v canceled via web interface", scan.ScanID) 488 } 489 if status == "Failed" { 490 return fmt.Errorf("Checkmarx One scan failed with the following error: %v", statusDetails) 491 } 492 return nil 493 } 494 495 func (c *checkmarxOneExecuteScanHelper) CheckCompliance(scan *checkmarxOne.Scan, detailedResults *map[string]interface{}) error { 496 497 links := []piperutils.Path{{Target: (*detailedResults)["DeepLink"].(string), Name: "Checkmarx One Web UI"}} 498 499 insecure := false 500 var insecureResults []string 501 var neutralResults []string 502 503 if c.config.VulnerabilityThresholdEnabled { 504 insecure, insecureResults, neutralResults = c.enforceThresholds(detailedResults) 505 scanReport := checkmarxOne.CreateCustomReport(detailedResults, insecureResults, neutralResults) 506 507 if insecure && c.config.CreateResultIssue && len(c.config.GithubToken) > 0 && len(c.config.GithubAPIURL) > 0 && len(c.config.Owner) > 0 && len(c.config.Repository) > 0 { 508 log.Entry().Debug("Creating/updating GitHub issue with check results") 509 gh := reporting.GitHub{ 510 Owner: &c.config.Owner, 511 Repository: &c.config.Repository, 512 Assignees: &c.config.Assignees, 513 IssueService: c.utils.GetIssueService(), 514 SearchService: c.utils.GetSearchService(), 515 } 516 if err := gh.UploadSingleReport(c.ctx, scanReport); err != nil { 517 return fmt.Errorf("failed to upload scan results into GitHub: %s", err) 518 } 519 } 520 521 paths, err := checkmarxOne.WriteCustomReports(scanReport, c.Project.Name, c.Project.ProjectID) 522 if err != nil { 523 // do not fail until we have a better idea to handle it 524 log.Entry().Warning("failed to write HTML/MarkDown report file ...", err) 525 } else { 526 c.reports = append(c.reports, paths...) 527 } 528 } 529 530 piperutils.PersistReportsAndLinks("checkmarxOneExecuteScan", c.utils.GetWorkspace(), c.utils, c.reports, links) 531 532 c.reportToInflux(detailedResults) 533 534 if insecure { 535 if c.config.VulnerabilityThresholdResult == "FAILURE" { 536 log.SetErrorCategory(log.ErrorCompliance) 537 return fmt.Errorf("the project is not compliant - see report for details") 538 } 539 log.Entry().Errorf("Checkmarx One scan result set to %v, some results are not meeting defined thresholds. For details see the archived report.", c.config.VulnerabilityThresholdResult) 540 } else { 541 log.Entry().Infoln("Checkmarx One scan finished successfully") 542 } 543 return nil 544 } 545 546 func (c *checkmarxOneExecuteScanHelper) GetReportPDF(scan *checkmarxOne.Scan) error { 547 if c.config.GeneratePdfReport { 548 pdfReportName := c.createReportName(c.utils.GetWorkspace(), "Cx1_SASTReport_%v.pdf") 549 err := c.downloadAndSaveReport(pdfReportName, scan, "pdf") 550 if err != nil { 551 return fmt.Errorf("Report download failed: %s", err) 552 } else { 553 c.reports = append(c.reports, piperutils.Path{Target: pdfReportName, Mandatory: true}) 554 } 555 } else { 556 log.Entry().Debug("Report generation is disabled via configuration") 557 } 558 559 return nil 560 } 561 562 func (c *checkmarxOneExecuteScanHelper) GetReportSARIF(scan *checkmarxOne.Scan, scanmeta *checkmarxOne.ScanMetadata, results *[]checkmarxOne.ScanResult) error { 563 if c.config.ConvertToSarif { 564 log.Entry().Info("Calling conversion to SARIF function.") 565 sarif, err := checkmarxOne.ConvertCxJSONToSarif(c.sys, c.config.ServerURL, results, scanmeta, scan) 566 if err != nil { 567 return fmt.Errorf("Failed to generate SARIF: %s", err) 568 } 569 paths, err := checkmarxOne.WriteSarif(sarif) 570 if err != nil { 571 return fmt.Errorf("Failed to write SARIF: %s", err) 572 } 573 c.reports = append(c.reports, paths...) 574 } 575 return nil 576 } 577 578 func (c *checkmarxOneExecuteScanHelper) GetReportJSON(scan *checkmarxOne.Scan) error { 579 jsonReportName := c.createReportName(c.utils.GetWorkspace(), "Cx1_SASTReport_%v.json") 580 err := c.downloadAndSaveReport(jsonReportName, scan, "json") 581 if err != nil { 582 return fmt.Errorf("Report download failed: %s", err) 583 } else { 584 c.reports = append(c.reports, piperutils.Path{Target: jsonReportName, Mandatory: true}) 585 } 586 return nil 587 } 588 589 func (c *checkmarxOneExecuteScanHelper) GetHeaderReportJSON(detailedResults *map[string]interface{}) error { 590 // This is for the SAP-piper-format short-form JSON report 591 jsonReport := checkmarxOne.CreateJSONHeaderReport(detailedResults) 592 paths, err := checkmarxOne.WriteJSONHeaderReport(jsonReport) 593 if err != nil { 594 return fmt.Errorf("Failed to write JSON header report: %s", err) 595 } else { 596 // add JSON report to archiving list 597 c.reports = append(c.reports, paths...) 598 } 599 return nil 600 } 601 602 func (c *checkmarxOneExecuteScanHelper) ParseResults(scan *checkmarxOne.Scan) (map[string]interface{}, error) { 603 var detailedResults map[string]interface{} 604 605 scanmeta, err := c.sys.GetScanMetadata(scan.ScanID) 606 if err != nil { 607 return detailedResults, fmt.Errorf("Unable to fetch scan metadata for scan %v: %s", scan.ScanID, err) 608 } 609 610 totalResultCount := uint64(0) 611 612 scansummary, err := c.sys.GetScanSummary(scan.ScanID) 613 if err != nil { 614 /* TODO: scansummary throws a 404 for 0-result scans, once the bug is fixed put this code back. */ 615 // return detailedResults, fmt.Errorf("Unable to fetch scan summary for scan %v: %s", scan.ScanID, err) 616 } else { 617 totalResultCount = scansummary.TotalCount() 618 } 619 620 results, err := c.sys.GetScanResults(scan.ScanID, totalResultCount) 621 if err != nil { 622 return detailedResults, fmt.Errorf("Unable to fetch scan results for scan %v: %s", scan.ScanID, err) 623 } 624 625 detailedResults, err = c.getDetailedResults(scan, &scanmeta, &results) 626 if err != nil { 627 return detailedResults, fmt.Errorf("Unable to fetch detailed results for scan %v: %s", scan.ScanID, err) 628 } 629 630 err = c.GetReportJSON(scan) 631 if err != nil { 632 log.Entry().WithError(err).Warnf("Failed to get JSON report") 633 } 634 err = c.GetReportPDF(scan) 635 if err != nil { 636 log.Entry().WithError(err).Warnf("Failed to get PDF report") 637 } 638 err = c.GetReportSARIF(scan, &scanmeta, &results) 639 if err != nil { 640 log.Entry().WithError(err).Warnf("Failed to get SARIF report") 641 } 642 err = c.GetHeaderReportJSON(&detailedResults) 643 if err != nil { 644 log.Entry().WithError(err).Warnf("Failed to generate JSON Header report") 645 } 646 647 // create toolrecord 648 toolRecordFileName, err := c.createToolRecordCx(&detailedResults) 649 if err != nil { 650 // do not fail until the framework is well established 651 log.Entry().Warning("TR_CHECKMARXONE: Failed to create toolrecord file ...", err) 652 } else { 653 c.reports = append(c.reports, piperutils.Path{Target: toolRecordFileName}) 654 } 655 656 return detailedResults, nil 657 } 658 659 func (c *checkmarxOneExecuteScanHelper) createReportName(workspace, reportFileNameTemplate string) string { 660 regExpFileName := regexp.MustCompile(`[^\w\d]`) 661 timeStamp, _ := time.Now().Local().MarshalText() 662 return filepath.Join(workspace, fmt.Sprintf(reportFileNameTemplate, regExpFileName.ReplaceAllString(string(timeStamp), "_"))) 663 } 664 665 func (c *checkmarxOneExecuteScanHelper) downloadAndSaveReport(reportFileName string, scan *checkmarxOne.Scan, reportType string) error { 666 report, err := c.generateAndDownloadReport(scan, reportType) 667 if err != nil { 668 return errors.Wrap(err, "failed to download the report") 669 } 670 log.Entry().Debugf("Saving report to file %v...", reportFileName) 671 return c.utils.WriteFile(reportFileName, report, 0o700) 672 } 673 674 func (c *checkmarxOneExecuteScanHelper) generateAndDownloadReport(scan *checkmarxOne.Scan, reportType string) ([]byte, error) { 675 var finalStatus checkmarxOne.ReportStatus 676 677 report, err := c.sys.RequestNewReport(scan.ScanID, scan.ProjectID, scan.Branch, reportType) 678 if err != nil { 679 return []byte{}, errors.Wrap(err, "failed to request new report") 680 } 681 for { 682 finalStatus, err = c.sys.GetReportStatus(report) 683 if err != nil { 684 return []byte{}, errors.Wrap(err, "failed to get report status") 685 } 686 687 if finalStatus.Status == "completed" { 688 break 689 } else if finalStatus.Status == "failed" { 690 return []byte{}, fmt.Errorf("report generation failed") 691 } 692 time.Sleep(10 * time.Second) 693 } 694 if finalStatus.Status == "completed" { 695 return c.sys.DownloadReport(finalStatus.ReportURL) 696 } 697 698 return []byte{}, fmt.Errorf("unexpected status %v recieved", finalStatus.Status) 699 } 700 701 func (c *checkmarxOneExecuteScanHelper) getNumCoherentIncrementalScans(scans []checkmarxOne.Scan) int { 702 count := 0 703 for _, scan := range scans { 704 inc, err := scan.IsIncremental() 705 if !inc && err == nil { 706 break 707 } 708 count++ 709 } 710 return count 711 } 712 713 func (c *checkmarxOneExecuteScanHelper) getDetailedResults(scan *checkmarxOne.Scan, scanmeta *checkmarxOne.ScanMetadata, results *[]checkmarxOne.ScanResult) (map[string]interface{}, error) { 714 // this converts the JSON format results from Cx1 into the "resultMap" structure used in other parts of this step (influx etc) 715 716 resultMap := map[string]interface{}{} 717 resultMap["InitiatorName"] = scan.Initiator 718 resultMap["Owner"] = "Cx1 Gap: no project owner" // TODO: check for functionality 719 resultMap["ScanId"] = scan.ScanID 720 resultMap["ProjectId"] = c.Project.ProjectID 721 resultMap["ProjectName"] = c.Project.Name 722 723 resultMap["Group"] = "" 724 resultMap["GroupFullPathOnReportDate"] = "" 725 726 if c.App != nil { 727 resultMap["Application"] = c.App.ApplicationID 728 resultMap["ApplicationFullPathOnReportDate"] = c.App.Name 729 } else { 730 resultMap["Application"] = "" 731 resultMap["ApplicationFullPathOnReportDate"] = "" 732 } 733 734 resultMap["ScanStart"] = scan.CreatedAt 735 736 scanCreated, err := time.Parse(time.RFC3339, scan.CreatedAt) 737 if err != nil { 738 log.Entry().Warningf("Failed to parse string %v into time: %s", scan.CreatedAt, err) 739 resultMap["ScanTime"] = "Error parsing scan.CreatedAt" 740 } else { 741 scanFinished, err := time.Parse(time.RFC3339, scan.UpdatedAt) 742 if err != nil { 743 log.Entry().Warningf("Failed to parse string %v into time: %s", scan.UpdatedAt, err) 744 resultMap["ScanTime"] = "Error parsing scan.UpdatedAt" 745 } else { 746 difference := scanFinished.Sub(scanCreated) 747 resultMap["ScanTime"] = difference.String() 748 } 749 } 750 751 resultMap["LinesOfCodeScanned"] = scanmeta.LOC 752 resultMap["FilesScanned"] = scanmeta.FileCount 753 754 version, err := c.sys.GetVersion() 755 if err != nil { 756 resultMap["ToolVersion"] = "Error fetching current version" 757 } else { 758 resultMap["ToolVersion"] = fmt.Sprintf("CxOne: %v, SAST: %v, KICS: %v", version.CxOne, version.SAST, version.KICS) 759 } 760 761 if scanmeta.IsIncremental { 762 resultMap["ScanType"] = "Incremental" 763 } else { 764 resultMap["ScanType"] = "Full" 765 } 766 767 resultMap["Preset"] = scanmeta.PresetName 768 resultMap["DeepLink"] = fmt.Sprintf("%v/projects/%v/overview?branch=%v", c.config.ServerURL, c.Project.ProjectID, scan.Branch) 769 resultMap["ReportCreationTime"] = time.Now().String() 770 resultMap["High"] = map[string]int{} 771 resultMap["Medium"] = map[string]int{} 772 resultMap["Low"] = map[string]int{} 773 resultMap["Information"] = map[string]int{} 774 775 if len(*results) > 0 { 776 for _, result := range *results { 777 key := "Information" 778 switch result.Severity { 779 case "HIGH": 780 key = "High" 781 case "MEDIUM": 782 key = "Medium" 783 case "LOW": 784 key = "Low" 785 case "INFORMATION": 786 default: 787 key = "Information" 788 } 789 790 var submap map[string]int 791 if resultMap[key] == nil { 792 submap = map[string]int{} 793 resultMap[key] = submap 794 } else { 795 submap = resultMap[key].(map[string]int) 796 } 797 submap["Issues"]++ 798 799 auditState := "ToVerify" 800 switch result.State { 801 case "NOT_EXPLOITABLE": 802 auditState = "NotExploitable" 803 case "CONFIRMED": 804 auditState = "Confirmed" 805 case "URGENT", "URGENT ": 806 auditState = "Urgent" 807 case "PROPOSED_NOT_EXPLOITABLE": 808 auditState = "ProposedNotExploitable" 809 case "TO_VERIFY": 810 default: 811 auditState = "ToVerify" 812 } 813 submap[auditState]++ 814 815 if auditState != "NotExploitable" { 816 submap["NotFalsePositive"]++ 817 } 818 819 } 820 821 // if the flag is switched on, build the list of Low findings per query 822 if c.config.VulnerabilityThresholdLowPerQuery { 823 var lowPerQuery = map[string]map[string]int{} 824 825 for _, result := range *results { 826 if result.Severity != "LOW" { 827 continue 828 } 829 key := result.Data.QueryName 830 var submap map[string]int 831 if lowPerQuery[key] == nil { 832 submap = map[string]int{} 833 lowPerQuery[key] = submap 834 } else { 835 submap = lowPerQuery[key] 836 } 837 submap["Issues"]++ 838 auditState := "ToVerify" 839 switch result.State { 840 case "NOT_EXPLOITABLE": 841 auditState = "NotExploitable" 842 case "CONFIRMED": 843 auditState = "Confirmed" 844 case "URGENT", "URGENT ": 845 auditState = "Urgent" 846 case "PROPOSED_NOT_EXPLOITABLE": 847 auditState = "ProposedNotExploitable" 848 case "TO_VERIFY": 849 default: 850 auditState = "ToVerify" 851 } 852 submap[auditState]++ 853 854 if auditState != "NotExploitable" { 855 submap["NotFalsePositive"]++ 856 } 857 } 858 859 resultMap["LowPerQuery"] = lowPerQuery 860 } 861 } 862 return resultMap, nil 863 } 864 865 func (c *checkmarxOneExecuteScanHelper) zipWorkspaceFiles(filterPattern string, utils checkmarxOneExecuteScanUtils) (*os.File, error) { 866 zipFileName := filepath.Join(utils.GetWorkspace(), "workspace.zip") 867 patterns := piperutils.Trim(strings.Split(filterPattern, ",")) 868 sort.Strings(patterns) 869 zipFile, err := os.Create(zipFileName) 870 if err != nil { 871 return zipFile, errors.Wrap(err, "failed to create archive of project sources") 872 } 873 defer zipFile.Close() 874 875 err = c.zipFolder(utils.GetWorkspace(), zipFile, patterns, utils) 876 if err != nil { 877 return nil, errors.Wrap(err, "failed to compact folder") 878 } 879 return zipFile, nil 880 } 881 882 func (c *checkmarxOneExecuteScanHelper) zipFolder(source string, zipFile io.Writer, patterns []string, utils checkmarxOneExecuteScanUtils) error { 883 archive := zip.NewWriter(zipFile) 884 defer archive.Close() 885 886 log.Entry().Infof("Zipping %v into workspace.zip", source) 887 888 info, err := utils.Stat(source) 889 if err != nil { 890 return nil 891 } 892 893 var baseDir string 894 if info.IsDir() { 895 baseDir = filepath.Base(source) 896 } 897 898 fileCount := 0 899 err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error { 900 if err != nil { 901 return err 902 } 903 904 if !info.Mode().IsRegular() || info.Size() == 0 { 905 return nil 906 } 907 908 noMatch, err := c.isFileNotMatchingPattern(patterns, path, info, utils) 909 if err != nil || noMatch { 910 return err 911 } 912 913 fileName := strings.TrimPrefix(path, baseDir) 914 writer, err := archive.Create(fileName) 915 if err != nil { 916 return err 917 } 918 919 file, err := utils.Open(path) 920 if err != nil { 921 return err 922 } 923 defer file.Close() 924 _, err = io.Copy(writer, file) 925 fileCount++ 926 return err 927 }) 928 log.Entry().Infof("Zipped %d files", fileCount) 929 err = c.handleZeroFilesZipped(source, err, fileCount) 930 return err 931 } 932 933 func (c *checkmarxOneExecuteScanHelper) adaptHeader(info os.FileInfo, header *zip.FileHeader) { 934 if info.IsDir() { 935 header.Name += "/" 936 } else { 937 header.Method = zip.Deflate 938 } 939 } 940 941 func (c *checkmarxOneExecuteScanHelper) handleZeroFilesZipped(source string, err error, fileCount int) error { 942 if err == nil && fileCount == 0 { 943 log.SetErrorCategory(log.ErrorConfiguration) 944 err = fmt.Errorf("filterPattern matched no files or workspace directory '%s' was empty", source) 945 } 946 return err 947 } 948 949 // isFileNotMatchingPattern checks if file path does not match one of the patterns. 950 // If it matches a negative pattern (starting with '!') then true is returned. 951 // 952 // If it is a directory, false is returned. 953 // If no patterns are provided, false is returned. 954 func (c *checkmarxOneExecuteScanHelper) isFileNotMatchingPattern(patterns []string, path string, info os.FileInfo, utils checkmarxOneExecuteScanUtils) (bool, error) { 955 if len(patterns) == 0 || info.IsDir() { 956 return false, nil 957 } 958 959 for _, pattern := range patterns { 960 negative := false 961 if strings.HasPrefix(pattern, "!") { 962 pattern = strings.TrimLeft(pattern, "!") 963 negative = true 964 } 965 match, err := utils.PathMatch(pattern, path) 966 if err != nil { 967 return false, errors.Wrapf(err, "Pattern %v could not get executed", pattern) 968 } 969 970 if match { 971 return negative, nil 972 } 973 } 974 return true, nil 975 } 976 977 func (c *checkmarxOneExecuteScanHelper) createToolRecordCx(results *map[string]interface{}) (string, error) { 978 workspace := c.utils.GetWorkspace() 979 record := toolrecord.New(c.utils, workspace, "checkmarxOne", c.config.ServerURL) 980 981 // Project 982 err := record.AddKeyData("project", 983 (*results)["ProjectId"].(string), 984 (*results)["ProjectName"].(string), 985 "") 986 if err != nil { 987 return "", err 988 } 989 // Scan 990 err = record.AddKeyData("scanid", 991 (*results)["ScanId"].(string), 992 (*results)["ScanId"].(string), 993 (*results)["DeepLink"].(string)) 994 if err != nil { 995 return "", err 996 } 997 err = record.Persist() 998 if err != nil { 999 return "", err 1000 } 1001 return record.GetFileName(), nil 1002 } 1003 1004 func (c *checkmarxOneExecuteScanHelper) enforceThresholds(results *map[string]interface{}) (bool, []string, []string) { 1005 neutralResults := []string{} 1006 insecureResults := []string{} 1007 insecure := false 1008 1009 cxHighThreshold := c.config.VulnerabilityThresholdHigh 1010 cxMediumThreshold := c.config.VulnerabilityThresholdMedium 1011 cxLowThreshold := c.config.VulnerabilityThresholdLow 1012 cxLowThresholdPerQuery := c.config.VulnerabilityThresholdLowPerQuery 1013 cxLowThresholdPerQueryMax := c.config.VulnerabilityThresholdLowPerQueryMax 1014 highValue := (*results)["High"].(map[string]int)["NotFalsePositive"] 1015 mediumValue := (*results)["Medium"].(map[string]int)["NotFalsePositive"] 1016 lowValue := (*results)["Low"].(map[string]int)["NotFalsePositive"] 1017 var unit string 1018 highViolation := "" 1019 mediumViolation := "" 1020 lowViolation := "" 1021 if c.config.VulnerabilityThresholdUnit == "percentage" { 1022 unit = "%" 1023 highAudited := (*results)["High"].(map[string]int)["Issues"] - (*results)["High"].(map[string]int)["NotFalsePositive"] 1024 highOverall := (*results)["High"].(map[string]int)["Issues"] 1025 if highOverall == 0 { 1026 highAudited = 1 1027 highOverall = 1 1028 } 1029 mediumAudited := (*results)["Medium"].(map[string]int)["Issues"] - (*results)["Medium"].(map[string]int)["NotFalsePositive"] 1030 mediumOverall := (*results)["Medium"].(map[string]int)["Issues"] 1031 if mediumOverall == 0 { 1032 mediumAudited = 1 1033 mediumOverall = 1 1034 } 1035 lowAudited := (*results)["Low"].(map[string]int)["Confirmed"] + (*results)["Low"].(map[string]int)["NotExploitable"] 1036 lowOverall := (*results)["Low"].(map[string]int)["Issues"] 1037 if lowOverall == 0 { 1038 lowAudited = 1 1039 lowOverall = 1 1040 } 1041 highValue = int(float32(highAudited) / float32(highOverall) * 100.0) 1042 mediumValue = int(float32(mediumAudited) / float32(mediumOverall) * 100.0) 1043 lowValue = int(float32(lowAudited) / float32(lowOverall) * 100.0) 1044 1045 if highValue < cxHighThreshold { 1046 insecure = true 1047 highViolation = fmt.Sprintf("<-- %v %v deviation", cxHighThreshold-highValue, unit) 1048 } 1049 if mediumValue < cxMediumThreshold { 1050 insecure = true 1051 mediumViolation = fmt.Sprintf("<-- %v %v deviation", cxMediumThreshold-mediumValue, unit) 1052 } 1053 // if the flag is switched on, calculate the Low findings threshold per query 1054 if cxLowThresholdPerQuery { 1055 if (*results)["LowPerQuery"] != nil { 1056 lowPerQueryMap := (*results)["LowPerQuery"].(map[string]map[string]int) 1057 1058 for lowQuery, resultsLowQuery := range lowPerQueryMap { 1059 lowAuditedPerQuery := resultsLowQuery["Confirmed"] + resultsLowQuery["NotExploitable"] 1060 lowOverallPerQuery := resultsLowQuery["Issues"] 1061 lowAuditedRequiredPerQuery := int(math.Ceil(float64(lowOverallPerQuery) * float64(cxLowThreshold) / 100.0)) 1062 if lowAuditedPerQuery < lowAuditedRequiredPerQuery && lowAuditedPerQuery < cxLowThresholdPerQueryMax { 1063 insecure = true 1064 msgSeperator := "|" 1065 if lowViolation == "" { 1066 msgSeperator = "<--" 1067 } 1068 lowViolation += fmt.Sprintf(" %v query: %v, audited: %v, required: %v ", msgSeperator, lowQuery, lowAuditedPerQuery, lowAuditedRequiredPerQuery) 1069 } 1070 } 1071 } 1072 } else { // calculate the Low findings threshold in total 1073 if lowValue < cxLowThreshold { 1074 insecure = true 1075 lowViolation = fmt.Sprintf("<-- %v %v deviation", cxLowThreshold-lowValue, unit) 1076 } 1077 } 1078 1079 } 1080 if c.config.VulnerabilityThresholdUnit == "absolute" { 1081 unit = " findings" 1082 if highValue > cxHighThreshold { 1083 insecure = true 1084 highViolation = fmt.Sprintf("<-- %v%v deviation", highValue-cxHighThreshold, unit) 1085 } 1086 if mediumValue > cxMediumThreshold { 1087 insecure = true 1088 mediumViolation = fmt.Sprintf("<-- %v%v deviation", mediumValue-cxMediumThreshold, unit) 1089 } 1090 if lowValue > cxLowThreshold { 1091 insecure = true 1092 lowViolation = fmt.Sprintf("<-- %v%v deviation", lowValue-cxLowThreshold, unit) 1093 } 1094 } 1095 1096 highText := fmt.Sprintf("High %v%v %v", highValue, unit, highViolation) 1097 mediumText := fmt.Sprintf("Medium %v%v %v", mediumValue, unit, mediumViolation) 1098 lowText := fmt.Sprintf("Low %v%v %v", lowValue, unit, lowViolation) 1099 if len(highViolation) > 0 { 1100 insecureResults = append(insecureResults, highText) 1101 log.Entry().Error(highText) 1102 } else { 1103 neutralResults = append(neutralResults, highText) 1104 log.Entry().Info(highText) 1105 } 1106 if len(mediumViolation) > 0 { 1107 insecureResults = append(insecureResults, mediumText) 1108 log.Entry().Error(mediumText) 1109 } else { 1110 neutralResults = append(neutralResults, mediumText) 1111 log.Entry().Info(mediumText) 1112 } 1113 if len(lowViolation) > 0 { 1114 insecureResults = append(insecureResults, lowText) 1115 log.Entry().Error(lowText) 1116 } else { 1117 neutralResults = append(neutralResults, lowText) 1118 log.Entry().Info(lowText) 1119 } 1120 1121 return insecure, insecureResults, neutralResults 1122 } 1123 1124 func (c *checkmarxOneExecuteScanHelper) reportToInflux(results *map[string]interface{}) { 1125 1126 c.influx.checkmarxOne_data.fields.high_issues = (*results)["High"].(map[string]int)["Issues"] 1127 c.influx.checkmarxOne_data.fields.high_not_false_postive = (*results)["High"].(map[string]int)["NotFalsePositive"] 1128 c.influx.checkmarxOne_data.fields.high_not_exploitable = (*results)["High"].(map[string]int)["NotExploitable"] 1129 c.influx.checkmarxOne_data.fields.high_confirmed = (*results)["High"].(map[string]int)["Confirmed"] 1130 c.influx.checkmarxOne_data.fields.high_urgent = (*results)["High"].(map[string]int)["Urgent"] 1131 c.influx.checkmarxOne_data.fields.high_proposed_not_exploitable = (*results)["High"].(map[string]int)["ProposedNotExploitable"] 1132 c.influx.checkmarxOne_data.fields.high_to_verify = (*results)["High"].(map[string]int)["ToVerify"] 1133 c.influx.checkmarxOne_data.fields.medium_issues = (*results)["Medium"].(map[string]int)["Issues"] 1134 c.influx.checkmarxOne_data.fields.medium_not_false_postive = (*results)["Medium"].(map[string]int)["NotFalsePositive"] 1135 c.influx.checkmarxOne_data.fields.medium_not_exploitable = (*results)["Medium"].(map[string]int)["NotExploitable"] 1136 c.influx.checkmarxOne_data.fields.medium_confirmed = (*results)["Medium"].(map[string]int)["Confirmed"] 1137 c.influx.checkmarxOne_data.fields.medium_urgent = (*results)["Medium"].(map[string]int)["Urgent"] 1138 c.influx.checkmarxOne_data.fields.medium_proposed_not_exploitable = (*results)["Medium"].(map[string]int)["ProposedNotExploitable"] 1139 c.influx.checkmarxOne_data.fields.medium_to_verify = (*results)["Medium"].(map[string]int)["ToVerify"] 1140 c.influx.checkmarxOne_data.fields.low_issues = (*results)["Low"].(map[string]int)["Issues"] 1141 c.influx.checkmarxOne_data.fields.low_not_false_postive = (*results)["Low"].(map[string]int)["NotFalsePositive"] 1142 c.influx.checkmarxOne_data.fields.low_not_exploitable = (*results)["Low"].(map[string]int)["NotExploitable"] 1143 c.influx.checkmarxOne_data.fields.low_confirmed = (*results)["Low"].(map[string]int)["Confirmed"] 1144 c.influx.checkmarxOne_data.fields.low_urgent = (*results)["Low"].(map[string]int)["Urgent"] 1145 c.influx.checkmarxOne_data.fields.low_proposed_not_exploitable = (*results)["Low"].(map[string]int)["ProposedNotExploitable"] 1146 c.influx.checkmarxOne_data.fields.low_to_verify = (*results)["Low"].(map[string]int)["ToVerify"] 1147 c.influx.checkmarxOne_data.fields.information_issues = (*results)["Information"].(map[string]int)["Issues"] 1148 c.influx.checkmarxOne_data.fields.information_not_false_postive = (*results)["Information"].(map[string]int)["NotFalsePositive"] 1149 c.influx.checkmarxOne_data.fields.information_not_exploitable = (*results)["Information"].(map[string]int)["NotExploitable"] 1150 c.influx.checkmarxOne_data.fields.information_confirmed = (*results)["Information"].(map[string]int)["Confirmed"] 1151 c.influx.checkmarxOne_data.fields.information_urgent = (*results)["Information"].(map[string]int)["Urgent"] 1152 c.influx.checkmarxOne_data.fields.information_proposed_not_exploitable = (*results)["Information"].(map[string]int)["ProposedNotExploitable"] 1153 c.influx.checkmarxOne_data.fields.information_to_verify = (*results)["Information"].(map[string]int)["ToVerify"] 1154 c.influx.checkmarxOne_data.fields.initiator_name = (*results)["InitiatorName"].(string) 1155 c.influx.checkmarxOne_data.fields.owner = (*results)["Owner"].(string) 1156 c.influx.checkmarxOne_data.fields.scan_id = (*results)["ScanId"].(string) 1157 c.influx.checkmarxOne_data.fields.project_id = (*results)["ProjectId"].(string) 1158 c.influx.checkmarxOne_data.fields.projectName = (*results)["ProjectName"].(string) 1159 c.influx.checkmarxOne_data.fields.group = (*results)["Group"].(string) 1160 c.influx.checkmarxOne_data.fields.group_full_path_on_report_date = (*results)["GroupFullPathOnReportDate"].(string) 1161 c.influx.checkmarxOne_data.fields.scan_start = (*results)["ScanStart"].(string) 1162 c.influx.checkmarxOne_data.fields.scan_time = (*results)["ScanTime"].(string) 1163 c.influx.checkmarxOne_data.fields.lines_of_code_scanned = (*results)["LinesOfCodeScanned"].(int) 1164 c.influx.checkmarxOne_data.fields.files_scanned = (*results)["FilesScanned"].(int) 1165 c.influx.checkmarxOne_data.fields.tool_version = (*results)["ToolVersion"].(string) 1166 1167 c.influx.checkmarxOne_data.fields.scan_type = (*results)["ScanType"].(string) 1168 c.influx.checkmarxOne_data.fields.preset = (*results)["Preset"].(string) 1169 c.influx.checkmarxOne_data.fields.deep_link = (*results)["DeepLink"].(string) 1170 c.influx.checkmarxOne_data.fields.report_creation_time = (*results)["ReportCreationTime"].(string) 1171 } 1172 1173 // Utils Bundle 1174 // various utilities to set up or work with the workspace and prepare data to send to Cx1 1175 1176 func (c *checkmarxOneExecuteScanUtilsBundle) PathMatch(pattern, name string) (bool, error) { 1177 return doublestar.PathMatch(pattern, name) 1178 } 1179 1180 func (c *checkmarxOneExecuteScanUtilsBundle) GetWorkspace() string { 1181 return c.workspace 1182 } 1183 1184 func (c *checkmarxOneExecuteScanUtilsBundle) WriteFile(filename string, data []byte, perm os.FileMode) error { 1185 return os.WriteFile(filename, data, perm) 1186 } 1187 1188 func (c *checkmarxOneExecuteScanUtilsBundle) MkdirAll(path string, perm os.FileMode) error { 1189 return os.MkdirAll(path, perm) 1190 } 1191 1192 func (c *checkmarxOneExecuteScanUtilsBundle) FileInfoHeader(fi os.FileInfo) (*zip.FileHeader, error) { 1193 return zip.FileInfoHeader(fi) 1194 } 1195 1196 func (c *checkmarxOneExecuteScanUtilsBundle) Stat(name string) (os.FileInfo, error) { 1197 return os.Stat(name) 1198 } 1199 1200 func (c *checkmarxOneExecuteScanUtilsBundle) Open(name string) (*os.File, error) { 1201 return os.Open(name) 1202 } 1203 1204 func (c *checkmarxOneExecuteScanUtilsBundle) CreateIssue(ghCreateIssueOptions *piperGithub.CreateIssueOptions) error { 1205 _, err := piperGithub.CreateIssue(ghCreateIssueOptions) 1206 return err 1207 } 1208 1209 func (c *checkmarxOneExecuteScanUtilsBundle) GetIssueService() *github.IssuesService { 1210 return c.issues 1211 } 1212 1213 func (c *checkmarxOneExecuteScanUtilsBundle) GetSearchService() *github.SearchService { 1214 return c.search 1215 } 1216 1217 func newcheckmarxOneExecuteScanUtilsBundle(workspace string, client *github.Client) checkmarxOneExecuteScanUtils { 1218 utils := checkmarxOneExecuteScanUtilsBundle{ 1219 workspace: workspace, 1220 } 1221 if client != nil { 1222 utils.issues = client.Issues 1223 utils.search = client.Search 1224 } 1225 return &utils 1226 }