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