You've already forked sap-jenkins-library
							
							
				mirror of
				https://github.com/SAP/jenkins-library.git
				synced 2025-10-30 23:57:50 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			309 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package whitesource
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/SAP/jenkins-library/pkg/log"
 | |
| 	"github.com/SAP/jenkins-library/pkg/maven"
 | |
| 	"github.com/magiconair/properties"
 | |
| 	"github.com/pkg/errors"
 | |
| )
 | |
| 
 | |
| // ConfigOption defines a dedicated WhiteSource config which can be enforced if required
 | |
| type ConfigOption struct {
 | |
| 	Name          string
 | |
| 	Value         interface{}
 | |
| 	OmitIfPresent string
 | |
| 	Force         bool
 | |
| 	Append        bool
 | |
| }
 | |
| 
 | |
| // ConfigOptions contains a list of config options (ConfigOption)
 | |
| type ConfigOptions []ConfigOption
 | |
| 
 | |
| // RewriteUAConfigurationFile updates the user's Unified Agent configuration with configuration which should be enforced or just eases the overall configuration
 | |
| // It then returns the path to the file containing the updated configuration
 | |
| func (s *ScanOptions) RewriteUAConfigurationFile(utils Utils, projectName string) (string, error) {
 | |
| 
 | |
| 	uaContent, err := utils.FileRead(s.ConfigFilePath)
 | |
| 	uaConfig, propErr := properties.Load(uaContent, properties.UTF8)
 | |
| 	uaConfigMap := map[string]string{}
 | |
| 	if err != nil || propErr != nil {
 | |
| 		log.Entry().Warningf("Failed to load configuration file '%v'. Creating a configuration file from scratch.", s.ConfigFilePath)
 | |
| 	} else {
 | |
| 		uaConfigMap = uaConfig.Map()
 | |
| 	}
 | |
| 
 | |
| 	cOptions := ConfigOptions{}
 | |
| 	cOptions.addGeneralDefaults(s, utils, projectName)
 | |
| 	cOptions.addBuildToolDefaults(s, utils)
 | |
| 
 | |
| 	newConfigMap := cOptions.updateConfig(&uaConfigMap)
 | |
| 	newConfig := properties.LoadMap(newConfigMap)
 | |
| 
 | |
| 	now := time.Now().Format("20060102150405")
 | |
| 	newConfigFilePath := fmt.Sprintf("%v.%v", s.ConfigFilePath, now)
 | |
| 
 | |
| 	var configContent bytes.Buffer
 | |
| 	_, err = newConfig.Write(&configContent, properties.UTF8)
 | |
| 	if err != nil {
 | |
| 		return "", errors.Wrap(err, "failed to write properties")
 | |
| 	}
 | |
| 
 | |
| 	err = utils.FileWrite(newConfigFilePath, configContent.Bytes(), 0666)
 | |
| 	if err != nil {
 | |
| 		return "", errors.Wrap(err, "failed to write file")
 | |
| 	}
 | |
| 
 | |
| 	return newConfigFilePath, nil
 | |
| }
 | |
| 
 | |
| func (c *ConfigOptions) updateConfig(originalConfig *map[string]string) map[string]string {
 | |
| 	newConfig := map[string]string{}
 | |
| 	for k, v := range *originalConfig {
 | |
| 		newConfig[k] = v
 | |
| 	}
 | |
| 
 | |
| 	for _, cOpt := range *c {
 | |
| 		//omit default if value present
 | |
| 		var dependentValue string
 | |
| 		if len(cOpt.OmitIfPresent) > 0 {
 | |
| 			dependentValue = newConfig[cOpt.OmitIfPresent]
 | |
| 		}
 | |
| 
 | |
| 		if len(dependentValue) == 0 {
 | |
| 			if cOpt.Append {
 | |
| 				if len(newConfig[cOpt.Name]) > 0 {
 | |
| 					newConfig[cOpt.Name] = fmt.Sprintf("%v %v", newConfig[cOpt.Name], cOpt.Value)
 | |
| 				} else {
 | |
| 					newConfig[cOpt.Name] = fmt.Sprint(cOpt.Value)
 | |
| 				}
 | |
| 			} else if cOpt.Force || len(newConfig[cOpt.Name]) == 0 {
 | |
| 				newConfig[cOpt.Name] = fmt.Sprint(cOpt.Value)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return newConfig
 | |
| }
 | |
| 
 | |
| func (c *ConfigOptions) addGeneralDefaults(config *ScanOptions, utils Utils, projectName string) {
 | |
| 	cOptions := ConfigOptions{}
 | |
| 	if strings.HasPrefix(config.ProductName, "DIST - ") {
 | |
| 		cOptions = append(cOptions, []ConfigOption{
 | |
| 			{Name: "checkPolicies", Value: false, Force: true},
 | |
| 			{Name: "forceCheckAllDependencies", Value: false, Force: true},
 | |
| 		}...)
 | |
| 	} else {
 | |
| 		cOptions = append(cOptions, []ConfigOption{
 | |
| 			{Name: "checkPolicies", Value: true, Force: true},
 | |
| 			{Name: "forceCheckAllDependencies", Value: true, Force: true},
 | |
| 		}...)
 | |
| 	}
 | |
| 
 | |
| 	if config.Verbose {
 | |
| 		cOptions = append(cOptions, []ConfigOption{
 | |
| 			{Name: "log.level", Value: "trace"},
 | |
| 			{Name: "log.files.level", Value: "trace"},
 | |
| 		}...)
 | |
| 	}
 | |
| 
 | |
| 	if len(config.Excludes) > 0 {
 | |
| 		cOptions = append(cOptions, ConfigOption{Name: "excludes", Value: strings.Join(config.Excludes, " "), Force: true})
 | |
| 	}
 | |
| 
 | |
| 	if len(config.Includes) > 0 {
 | |
| 		cOptions = append(cOptions, ConfigOption{Name: "includes", Value: strings.Join(config.Includes, " "), Force: true})
 | |
| 	}
 | |
| 
 | |
| 	// might need some refactoring later
 | |
| 	if len(projectName) == 0 {
 | |
| 		projectName = config.ProjectName
 | |
| 	}
 | |
| 
 | |
| 	cOptions = append(cOptions, []ConfigOption{
 | |
| 		{Name: "apiKey", Value: config.OrgToken, Force: true},
 | |
| 		{Name: "productName", Value: config.ProductName, Force: true},
 | |
| 		{Name: "productVersion", Value: config.ProductVersion, Force: true},
 | |
| 		{Name: "projectName", Value: projectName, Force: true},
 | |
| 		{Name: "projectVersion", Value: config.ProductVersion, Force: true},
 | |
| 		{Name: "productToken", Value: config.ProductToken, OmitIfPresent: "projectToken", Force: true},
 | |
| 		{Name: "userKey", Value: config.UserToken, Force: true},
 | |
| 		{Name: "forceUpdate", Value: true, Force: true},
 | |
| 		{Name: "offline", Value: false, Force: true},
 | |
| 		{Name: "resolveAllDependencies", Value: false, Force: true},
 | |
| 		{Name: "failErrorLevel", Value: "ALL", Force: true},
 | |
| 		{Name: "case.sensitive.glob", Value: false},
 | |
| 		{Name: "followSymbolicLinks", Value: true},
 | |
| 	}...)
 | |
| 
 | |
| 	for _, cOpt := range cOptions {
 | |
| 		*c = append(*c, cOpt)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *ConfigOptions) addBuildToolDefaults(config *ScanOptions, utils Utils) error {
 | |
| 	buildToolDefaults := map[string]ConfigOptions{
 | |
| 		"docker": {
 | |
| 			{Name: "docker.scanImages", Value: true, Force: true},
 | |
| 			{Name: "docker.scanTarFiles", Value: true, Force: true},
 | |
| 			{Name: "docker.includes", Value: ".*.tar", Force: true},
 | |
| 			{Name: "ignoreSourceFiles", Value: false},
 | |
| 			{Name: "python.resolveGlobalPackages", Value: true, Force: false},
 | |
| 			{Name: "resolveAllDependencies", Value: true, Force: false},
 | |
| 			{Name: "updateType", Value: "OVERRIDE", Force: true},
 | |
| 			{Name: "docker.excludeBaseImage", Value: "true", Force: false},
 | |
| 		},
 | |
| 		"dub": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "includes", Value: "**/*.d **/*.di"},
 | |
| 		},
 | |
| 		//ToDo: rename to go?
 | |
| 		//ToDo: switch to gomod as dependency manager
 | |
| 		"golang": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "go.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "go.collectDependenciesAtRuntime", Value: false},
 | |
| 			{Name: "go.modules.resolveDependencies", Value: true, Force: true},
 | |
| 			{Name: "go.modules.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "includes", Value: "**/*.lock **/*.y*ml **/*.json **/*.tsv"},
 | |
| 		},
 | |
| 		"gradle": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "gradle.resolveDependencies", Value: true, Force: true},
 | |
| 			{Name: "gradle.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "gradle.aggregateModules", Value: false, Force: true},
 | |
| 			{Name: "gradle.runAssembleCommand", Value: true},
 | |
| 			{Name: "gradle.runPreStep", Value: true},
 | |
| 			{Name: "gradle.preferredEnvironment", Value: "wrapper"},
 | |
| 			{Name: "resolveAllDependencies", Value: false},
 | |
| 			{Name: "includes", Value: "**/*.jar"},
 | |
| 			{Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"},
 | |
| 		},
 | |
| 		"maven": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "updateEmptyProject", Value: true, Force: true},
 | |
| 			{Name: "maven.resolveDependencies", Value: true, Force: true},
 | |
| 			{Name: "maven.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "maven.aggregateModules", Value: false},
 | |
| 			{Name: "maven.ignoredScopes", Value: "test provided"},
 | |
| 			{Name: "maven.ignorePomModules", Value: false},
 | |
| 			{Name: "maven.runPreStep", Value: true},
 | |
| 			// ToDo: check with Klaus since when set to true name will not include groupId any longer
 | |
| 			{Name: "maven.projectNameFromDependencyFile", Value: false},
 | |
| 			{Name: "includes", Value: "**/*.jar"},
 | |
| 			{Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"},
 | |
| 		},
 | |
| 		"npm": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "npm.resolveDependencies", Value: true, Force: true},
 | |
| 			{Name: "npm.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "npm.ignoreNpmLsErrors", Value: true},
 | |
| 			{Name: "npm.failOnNpmLsErrors", Value: false},
 | |
| 			{Name: "npm.runPreStep", Value: true},
 | |
| 			{Name: "npm.projectNameFromDependencyFile", Value: true},
 | |
| 			{Name: "npm.resolveLockFile", Value: true},
 | |
| 		},
 | |
| 		"pip": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "python.resolveDependencies", Value: true, Force: true},
 | |
| 			{Name: "python.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "python.ignorePipInstallErrors", Value: false},
 | |
| 			{Name: "python.installVirtualEnv", Value: true},
 | |
| 			{Name: "python.resolveHierarchyTree", Value: true},
 | |
| 			{Name: "python.requirementsFileIncludes", Value: "requirements.txt"},
 | |
| 			{Name: "python.resolveSetupPyFiles", Value: true},
 | |
| 			{Name: "python.runPipenvPreStep", Value: true},
 | |
| 			{Name: "python.pipenvDevDependencies", Value: true},
 | |
| 			{Name: "python.IgnorePipenvInstallErrors", Value: false},
 | |
| 			{Name: "includes", Value: "**/*.py **/*.txt"},
 | |
| 			{Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"},
 | |
| 		},
 | |
| 		"sbt": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "sbt.resolveDependencies", Value: true, Force: true},
 | |
| 			{Name: "sbt.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "sbt.aggregateModules", Value: false, Force: true},
 | |
| 			{Name: "sbt.runPreStep", Value: true},
 | |
| 			{Name: "includes", Value: "**/*.jar"},
 | |
| 			{Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"},
 | |
| 		},
 | |
| 		"yarn": {
 | |
| 			{Name: "ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "npm.resolveDependencies", Value: true, Force: true},
 | |
| 			{Name: "npm.ignoreSourceFiles", Value: true, Force: true},
 | |
| 			{Name: "npm.yarnProject", Value: true, Force: true},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	if config.BuildTool == "maven" {
 | |
| 		if len(config.M2Path) > 0 {
 | |
| 			*c = append(*c, ConfigOption{Name: "maven.m2RepositoryPath", Value: config.M2Path, Force: true})
 | |
| 		}
 | |
| 
 | |
| 		mvnAdditionalArguments, _ := maven.DownloadAndGetMavenParameters(config.GlobalSettingsFile, config.ProjectSettingsFile, utils)
 | |
| 		mvnAdditionalArguments = append(mvnAdditionalArguments, mvnProjectExcludes(config.BuildDescriptorExcludeList, utils)...)
 | |
| 
 | |
| 		if len(mvnAdditionalArguments) > 0 {
 | |
| 			*c = append(*c, ConfigOption{Name: "maven.additionalArguments", Value: strings.Join(mvnAdditionalArguments, " "), Append: true})
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if config.BuildTool == "docker" {
 | |
| 		// for now only support default name of Dockerfile
 | |
| 		// ToDo: evaluate possibilities to allow also non-default Dockerfile names
 | |
| 		dockerFile := "Dockerfile"
 | |
| 		if exists, _ := utils.FileExists("Dockerfile"); exists {
 | |
| 			*c = append(*c, ConfigOption{Name: "docker.dockerfilePath", Value: dockerFile, Force: false})
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	if cOptions := buildToolDefaults[config.BuildTool]; cOptions != nil {
 | |
| 		for _, cOpt := range cOptions {
 | |
| 			*c = append(*c, cOpt)
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	//ToDo: Do we want to auto generate the config via autoGenerateWhitesourceConfig() here?
 | |
| 	// -> try to load original config file -> if not available generate?
 | |
| 
 | |
| 	log.Entry().Infof("Configuration for buildTool: '%v' is not yet hardened, please do a quality assessment of your scan results.", config.BuildTool)
 | |
| 	return fmt.Errorf("configuration not hardened")
 | |
| }
 | |
| 
 | |
| // handle modules to exclude based on buildDescriptorExcludeList returning e.g. --projects !integration-tests
 | |
| func mvnProjectExcludes(buildDescriptorExcludeList []string, utils Utils) []string {
 | |
| 	projectExcludes := []string{}
 | |
| 	for _, buildDescriptor := range buildDescriptorExcludeList {
 | |
| 		exists, _ := utils.FileExists(buildDescriptor)
 | |
| 		if strings.Contains(buildDescriptor, "pom.xml") && exists {
 | |
| 			module, _ := filepath.Split(buildDescriptor)
 | |
| 			projectExcludes = append(projectExcludes, fmt.Sprintf("!%v", strings.TrimSuffix(module, "/")))
 | |
| 		}
 | |
| 	}
 | |
| 	if len(projectExcludes) > 0 {
 | |
| 		return []string{"--projects", strings.Join(projectExcludes, ",")}
 | |
| 	}
 | |
| 	return []string{}
 | |
| }
 | |
| 
 | |
| //ToDo: Check if we want to optionally allow auto generation for unknown projects
 | |
| func autoGenerateWhitesourceConfig(config *ScanOptions, utils Utils) error {
 | |
| 	// TODO: Should we rely on -detect, or set the parameters manually?
 | |
| 	if err := utils.RunExecutable("java", "-jar", config.AgentFileName, "-d", ".", "-detect"); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// Rename generated config file to config.ConfigFilePath parameter
 | |
| 	if err := utils.FileRename("wss-generated-file.config", config.ConfigFilePath); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |