diff --git a/config/cx_console.properties b/config/cx_console.properties index ca818ca..024f455 100644 --- a/config/cx_console.properties +++ b/config/cx_console.properties @@ -19,7 +19,7 @@ scan.osa.exclude.files= #List of files which will be extracted in order to get files for OSA analysis (wildcards are supported) #Supported archive files are: "jar", "war", "ear", "sca", "gem", "whl", "egg", "tar", "tar.gz", "tgz", "zip", "rar" -scan.osa.extractable.include.files=*.zip, *.war, *.ear, *.tgz +scan.osa.extractable.include.files=**/*.jar,**/*.war,**/*.ear,**/*.sca,**/*.gem,**/*.whl,**/*.egg,**/*.tar,**/*.tar.gz,**/*.tgz,**/*.zip,**/*.rar #Value of the unzip depth for extracting files for OSA analysis scan.osa.extractable.depth=4 diff --git a/pom.xml b/pom.xml index 1cb3288..90771bf 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.cx.plugin CxConsolePlugin - 1.1.12 + 1.1.13 jar @@ -132,110 +132,120 @@ com.checkmarx cx-client-common - 2022.1.10 + 2022.2.12 - - - org.freemarker - freemarker - - - com.github.junrar - junrar - - - org.mozilla - rhino - - - io.vertx - vertx-web - - - ch.qos.logback - logback-classic - - - io.netty - netty-codec-http - - - io.netty - netty-codec - - - com.fasterxml.jackson.core - jackson-databind - - - org.springframework - spring-core - - - + + + org.freemarker + freemarker + + + com.github.junrar + junrar + + + org.mozilla + rhino + + + io.vertx + vertx-web + + + ch.qos.logback + logback-classic + + + io.netty + netty-codec-http + + + io.netty + netty-codec + + + com.fasterxml.jackson.core + jackson-databind + + + org.springframework + spring-core + + + net.lingala.zip4j + zip4j + + + - - com.github.junrar - junrar - 7.4.1 - - - org.slf4j - slf4j-api - - - - - org.freemarker - freemarker - 2.3.31 - - - io.netty - netty-codec-http - 4.1.75.Final - - - com.fasterxml.jackson.core - jackson-databind - 2.13.2.1 - - - org.springframework - spring-core - 5.3.18 - - org.mozilla - rhino - 1.7.12 + com.github.junrar + junrar + 7.4.1 + + + org.slf4j + slf4j-api + + - io.vertx - vertx-web - 4.0.2 - - - io.netty - netty-codec-http - - - io.netty - netty-codec - - - io.netty - netty-codec-http2 - - + org.freemarker + freemarker + 2.3.31 - + io.netty - netty-codec - 4.1.75.Final + netty-codec-http + 4.1.77.Final + + + com.fasterxml.jackson.core + jackson-databind + 2.13.2.1 + + + org.springframework + spring-core + 5.3.20 + + + org.mozilla + rhino + 1.7.12 - + + io.vertx + vertx-web + 4.0.2 + + + io.netty + netty-codec-http + + + io.netty + netty-codec + + + io.netty + netty-codec-http2 + + + + + io.netty + netty-codec + 4.1.77.Final + + + net.lingala.zip4j + zip4j + 2.10.0 + + + commons-cli commons-cli @@ -258,27 +268,31 @@ cx-config-provider 1.0.13 - - commons-io - commons-io - - - com.fasterxml.jackson.core - jackson-databind - - - org.apache.logging.log4j - log4j-core - - - org.apache.logging.log4j - log4j-api - - - org.apache.logging.log4j - log4j-slf4j-impl - - + + commons-io + commons-io + + + com.fasterxml.jackson.core + jackson-databind + + + org.slf4j + slf4j-api + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-slf4j-impl + + commons-io @@ -289,36 +303,36 @@ org.apache.httpcomponents httpclient 4.5.13 - - - commons-codec - commons-codec - - + + + commons-codec + commons-codec + + - + commons-codec commons-codec 1.15 - - - org.apache.logging.log4j - log4j-slf4j-impl - 2.17.1 - - - - org.apache.logging.log4j - log4j-core - 2.17.1 - - - - org.apache.logging.log4j - log4j-api - 2.17.1 - + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.17.1 + + + + org.apache.logging.log4j + log4j-core + 2.17.1 + + + + org.apache.logging.log4j + log4j-api + 2.17.1 + diff --git a/src/main/java/com/cx/plugin/cli/CxConsoleLauncher.java b/src/main/java/com/cx/plugin/cli/CxConsoleLauncher.java index f1a7ed3..344d4d6 100644 --- a/src/main/java/com/cx/plugin/cli/CxConsoleLauncher.java +++ b/src/main/java/com/cx/plugin/cli/CxConsoleLauncher.java @@ -13,14 +13,7 @@ import com.cx.restclient.dto.ScannerType; import com.cx.restclient.dto.scansummary.ScanSummary; import com.cx.restclient.exception.CxClientException; - - -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.CommandLineParser; -import org.apache.commons.cli.DefaultParser; -import org.apache.commons.cli.Options; -import org.apache.commons.cli.Option; -import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.*; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; @@ -37,15 +30,14 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.cx.plugin.cli.constants.Parameters.*; -import static com.cx.plugin.cli.errorsconstants.ErrorMessages.INVALID_COMMAND_COUNT; -import static com.cx.plugin.cli.errorsconstants.ErrorMessages.INVALID_COMMAND_ERROR; +import static com.cx.plugin.cli.errorsconstants.ErrorMessages.*; +import static com.cx.plugin.cli.utils.CxConfigHelper.EMPTY_JSON; /** * Created by idanA on 11/4/2018. @@ -92,6 +84,7 @@ public static void main(String[] args) { log.error(String.format("%n%n[CxConsole] Error parsing command: %n%s%n%n", e)); exitCode = ErrorParsingHelper.parseError(e.getMessage()); } catch (CxClientException | IOException | InterruptedException e) { + log.error("CLI process terminated, error: " + e.getMessage()); exitCode = ErrorParsingHelper.parseError(e.getMessage()); } @@ -212,12 +205,19 @@ private static int execute(Command command, CommandLine commandLine) return exitCode; } - private static void validateScanParameters(CxScanConfig cxScanConfig) throws CLIParsingException { + private static void validateScanParameters(CxScanConfig cxScanConfig) throws CLIParsingException, CxClientException { if (cxScanConfig == null) return; - if (cxScanConfig.isAstScaEnabled() && checkContainsSpecialChars(cxScanConfig.getProjectName(), SCA_PROJECT_NAME_INVALID_CHARS)) + if (cxScanConfig.isOsaEnabled() && + cxScanConfig.getOsaDependenciesJson() != null && + cxScanConfig.getOsaDependenciesJson().equals(EMPTY_JSON)) { + throw new CxClientException(OSA_NO_DEPENDENCIES_ERROR_MSG); + } + + if (cxScanConfig.isAstScaEnabled() && checkContainsSpecialChars(cxScanConfig.getProjectName(), SCA_PROJECT_NAME_INVALID_CHARS)) { throw new CLIParsingException("[CxConsole] SCA project name cannot contain special characters."); + } } private static boolean checkContainsSpecialChars(String valueToCheck, String regex) { @@ -284,20 +284,20 @@ private static int countCommands(CommandLine commandLine) { private static String[] convertParamToLowerCase(String[] args) { return Arrays .stream(args) - .map(arg -> arg.startsWith("-") && isCliCmdOption(arg)? arg.toLowerCase() : arg) + .map(arg -> arg.startsWith("-") && isCliCmdOption(arg) ? arg.toLowerCase() : arg) .toArray(String[]::new); } private static boolean isCliCmdOption(String argName) { - - Option argfound = Command.getOptions().getOption(argName.toLowerCase()); - if(argfound != null && !StringUtils.isEmpty(argfound.getOpt().toString())) - return true; - else - return false; + + Option argfound = Command.getOptions().getOption(argName.toLowerCase()); + if (argfound != null && !StringUtils.isEmpty(argfound.getOpt().toString())) + return true; + else + return false; } - - + + private static void initFileLogging(String logLocation, String logLevel) { System.setProperty("cliLogPath", logLocation); System.setProperty("logLevel", logLevel); diff --git a/src/main/java/com/cx/plugin/cli/configascode/SastConfig.java b/src/main/java/com/cx/plugin/cli/configascode/SastConfig.java index e3a11b7..6f2fab6 100644 --- a/src/main/java/com/cx/plugin/cli/configascode/SastConfig.java +++ b/src/main/java/com/cx/plugin/cli/configascode/SastConfig.java @@ -21,6 +21,8 @@ public class SastConfig { private int medium; @Optional private int high; + @Optional + private boolean isOverrideProjectSetting; public SastConfig() { @@ -97,4 +99,15 @@ public boolean isPrivateScan() { public void setPrivateScan(boolean privateScan) { this.privateScan = privateScan; } + + public boolean isOverrideProjectSetting() { + return isOverrideProjectSetting; + } + + public void setOverrideProjectSetting(boolean isOverrideProjectSetting) { + this.isOverrideProjectSetting = isOverrideProjectSetting; + } + + + } diff --git a/src/main/java/com/cx/plugin/cli/constants/ArgDescriptions.java b/src/main/java/com/cx/plugin/cli/constants/ArgDescriptions.java index ca51815..3f57519 100644 --- a/src/main/java/com/cx/plugin/cli/constants/ArgDescriptions.java +++ b/src/main/java/com/cx/plugin/cli/constants/ArgDescriptions.java @@ -72,6 +72,10 @@ private ArgDescriptions() { static final String OSA_ENABLED = "Enable open source analysis (CxOSA). -osaLocationPath should be specified or the -LocationType parameter needs to be defined as 'folder' or 'shared' (if -osaLocationPath doesn't exist, use -locationPath). Optional."; static final String SCA_ENABLED = String.format("Enable software composition analysis (SCA). SCA is the successor of CxOSA. Normally either -%1$s or -%2$s should be specified. If both are specified, -%2$s will be used. -osaLocationPath should be specified or the -LocationType parameter needs to be defined as 'folder' or 'shared' (if -osaLocationPath doesn't exist, use -locationPath). Optional.", Parameters.OSA_ENABLED, Parameters.SCA_ENABLED); static final String OSA_JSON_REPORT = "Generate CxOSA JSON report. Optional, not supported in AsyncScan mode"; + static final String OSA_FAIL_ON_ERROR = "Fails the execution in case of any error during the dependencies resolution process."; + static final String OSA_FSA_CONF = "Comma separated list of FSA args, Example: 'npm.runPreStep,nuget.resolveDependencies' will override default values."; + static final String OSA_ERR_LOG_DIR = "OSA error logs root path, if not provided will be stored at runtime directory."; + static final String OSA_SCAN_JSON = "OSA dependencies json to scan."; static final String SCA_JSON_REPORT = "Generates three CxSCA JSON reports. Saves the reports in the specified folder path, Optional. Not supported in AsyncScaScan/AsyncScan mode"; static final String INSTALL_PACKAGE_MANAGER = "Retrieve all supported package dependencies before performing OSA scan (see Remarks section). Optional."; static final String DOCKER_IMAGE_PATTERN = "The docker images to be selected for scan"; diff --git a/src/main/java/com/cx/plugin/cli/constants/Command.java b/src/main/java/com/cx/plugin/cli/constants/Command.java index cf91f1f..b4cf428 100644 --- a/src/main/java/com/cx/plugin/cli/constants/Command.java +++ b/src/main/java/com/cx/plugin/cli/constants/Command.java @@ -113,6 +113,10 @@ public static Options getOptions() { options.addOption(OSA_JSON_REPORT, true, ArgDescriptions.OSA_JSON_REPORT); options.addOption(SCA_JSON_REPORT, true, ArgDescriptions.SCA_JSON_REPORT); options.addOption(INSTALL_PACKAGE_MANAGER, false, ArgDescriptions.INSTALL_PACKAGE_MANAGER); + options.addOption(OSA_FAIL_ON_ERROR, false, ArgDescriptions.OSA_FAIL_ON_ERROR); + options.addOption(Option.builder(OSA_FSA_CONF).hasArg(true).hasArgs().argName("fsa configuration").desc(ArgDescriptions.OSA_FSA_CONF).valueSeparator(',').build()); + options.addOption(Option.builder(OSA_ERR_LOG_DIR).hasArg(true).hasArgs().argName("osa error log dir").desc(ArgDescriptions.OSA_ERR_LOG_DIR).build()); + options.addOption(Option.builder(OSA_SCAN_JSON).hasArg(true).hasArgs().argName("osa scan json").desc(ArgDescriptions.OSA_SCAN_JSON).build()); options.addOption(PDF_REPORT, true, ArgDescriptions.PDF_REPORT); options.addOption(XML_REPORT, true, ArgDescriptions.XML_REPORT); @@ -164,7 +168,7 @@ public static Options getOptions() { options.addOption(SAST_SERVER_URL, true, ArgDescriptions.SAST_SERVER_URL); options.addOption(SAST_USER, true, ArgDescriptions.SAST_USER); options.addOption(SAST_PASSWORD, true, ArgDescriptions.SAST_PASSWORD); - + options.addOption(SCA_CONFIG_FILE, true, ArgDescriptions.SCA_CONFIG_FILE); options.addOption(SCA_INCLUDE_SOURCE_FLAG, false, ArgDescriptions.SCA_INCLUDE_SOURCE_FLAG); options.addOption(SCA_TIMEOUT, true, ArgDescriptions.SCA_TIME_OUT); diff --git a/src/main/java/com/cx/plugin/cli/constants/Parameters.java b/src/main/java/com/cx/plugin/cli/constants/Parameters.java index 5d49d76..b9f07ed 100644 --- a/src/main/java/com/cx/plugin/cli/constants/Parameters.java +++ b/src/main/java/com/cx/plugin/cli/constants/Parameters.java @@ -44,6 +44,10 @@ private Parameters() { public static final String OSA_ARCHIVE_TO_EXTRACT = "osaarchivetoextract"; public static final String OSA_SCAN_DEPTH = "osascandepth"; public static final String OSA_ENABLED = "enableosa"; + public static final String OSA_FAIL_ON_ERROR = "osafailonerror"; + public static final String OSA_FSA_CONF = "osafsaconf"; + public static final String OSA_ERR_LOG_DIR = "osaerrorlogdir"; + public static final String OSA_SCAN_JSON = "osascanjson"; public static final String SCA_ENABLED = "enablesca"; public static final String OSA_JSON_REPORT = "osajson"; public static final String SCA_JSON_REPORT = "scajsondirpath"; diff --git a/src/main/java/com/cx/plugin/cli/errorsconstants/ErrorMessages.java b/src/main/java/com/cx/plugin/cli/errorsconstants/ErrorMessages.java index 005d8ef..1358a96 100644 --- a/src/main/java/com/cx/plugin/cli/errorsconstants/ErrorMessages.java +++ b/src/main/java/com/cx/plugin/cli/errorsconstants/ErrorMessages.java @@ -12,6 +12,8 @@ private ErrorMessages() { public static final String GENERAL_ERROR_MSG = "Failed to start scan (missing or invalid parameters)"; public static final String SDLC_ERROR_MSG = "This feature is available only on SDLC edition"; public static final String NO_OSA_LICENSE_ERROR_MSG = "Open Source Analysis License is not enabled for this project.Please contact your CxSAST Administrator"; + public static final String OSA_RESOLVE_ERROR_MSG = "Failed to resolve dependencies for OSA scan:"; + public static final String OSA_NO_DEPENDENCIES_ERROR_MSG = "No dependencies found for OSA scan"; public static final String LOGIN_FAILED_MSG = "Login Failed"; public static final String UNSUCCESSFUL_LOGIN_ERROR_MSG = "Unsuccessful login"; public static final String UNSUCCESSFUL_REST_LOGIN = "Fail to login with credentials: Fail to authenticate: status code: HTTP/1.1 403 Forbidden."; @@ -37,4 +39,9 @@ private ErrorMessages() { public static final String INVALID_COMMAND_ERROR = "command [%s] is invalid, valid command must start with one of %s"; public static final String INVALID_COMMAND_COUNT = "Invalid command count, One command must be provided Only. One of these commands must be provided only %s. "; + public static final String OSA_MAVEN_RESOLVE_ERROR_MSG = "Failed to resolve Maven dependencies for OSA scan"; + public static final String OSA_GRADLE_RESOLVE_ERROR_MSG = "Failed to resolve Gradle dependencies for OSA scan"; + public static final String OSA_NPM_RESOLVE_ERROR_MSG = "Failed to resolve NPM dependencies for OSA scan"; + public static final String OSA_NUGET_RESOLVE_ERROR_MSG = "Failed to resolve Nuget/DotNet dependencies for OSA scan"; + } diff --git a/src/main/java/com/cx/plugin/cli/errorsconstants/Errors.java b/src/main/java/com/cx/plugin/cli/errorsconstants/Errors.java index 8025059..d9a8868 100644 --- a/src/main/java/com/cx/plugin/cli/errorsconstants/Errors.java +++ b/src/main/java/com/cx/plugin/cli/errorsconstants/Errors.java @@ -7,12 +7,15 @@ * Created by idanA on 11/5/2018. */ public enum Errors { + SCAN_SUCCEEDED(0, NO_ERROR_MSG), GENERAL_ERROR(1, GENERAL_ERROR_MSG), SDLC_ERROR(2, SDLC_ERROR_MSG), NO_OSA_LICENSE_ERROR(3, NO_OSA_LICENSE_ERROR_MSG), LOGIN_FAILED_ERROR(4, LOGIN_FAILED_MSG), NO_PROJECT_PRIOR_TO_OSA_SCAN_ERROR(5, NO_PROJECT_PRIOR_TO_OSA_SCAN_ERROR_MSG), + OSA_RESOLVE_ERROR(6, OSA_RESOLVE_ERROR_MSG), + OSA_NO_DEPENDENCIES_ERROR(7, OSA_NO_DEPENDENCIES_ERROR_MSG), SAST_HIGH_THRESHOLD_ERROR(10, SAST_HIGH_THRESHOLD_ERROR_MSG), SAST_MEDIUM_THRESHOLD_ERROR(11, SAST_MEDIUM_THRESHOLD_ERROR_MSG), @@ -24,6 +27,11 @@ public enum Errors { POLICY_VIOLATION_ERROR(18, POLICY_VIOLATED_ERROR_MSG), GENERIC_THRESHOLD_FAILURE_ERROR(19, GENERIC_THRESHOLD_FAILURE_ERROR_MSG), + OSA_MAVEN_RESOLVE_ERROR(30, OSA_MAVEN_RESOLVE_ERROR_MSG), + OSA_GRADLE_RESOLVE_ERROR(31, OSA_GRADLE_RESOLVE_ERROR_MSG), + OSA_NPM_RESOLVE_ERROR(32, OSA_NPM_RESOLVE_ERROR_MSG), + OSA_NUGET_RESOLVE_ERROR(33, OSA_NUGET_RESOLVE_ERROR_MSG), + CANCELED_BY_USER_ERROR(130, CANCELED_BY_USER_ERROR_MSG); private final int exitCode; diff --git a/src/main/java/com/cx/plugin/cli/utils/CxConfigHelper.java b/src/main/java/com/cx/plugin/cli/utils/CxConfigHelper.java index 48b5439..2a63625 100644 --- a/src/main/java/com/cx/plugin/cli/utils/CxConfigHelper.java +++ b/src/main/java/com/cx/plugin/cli/utils/CxConfigHelper.java @@ -16,7 +16,6 @@ import com.cx.plugin.cli.constants.Parameters; import com.cx.plugin.cli.exceptions.BadOptionCombinationException; import com.cx.plugin.cli.exceptions.CLIParsingException; -import com.cx.plugin.cli.utils.PropertiesManager; import com.cx.restclient.ast.dto.sca.AstScaConfig; import com.cx.restclient.configuration.CxScanConfig; import com.cx.restclient.dto.ProxyConfig; @@ -24,31 +23,30 @@ import com.cx.restclient.dto.ScannerType; import com.cx.restclient.dto.SourceLocationType; import com.cx.restclient.sca.utils.CxSCAFileSystemUtils; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.codec.digest.DigestUtils; -import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.jgit.transport.URIish; +import org.whitesource.agent.api.model.AgentProjectInfo; import javax.annotation.Nullable; import javax.naming.ConfigurationException; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -62,6 +60,8 @@ */ public final class CxConfigHelper { + public static final String EMPTY_JSON = "EMPTY_JSON"; + private static final String CONFIG_AS_CODE_FILE_NAME = "cx.config"; public static final String CONFIG_FILE_S_NOT_FOUND_OR_COULDN_T_BE_LOADED = "Config file %s not found or couldn't " + "be loaded,please check if file exist's and if file content is valid"; @@ -129,8 +129,8 @@ public CxScanConfig resolveConfiguration(Command command, CommandLine cmd) throw } scanConfig.setEngineConfigurationName(cmd.getOptionValue(CONFIGURATION)); - if(cmd.hasOption(CUSTOM_FIELDS)) { - scanConfig.setCustomFields(apiFormat(cmd.getOptionValue(CUSTOM_FIELDS))); + if (cmd.hasOption(CUSTOM_FIELDS)) { + scanConfig.setCustomFields(apiFormat(cmd.getOptionValue(CUSTOM_FIELDS))); } scanConfig.setUseSSOLogin(cmd.hasOption(IS_SSO)); scanConfig.setDisableCertificateValidation(cmd.hasOption(TRUSTED_CERTIFICATES)); @@ -141,9 +141,9 @@ public CxScanConfig resolveConfiguration(Command command, CommandLine cmd) throw if ((command.equals(Command.SCA_SCAN)) || (command.equals(Command.ASYNC_SCA_SCAN))) { scanConfig.setProjectName(extractProjectName(cmd.getOptionValue(FULL_PROJECT_PATH), true)); scanConfig.setTeamPath(extractTeamPath(cmd.getOptionValue(FULL_PROJECT_PATH), true)); - if (cmd.hasOption(SCA_TIMEOUT)) { - scanConfig.setSCAScanTimeoutInMinutes(Integer.valueOf(cmd.getOptionValue(SCA_TIMEOUT))); - } + if (cmd.hasOption(SCA_TIMEOUT)) { + scanConfig.setSCAScanTimeoutInMinutes(Integer.valueOf(cmd.getOptionValue(SCA_TIMEOUT))); + } } else { scanConfig.setProjectName(extractProjectName(cmd.getOptionValue(FULL_PROJECT_PATH), false)); scanConfig.setTeamPath(extractTeamPath(cmd.getOptionValue(FULL_PROJECT_PATH), false)); @@ -180,16 +180,16 @@ public CxScanConfig resolveConfiguration(Command command, CommandLine cmd) throw return scanConfig; } - + private String apiFormat(String customFields) { - if(StringUtils.isNotEmpty(customFields)) { - customFields = customFields.replaceAll(":", "\":\""); - customFields = customFields.replaceAll(",", "\",\""); - customFields = "{\"".concat(customFields).concat("\"}"); - } - return customFields; - } - + if (StringUtils.isNotEmpty(customFields)) { + customFields = customFields.replaceAll(":", "\":\""); + customFields = customFields.replaceAll(",", "\",\""); + customFields = "{\"".concat(customFields).concat("\"}"); + } + return customFields; + } + private void checkForConfigAsCode(CxScanConfig scanConfig) throws CLIParsingException, IOException, ConfigurationException { if (StringUtils.isNotEmpty(scanConfig.getRemoteSrcUrl()) && RemoteSourceTypes.GIT == scanConfig.getRemoteType()) { resolveConfigAsCodeFromRemote(scanConfig); @@ -203,9 +203,9 @@ private void checkForConfigAsCode(CxScanConfig scanConfig) throws CLIParsingExce private void resolveConfigAsCodeLocal(CxScanConfig scanConfig) throws CLIParsingException, IOException, ConfigurationException { log.info("loading config file from local working directory .."); - ConfigAsCode configAsCodeFromFile = getConfigAsCodeFromFile( - scanConfig.getSourceDir() + File.separator + ".checkmarx" + File.separator + CONFIG_AS_CODE_FILE_NAME); - overrideConfigAsCode(configAsCodeFromFile, scanConfig); + ConfigAsCode configAsCodeFromFile = getConfigAsCodeFromFile( + scanConfig.getSourceDir() + File.separator + ".checkmarx" + File.separator + CONFIG_AS_CODE_FILE_NAME); + overrideConfigAsCode(configAsCodeFromFile, scanConfig); } @@ -249,7 +249,7 @@ private ConfigAsCode getConfigAsCode(ConfigReader reader) throws ConfigurationEx configProvider.init(CX_ORIGIN, reader); if (!configProvider.hasAnyConfiguration(CX_ORIGIN)) - throw new ConfigurationException(String.format(CONFIG_FILE_S_NOT_FOUND_OR_COULDN_T_BE_LOADED, ".checkmarx/"+CONFIG_AS_CODE_FILE_NAME)); + throw new ConfigurationException(String.format(CONFIG_FILE_S_NOT_FOUND_OR_COULDN_T_BE_LOADED, ".checkmarx/" + CONFIG_AS_CODE_FILE_NAME)); ConfigAsCode configAsCodeFromFile = new ConfigAsCode(); @@ -330,7 +330,8 @@ private void mapScaConfiguration(Optional sca, CxScanConfig scanConfi scanConfig.setOsaLowThreshold(pValue); overridesResults.put("Sca Low", String.valueOf(pValue)); }); - + + //build include/exclude file pattern if (!fileExclude.get().isEmpty() || !fileInclude.get().isEmpty()) setDependencyScanFilterPattern(scanConfig, fileInclude.get(), fileExclude.get()); @@ -414,10 +415,16 @@ private void mapSastConfiguration(Optional sast, CxScanConfig scanCo scanConfig.setSastFilterPattern(pValue); overridesResults.put("Include/Exclude pattern", pValue); }); + + + sast.map(SastConfig::isOverrideProjectSetting) + .ifPresent(pValue -> { + scanConfig.setIsOverrideProjectSetting(pValue); + overridesResults.put("Is Overridable", String.valueOf(pValue)); + }); } private void mapProjectConfiguration(Optional project, CxScanConfig scanConfig, Map overridesResults) throws CLIParsingException { - String projectName = project.map(ProjectConfig::getFullPath).orElse(null); if ((command.equals(Command.SCA_SCAN)) || (command.equals(Command.ASYNC_SCA_SCAN))) { @@ -428,7 +435,6 @@ private void mapProjectConfiguration(Optional project, CxScanConf scanConfig.setTeamPath(extractTeamPath(projectName, false)); } - if (projectName != null) overridesResults.put("Project Name", projectName); @@ -442,15 +448,11 @@ private void mapProjectConfiguration(Optional project, CxScanConf } private ConfigAsCode getConfigAsCodeFromFile(String filePath) throws ConfigurationException, IOException { - com.checkmarx.configprovider.readers.FileReader reader = new FileReader(ResourceType.YAML, filePath); return getConfigAsCode(reader); - - } - private URIish prepareRemoteUrl(String remoteSrcUrl, int remoteSrcPort) throws URISyntaxException { URIish urIish = new URIish(remoteSrcUrl); if (remoteSrcPort > 0) { @@ -466,6 +468,7 @@ private void configureDependencyScan(CxScanConfig scanConfig) throws CLIParsingE } if (scanConfig.isOsaEnabled()) { + setOSAEnv(); setOsaSpecificConfig(scanConfig); } else if (scanConfig.isAstScaEnabled()) { setScaSpecificConfig(scanConfig); @@ -474,6 +477,32 @@ private void configureDependencyScan(CxScanConfig scanConfig) throws CLIParsingE setSharedDependencyScanConfig(scanConfig); } + private void setOSAEnv() { + System.setProperty("FSA_ENRICH_ERR_LOGS", "true"); + + String[] fsaConfArr = commandLine.getOptionValues(OSA_FSA_CONF); + if (fsaConfArr != null && !Arrays.asList(fsaConfArr).isEmpty()) { + StringBuilder sb = new StringBuilder(); + String prefix = ""; + for (String arg : fsaConfArr) { + arg = arg.trim(); + if (StringUtils.isNotEmpty(arg)) { + sb.append(prefix + arg); + prefix = ";"; + } + } + String fsaConf = sb.toString().trim(); + if (StringUtils.isNotEmpty(fsaConf)) { + System.setProperty("FSA_CONFIGURATION", fsaConf); + } + } + + String errLogDir = commandLine.getOptionValue(OSA_ERR_LOG_DIR); + if (StringUtils.isNotEmpty(errLogDir)) { + System.setProperty("FSA_ERR_LOGS_PATH", errLogDir); + } + } + private String getRelevantCommand() { String oldCommandLineValue = commandLine.getOptionValue(LOCATION_FILES_EXCLUDE); String newCommandLineValue = commandLine.getOptionValue(INCLUDE_EXCLUDE_PATTERN); @@ -487,6 +516,19 @@ private String getRelevantCommand() { private void setOsaSpecificConfig(CxScanConfig scanConfig) { scanConfig.setOsaRunInstall(commandLine.hasOption(INSTALL_PACKAGE_MANAGER)); + scanConfig.setOsaFailOnError(commandLine.hasOption(OSA_FAIL_ON_ERROR)); + + String osaJsonFile = commandLine.getOptionValue(OSA_SCAN_JSON); + if (StringUtils.isNotEmpty(osaJsonFile)) { + if (verifyOsaJson(osaJsonFile)) { + String json = getFileContent(new File(osaJsonFile)); + scanConfig.setOsaDependenciesJson(json); + log.info("FSA resolver override SUCCESS, OSA dependencies json used: " + osaJsonFile); + } else { + log.error("FSA resolver override: FAILED, OSA dependencies json used: " + osaJsonFile); + scanConfig.setOsaDependenciesJson(EMPTY_JSON); + } + } String reportDir = commandLine.getOptionValue(OSA_JSON_REPORT); scanConfig.setReportsDir(reportDir != null ? new File(reportDir) : null); @@ -499,6 +541,31 @@ private void setOsaSpecificConfig(CxScanConfig scanConfig) { scanConfig.setOsaScanDepth(osaScanDepth); } + private boolean verifyOsaJson(String osaJsonFile) { + try { + File jsonFile = new File(osaJsonFile); + if (jsonFile.exists()) { + ObjectMapper jsonMapper = new ObjectMapper(); + String json = getFileContent(jsonFile); + Collection dependenciesObj = jsonMapper.readValue(json, jsonMapper.getTypeFactory().constructCollectionType(Collection.class, AgentProjectInfo.class)); + if (dependenciesObj != null) { + return true; + } + } + } catch (Exception e) { + log.error("Fail parse dependencies json file."); + } + return false; + } + + private String getFileContent(File file) { + try { + return IOUtils.toString(file.toURI(), StandardCharsets.UTF_8); + } catch (IOException e) { + log.error("Fail to get file content."); + return ""; + } + } private void setScaSpecificConfig(CxScanConfig scanConfig) throws CLIParsingException { AstScaConfig sca = new AstScaConfig(); @@ -513,29 +580,28 @@ private void setScaSpecificConfig(CxScanConfig scanConfig) throws CLIParsingExce sca.setWebAppUrl(webAppUrl); String envVariables = getOptionalParam(ENV_VARIABLE, ""); - if(StringUtils.isNotEmpty(envVariables)) - { + if (StringUtils.isNotEmpty(envVariables)) { sca.setEnvVariables(CxSCAFileSystemUtils.convertStringToKeyValueMap(envVariables)); } String configFilePaths = getOptionalParam(SCA_CONFIG_FILE, ""); - if (StringUtils.isNotEmpty(configFilePaths)) { - sca.setConfigFilePaths(Arrays.asList(configFilePaths.split("\\s*,\\s*"))); - } + if (StringUtils.isNotEmpty(configFilePaths)) { + sca.setConfigFilePaths(Arrays.asList(configFilePaths.split("\\s*,\\s*"))); + } configureScaWithSastDetails(sca); - - if (commandLine.hasOption(SCA_INCLUDE_SOURCE_FLAG)) { - sca.setIncludeSources(true); - } - - if (commandLine.hasOption(ENABLE_SCA_RESOLVER)) { - sca.setEnableScaResolver(true); + + if (commandLine.hasOption(SCA_INCLUDE_SOURCE_FLAG)) { + sca.setIncludeSources(true); + } + + if (commandLine.hasOption(ENABLE_SCA_RESOLVER)) { + sca.setEnableScaResolver(true); String pathToResolver = getRequiredParam(commandLine, PATH_TO_RESOLVER, null); String additionalParams = getRequiredParam(commandLine, SCA_RESOLVER_ADD_PARAMETERS, null); validateSCAResolverParams(); sca.setPathToScaResolver(pathToResolver); sca.setScaResolverAddParameters(additionalParams); - } + } sca.setUsername(getRequiredParam(commandLine, SCA_USERNAME, null)); sca.setPassword(getRequiredParam(commandLine, SCA_PASSWORD, null)); @@ -561,77 +627,76 @@ private void setScaSpecificConfig(CxScanConfig scanConfig) throws CLIParsingExce throw new CLIParsingException(reportDir + " directory doesn't exist."); } } + scanConfig.setAstScaConfig(sca); } - private void configureScaWithSastDetails(AstScaConfig sca) throws CLIParsingException - { - //We are in SCA function whether SCA alone or with SAST scan, start with SCA specific SAST params - String serverURL = getOptionalParam(SAST_SERVER_URL, ""); - String user = getOptionalParam(SAST_USER, ""); - String password = getOptionalParam(SAST_PASSWORD, ""); - String projectId = getOptionalParam(SAST_PROJECT_ID, ""); - String projectName = getOptionalParam(SAST_PROJECT_NAME, ""); - - - //SCA alone scan - if ((!commandLine.hasOption(SCA_ENABLED))) { - if (exploitablePathParamsIncomplete(serverURL, user, password, projectId, projectName)) { - if (!exploitablePathParamsEmpty(serverURL, user, password, projectId, projectName)) - throw new CLIParsingException( - "[CxConsole] For SCA exploitable path, CxSAST server details like url, user, password and full project path or project id are required. Received partial parameters."); - } - // set the SCA Params after the above validation is done for only - // SCA - else { - prepareExpPathScaConfig(sca, serverURL, user, password, projectId, projectName); - } - } - //SCA with SAST - else if (exploitablePathParamsEmpty(serverURL, user, password, projectId, projectName) || exploitablePathParamsIncomplete(serverURL,user,password,projectId,projectName)) { - // assign SAST values to scaconfig - prepareExpPathScaConfig(sca, getOptionalParam(SERVER_URL, ""), getOptionalParam(USER_NAME, ""), - getOptionalParam(USER_PASSWORD, ""), "", commandLine.getOptionValue(FULL_PROJECT_PATH)); - if (StringUtils.isNotEmpty(projectId)) { - // override SCA's SAST project ID - sca.setSastProjectId(projectId); - } - if (StringUtils.isNotEmpty(projectName)) { - // override SCA's SAST project Name - sca.setSastProjectName(projectName); - } - } else { - prepareExpPathScaConfig(sca, serverURL, user, password, projectId, projectName); - } - } - - private void prepareExpPathScaConfig(AstScaConfig sca, String serverURL, String user, String password, - String projectId, String projectName) { - sca.setSastServerUrl(serverURL); - sca.setSastUsername(user); - sca.setSastPassword(password); - sca.setSastProjectName(projectName); - sca.setSastProjectId(projectId); - - } - - private boolean exploitablePathParamsEmpty(String serverURL, String user, String password, String projectId, - String projectName) { - return StringUtils.isEmpty(serverURL) && StringUtils.isEmpty(user) && StringUtils.isEmpty(password) - && (StringUtils.isEmpty(projectId) && StringUtils.isEmpty(projectName)) ? true : false; - - } - - private boolean exploitablePathParamsIncomplete(String serverURL, String user, String password, String projectId, - String projectName) { - boolean partialParams = false; - partialParams = StringUtils.isNotEmpty(serverURL) && !partialParams ? false : true; - partialParams = StringUtils.isNotEmpty(user) && !partialParams ? false : true; - partialParams = StringUtils.isNotEmpty(password) && !partialParams ? false : true; - partialParams = (StringUtils.isNotEmpty(projectId) || StringUtils.isNotEmpty(projectName)) && !partialParams - ? false : true; - return partialParams; - } + private void configureScaWithSastDetails(AstScaConfig sca) throws CLIParsingException { + //We are in SCA function whether SCA alone or with SAST scan, start with SCA specific SAST params + String serverURL = getOptionalParam(SAST_SERVER_URL, ""); + String user = getOptionalParam(SAST_USER, ""); + String password = getOptionalParam(SAST_PASSWORD, ""); + String projectId = getOptionalParam(SAST_PROJECT_ID, ""); + String projectName = getOptionalParam(SAST_PROJECT_NAME, ""); + + + //SCA alone scan + if ((!commandLine.hasOption(SCA_ENABLED))) { + if (exploitablePathParamsIncomplete(serverURL, user, password, projectId, projectName)) { + if (!exploitablePathParamsEmpty(serverURL, user, password, projectId, projectName)) + throw new CLIParsingException( + "[CxConsole] For SCA exploitable path, CxSAST server details like url, user, password and full project path or project id are required. Received partial parameters."); + } + // set the SCA Params after the above validation is done for only + // SCA + else { + prepareExpPathScaConfig(sca, serverURL, user, password, projectId, projectName); + } + } + //SCA with SAST + else if (exploitablePathParamsEmpty(serverURL, user, password, projectId, projectName) || exploitablePathParamsIncomplete(serverURL, user, password, projectId, projectName)) { + // assign SAST values to scaconfig + prepareExpPathScaConfig(sca, getOptionalParam(SERVER_URL, ""), getOptionalParam(USER_NAME, ""), + getOptionalParam(USER_PASSWORD, ""), "", commandLine.getOptionValue(FULL_PROJECT_PATH)); + if (StringUtils.isNotEmpty(projectId)) { + // override SCA's SAST project ID + sca.setSastProjectId(projectId); + } + if (StringUtils.isNotEmpty(projectName)) { + // override SCA's SAST project Name + sca.setSastProjectName(projectName); + } + } else { + prepareExpPathScaConfig(sca, serverURL, user, password, projectId, projectName); + } + } + + private void prepareExpPathScaConfig(AstScaConfig sca, String serverURL, String user, String password, + String projectId, String projectName) { + sca.setSastServerUrl(serverURL); + sca.setSastUsername(user); + sca.setSastPassword(password); + sca.setSastProjectName(projectName); + sca.setSastProjectId(projectId); + + } + + private boolean exploitablePathParamsEmpty(String serverURL, String user, String password, String projectId, + String projectName) { + return StringUtils.isEmpty(serverURL) && StringUtils.isEmpty(user) && StringUtils.isEmpty(password) + && (StringUtils.isEmpty(projectId) && StringUtils.isEmpty(projectName)) ? true : false; + } + + private boolean exploitablePathParamsIncomplete(String serverURL, String user, String password, String projectId, + String projectName) { + boolean partialParams = false; + partialParams = StringUtils.isNotEmpty(serverURL) && !partialParams ? false : true; + partialParams = StringUtils.isNotEmpty(user) && !partialParams ? false : true; + partialParams = StringUtils.isNotEmpty(password) && !partialParams ? false : true; + partialParams = (StringUtils.isNotEmpty(projectId) || StringUtils.isNotEmpty(projectName)) && !partialParams + ? false : true; + return partialParams; + } private void setSharedDependencyScanConfig(CxScanConfig scanConfig) { setDependencyScanThresholds(scanConfig); @@ -842,11 +907,11 @@ private static String extractTeamPath(String fullPath, boolean isScaScan) throws throw new CLIParsingException("[CxConsole] No project path was specified"); } int lastIdx = getLastIndexOfTeam(fullPath, isScaScan); - if(lastIdx == -1) - return ""; + if (lastIdx == -1) + return ""; else - return fullPath.substring(0, lastIdx); - + return fullPath.substring(0, lastIdx); + } public static void printConfig(CommandLine commandLine) { @@ -898,36 +963,35 @@ private String getOptionalParam(String commandLineKey, String fallbackProperty) /** * This method validates SCA Resolver flow parameters + * * @return * @throws CLIParsingException */ - private boolean validateSCAResolverParams()throws CLIParsingException { + private boolean validateSCAResolverParams() throws CLIParsingException { boolean isValidParams = true; String pathToResolver = commandLine.getOptionValue(PATH_TO_RESOLVER); String additionalParams = commandLine.getOptionValue(SCA_RESOLVER_ADD_PARAMETERS); - + pathToResolver = pathToResolver + File.separator + "ScaResolver"; - if(!SystemUtils.IS_OS_UNIX) - pathToResolver = pathToResolver + ".exe"; - + if (!SystemUtils.IS_OS_UNIX) + pathToResolver = pathToResolver + ".exe"; + File file = new File(pathToResolver); - if(!file.exists()) - { - throw new CLIParsingException("SCA Resolver path does not exist. Path="+file.getAbsolutePath()); + if (!file.exists()) { + throw new CLIParsingException("SCA Resolver path does not exist. Path=" + file.getAbsolutePath()); } String[] arguments = additionalParams.split(" "); String dirPath; - for (int i = 0; i < arguments.length ; i++) { - if (arguments[i].equals("-r") || arguments[i].equals("-s") ) { + for (int i = 0; i < arguments.length; i++) { + if (arguments[i].equals("-r") || arguments[i].equals("-s")) { dirPath = arguments[i + 1]; - if(dirPath.endsWith(File.separator)){ - dirPath = dirPath.substring(0,dirPath.length()-2); + if (dirPath.endsWith(File.separator)) { + dirPath = dirPath.substring(0, dirPath.length() - 2); } File resultPath = new File(dirPath); - if(!resultPath.exists()) - { - if(arguments[i].equals("-s") ) { + if (!resultPath.exists()) { + if (arguments[i].equals("-s")) { throw new CLIParsingException("Source code path does not exist. " + resultPath.getAbsolutePath()); } } diff --git a/src/main/java/com/cx/plugin/cli/utils/ErrorParsingHelper.java b/src/main/java/com/cx/plugin/cli/utils/ErrorParsingHelper.java index 8f34adc..80776eb 100644 --- a/src/main/java/com/cx/plugin/cli/utils/ErrorParsingHelper.java +++ b/src/main/java/com/cx/plugin/cli/utils/ErrorParsingHelper.java @@ -4,10 +4,13 @@ import com.cx.restclient.dto.scansummary.ErrorSource; import com.cx.restclient.dto.scansummary.ScanSummary; import com.cx.restclient.dto.scansummary.ThresholdError; +import org.apache.commons.lang3.StringUtils; +import org.whitesource.agent.utils.CommandLineErrors; import java.util.Comparator; import java.util.HashMap; import java.util.Map; +import java.util.Set; import static com.cx.plugin.cli.errorsconstants.ErrorMessages.*; @@ -30,6 +33,8 @@ private static Map createMessageToCodeMap() { messageToCodeMap.put(INVALID_CREDENTIALS, Errors.LOGIN_FAILED_ERROR.getCode()); messageToCodeMap.put(SDLC_ERROR_MSG, Errors.SDLC_ERROR.getCode()); messageToCodeMap.put(NO_OSA_LICENSE_ERROR_MSG, Errors.NO_OSA_LICENSE_ERROR.getCode()); + messageToCodeMap.put(OSA_RESOLVE_ERROR_MSG, Errors.OSA_RESOLVE_ERROR.getCode()); + messageToCodeMap.put(OSA_NO_DEPENDENCIES_ERROR_MSG, Errors.OSA_NO_DEPENDENCIES_ERROR.getCode()); messageToCodeMap.put(NO_PROJECT_PRIOR_TO_OSA_SCAN_ERROR_MSG, Errors.NO_PROJECT_PRIOR_TO_OSA_SCAN_ERROR.getCode()); messageToCodeMap.put(REPORT_PARAMETER_IN_ASYNC_SCAN, Errors.GENERAL_ERROR.getCode()); messageToCodeMap.put(THRESHOLD_PARAMETER_IN_ASYNC_SCAN, Errors.GENERAL_ERROR.getCode()); @@ -43,6 +48,11 @@ private static Map createMessageToCodeMap() { messageToCodeMap.put(SAST_HIGH_THRESHOLD_ERROR_MSG, Errors.SAST_HIGH_THRESHOLD_ERROR.getCode()); messageToCodeMap.put(SAST_MEDIUM_THRESHOLD_ERROR_MSG, Errors.SAST_MEDIUM_THRESHOLD_ERROR.getCode()); messageToCodeMap.put(SAST_LOW_THRESHOLD_ERROR_MSG, Errors.SAST_LOW_THRESHOLD_ERROR.getCode()); + // OSA resolvers + messageToCodeMap.put(OSA_MAVEN_RESOLVE_ERROR_MSG, Errors.OSA_MAVEN_RESOLVE_ERROR.getCode()); + messageToCodeMap.put(OSA_GRADLE_RESOLVE_ERROR_MSG, Errors.OSA_GRADLE_RESOLVE_ERROR.getCode()); + messageToCodeMap.put(OSA_NPM_RESOLVE_ERROR_MSG, Errors.OSA_NPM_RESOLVE_ERROR.getCode()); + messageToCodeMap.put(OSA_NUGET_RESOLVE_ERROR_MSG, Errors.OSA_NUGET_RESOLVE_ERROR.getCode()); return messageToCodeMap; } @@ -64,9 +74,9 @@ public static Errors getErrorType(ScanSummary scanSummary) { } else if (sastMostSevere == null && osaMostSevere == null && scaMostSevere == null) { return Errors.SCAN_SUCCEEDED; } else { - if(sastMostSevere != null){ + if (sastMostSevere != null) { return toThresholdErrorCode(sastMostSevere); - }else { + } else { return toThresholdErrorCode(osaMostSevere != null ? osaMostSevere : scaMostSevere); } } @@ -107,8 +117,28 @@ public static int parseError(String errorMsg) { return Errors.GENERAL_ERROR.getCode(); } + if (StringUtils.containsIgnoreCase(errorMsg, OSA_RESOLVE_ERROR_MSG)) { + Set failedResolver = CommandLineErrors.getFailedResolver(); + if (failedResolver.size() > 1) { + return Errors.OSA_RESOLVE_ERROR.getCode(); + } + + String resolver = failedResolver.iterator().next(); + if (resolver.equalsIgnoreCase("Maven")) { + return Errors.OSA_MAVEN_RESOLVE_ERROR.getCode(); + } else if (resolver.equalsIgnoreCase("Gradle")) { + return Errors.OSA_GRADLE_RESOLVE_ERROR.getCode(); + } else if (resolver.equalsIgnoreCase("NPM")) { + return Errors.OSA_NPM_RESOLVE_ERROR.getCode(); + } else if (resolver.equalsIgnoreCase("DotNet") || resolver.equalsIgnoreCase("Nuget")) { + return Errors.OSA_NUGET_RESOLVE_ERROR.getCode(); + } + + return Errors.OSA_RESOLVE_ERROR.getCode(); + } + for (Map.Entry entry : errorMsgToCodeMap.entrySet()) { - if (errorMsg.toLowerCase().contains(entry.getKey().toLowerCase())) { + if (StringUtils.containsIgnoreCase(errorMsg, entry.getKey())) { return entry.getValue(); } }