github.com/jaylevin/jenkins-library@v1.230.4/cmd/detectExecuteScan.go (about) 1 package cmd 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/http" 8 "path/filepath" 9 "regexp" 10 "sort" 11 "strconv" 12 "strings" 13 "time" 14 15 bd "github.com/SAP/jenkins-library/pkg/blackduck" 16 piperGithub "github.com/SAP/jenkins-library/pkg/github" 17 piperhttp "github.com/SAP/jenkins-library/pkg/http" 18 "github.com/SAP/jenkins-library/pkg/maven" 19 "github.com/SAP/jenkins-library/pkg/reporting" 20 "github.com/SAP/jenkins-library/pkg/versioning" 21 "github.com/pkg/errors" 22 23 "github.com/SAP/jenkins-library/pkg/command" 24 "github.com/SAP/jenkins-library/pkg/log" 25 "github.com/SAP/jenkins-library/pkg/piperutils" 26 "github.com/SAP/jenkins-library/pkg/telemetry" 27 "github.com/SAP/jenkins-library/pkg/toolrecord" 28 ) 29 30 type detectUtils interface { 31 piperutils.FileUtils 32 33 GetExitCode() int 34 GetOsEnv() []string 35 Stdout(out io.Writer) 36 Stderr(err io.Writer) 37 SetDir(dir string) 38 SetEnv(env []string) 39 RunExecutable(e string, p ...string) error 40 RunShell(shell, script string) error 41 42 DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error 43 44 CreateIssue(ghCreateIssueOptions *piperGithub.CreateIssueOptions) error 45 } 46 47 type detectUtilsBundle struct { 48 *command.Command 49 *piperutils.Files 50 *piperhttp.Client 51 } 52 53 // CreateIssue supplies capability for GitHub issue creation 54 func (d *detectUtilsBundle) CreateIssue(ghCreateIssueOptions *piperGithub.CreateIssueOptions) error { 55 return piperGithub.CreateIssue(ghCreateIssueOptions) 56 } 57 58 type blackduckSystem struct { 59 Client bd.Client 60 } 61 62 func newDetectUtils() detectUtils { 63 utils := detectUtilsBundle{ 64 Command: &command.Command{ 65 ErrorCategoryMapping: map[string][]string{ 66 log.ErrorCompliance.String(): { 67 "FAILURE_POLICY_VIOLATION - Detect found policy violations.", 68 }, 69 log.ErrorConfiguration.String(): { 70 "FAILURE_CONFIGURATION - Detect was unable to start due to issues with it's configuration.", 71 "FAILURE_DETECTOR - Detect had one or more detector failures while extracting dependencies. Check that all projects build and your environment is configured correctly.", 72 "FAILURE_SCAN - Detect was unable to run the signature scanner against your source. Check your configuration.", 73 }, 74 log.ErrorInfrastructure.String(): { 75 "FAILURE_PROXY_CONNECTIVITY - Detect was unable to use the configured proxy. Check your configuration and connection.", 76 "FAILURE_BLACKDUCK_CONNECTIVITY - Detect was unable to connect to Black Duck. Check your configuration and connection.", 77 "FAILURE_POLARIS_CONNECTIVITY - Detect was unable to connect to Polaris. Check your configuration and connection.", 78 }, 79 log.ErrorService.String(): { 80 "FAILURE_TIMEOUT - Detect could not wait for actions to be completed on Black Duck. Check your Black Duck server or increase your timeout.", 81 "FAILURE_DETECTOR_REQUIRED - Detect did not run all of the required detectors. Fix detector issues or disable required detectors.", 82 "FAILURE_BLACKDUCK_VERSION_NOT_SUPPORTED - Detect attempted an operation that was not supported by your version of Black Duck. Ensure your Black Duck is compatible with this version of detect.", 83 "FAILURE_BLACKDUCK_FEATURE_ERROR - Detect encountered an error while attempting an operation on Black Duck. Ensure your Black Duck is compatible with this version of detect.", 84 "FAILURE_GENERAL_ERROR - Detect encountered a known error, details of the error are provided.", 85 "FAILURE_UNKNOWN_ERROR - Detect encountered an unknown error.", 86 }, 87 }, 88 }, 89 Files: &piperutils.Files{}, 90 Client: &piperhttp.Client{}, 91 } 92 utils.Stdout(log.Writer()) 93 utils.Stderr(log.Writer()) 94 return &utils 95 } 96 97 func newBlackduckSystem(config detectExecuteScanOptions) *blackduckSystem { 98 sys := blackduckSystem{ 99 Client: bd.NewClient(config.Token, config.ServerURL, &piperhttp.Client{}), 100 } 101 return &sys 102 } 103 104 func detectExecuteScan(config detectExecuteScanOptions, _ *telemetry.CustomData, influx *detectExecuteScanInflux) { 105 influx.step_data.fields.detect = false 106 utils := newDetectUtils() 107 err := runDetect(config, utils, influx) 108 109 if err != nil { 110 log.Entry(). 111 WithError(err). 112 Fatal("failed to execute detect scan") 113 } 114 115 influx.step_data.fields.detect = true 116 } 117 118 func runDetect(config detectExecuteScanOptions, utils detectUtils, influx *detectExecuteScanInflux) error { 119 // detect execution details, see https://synopsys.atlassian.net/wiki/spaces/INTDOCS/pages/88440888/Sample+Synopsys+Detect+Scan+Configuration+Scenarios+for+Black+Duck 120 err := getDetectScript(config, utils) 121 if err != nil { 122 return fmt.Errorf("failed to download 'detect.sh' script: %w", err) 123 } 124 defer func() { 125 err := utils.FileRemove("detect.sh") 126 if err != nil { 127 log.Entry().Warnf("failed to delete 'detect.sh' script: %v", err) 128 } 129 }() 130 err = utils.Chmod("detect.sh", 0700) 131 if err != nil { 132 return err 133 } 134 135 if config.InstallArtifacts { 136 err := maven.InstallMavenArtifacts(&maven.EvaluateOptions{ 137 M2Path: config.M2Path, 138 ProjectSettingsFile: config.ProjectSettingsFile, 139 GlobalSettingsFile: config.GlobalSettingsFile, 140 }, utils) 141 if err != nil { 142 return err 143 } 144 } 145 146 args := []string{"./detect.sh"} 147 args, err = addDetectArgs(args, config, utils) 148 if err != nil { 149 return err 150 } 151 script := strings.Join(args, " ") 152 153 envs := []string{"BLACKDUCK_SKIP_PHONE_HOME=true"} 154 envs = append(envs, config.CustomEnvironmentVariables...) 155 156 utils.SetDir(".") 157 utils.SetEnv(envs) 158 159 err = utils.RunShell("/bin/bash", script) 160 blackduckSystem := newBlackduckSystem(config) 161 reportingErr := postScanChecksAndReporting(config, influx, utils, blackduckSystem) 162 if reportingErr != nil { 163 if strings.Contains(reportingErr.Error(), "License Policy Violations found") { 164 log.Entry().Errorf("License Policy Violations found") 165 log.SetErrorCategory(log.ErrorCompliance) 166 if err == nil && !piperutils.ContainsStringPart(config.FailOn, "NONE") { 167 err = errors.New("License Policy Violations found") 168 } 169 } else { 170 log.Entry().Warnf("Failed to generate reports: %v", reportingErr) 171 } 172 } 173 if err != nil { 174 // Setting error category based on exit code 175 mapErrorCategory(utils.GetExitCode()) 176 177 // Error code mapping with more human readable text 178 err = errors.Wrapf(err, exitCodeMapping(utils.GetExitCode())) 179 } 180 // create Toolrecord file 181 toolRecordFileName, toolRecordErr := createToolRecordDetect("./", config, blackduckSystem) 182 if toolRecordErr != nil { 183 // do not fail until the framework is well established 184 log.Entry().Warning("TR_DETECT: Failed to create toolrecord file "+toolRecordFileName, err) 185 } 186 return err 187 } 188 189 // Get proper error category 190 func mapErrorCategory(exitCodeKey int) { 191 switch exitCodeKey { 192 case 0: 193 //In case detect exits successfully, we rely on the function 'postScanChecksAndReporting' to determine the error category 194 //hence this method doesnt need to set an error category or go to 'default' case 195 break 196 case 1: 197 log.SetErrorCategory(log.ErrorInfrastructure) 198 case 2: 199 log.SetErrorCategory(log.ErrorService) 200 case 3: 201 log.SetErrorCategory(log.ErrorCompliance) 202 case 4: 203 log.SetErrorCategory(log.ErrorInfrastructure) 204 case 5: 205 log.SetErrorCategory(log.ErrorConfiguration) 206 case 6: 207 log.SetErrorCategory(log.ErrorConfiguration) 208 case 7: 209 log.SetErrorCategory(log.ErrorConfiguration) 210 case 9: 211 log.SetErrorCategory(log.ErrorService) 212 case 10: 213 log.SetErrorCategory(log.ErrorService) 214 case 11: 215 log.SetErrorCategory(log.ErrorService) 216 case 12: 217 log.SetErrorCategory(log.ErrorInfrastructure) 218 case 99: 219 log.SetErrorCategory(log.ErrorService) 220 case 100: 221 log.SetErrorCategory(log.ErrorUndefined) 222 default: 223 log.SetErrorCategory(log.ErrorUndefined) 224 } 225 } 226 227 // Exit codes/error code mapping 228 func exitCodeMapping(exitCodeKey int) string { 229 230 exitCodes := map[int]string{ 231 0: "Detect Scan completed successfully", 232 1: "FAILURE_BLACKDUCK_CONNECTIVITY => Detect was unable to connect to Black Duck. Check your configuration and connection.", 233 2: "FAILURE_TIMEOUT => Detect could not wait for actions to be completed on Black Duck. Check your Black Duck server or increase your timeout.", 234 3: "FAILURE_POLICY_VIOLATION => Detect found policy violations.", 235 4: "FAILURE_PROXY_CONNECTIVITY => Detect was unable to use the configured proxy. Check your configuration and connection.", 236 5: "FAILURE_DETECTOR => Detect had one or more detector failures while extracting dependencies. Check that all projects build and your environment is configured correctly.", 237 6: "FAILURE_SCAN => Detect was unable to run the signature scanner against your source. Check your configuration.", 238 7: "FAILURE_CONFIGURATION => Detect was unable to start because of a configuration issue. Check and fix your configuration.", 239 9: "FAILURE_DETECTOR_REQUIRED => Detect did not run all of the required detectors. Fix detector issues or disable required detectors.", 240 10: "FAILURE_BLACKDUCK_VERSION_NOT_SUPPORTED => Detect attempted an operation that was not supported by your version of Black Duck. Ensure your Black Duck is compatible with this version of detect.", 241 11: "FAILURE_BLACKDUCK_FEATURE_ERROR => Detect encountered an error while attempting an operation on Black Duck. Ensure your Black Duck is compatible with this version of detect.", 242 12: "FAILURE_POLARIS_CONNECTIVITY => Detect was unable to connect to Polaris. Check your configuration and connection.", 243 99: "FAILURE_GENERAL_ERROR => Detect encountered a known error, details of the error are provided.", 244 100: "FAILURE_UNKNOWN_ERROR => Detect encountered an unknown error.", 245 } 246 247 if _, isKeyExists := exitCodes[exitCodeKey]; isKeyExists { 248 return exitCodes[exitCodeKey] 249 } 250 251 return "[" + strconv.Itoa(exitCodeKey) + "]: Not known exit code key" 252 } 253 254 func getDetectScript(config detectExecuteScanOptions, utils detectUtils) error { 255 if config.ScanOnChanges { 256 log.Entry().Infof("Using Detect Rescan script") 257 return utils.DownloadFile("https://raw.githubusercontent.com/blackducksoftware/detect_rescan/master/detect_rescan.sh", "detect.sh", nil, nil) 258 } 259 env := utils.GetOsEnv() 260 env = append(env, config.CustomEnvironmentVariables...) 261 if piperutils.ContainsStringPart(env, "DETECT_LATEST_RELEASE_VERSION") { 262 releaseVersion := "" 263 for _, i := range env { 264 if strings.Contains(i, "DETECT_LATEST_RELEASE_VERSION") { 265 releaseVersion = strings.Split(i, "=")[1] 266 } 267 } 268 log.Entry().Infof("Using detect script Version %v ", releaseVersion) 269 detect6, _ := regexp.MatchString("6\\.\\d\\.\\d", releaseVersion) 270 if detect6 { 271 log.Entry().Infof("Downloading Detect 6.x") 272 return utils.DownloadFile("https://detect.synopsys.com/detect.sh", "detect.sh", nil, nil) 273 } 274 } 275 log.Entry().Infof("Downloading Detect7") 276 return utils.DownloadFile("https://detect.synopsys.com/detect7.sh", "detect.sh", nil, nil) 277 } 278 279 func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectUtils) ([]string, error) { 280 detectVersionName := getVersionName(config) 281 //Split on spaces, the scanPropeties, so that each property is available as a single string 282 //instead of all properties being part of a single string 283 config.ScanProperties = piperutils.SplitAndTrim(config.ScanProperties, " ") 284 285 if config.ScanOnChanges { 286 args = append(args, "--report") 287 config.Unmap = false 288 } 289 290 if config.Unmap { 291 if !piperutils.ContainsString(config.ScanProperties, "--detect.project.codelocation.unmap=true") { 292 args = append(args, fmt.Sprintf("--detect.project.codelocation.unmap=true")) 293 } 294 config.ScanProperties, _ = piperutils.RemoveAll(config.ScanProperties, "--detect.project.codelocation.unmap=false") 295 } else { 296 //When unmap is set to false, any occurances of unmap=true from scanProperties must be removed 297 config.ScanProperties, _ = piperutils.RemoveAll(config.ScanProperties, "--detect.project.codelocation.unmap=true") 298 } 299 300 args = append(args, config.ScanProperties...) 301 302 args = append(args, fmt.Sprintf("--blackduck.url=%v", config.ServerURL)) 303 args = append(args, fmt.Sprintf("--blackduck.api.token=%v", config.Token)) 304 // ProjectNames, VersionName, GroupName etc can contain spaces and need to be escaped using double quotes in CLI 305 // Hence the string need to be surrounded by \" 306 args = append(args, fmt.Sprintf("\"--detect.project.name='%v'\"", config.ProjectName)) 307 args = append(args, fmt.Sprintf("\"--detect.project.version.name='%v'\"", detectVersionName)) 308 309 // Groups parameter is added only when there is atleast one non-empty groupname provided 310 if len(config.Groups) > 0 && len(config.Groups[0]) > 0 { 311 args = append(args, fmt.Sprintf("\"--detect.project.user.groups='%v'\"", strings.Join(config.Groups, ","))) 312 } 313 314 // Atleast 1, non-empty category to fail on must be provided 315 if len(config.FailOn) > 0 && len(config.FailOn[0]) > 0 { 316 args = append(args, fmt.Sprintf("--detect.policy.check.fail.on.severities=%v", strings.Join(config.FailOn, ","))) 317 } 318 319 codelocation := config.CodeLocation 320 if len(codelocation) == 0 && len(config.ProjectName) > 0 { 321 codelocation = fmt.Sprintf("%v/%v", config.ProjectName, detectVersionName) 322 } 323 args = append(args, fmt.Sprintf("\"--detect.code.location.name='%v'\"", codelocation)) 324 325 if len(config.ScanPaths) > 0 && len(config.ScanPaths[0]) > 0 { 326 args = append(args, fmt.Sprintf("--detect.blackduck.signature.scanner.paths=%v", strings.Join(config.ScanPaths, ","))) 327 } 328 329 if len(config.DependencyPath) > 0 { 330 args = append(args, fmt.Sprintf("--detect.source.path=%v", config.DependencyPath)) 331 } else { 332 args = append(args, fmt.Sprintf("--detect.source.path='.'")) 333 } 334 335 if len(config.IncludedPackageManagers) > 0 { 336 args = append(args, fmt.Sprintf("--detect.included.detector.types=%v", strings.ToUpper(strings.Join(config.IncludedPackageManagers, ",")))) 337 } 338 339 if len(config.ExcludedPackageManagers) > 0 { 340 args = append(args, fmt.Sprintf("--detect.excluded.detector.types=%v", strings.ToUpper(strings.Join(config.ExcludedPackageManagers, ",")))) 341 } 342 343 if len(config.MavenExcludedScopes) > 0 { 344 args = append(args, fmt.Sprintf("--detect.maven.excluded.scopes=%v", strings.ToLower(strings.Join(config.MavenExcludedScopes, ",")))) 345 } 346 347 if len(config.DetectTools) > 0 { 348 args = append(args, fmt.Sprintf("--detect.tools=%v", strings.Join(config.DetectTools, ","))) 349 } 350 351 mavenArgs, err := maven.DownloadAndGetMavenParameters(config.GlobalSettingsFile, config.ProjectSettingsFile, utils) 352 if err != nil { 353 return nil, err 354 } 355 356 if len(config.M2Path) > 0 { 357 absolutePath, err := utils.Abs(config.M2Path) 358 if err != nil { 359 return nil, err 360 } 361 mavenArgs = append(mavenArgs, fmt.Sprintf("-Dmaven.repo.local=%v", absolutePath)) 362 } 363 364 if len(mavenArgs) > 0 { 365 args = append(args, fmt.Sprintf("\"--detect.maven.build.command='%v'\"", strings.Join(mavenArgs, " "))) 366 } 367 368 return args, nil 369 } 370 371 func getVersionName(config detectExecuteScanOptions) string { 372 detectVersionName := config.CustomScanVersion 373 if len(detectVersionName) > 0 { 374 log.Entry().Infof("Using custom version: %v", detectVersionName) 375 } else { 376 detectVersionName = versioning.ApplyVersioningModel(config.VersioningModel, config.Version) 377 } 378 return detectVersionName 379 } 380 381 func createVulnerabilityReport(config detectExecuteScanOptions, vulns *bd.Vulnerabilities, influx *detectExecuteScanInflux, sys *blackduckSystem) reporting.ScanReport { 382 versionName := getVersionName(config) 383 versionUrl, _ := sys.Client.GetProjectVersionLink(config.ProjectName, versionName) 384 scanReport := reporting.ScanReport{ 385 ReportTitle: "BlackDuck Security Vulnerability Report", 386 Subheaders: []reporting.Subheader{ 387 {Description: "BlackDuck Project Name ", Details: config.ProjectName}, 388 {Description: "BlackDuck Project Version ", Details: fmt.Sprintf("<a href='%v'>%v</a>", versionUrl, versionName)}, 389 }, 390 Overview: []reporting.OverviewRow{ 391 {Description: "Total number of vulnerabilities ", Details: fmt.Sprint(influx.detect_data.fields.vulnerabilities)}, 392 {Description: "Total number of Critical/High vulnerabilties ", Details: fmt.Sprint(influx.detect_data.fields.major_vulnerabilities)}, 393 }, 394 SuccessfulScan: influx.detect_data.fields.major_vulnerabilities == 0, 395 ReportTime: time.Now(), 396 } 397 398 detailTable := reporting.ScanDetailTable{ 399 NoRowsMessage: "No publicly known vulnerabilities detected", 400 Headers: []string{ 401 "Vulnerability Name", 402 "Severity", 403 "Overall Score", 404 "Base Score", 405 "Component Name", 406 "Component Version", 407 "Description", 408 "Status", 409 }, 410 WithCounter: true, 411 CounterHeader: "Entry#", 412 } 413 414 vulnItems := vulns.Items 415 sort.Slice(vulnItems, func(i, j int) bool { 416 return vulnItems[i].OverallScore > vulnItems[j].OverallScore 417 }) 418 419 for _, vuln := range vulnItems { 420 row := reporting.ScanRow{} 421 row.AddColumn(vuln.VulnerabilityWithRemediation.VulnerabilityName, 0) 422 row.AddColumn(vuln.VulnerabilityWithRemediation.Severity, 0) 423 424 var scoreStyle reporting.ColumnStyle = reporting.Yellow 425 if isMajorVulnerability(vuln) { 426 scoreStyle = reporting.Red 427 } 428 if !isActiveVulnerability(vuln) { 429 scoreStyle = reporting.Grey 430 } 431 row.AddColumn(vuln.VulnerabilityWithRemediation.OverallScore, scoreStyle) 432 row.AddColumn(vuln.VulnerabilityWithRemediation.BaseScore, 0) 433 row.AddColumn(vuln.Name, 0) 434 row.AddColumn(vuln.Version, 0) 435 row.AddColumn(vuln.VulnerabilityWithRemediation.Description, 0) 436 row.AddColumn(vuln.VulnerabilityWithRemediation.RemediationStatus, 0) 437 438 detailTable.Rows = append(detailTable.Rows, row) 439 } 440 441 scanReport.DetailTable = detailTable 442 return scanReport 443 } 444 445 func isActiveVulnerability(v bd.Vulnerability) bool { 446 switch v.VulnerabilityWithRemediation.RemediationStatus { 447 case "NEW": 448 return true 449 case "REMEDIATION_REQUIRED": 450 return true 451 case "NEEDS_REVIEW": 452 return true 453 default: 454 return false 455 } 456 } 457 458 func isMajorVulnerability(v bd.Vulnerability) bool { 459 switch v.VulnerabilityWithRemediation.Severity { 460 case "CRITICAL": 461 return true 462 case "HIGH": 463 return true 464 default: 465 return false 466 } 467 } 468 469 func postScanChecksAndReporting(config detectExecuteScanOptions, influx *detectExecuteScanInflux, utils detectUtils, sys *blackduckSystem) error { 470 errorsOccured := []string{} 471 vulns, _, err := getVulnsAndComponents(config, influx, sys) 472 if err != nil { 473 return errors.Wrap(err, "failed to fetch vulnerabilities") 474 } 475 476 if config.CreateResultIssue && len(config.GithubToken) > 0 && len(config.GithubAPIURL) > 0 && len(config.Owner) > 0 && len(config.Repository) > 0 { 477 log.Entry().Debugf("Creating result issues for %v alert(s)", len(vulns.Items)) 478 issueDetails := make([]reporting.IssueDetail, len(vulns.Items)) 479 piperutils.CopyAtoB(vulns.Items, issueDetails) 480 err = reporting.UploadMultipleReportsToGithub(&issueDetails, config.GithubToken, config.GithubAPIURL, config.Owner, config.Repository, config.Assignees, config.CustomTLSCertificateLinks, utils) 481 if err != nil { 482 errorsOccured = append(errorsOccured, fmt.Sprint(err)) 483 } 484 } 485 486 sarif := bd.CreateSarifResultFile(vulns) 487 paths, err := bd.WriteSarifFile(sarif, utils) 488 if err != nil { 489 errorsOccured = append(errorsOccured, fmt.Sprint(err)) 490 } 491 492 scanReport := createVulnerabilityReport(config, vulns, influx, sys) 493 vulnerabilityReportPaths, err := bd.WriteVulnerabilityReports(scanReport, utils) 494 if err != nil { 495 errorsOccured = append(errorsOccured, fmt.Sprint(err)) 496 } 497 paths = append(paths, vulnerabilityReportPaths...) 498 499 policyStatus, err := getPolicyStatus(config, influx, sys) 500 policyReport := createPolicyStatusReport(config, policyStatus, influx, sys) 501 policyReportPaths, err := writePolicyStatusReports(policyReport, config, utils) 502 if err != nil { 503 errorsOccured = append(errorsOccured, fmt.Sprint(err)) 504 } 505 paths = append(paths, policyReportPaths...) 506 507 piperutils.PersistReportsAndLinks("detectExecuteScan", "", paths, nil) 508 if err != nil { 509 errorsOccured = append(errorsOccured, fmt.Sprint(err)) 510 } 511 512 err, violationCount := writeIpPolicyJson(config, utils, paths, sys) 513 if err != nil { 514 errorsOccured = append(errorsOccured, fmt.Sprint(err)) 515 } 516 517 if violationCount > 0 { 518 log.SetErrorCategory(log.ErrorCompliance) 519 errorsOccured = append(errorsOccured, fmt.Sprint("License Policy Violations found")) 520 } 521 522 if len(errorsOccured) > 0 { 523 return fmt.Errorf(strings.Join(errorsOccured, ": ")) 524 } 525 526 return nil 527 } 528 529 func getVulnsAndComponents(config detectExecuteScanOptions, influx *detectExecuteScanInflux, sys *blackduckSystem) (*bd.Vulnerabilities, *bd.Components, error) { 530 detectVersionName := getVersionName(config) 531 vulns, err := sys.Client.GetVulnerabilities(config.ProjectName, detectVersionName) 532 if err != nil { 533 return nil, nil, err 534 } 535 536 majorVulns := 0 537 activeVulns := 0 538 for _, vuln := range vulns.Items { 539 if isActiveVulnerability(vuln) { 540 activeVulns++ 541 if isMajorVulnerability(vuln) { 542 majorVulns++ 543 } 544 } 545 } 546 influx.detect_data.fields.vulnerabilities = activeVulns 547 influx.detect_data.fields.major_vulnerabilities = majorVulns 548 influx.detect_data.fields.minor_vulnerabilities = activeVulns - majorVulns 549 550 components, err := sys.Client.GetComponents(config.ProjectName, detectVersionName) 551 if err != nil { 552 return vulns, nil, err 553 } 554 influx.detect_data.fields.components = components.TotalCount 555 556 return vulns, components, nil 557 } 558 559 func getPolicyStatus(config detectExecuteScanOptions, influx *detectExecuteScanInflux, sys *blackduckSystem) (*bd.PolicyStatus, error) { 560 policyStatus, err := sys.Client.GetPolicyStatus(config.ProjectName, getVersionName(config)) 561 if err != nil { 562 return nil, err 563 } 564 565 totalViolations := 0 566 for _, level := range policyStatus.SeverityLevels { 567 totalViolations += level.Value 568 } 569 influx.detect_data.fields.policy_violations = totalViolations 570 571 return policyStatus, nil 572 } 573 574 func createPolicyStatusReport(config detectExecuteScanOptions, policyStatus *bd.PolicyStatus, influx *detectExecuteScanInflux, sys *blackduckSystem) reporting.ScanReport { 575 versionName := getVersionName(config) 576 versionUrl, _ := sys.Client.GetProjectVersionLink(config.ProjectName, versionName) 577 policyReport := reporting.ScanReport{ 578 ReportTitle: "BlackDuck Policy Violations Report", 579 Subheaders: []reporting.Subheader{ 580 {Description: "BlackDuck project name ", Details: config.ProjectName}, 581 {Description: "BlackDuck project version name", Details: fmt.Sprintf("<a href='%v'>%v</a>", versionUrl, versionName)}, 582 }, 583 Overview: []reporting.OverviewRow{ 584 {Description: "Overall Policy Violation Status", Details: policyStatus.OverallStatus}, 585 {Description: "Total Number of Policy Vioaltions", Details: fmt.Sprint(influx.detect_data.fields.policy_violations)}, 586 }, 587 SuccessfulScan: influx.detect_data.fields.policy_violations > 0, 588 ReportTime: time.Now(), 589 } 590 591 detailTable := reporting.ScanDetailTable{ 592 Headers: []string{ 593 "Policy Severity Level", "Number of Components in Violation", 594 }, 595 WithCounter: false, 596 } 597 598 for _, level := range policyStatus.SeverityLevels { 599 row := reporting.ScanRow{} 600 row.AddColumn(level.Name, 0) 601 row.AddColumn(level.Value, 0) 602 detailTable.Rows = append(detailTable.Rows, row) 603 } 604 policyReport.DetailTable = detailTable 605 606 return policyReport 607 } 608 609 func writePolicyStatusReports(scanReport reporting.ScanReport, config detectExecuteScanOptions, utils detectUtils) ([]piperutils.Path, error) { 610 reportPaths := []piperutils.Path{} 611 612 htmlReport, _ := scanReport.ToHTML() 613 htmlReportPath := "piper_detect_policy_violation_report.html" 614 if err := utils.FileWrite(htmlReportPath, htmlReport, 0666); err != nil { 615 log.SetErrorCategory(log.ErrorConfiguration) 616 return reportPaths, errors.Wrapf(err, "failed to write html report") 617 } 618 reportPaths = append(reportPaths, piperutils.Path{Name: "BlackDuck Policy Violation Report", Target: htmlReportPath}) 619 620 jsonReport, _ := scanReport.ToJSON() 621 if exists, _ := utils.DirExists(reporting.StepReportDirectory); !exists { 622 err := utils.MkdirAll(reporting.StepReportDirectory, 0777) 623 if err != nil { 624 return reportPaths, errors.Wrap(err, "failed to create reporting directory") 625 } 626 } 627 if err := utils.FileWrite(filepath.Join(reporting.StepReportDirectory, fmt.Sprintf("detectExecuteScan_policy_%v.json", fmt.Sprintf("%v", time.Now()))), jsonReport, 0666); err != nil { 628 return reportPaths, errors.Wrapf(err, "failed to write json report") 629 } 630 631 return reportPaths, nil 632 } 633 634 func writeIpPolicyJson(config detectExecuteScanOptions, utils detectUtils, paths []piperutils.Path, sys *blackduckSystem) (error, int) { 635 components, err := sys.Client.GetComponentsWithLicensePolicyRule(config.ProjectName, getVersionName(config)) 636 if err != nil { 637 errors.Wrapf(err, "failed to get License Policy Violations") 638 return err, 0 639 } 640 641 violationCount := getActivePolicyViolations(components) 642 violations := struct { 643 PolicyViolations int `json:"policyViolations"` 644 Reports []string `json:"reports"` 645 }{ 646 PolicyViolations: violationCount, 647 Reports: []string{}, 648 } 649 650 for _, path := range paths { 651 violations.Reports = append(violations.Reports, path.Target) 652 } 653 if files, err := utils.Glob("**/*BlackDuck_RiskReport.pdf"); err == nil && len(files) > 0 { 654 // there should only be one RiskReport thus only taking the first one 655 _, reportFile := filepath.Split(files[0]) 656 violations.Reports = append(violations.Reports, reportFile) 657 } 658 659 violationContent, err := json.Marshal(violations) 660 if err != nil { 661 return fmt.Errorf("failed to marshal policy violation data: %w", err), violationCount 662 } 663 664 err = utils.FileWrite("blackduck-ip.json", violationContent, 0666) 665 if err != nil { 666 return fmt.Errorf("failed to write policy violation report: %w", err), violationCount 667 } 668 return nil, violationCount 669 } 670 671 func getActivePolicyViolations(components *bd.Components) int { 672 if components.TotalCount == 0 { 673 return 0 674 } 675 activeViolations := 0 676 for _, component := range components.Items { 677 if isActivePolicyViolation(component.PolicyStatus) { 678 activeViolations++ 679 } 680 } 681 return activeViolations 682 } 683 684 func isActivePolicyViolation(status string) bool { 685 if status == "IN_VIOLATION" { 686 return true 687 } 688 return false 689 } 690 691 // create toolrecord file for detectExecute 692 func createToolRecordDetect(workspace string, config detectExecuteScanOptions, sys *blackduckSystem) (string, error) { 693 record := toolrecord.New(workspace, "detectExecute", config.ServerURL) 694 project, err := sys.Client.GetProject(config.ProjectName) 695 if err != nil { 696 return "", fmt.Errorf("TR_DETECT: GetProject failed %v", err) 697 } 698 metadata := project.Metadata 699 projectURL := metadata.Href 700 if projectURL == "" { 701 return "", fmt.Errorf("TR_DETECT: no project URL") 702 } 703 // project UUID comes as last part of the URL 704 parts := strings.Split(projectURL, "/") 705 projectId := parts[len(parts)-1] 706 if projectId == "" { 707 return "", fmt.Errorf("TR_DETECT: no project id in %v", projectURL) 708 } 709 err = record.AddKeyData("project", 710 projectId, 711 config.ProjectName, 712 projectURL) 713 if err != nil { 714 return "", err 715 } 716 record.AddContext("DetectTools", config.DetectTools) 717 err = record.Persist() 718 if err != nil { 719 return "", err 720 } 721 return record.GetFileName(), nil 722 }