2019-12-13 11:55:45 +02:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-11-03 12:08:23 +02:00
|
|
|
"io"
|
|
|
|
"net/http"
|
2020-10-27 15:29:22 +02:00
|
|
|
"os"
|
2020-10-01 13:34:51 +02:00
|
|
|
"strings"
|
|
|
|
|
2020-07-30 10:35:46 +02:00
|
|
|
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/maven"
|
2019-12-13 11:55:45 +02:00
|
|
|
|
|
|
|
"github.com/SAP/jenkins-library/pkg/command"
|
|
|
|
"github.com/SAP/jenkins-library/pkg/log"
|
2020-07-30 10:35:46 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/piperutils"
|
2020-02-04 11:46:43 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/telemetry"
|
2021-07-23 08:48:48 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/toolrecord"
|
2020-07-27 12:01:59 +02:00
|
|
|
"github.com/SAP/jenkins-library/pkg/versioning"
|
2019-12-13 11:55:45 +02:00
|
|
|
)
|
|
|
|
|
2020-11-03 12:08:23 +02:00
|
|
|
type detectUtils interface {
|
2020-10-27 15:29:22 +02:00
|
|
|
Abs(path string) (string, error)
|
|
|
|
FileExists(filename string) (bool, error)
|
|
|
|
FileRemove(filename string) error
|
|
|
|
Copy(src, dest string) (int64, error)
|
|
|
|
FileRead(path string) ([]byte, error)
|
|
|
|
FileWrite(path string, content []byte, perm os.FileMode) error
|
|
|
|
MkdirAll(path string, perm os.FileMode) error
|
|
|
|
Chmod(path string, mode os.FileMode) error
|
|
|
|
Glob(pattern string) (matches []string, err error)
|
2020-11-03 12:08:23 +02:00
|
|
|
|
|
|
|
Stdout(out io.Writer)
|
|
|
|
Stderr(err io.Writer)
|
|
|
|
SetDir(dir string)
|
|
|
|
SetEnv(env []string)
|
|
|
|
RunExecutable(e string, p ...string) error
|
|
|
|
RunShell(shell, script string) error
|
|
|
|
|
|
|
|
DownloadFile(url, filename string, header http.Header, cookies []*http.Cookie) error
|
2020-10-27 15:29:22 +02:00
|
|
|
}
|
|
|
|
|
2020-11-03 12:08:23 +02:00
|
|
|
type detectUtilsBundle struct {
|
|
|
|
*command.Command
|
|
|
|
*piperutils.Files
|
|
|
|
*piperhttp.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
func newDetectUtils() detectUtils {
|
|
|
|
utils := detectUtilsBundle{
|
|
|
|
Command: &command.Command{
|
|
|
|
ErrorCategoryMapping: map[string][]string{
|
|
|
|
log.ErrorCompliance.String(): {
|
|
|
|
"FAILURE_POLICY_VIOLATION - Detect found policy violations.",
|
|
|
|
},
|
|
|
|
log.ErrorConfiguration.String(): {
|
|
|
|
"FAILURE_CONFIGURATION - Detect was unable to start due to issues with it's configuration.",
|
|
|
|
},
|
2020-10-06 14:22:35 +02:00
|
|
|
},
|
2020-10-06 11:55:05 +02:00
|
|
|
},
|
2020-11-03 12:08:23 +02:00
|
|
|
Files: &piperutils.Files{},
|
|
|
|
Client: &piperhttp.Client{},
|
2020-10-06 11:55:05 +02:00
|
|
|
}
|
2020-11-03 12:08:23 +02:00
|
|
|
utils.Stdout(log.Writer())
|
|
|
|
utils.Stderr(log.Writer())
|
|
|
|
return &utils
|
|
|
|
}
|
2019-12-13 11:55:45 +02:00
|
|
|
|
2020-11-03 12:08:23 +02:00
|
|
|
func detectExecuteScan(config detectExecuteScanOptions, _ *telemetry.CustomData) {
|
|
|
|
utils := newDetectUtils()
|
|
|
|
err := runDetect(config, utils)
|
2019-12-13 11:55:45 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Entry().
|
|
|
|
WithError(err).
|
|
|
|
Fatal("failed to execute detect scan")
|
|
|
|
}
|
2021-07-23 08:48:48 +02:00
|
|
|
|
|
|
|
// create Toolrecord file
|
|
|
|
toolRecordFileName, err := createToolRecordDetect("./", config)
|
|
|
|
if err != nil {
|
|
|
|
// do not fail until the framework is well established
|
|
|
|
log.Entry().Warning("TR_DETECT: Failed to create toolrecord file "+toolRecordFileName, err)
|
|
|
|
}
|
2019-12-13 11:55:45 +02:00
|
|
|
}
|
|
|
|
|
2020-11-03 12:08:23 +02:00
|
|
|
func runDetect(config detectExecuteScanOptions, utils detectUtils) error {
|
2020-07-30 10:35:46 +02:00
|
|
|
// detect execution details, see https://synopsys.atlassian.net/wiki/spaces/INTDOCS/pages/88440888/Sample+Synopsys+Detect+Scan+Configuration+Scenarios+for+Black+Duck
|
2021-01-29 11:17:02 +02:00
|
|
|
err := getDetectScript(config, utils)
|
2020-10-27 15:29:22 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to download 'detect.sh' script: %w", err)
|
|
|
|
}
|
|
|
|
defer func() {
|
2020-11-03 12:08:23 +02:00
|
|
|
err := utils.FileRemove("detect.sh")
|
2020-10-27 15:29:22 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Entry().Warnf("failed to delete 'detect.sh' script: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
2020-11-03 12:08:23 +02:00
|
|
|
err = utils.Chmod("detect.sh", 0700)
|
2020-07-30 10:35:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-11-03 12:08:23 +02:00
|
|
|
|
|
|
|
if config.InstallArtifacts {
|
2020-11-10 18:14:55 +02:00
|
|
|
err := maven.InstallMavenArtifacts(&maven.EvaluateOptions{
|
2020-11-03 12:08:23 +02:00
|
|
|
M2Path: config.M2Path,
|
|
|
|
ProjectSettingsFile: config.ProjectSettingsFile,
|
|
|
|
GlobalSettingsFile: config.GlobalSettingsFile,
|
2020-11-10 18:14:55 +02:00
|
|
|
}, utils)
|
2020-11-03 12:08:23 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 10:35:46 +02:00
|
|
|
args := []string{"./detect.sh"}
|
2020-11-03 12:08:23 +02:00
|
|
|
args, err = addDetectArgs(args, config, utils)
|
2020-07-30 10:35:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
script := strings.Join(args, " ")
|
|
|
|
|
|
|
|
envs := []string{"BLACKDUCK_SKIP_PHONE_HOME=true"}
|
2021-04-01 11:24:25 +02:00
|
|
|
envs = append(envs, config.CustomEnvironmentVariables...)
|
2020-07-30 10:35:46 +02:00
|
|
|
|
2020-11-03 12:08:23 +02:00
|
|
|
utils.SetDir(".")
|
|
|
|
utils.SetEnv(envs)
|
2020-07-30 10:35:46 +02:00
|
|
|
|
2020-11-03 12:08:23 +02:00
|
|
|
return utils.RunShell("/bin/bash", script)
|
2020-07-30 10:35:46 +02:00
|
|
|
}
|
|
|
|
|
2021-01-29 11:17:02 +02:00
|
|
|
func getDetectScript(config detectExecuteScanOptions, utils detectUtils) error {
|
|
|
|
if config.ScanOnChanges {
|
|
|
|
return utils.DownloadFile("https://raw.githubusercontent.com/blackducksoftware/detect_rescan/master/detect_rescan.sh", "detect.sh", nil, nil)
|
|
|
|
}
|
|
|
|
return utils.DownloadFile("https://detect.synopsys.com/detect.sh", "detect.sh", nil, nil)
|
|
|
|
}
|
|
|
|
|
2020-11-03 12:08:23 +02:00
|
|
|
func addDetectArgs(args []string, config detectExecuteScanOptions, utils detectUtils) ([]string, error) {
|
2021-05-05 10:24:05 +02:00
|
|
|
detectVersionName := config.CustomScanVersion
|
|
|
|
if len(detectVersionName) > 0 {
|
|
|
|
log.Entry().Infof("Using custom version: %v", detectVersionName)
|
|
|
|
} else {
|
2021-05-14 09:35:31 +02:00
|
|
|
detectVersionName = versioning.ApplyVersioningModel(config.VersioningModel, config.Version)
|
2021-05-05 10:24:05 +02:00
|
|
|
}
|
2021-03-26 14:06:13 +02:00
|
|
|
//Split on spaces, the scanPropeties, so that each property is available as a single string
|
|
|
|
//instead of all properties being part of a single string
|
|
|
|
config.ScanProperties = piperutils.SplitAndTrim(config.ScanProperties, " ")
|
|
|
|
|
2021-01-29 11:17:02 +02:00
|
|
|
if config.ScanOnChanges {
|
|
|
|
args = append(args, "--report")
|
2021-03-26 14:06:13 +02:00
|
|
|
config.Unmap = false
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.Unmap {
|
2021-03-31 10:53:49 +02:00
|
|
|
if !piperutils.ContainsString(config.ScanProperties, "--detect.project.codelocation.unmap=true") {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.project.codelocation.unmap=true"))
|
|
|
|
}
|
2021-03-26 14:06:13 +02:00
|
|
|
config.ScanProperties, _ = piperutils.RemoveAll(config.ScanProperties, "--detect.project.codelocation.unmap=false")
|
|
|
|
} else {
|
|
|
|
//When unmap is set to false, any occurances of unmap=true from scanProperties must be removed
|
|
|
|
config.ScanProperties, _ = piperutils.RemoveAll(config.ScanProperties, "--detect.project.codelocation.unmap=true")
|
2021-01-29 11:17:02 +02:00
|
|
|
}
|
|
|
|
|
2020-02-04 11:46:43 +02:00
|
|
|
args = append(args, config.ScanProperties...)
|
2019-12-13 11:55:45 +02:00
|
|
|
|
2020-02-04 11:46:43 +02:00
|
|
|
args = append(args, fmt.Sprintf("--blackduck.url=%v", config.ServerURL))
|
2020-10-01 13:34:51 +02:00
|
|
|
args = append(args, fmt.Sprintf("--blackduck.api.token=%v", config.Token))
|
2020-07-28 10:48:19 +02:00
|
|
|
// ProjectNames, VersionName, GroupName etc can contain spaces and need to be escaped using double quotes in CLI
|
|
|
|
// Hence the string need to be surrounded by \"
|
2021-02-02 16:43:12 +02:00
|
|
|
args = append(args, fmt.Sprintf("\"--detect.project.name='%v'\"", config.ProjectName))
|
|
|
|
args = append(args, fmt.Sprintf("\"--detect.project.version.name='%v'\"", detectVersionName))
|
2020-07-28 10:48:19 +02:00
|
|
|
|
|
|
|
// Groups parameter is added only when there is atleast one non-empty groupname provided
|
|
|
|
if len(config.Groups) > 0 && len(config.Groups[0]) > 0 {
|
2021-02-02 16:43:12 +02:00
|
|
|
args = append(args, fmt.Sprintf("\"--detect.project.user.groups='%v'\"", strings.Join(config.Groups, ",")))
|
2020-07-28 10:48:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Atleast 1, non-empty category to fail on must be provided
|
|
|
|
if len(config.FailOn) > 0 && len(config.FailOn[0]) > 0 {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.policy.check.fail.on.severities=%v", strings.Join(config.FailOn, ",")))
|
|
|
|
}
|
2019-12-13 11:55:45 +02:00
|
|
|
|
2021-04-08 11:04:49 +02:00
|
|
|
codelocation := config.CodeLocation
|
|
|
|
if len(codelocation) == 0 && len(config.ProjectName) > 0 {
|
|
|
|
codelocation = fmt.Sprintf("%v/%v", config.ProjectName, detectVersionName)
|
2019-12-13 11:55:45 +02:00
|
|
|
}
|
2021-04-08 11:04:49 +02:00
|
|
|
args = append(args, fmt.Sprintf("\"--detect.code.location.name='%v'\"", codelocation))
|
2019-12-13 11:55:45 +02:00
|
|
|
|
2021-01-21 15:57:00 +02:00
|
|
|
if len(config.ScanPaths) > 0 && len(config.ScanPaths[0]) > 0 {
|
2020-02-04 11:46:43 +02:00
|
|
|
args = append(args, fmt.Sprintf("--detect.blackduck.signature.scanner.paths=%v", strings.Join(config.ScanPaths, ",")))
|
2019-12-13 11:55:45 +02:00
|
|
|
}
|
|
|
|
|
2021-01-21 15:57:00 +02:00
|
|
|
if len(config.DependencyPath) > 0 {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.source.path=%v", config.DependencyPath))
|
|
|
|
} else {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.source.path='.'"))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.IncludedPackageManagers) > 0 {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.included.detector.types=%v", strings.ToUpper(strings.Join(config.IncludedPackageManagers, ","))))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.ExcludedPackageManagers) > 0 {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.excluded.detector.types=%v", strings.ToUpper(strings.Join(config.ExcludedPackageManagers, ","))))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.MavenExcludedScopes) > 0 {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.maven.excluded.scopes=%v", strings.ToLower(strings.Join(config.MavenExcludedScopes, ","))))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.DetectTools) > 0 {
|
|
|
|
args = append(args, fmt.Sprintf("--detect.tools=%v", strings.Join(config.DetectTools, ",")))
|
2019-12-13 11:55:45 +02:00
|
|
|
}
|
2020-07-30 10:35:46 +02:00
|
|
|
|
2020-11-10 18:14:55 +02:00
|
|
|
mavenArgs, err := maven.DownloadAndGetMavenParameters(config.GlobalSettingsFile, config.ProjectSettingsFile, utils)
|
2020-07-30 10:35:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(config.M2Path) > 0 {
|
2020-11-03 12:08:23 +02:00
|
|
|
absolutePath, err := utils.Abs(config.M2Path)
|
2020-07-30 10:35:46 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
mavenArgs = append(mavenArgs, fmt.Sprintf("-Dmaven.repo.local=%v", absolutePath))
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(mavenArgs) > 0 {
|
|
|
|
args = append(args, fmt.Sprintf("\"--detect.maven.build.command='%v'\"", strings.Join(mavenArgs, " ")))
|
|
|
|
}
|
|
|
|
|
|
|
|
return args, nil
|
2019-12-13 11:55:45 +02:00
|
|
|
}
|
2021-07-23 08:48:48 +02:00
|
|
|
|
|
|
|
// create toolrecord file for detect
|
|
|
|
//
|
|
|
|
//
|
|
|
|
func createToolRecordDetect(workspace string, config detectExecuteScanOptions) (string, error) {
|
|
|
|
record := toolrecord.New(workspace, "detectExecute", config.ServerURL)
|
|
|
|
|
|
|
|
projectId := "" // todo needs more research; according to synopsis documentation
|
|
|
|
productURL := "" // relevant ids can be found in the logfile
|
|
|
|
err := record.AddKeyData("project",
|
|
|
|
projectId,
|
|
|
|
config.ProjectName,
|
|
|
|
productURL)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
record.AddContext("DetectTools", config.DetectTools)
|
|
|
|
err = record.Persist()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return record.GetFileName(), nil
|
|
|
|
}
|