diff --git a/.gitignore b/.gitignore index 8f94552..ab9fdba 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ target # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +.classpath +.project +.settings/org.eclipse.core.resources.prefs +.settings/org.eclipse.jdt.core.prefs diff --git a/config/cx_console.properties b/config/cx_console.properties index 024f455..7bb20d7 100644 --- a/config/cx_console.properties +++ b/config/cx_console.properties @@ -60,3 +60,7 @@ scan.sca.accesscontrol.url=https://platform.checkmarx.net # URL of the SCA web application. Used to generate web report URL. Optional. If omitted, the scan will run as usual, # but no report URL will be generated. scan.sca.webapp.url=https://sca.checkmarx.net + +# Custom trust store location and password. If defined, it must contain trust certificates/chain for all endpoints of CxSAST and CxSCA. +trustStore= +trustStorePassword= diff --git a/pom.xml b/pom.xml index d699cf4..4cfc4d0 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 com.cx.plugin CxConsolePlugin - 1.1.14 + 1.1.18 jar @@ -132,7 +132,7 @@ com.checkmarx cx-client-common - 2022.2.14 + 2022.4.4 @@ -170,12 +170,23 @@ org.springframework spring-core + + + org.yaml + snakeyaml - - net.lingala.zip4j - zip4j + + org.jsoup + jsoup + + + com.google.code.gson + gson + + + org.tmatesoft.sqljet + sqljet - @@ -203,7 +214,7 @@ com.fasterxml.jackson.core jackson-databind - 2.13.2.1 + 2.14.0 org.springframework @@ -237,14 +248,28 @@ io.netty netty-codec - 4.1.77.Final - + 4.1.86.Final + + + org.yaml + snakeyaml + 1.33 + - net.lingala.zip4j - zip4j - 2.10.0 + org.jsoup + jsoup + 1.15.3 + + + com.google.code.gson + gson + 2.10 + + + org.tmatesoft.sqljet + sqljet + 1.1.15 - commons-cli @@ -297,7 +322,7 @@ commons-io commons-io - 2.8.0 + 2.11.0 org.apache.httpcomponents 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 6f2fab6..dca4db5 100644 --- a/src/main/java/com/cx/plugin/cli/configascode/SastConfig.java +++ b/src/main/java/com/cx/plugin/cli/configascode/SastConfig.java @@ -23,7 +23,10 @@ public class SastConfig { private int high; @Optional private boolean isOverrideProjectSetting; - + @Optional + private boolean enableSastBranching; + @Optional + private String masterBranchProjName; public SastConfig() { } @@ -107,7 +110,22 @@ public boolean isOverrideProjectSetting() { public void setOverrideProjectSetting(boolean isOverrideProjectSetting) { this.isOverrideProjectSetting = isOverrideProjectSetting; } + + public boolean isEnableSASTBranching() { + return enableSastBranching; + } + public void setEnableSASTBranching(boolean enableSASTBranching) { + this.enableSastBranching = enableSASTBranching; + } + + public String getMasterBranchProjName() { + return masterBranchProjName; + } + + public void setMasterBranchProjName(String masterBranchProjName) { + this.masterBranchProjName = masterBranchProjName; + } } 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 3f57519..9ccc43e 100644 --- a/src/main/java/com/cx/plugin/cli/constants/ArgDescriptions.java +++ b/src/main/java/com/cx/plugin/cli/constants/ArgDescriptions.java @@ -91,9 +91,15 @@ private ArgDescriptions() { static final String SCAN_COMMENT = "Saves a comment with the scan results. For example: -comment 'important scan1'. Optional. Not supported in AsyncScan mode"; static final String IS_SSO = "Single Sign-On: Use Windows credentials of current user to log into CxSAST. Optional."; + + + static final String GENERATE_SCA_REPORT = "Set Generate Sca Report. Optional."; + static final String SCA_REPORT_FORMAT = "Set Sca Report Format. Optional."; + static final String HIGH = "high"; static final String MEDIUM = "medium"; static final String LOW = "low"; + static final String SAST_HIGH = String.format(THRESHOLD_TEMPLATE, CX_SAST, HIGH); static final String SAST_MEDIUM = String.format(THRESHOLD_TEMPLATE, CX_SAST, MEDIUM); @@ -144,4 +150,10 @@ private ArgDescriptions() { static final String SCA_INCLUDE_SOURCE_FLAG = "Flag to include the entire source code for the SCA scan."; static final String SCA_TIME_OUT = "Timeout duration for SCA scan."; static final String SCAN_LEVEL_CUSTOM_FIELDS = "Scan level custom fields"; + + static final String ENABLE_SAST_BRANCHING = "Enable to create child project"; + static final String MASTER_BRANCH_PROJ_NAME = "Master branch project name"; + + static final String PERIODIC_FULL_SCAN = "Run a full scan after X incremental scans . Scans all files, (-Incremental should be enable). Optional."; + } 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 b4cf428..b04925d 100644 --- a/src/main/java/com/cx/plugin/cli/constants/Command.java +++ b/src/main/java/com/cx/plugin/cli/constants/Command.java @@ -123,6 +123,10 @@ public static Options getOptions() { options.addOption(CSV_REPORT, true, ArgDescriptions.CSV_REPORT); options.addOption(RTF_REPORT, true, ArgDescriptions.RTF_REPORT); + + options.addOption(GENERATE_SCA_REPORT, false, ArgDescriptions.GENERATE_SCA_REPORT); + options.addOption(SCA_REPORT_FORMAT, true, ArgDescriptions.SCA_REPORT_FORMAT); + options.addOption(IS_INCREMENTAL, false, ArgDescriptions.IS_INCREMENTAL); options.addOption(IS_FORCE_SCAN, false, ArgDescriptions.IS_FORCE_SCAN); options.addOption(IS_PRIVATE, false, ArgDescriptions.IS_PRIVATE); @@ -173,6 +177,11 @@ public static Options getOptions() { options.addOption(SCA_INCLUDE_SOURCE_FLAG, false, ArgDescriptions.SCA_INCLUDE_SOURCE_FLAG); options.addOption(SCA_TIMEOUT, true, ArgDescriptions.SCA_TIME_OUT); + options.addOption(ENABLE_SAST_BRANCHING, false, ArgDescriptions.ENABLE_SAST_BRANCHING); + options.addOption(MASTER_BRANCH_PROJ_NAME, true, ArgDescriptions.MASTER_BRANCH_PROJ_NAME); + + options.addOption(PERIODIC_FULL_SCAN, true, ArgDescriptions.PERIODIC_FULL_SCAN); + return options; } 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 b9f07ed..f4d5c13 100644 --- a/src/main/java/com/cx/plugin/cli/constants/Parameters.java +++ b/src/main/java/com/cx/plugin/cli/constants/Parameters.java @@ -59,6 +59,10 @@ private Parameters() { public static final String PDF_REPORT = "reportpdf"; public static final String XML_REPORT = "reportxml"; public static final String CSV_REPORT = "reportcsv"; + + public static final String GENERATE_SCA_REPORT = "generateScaReport"; + public static final String SCA_REPORT_FORMAT = "scareportformat"; + public static final String RTF_REPORT = "reportrtf"; public static final String IS_INCREMENTAL = "incremental"; public static final String IS_FORCE_SCAN = "forcescan"; @@ -110,4 +114,10 @@ private Parameters() { public static final String SAST_USER = "cxsastuser"; public static final String SCA_TIMEOUT = "scatimeout"; + public static final String ENABLE_SAST_BRANCHING = "enablesastbranching"; + public static final String MASTER_BRANCH_PROJ_NAME = "masterbranchprojname"; + + + public static final String PERIODIC_FULL_SCAN = "periodicfullscan"; + } diff --git a/src/main/java/com/cx/plugin/cli/constants/UsageExamples.java b/src/main/java/com/cx/plugin/cli/constants/UsageExamples.java index 3e25d2a..40fe9c7 100644 --- a/src/main/java/com/cx/plugin/cli/constants/UsageExamples.java +++ b/src/main/java/com/cx/plugin/cli/constants/UsageExamples.java @@ -13,8 +13,11 @@ private UsageExamples() { " -locationurl http://vsts2003:8080 -locationuser dm\\matys -locationpassword XYZ -preset default -reportxml a.xml -reportpdf b.pdf" + " -incremental -forcescan\nCxConsole Scan -projectname SP\\Cx\\Engine\\AST -cxserver http://localhost -cxuser admin@cx -cxpassword admin -locationtype share" + " -locationpath '\\\\storage\\path1;\\\\storage\\path2' -locationuser dm\\matys -locationpassword XYZ -preset \"Sans 25\" -reportxls a.xls -reportpdf b.pdf -private -verbose -log a.log\n" + - " -LocationPathExclude test*, *log* -LocationFilesExclude web.config , *.class\n"; - + " -LocationPathExclude test*, *log* -LocationFilesExclude web.config , *.class\n" + + "-enablesastbranching to check if branching support is enabled\n" + + "-masterbranchprojname master branch project name\n" + + "-periodicfullscan number like 4,5,etc"; + static final String TOKEN_GEN = "runCxConsole.cmd GenerateToken -CxServer http://localhost -cxuser admin@company -cxpassword admin -v"; static final String TOKEN_REVOKE = "runCxConsole.cmd RevokeToken -CxToken 1241513513tsfrg42 -CxServer http://localhost -v"; 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 2a63625..4691812 100644 --- a/src/main/java/com/cx/plugin/cli/utils/CxConfigHelper.java +++ b/src/main/java/com/cx/plugin/cli/utils/CxConfigHelper.java @@ -55,6 +55,9 @@ import static com.cx.plugin.cli.utils.PropertiesManager.*; import static org.apache.commons.lang3.StringUtils.isNotEmpty; +import static com.cx.plugin.cli.utils.PropertiesManager.KEY_CUSTOM_TRUSTSTORE; +import static com.cx.plugin.cli.utils.PropertiesManager.KEY_CUSTOM_TRUSTSTORE_PASSWORD; + /** * Created by idanA on 11/5/2018. */ @@ -73,7 +76,10 @@ public final class CxConfigHelper { private Command command; private CommandLine commandLine; - + private int fullScanCycle; + public static final int FULL_SCAN_CYCLE_MIN = 1; + public static final int FULL_SCAN_CYCLE_MAX = 99; + public CxConfigHelper(String configFilePath) { props = PropertiesManager.getProps(configFilePath); } @@ -91,13 +97,24 @@ public CxScanConfig resolveConfiguration(Command command, CommandLine cmd) throw checkForAmbiguousArgs(); + //pass trust store + setSystemProperties(); + CxScanConfig scanConfig = new CxScanConfig(); if (testConnection(scanConfig)) { return scanConfig; } - scanConfig.setProxyConfig(genProxyConfig()); + ProxyConfig proxyConfig = genProxyConfig(); + if (proxyConfig != null) { + scanConfig.setProxy(true); + scanConfig.setScaProxy(true); + scanConfig.setProxyConfig(proxyConfig); + scanConfig.setScaProxyConfig(proxyConfig); + log.info("Proxy configuration have been provided"); + } + scanConfig.setNTLM(cmd.hasOption(NTLM)); if (command.equals(Command.SCAN) || command.equals(Command.ASYNC_SCAN)) { @@ -158,10 +175,39 @@ public CxScanConfig resolveConfiguration(Command command, CommandLine cmd) throw scanConfig.setSastFilterPattern(sastFilterPattern); scanConfig.setScanComment(cmd.getOptionValue(SCAN_COMMENT)); setScanReports(scanConfig); + scanConfig.setGenerateScaReport(cmd.hasOption(GENERATE_SCA_REPORT)); + scanConfig.setScaReportFormat(cmd.getOptionValue(SCA_REPORT_FORMAT)); + if(scanConfig.isGenerateScaReport()) + throwForInvalidScaReportFormat(scanConfig.getScaReportFormat()); + scanConfig.setIncremental(cmd.hasOption(IS_INCREMENTAL)); - scanConfig.setForceScan(cmd.hasOption(IS_FORCE_SCAN)); - setSASTThresholds(scanConfig); - + scanConfig.setForceScan(cmd.hasOption(IS_FORCE_SCAN)); + scanConfig.setEnableSASTBranching(cmd.hasOption(ENABLE_SAST_BRANCHING)); + if (cmd.hasOption(ENABLE_SAST_BRANCHING)) { + if (!cmd.hasOption(MASTER_BRANCH_PROJ_NAME)) { + getRequiredParam(cmd, MASTER_BRANCH_PROJ_NAME, null); + } else { + scanConfig.setMasterBranchProjName(cmd.getOptionValue(MASTER_BRANCH_PROJ_NAME)); + } + } + + if (cmd.hasOption(IS_INCREMENTAL)) { + scanConfig.setIncremental(cmd.hasOption(IS_INCREMENTAL)); + } + + if (cmd.hasOption(PERIODIC_FULL_SCAN)) { + if (!cmd.hasOption(IS_INCREMENTAL)) { + getRequiredParam(cmd, IS_INCREMENTAL, null); + } else { + String periodicFullScan = cmd.getOptionValue(PERIODIC_FULL_SCAN); + this.fullScanCycle = Integer.valueOf(periodicFullScan); + boolean isIncremental = isThisBuildIncremental(); + scanConfig.setIncremental(isIncremental); + } + } + + setSASTThresholds(scanConfig); + String dsLocationPath = getSharedDependencyScanOption(scanConfig, OSA_LOCATION_PATH, SCA_LOCATION_PATH); if (scanConfig.isSastEnabled() || dsLocationPath == null) { ScanSourceConfigurator locator = new ScanSourceConfigurator(); @@ -179,6 +225,21 @@ public CxScanConfig resolveConfiguration(Command command, CommandLine cmd) throw return scanConfig; } + + + /* + * Sets JSSE JVM properties to pass custom trust store and its password + */ + public void setSystemProperties() { + + String customTrustStore = props.getProperty(KEY_CUSTOM_TRUSTSTORE); + String customTrustStorePassword = props.getProperty(KEY_CUSTOM_TRUSTSTORE_PASSWORD); + if(customTrustStore != null && !customTrustStore.isEmpty()) { + System.setProperty("javax.net.ssl.trustStore", customTrustStore); + System.setProperty("javax.net.ssl.trustStorePassword", customTrustStorePassword); + System.setProperty("javax.net.ssl.trustStoreType", "JKS"); + } + } private String apiFormat(String customFields) { @@ -330,8 +391,7 @@ 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()); @@ -415,13 +475,25 @@ 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)); }); + + sast.map(SastConfig::isEnableSASTBranching) + .ifPresent(pValue -> { + scanConfig.setEnableSASTBranching(pValue); + overridesResults.put("Enable SAST Branching", String.valueOf(pValue)); + }); + + sast.map(SastConfig::getMasterBranchProjName) + .ifPresent(pValue -> { + scanConfig.setMasterBranchProjName(pValue); + overridesResults.put("Master Branch Project Name", String.valueOf(pValue)); + }); + } private void mapProjectConfiguration(Optional project, CxScanConfig scanConfig, Map overridesResults) throws CLIParsingException { @@ -802,6 +874,7 @@ private void setScanReports(CxScanConfig scanConfig) { if (reportPath != null) { scanConfig.addRTFReport(reportPath); } + } private String getReportPath(String optionName) { @@ -932,7 +1005,24 @@ public static void printConfig(CommandLine commandLine) { value = param.getValue(); log.debug("{}: {}", name, value); value = DigestUtils.sha256Hex(param.getValue()); - } else if (param.hasArg()) { + }else if (param.getOpt().equalsIgnoreCase(LOCATION_URL)) { + String value1 = param.getValue(); + String[] arrOfStr = value1.split("@"); + value = ""; + if(arrOfStr.length==1) { + for (int i = 0; i < arrOfStr[0].length(); i++) { + value+="*"; + } + } + else { + for (int i = 0; i < arrOfStr[0].length(); i++) { + value+="*"; + } + value+="@"; + value+=arrOfStr[1]; + } + + }else if (param.hasArg()) { value = param.getValue(); } else { value = "true"; @@ -1042,16 +1132,23 @@ private static String normalizeUrl(String rawValue) { return rawValue.startsWith("http") ? rawValue : "http://" + rawValue; } - private ProxyConfig genProxyConfig() { - final String HTTP_HOST = System.getProperty("http.proxyHost"); - final String HTTP_PORT = System.getProperty("http.proxyPort"); - final String HTTP_USERNAME = System.getProperty("http.proxyUser"); - final String HTTP_PASSWORD = System.getProperty("http.proxyPassword"); + private String getVariable(String var) { + String value = StringUtils.isNotEmpty(System.getenv(var)) ? System.getenv(var) : System.getProperty(var); + return StringUtils.isNotEmpty(value) ? value.trim() : null; + } - final String HTTPS_HOST = System.getProperty("https.proxyHost"); - final String HTTPS_PORT = System.getProperty("https.proxyPort"); - final String HTTPS_USERNAME = System.getProperty("https.proxyUser"); - final String HTTPS_PASSWORD = System.getProperty("https.proxyPassword"); + private ProxyConfig genProxyConfig() { + final String HTTP_HOST = getVariable("http.proxyHost"); + final String HTTP_PORT = getVariable("http.proxyPort"); + final String HTTP_USERNAME = getVariable("http.proxyUser"); + final String HTTP_PASSWORD = getVariable("http.proxyPassword"); + final String HTTP_NON_HOSTS = getVariable("http.nonProxyHosts"); + + final String HTTPS_HOST = getVariable("https.proxyHost"); + final String HTTPS_PORT = getVariable("https.proxyPort"); + final String HTTPS_USERNAME = getVariable("https.proxyUser"); + final String HTTPS_PASSWORD = getVariable("https.proxyPassword"); + final String HTTPS_NON_HOSTS = getVariable("https.nonProxyHosts"); ProxyConfig proxyConfig = null; try { @@ -1060,6 +1157,7 @@ private ProxyConfig genProxyConfig() { proxyConfig.setUseHttps(false); proxyConfig.setHost(HTTP_HOST); proxyConfig.setPort(Integer.parseInt(HTTP_PORT)); + proxyConfig.setNoproxyHosts(StringUtils.isEmpty(HTTP_NON_HOSTS) ? "" : HTTP_NON_HOSTS); if (isNotEmpty(HTTP_USERNAME) && isNotEmpty(HTTP_PASSWORD)) { proxyConfig.setUsername(HTTP_USERNAME); proxyConfig.setPassword(HTTP_PASSWORD); @@ -1069,6 +1167,7 @@ private ProxyConfig genProxyConfig() { proxyConfig.setUseHttps(true); proxyConfig.setHost(HTTPS_HOST); proxyConfig.setPort(Integer.parseInt(HTTPS_PORT)); + proxyConfig.setNoproxyHosts(StringUtils.isEmpty(HTTPS_NON_HOSTS) ? "" : HTTPS_NON_HOSTS); if (isNotEmpty(HTTPS_USERNAME) && isNotEmpty(HTTPS_PASSWORD)) { proxyConfig.setUsername(HTTPS_USERNAME); proxyConfig.setPassword(HTTPS_PASSWORD); @@ -1094,4 +1193,37 @@ private boolean testConnection(CxScanConfig scanConfig) throws CLIParsingExcepti } return false; } + + //function to test whether build will be incremental or full scan + private boolean isThisBuildIncremental() { + int buildNumber = 0; + Map env = System.getenv(); + + if(env.get("BUILD_NUMBER") != null) { + buildNumber = Integer.valueOf(env.get("BUILD_NUMBER")); + } + + if (fullScanCycle == 0) { + return true; + } + + // if user entered invalid value for full scan cycle - all scans will be incremental; + if (fullScanCycle < FULL_SCAN_CYCLE_MIN || fullScanCycle > FULL_SCAN_CYCLE_MAX) { + return true; + } + + // If user asked to perform full scan after every 9 incremental scans - + // it means that every 10th scan should be full, + // that is the ordinal numbers of full scans will be "1", "11", "21" and so on... + boolean shouldBeFullScan = buildNumber % (fullScanCycle + 1) == 1; + return !shouldBeFullScan; + } + + private void throwForInvalidScaReportFormat(String format) throws ConfigurationException { + boolean valid = false; + List supportedFormats = Arrays.asList(new String[] {"json", "xml", "pdf", "csv", "cyclonedxjson", "cyclonedxxml"}); + + if(format == null || !supportedFormats.contains(format.toLowerCase())) + throw new ConfigurationException("Invalid SCA report format:" + format +". Supported formats are:" + supportedFormats.toString()); + } } diff --git a/src/main/java/com/cx/plugin/cli/utils/PropertiesManager.java b/src/main/java/com/cx/plugin/cli/utils/PropertiesManager.java index 99865fd..5979ff9 100644 --- a/src/main/java/com/cx/plugin/cli/utils/PropertiesManager.java +++ b/src/main/java/com/cx/plugin/cli/utils/PropertiesManager.java @@ -44,6 +44,9 @@ public class PropertiesManager { static final String KEY_SCA_INCLUDED_FILES = "scan.sca.include.files"; static final String KEY_SCA_EXCLUDED_FILES = "scan.sca.exclude.files"; static final String KEY_SCA_PROGRESS_INTERVAL = "scan.sca.job.progress.interval"; + + static final String KEY_CUSTOM_TRUSTSTORE = "trustStore"; + static final String KEY_CUSTOM_TRUSTSTORE_PASSWORD = "trustStorePassword"; private final String SEPARATOR = FileSystems.getDefault().getSeparator(); private String userDir = System.getProperty("user.dir"); @@ -82,7 +85,7 @@ private void loadProperties(String confPath) { log.info("Default configuration file location: {}", defaultPath); } catch (Exception ex) { - log.warn("Error occurred during loading configuration file."); + log.warn("Error occurred during loading configuration file.", ex); } } diff --git a/src/main/java/com/cx/plugin/cli/utils/ScanSourceConfigurator.java b/src/main/java/com/cx/plugin/cli/utils/ScanSourceConfigurator.java index 2568880..b9c87e9 100644 --- a/src/main/java/com/cx/plugin/cli/utils/ScanSourceConfigurator.java +++ b/src/main/java/com/cx/plugin/cli/utils/ScanSourceConfigurator.java @@ -95,7 +95,7 @@ private void setLocalSourceLocation(String locationPath, PropertiesManager props throw new CLIParsingException(String.format(LOCATION_PATH_EXCEPTION, "folder")); } scanConfig.setSourceDir(locationPath); - scanConfig.setMaxZipSize(props.getIntProperty(KEY_MAX_ZIP_SIZE)); + scanConfig.setMaxZipSize(props.getIntProperty(KEY_MAX_ZIP_SIZE)); } private void setSharedSourceLocation(String locationPath, String locationUser, String locationPass) throws CLIParsingException {