github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/whitesource/configHelper.go (about) 1 package whitesource 2 3 import ( 4 "bytes" 5 "fmt" 6 "path/filepath" 7 "strings" 8 "time" 9 10 "github.com/SAP/jenkins-library/pkg/log" 11 "github.com/SAP/jenkins-library/pkg/maven" 12 "github.com/magiconair/properties" 13 "github.com/pkg/errors" 14 ) 15 16 // ConfigOption defines a dedicated WhiteSource config which can be enforced if required 17 type ConfigOption struct { 18 Name string 19 Value interface{} 20 OmitIfPresent string 21 Force bool 22 Append bool 23 } 24 25 // ConfigOptions contains a list of config options (ConfigOption) 26 type ConfigOptions []ConfigOption 27 28 // RewriteUAConfigurationFile updates the user's Unified Agent configuration with configuration which should be enforced or just eases the overall configuration 29 // It then returns the path to the file containing the updated configuration 30 func (s *ScanOptions) RewriteUAConfigurationFile(utils Utils, projectName string) (string, error) { 31 32 uaContent, err := utils.FileRead(s.ConfigFilePath) 33 uaConfig, propErr := properties.Load(uaContent, properties.UTF8) 34 uaConfigMap := map[string]string{} 35 if err != nil || propErr != nil { 36 log.Entry().Warningf("Failed to load configuration file '%v'. Creating a configuration file from scratch.", s.ConfigFilePath) 37 } else { 38 uaConfigMap = uaConfig.Map() 39 } 40 41 cOptions := ConfigOptions{} 42 cOptions.addGeneralDefaults(s, utils, projectName) 43 cOptions.addBuildToolDefaults(s, utils) 44 45 newConfigMap := cOptions.updateConfig(&uaConfigMap) 46 newConfig := properties.LoadMap(newConfigMap) 47 48 now := time.Now().Format("20060102150405") 49 newConfigFilePath := fmt.Sprintf("%v.%v", s.ConfigFilePath, now) 50 51 var configContent bytes.Buffer 52 _, err = newConfig.Write(&configContent, properties.UTF8) 53 if err != nil { 54 return "", errors.Wrap(err, "failed to write properties") 55 } 56 57 err = utils.FileWrite(newConfigFilePath, configContent.Bytes(), 0666) 58 if err != nil { 59 return "", errors.Wrap(err, "failed to write file") 60 } 61 62 return newConfigFilePath, nil 63 } 64 65 func (c *ConfigOptions) updateConfig(originalConfig *map[string]string) map[string]string { 66 newConfig := map[string]string{} 67 for k, v := range *originalConfig { 68 newConfig[k] = v 69 } 70 71 for _, cOpt := range *c { 72 //omit default if value present 73 var dependentValue string 74 if len(cOpt.OmitIfPresent) > 0 { 75 dependentValue = newConfig[cOpt.OmitIfPresent] 76 } 77 78 if len(dependentValue) == 0 { 79 if cOpt.Append { 80 if len(newConfig[cOpt.Name]) > 0 { 81 newConfig[cOpt.Name] = fmt.Sprintf("%v %v", newConfig[cOpt.Name], cOpt.Value) 82 } else { 83 newConfig[cOpt.Name] = fmt.Sprint(cOpt.Value) 84 } 85 } else if cOpt.Force || len(newConfig[cOpt.Name]) == 0 { 86 newConfig[cOpt.Name] = fmt.Sprint(cOpt.Value) 87 } 88 } 89 } 90 return newConfig 91 } 92 93 func (c *ConfigOptions) addGeneralDefaults(config *ScanOptions, utils Utils, projectName string) { 94 cOptions := ConfigOptions{} 95 if strings.HasPrefix(config.ProductName, "DIST - ") { 96 cOptions = append(cOptions, []ConfigOption{ 97 {Name: "checkPolicies", Value: false, Force: true}, 98 {Name: "forceCheckAllDependencies", Value: false, Force: true}, 99 }...) 100 } else { 101 cOptions = append(cOptions, []ConfigOption{ 102 {Name: "checkPolicies", Value: true, Force: true}, 103 {Name: "forceCheckAllDependencies", Value: true, Force: true}, 104 }...) 105 } 106 107 if config.Verbose { 108 cOptions = append(cOptions, []ConfigOption{ 109 {Name: "log.level", Value: "debug"}, 110 {Name: "log.files.level", Value: "debug"}, 111 }...) 112 } 113 114 if len(config.Excludes) > 0 { 115 cOptions = append(cOptions, ConfigOption{Name: "excludes", Value: strings.Join(config.Excludes, " "), Force: true}) 116 } 117 118 if len(config.Includes) > 0 { 119 cOptions = append(cOptions, ConfigOption{Name: "includes", Value: strings.Join(config.Includes, " "), Force: true}) 120 } 121 122 // might need some refactoring later 123 if len(projectName) == 0 { 124 projectName = config.ProjectName 125 } 126 127 cOptions = append(cOptions, []ConfigOption{ 128 {Name: "apiKey", Value: config.OrgToken, Force: true}, 129 {Name: "productName", Value: config.ProductName, Force: true}, 130 {Name: "productVersion", Value: config.ProductVersion, Force: true}, 131 {Name: "projectName", Value: projectName, Force: true}, 132 {Name: "projectVersion", Value: config.ProductVersion, Force: true}, 133 {Name: "productToken", Value: config.ProductToken, OmitIfPresent: "projectToken", Force: true}, 134 {Name: "userKey", Value: config.UserToken, Force: true}, 135 {Name: "forceUpdate", Value: true, Force: true}, 136 {Name: "offline", Value: false, Force: true}, 137 {Name: "resolveAllDependencies", Value: false, Force: false}, 138 {Name: "failErrorLevel", Value: "ALL", Force: true}, 139 {Name: "case.sensitive.glob", Value: false}, 140 {Name: "followSymbolicLinks", Value: true}, 141 }...) 142 143 for _, cOpt := range cOptions { 144 *c = append(*c, cOpt) 145 } 146 } 147 148 func (c *ConfigOptions) addBuildToolDefaults(config *ScanOptions, utils Utils) error { 149 buildToolDefaults := map[string]ConfigOptions{ 150 "docker": { 151 {Name: "docker.scanImages", Value: true, Force: true}, 152 {Name: "docker.scanTarFiles", Value: true, Force: true}, 153 {Name: "docker.includes", Value: ".*.tar", Force: true}, 154 {Name: "fileSystemScan", Value: true}, 155 {Name: "ignoreSourceFiles", Value: false}, 156 {Name: "python.resolveGlobalPackages", Value: true, Force: false}, 157 {Name: "updateType", Value: "OVERRIDE", Force: true}, 158 {Name: "docker.excludeBaseImage", Value: "true", Force: false}, 159 }, 160 "dub": { 161 {Name: "ignoreSourceFiles", Value: true, Force: true}, 162 {Name: "includes", Value: "**/*.d **/*.di"}, 163 }, 164 "dub2": { 165 {Name: "fileSystemScan", Value: false, Force: true}, 166 {Name: "includes", Value: "**/*.d **/*.di"}, 167 }, 168 //ToDo: rename to go? 169 //ToDo: switch to gomod as dependency manager 170 "golang": { 171 {Name: "fileSystemScan", Value: false, Force: true}, 172 {Name: "ignoreSourceFiles", Value: true, Force: true}, 173 {Name: "go.ignoreSourceFiles", Value: true, Force: true}, 174 {Name: "go.collectDependenciesAtRuntime", Value: false}, 175 {Name: "go.modules.resolveDependencies", Value: true, Force: true}, 176 {Name: "go.modules.ignoreSourceFiles", Value: true, Force: true}, 177 {Name: "includes", Value: "**/*.lock **/*.y*ml **/*.json **/*.tsv"}, 178 }, 179 "gradle": { 180 {Name: "fileSystemScan", Value: false, Force: true}, 181 {Name: "ignoreSourceFiles", Value: true, Force: true}, 182 {Name: "gradle.resolveDependencies", Value: true, Force: true}, 183 {Name: "gradle.ignoreSourceFiles", Value: true, Force: true}, 184 {Name: "gradle.aggregateModules", Value: false, Force: true}, 185 {Name: "gradle.runAssembleCommand", Value: true}, 186 {Name: "gradle.runPreStep", Value: true}, 187 {Name: "gradle.preferredEnvironment", Value: "wrapper"}, 188 {Name: "resolveAllDependencies", Value: false}, 189 {Name: "includes", Value: "**/*.jar"}, 190 {Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"}, 191 }, 192 "maven": { 193 {Name: "fileSystemScan", Value: false, Force: true}, 194 {Name: "ignoreSourceFiles", Value: true, Force: true}, 195 {Name: "updateEmptyProject", Value: true, Force: true}, 196 {Name: "maven.resolveDependencies", Value: true, Force: true}, 197 {Name: "maven.ignoreSourceFiles", Value: true, Force: true}, 198 {Name: "maven.aggregateModules", Value: false}, 199 {Name: "maven.ignoredScopes", Value: "test provided"}, 200 {Name: "maven.ignorePomModules", Value: false}, 201 {Name: "maven.runPreStep", Value: true}, 202 // ToDo: check with Klaus since when set to true name will not include groupId any longer 203 {Name: "maven.projectNameFromDependencyFile", Value: false}, 204 {Name: "includes", Value: "**/*.jar"}, 205 {Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"}, 206 }, 207 "npm": { 208 {Name: "fileSystemScan", Value: false, Force: true}, 209 {Name: "ignoreSourceFiles", Value: true, Force: true}, 210 {Name: "npm.resolveDependencies", Value: true, Force: true}, 211 {Name: "npm.ignoreSourceFiles", Value: true, Force: true}, 212 {Name: "npm.ignoreNpmLsErrors", Value: true}, 213 {Name: "npm.failOnNpmLsErrors", Value: false}, 214 {Name: "npm.runPreStep", Value: true}, 215 {Name: "npm.projectNameFromDependencyFile", Value: true}, 216 {Name: "npm.resolveLockFile", Value: true}, 217 }, 218 "pip": { 219 {Name: "fileSystemScan", Value: false, Force: true}, 220 {Name: "ignoreSourceFiles", Value: true, Force: true}, 221 {Name: "python.resolveDependencies", Value: true, Force: true}, 222 {Name: "python.ignoreSourceFiles", Value: true, Force: true}, 223 {Name: "python.ignorePipInstallErrors", Value: false}, 224 {Name: "python.installVirtualEnv", Value: true}, 225 {Name: "python.resolveHierarchyTree", Value: true}, 226 {Name: "python.requirementsFileIncludes", Value: "requirements.txt"}, 227 {Name: "python.resolveSetupPyFiles", Value: true}, 228 {Name: "python.runPipenvPreStep", Value: true}, 229 {Name: "python.pipenvDevDependencies", Value: true}, 230 {Name: "python.IgnorePipenvInstallErrors", Value: false}, 231 {Name: "includes", Value: "**/*.py **/*.txt"}, 232 {Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"}, 233 }, 234 "sbt": { 235 {Name: "fileSystemScan", Value: false, Force: true}, 236 {Name: "ignoreSourceFiles", Value: true, Force: true}, 237 {Name: "sbt.resolveDependencies", Value: true, Force: true}, 238 {Name: "sbt.ignoreSourceFiles", Value: true, Force: true}, 239 {Name: "sbt.aggregateModules", Value: false, Force: true}, 240 {Name: "sbt.runPreStep", Value: true}, 241 {Name: "includes", Value: "**/*.jar"}, 242 {Name: "excludes", Value: "**/*sources.jar **/*javadoc.jar"}, 243 }, 244 "yarn": { 245 {Name: "fileSystemScan", Value: false, Force: true}, 246 {Name: "ignoreSourceFiles", Value: true, Force: true}, 247 {Name: "npm.resolveDependencies", Value: true, Force: true}, 248 {Name: "npm.ignoreSourceFiles", Value: true, Force: true}, 249 {Name: "npm.yarnProject", Value: true, Force: true}, 250 }, 251 } 252 253 if config.BuildTool == "maven" { 254 if len(config.M2Path) > 0 { 255 *c = append(*c, ConfigOption{Name: "maven.m2RepositoryPath", Value: config.M2Path, Force: true}) 256 } 257 258 mvnAdditionalArguments, _ := maven.DownloadAndGetMavenParameters(config.GlobalSettingsFile, config.ProjectSettingsFile, utils) 259 mvnAdditionalArguments = append(mvnAdditionalArguments, mvnProjectExcludes(config.BuildDescriptorExcludeList, utils)...) 260 261 if len(mvnAdditionalArguments) > 0 { 262 *c = append(*c, ConfigOption{Name: "maven.additionalArguments", Value: strings.Join(mvnAdditionalArguments, " "), Append: true}) 263 } 264 265 } 266 267 if config.BuildTool == "docker" { 268 // for now only support default name of Dockerfile 269 // ToDo: evaluate possibilities to allow also non-default Dockerfile names 270 dockerFile := "Dockerfile" 271 if exists, _ := utils.FileExists("Dockerfile"); exists { 272 *c = append(*c, ConfigOption{Name: "docker.dockerfilePath", Value: dockerFile, Force: false}) 273 } 274 275 } 276 277 if cOptions := buildToolDefaults[config.BuildTool]; cOptions != nil { 278 for _, cOpt := range cOptions { 279 *c = append(*c, cOpt) 280 } 281 return nil 282 } 283 284 //ToDo: Do we want to auto generate the config via autoGenerateWhitesourceConfig() here? 285 // -> try to load original config file -> if not available generate? 286 287 log.Entry().Infof("Configuration for buildTool: '%v' is not yet hardened, please do a quality assessment of your scan results.", config.BuildTool) 288 return fmt.Errorf("configuration not hardened") 289 } 290 291 // handle modules to exclude based on buildDescriptorExcludeList returning e.g. --projects !integration-tests 292 func mvnProjectExcludes(buildDescriptorExcludeList []string, utils Utils) []string { 293 projectExcludes := []string{} 294 for _, buildDescriptor := range buildDescriptorExcludeList { 295 exists, _ := utils.FileExists(buildDescriptor) 296 if strings.Contains(buildDescriptor, "pom.xml") && exists { 297 module, _ := filepath.Split(buildDescriptor) 298 projectExcludes = append(projectExcludes, fmt.Sprintf("!%v", strings.TrimSuffix(module, "/"))) 299 } 300 } 301 if len(projectExcludes) > 0 { 302 return []string{"--projects", strings.Join(projectExcludes, ",")} 303 } 304 return []string{} 305 } 306 307 // ToDo: Check if we want to optionally allow auto generation for unknown projects 308 func autoGenerateWhitesourceConfig(config *ScanOptions, utils Utils) error { 309 // TODO: Should we rely on -detect, or set the parameters manually? 310 if err := utils.RunExecutable("java", "-jar", config.AgentFileName, "-d", ".", "-detect"); err != nil { 311 return err 312 } 313 314 // Rename generated config file to config.ConfigFilePath parameter 315 if err := utils.FileRename("wss-generated-file.config", config.ConfigFilePath); err != nil { 316 return err 317 } 318 return nil 319 }