diff --git a/build.gradle b/build.gradle index 56787ff2..2797d57f 100644 --- a/build.gradle +++ b/build.gradle @@ -53,13 +53,16 @@ dependencies { } - compile ('com.checkmarx:cx-client-common:2023.4.4') { - exclude group: 'org.apache.commons', module: 'commons-compress' + compile ('com.checkmarx:cx-client-common:2024.2.3') { + exclude group: 'org.yaml' , module: 'snakeyaml' exclude group: 'com.google.code.gson', module: 'gson' exclude group: 'org.json', module: 'json' exclude group: 'commons-beanutils', module: 'commons-beanutils' - exclude group: 'com.google.guava', module: 'guava' + exclude group: 'com.google.guava', module: 'guava' + exclude group: 'commons-collections', module: 'commons-collections' + exclude group: 'io.netty', module: 'netty-codec-http' + exclude group: 'org.apache.commons', module: 'commons-compress' } compile 'com.fasterxml.jackson.core:jackson-core:2.11.3', @@ -71,9 +74,9 @@ dependencies { 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.1', 'org.apache.logging.log4j:log4j-api:2.17.1', 'org.apache.logging.log4j:log4j-core:2.17.1', - 'org.apache.commons:commons-compress:1.25.0', + 'org.apache.commons:commons-compress:1.26.0', 'com.google.code.gson:gson:2.8.9', - 'org.yaml:snakeyaml:1.33', + 'org.yaml:snakeyaml:2.2', 'org.json:json:20231013', 'org.eclipse.jgit:org.eclipse.jgit:6.8.0.202311291450-r', 'com.google.guava:guava:32.1.1-jre' @@ -85,7 +88,7 @@ dependencies { implementation('commons-beanutils:commons-beanutils:1.9.4') { because 'previous versions have a bug impacting this application' } - implementation('io.netty:netty-codec-http:4.1.60.Final') { + implementation('io.netty:netty-codec-http:4.1.101.Final') { because 'previous versions have a bug impacting this application' } implementation('org.apache.httpcomponents:httpclient:4.5.13') { diff --git a/gradle.properties b/gradle.properties index f68f7c4c..c1804e33 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ description = Provides automatic scan of code by Checkmarx server and shows results summary and trend in Jenkins interface. group = com.checkmarx.jenkins -version = 2023.4.3 +version = 2024.2.3 repositoryVersion= diff --git a/src/main/java/com/checkmarx/jenkins/CxScanBuilder.java b/src/main/java/com/checkmarx/jenkins/CxScanBuilder.java index ad16c548..59f833e9 100644 --- a/src/main/java/com/checkmarx/jenkins/CxScanBuilder.java +++ b/src/main/java/com/checkmarx/jenkins/CxScanBuilder.java @@ -302,7 +302,7 @@ public CxScanBuilder( this.sastEnabled = sastEnabled; this.preset = (preset != null && !preset.startsWith("Provide Checkmarx")) ? preset : null; this.jobStatusOnError = jobStatusOnError; - this.scaReportFormat = scaReportFormat; + this.scaReportFormat = (generateScaReport) ? scaReportFormat : null; this.presetSpecified = presetSpecified; this.exclusionsSetting = exclusionsSetting; this.globalExclusions = "global".equals(exclusionsSetting); @@ -1014,7 +1014,7 @@ public void perform(@Nonnull Run, ?> run, @Nonnull FilePath workspace, @Nonnul //validate at least one scan type is enabled if (!config.isSastEnabled() && !config.isAstScaEnabled() && !config.isOsaEnabled()) { log.error("Both SAST and dependency scan are disabled. Exiting."); - run.setResult(Result.FAILURE); + run.setResult(Result.SUCCESS); return; } @@ -1117,11 +1117,36 @@ public void perform(@Nonnull Run, ?> run, @Nonnull FilePath workspace, @Nonnul return; } - //Asynchronous scan - add note message and previous build reports - if (!descriptor.isAsyncHtmlRemoval() || config.getSynchronous()) { - String reportName = generateHTMLReport(workspace, checkmarxBuildDir, config, scanResults); - cxScanResult.setHtmlReportName(reportName); - } + /* For asynchronous scan, if reports are not ready or report of previous successful scan is not + found, HTML Report is not generated*/ + boolean generateAsyncReport = false; + SASTResults sastResults = scanResults.getSastResults(); + OSAResults osaResults = scanResults.getOsaResults(); + AstScaResults scaResults = scanResults.getScaResults(); + if (config.isSastEnabled() && sastResults != null && sastResults.isSastResultsReady()) { + generateAsyncReport = true; + } + + /*If a combination scan is run(SAST + OSA/SCA), + HTML report is generated only if, previous reports are available for both the scans*/ + if (config.isOsaEnabled() || config.isAstScaEnabled()) { + if ((config.isOsaEnabled() && (osaResults == null || !osaResults.isOsaResultsReady())) + || (config.isAstScaEnabled() && (scaResults == null || !scaResults.isScaResultReady())) + || (config.isSastEnabled() && (sastResults == null || !sastResults.isSastResultsReady()))) { + generateAsyncReport = false; + } else { + generateAsyncReport = true; + } + } + + if (!descriptor.isAsyncHtmlRemoval() || config.getSynchronous()) { + log.info("Running in Asynchronous mode. Not waiting for scan to finish."); + + if (generateAsyncReport) { + String reportName = generateHTMLReport(workspace, checkmarxBuildDir, config, scanResults); + cxScanResult.setHtmlReportName(reportName); + } + } run.addAction(cxScanResult); } catch (ConfigurationException e1) { e1.printStackTrace(); @@ -1928,7 +1953,6 @@ private void printConfiguration(CxScanConfig config, DescriptorImpl descriptor, log.info("enable Project Policy Enforcement SCA: " + config.getEnablePolicyViolationsSCA()); log.info("continue build when timed out: " + config.getContinueBuild()); log.info("post scan action: " + config.getPostScanActionId()); - log.info("is force scan: " + config.getForceScan()); log.info("scan level custom fields: " + config.getCustomFields()); log.info("project level custom fields: " + config.getProjectLevelCustomFields()); log.info("overrideProjectSetting value: " + overrideProjectSetting); @@ -2193,13 +2217,13 @@ private void writeFileToWorkspaceReports(FilePath workspace, File file) { private boolean shouldUseGlobalThreshold() { final DescriptorImpl descriptor = getDescriptor(); //locked by global or (job threshold enabled and points to 'global' and global is enabled) - return (descriptor.isForcingVulnerabilityThresholdEnabled() && descriptor.isLockVulnerabilitySettings()) || (isVulnerabilityThresholdEnabled() && "global".equals(getThresholdSettings()) && descriptor.isForcingVulnerabilityThresholdEnabled()); + return (descriptor.isForcingVulnerabilityThresholdEnabled() && descriptor.isLockVulnerabilitySettings()) || (isWaitForResultsEnabled() && isVulnerabilityThresholdEnabled() && descriptor.isForcingVulnerabilityThresholdEnabled() && "global".equals(getThresholdSettings())); } private boolean shouldUseJobThreshold() { final DescriptorImpl descriptor = getDescriptor(); //not locked by global and job threshold enabled and points to 'job' - return !(descriptor.isForcingVulnerabilityThresholdEnabled() && descriptor.isLockVulnerabilitySettings()) && isVulnerabilityThresholdEnabled(); + return !(descriptor.isForcingVulnerabilityThresholdEnabled() && descriptor.isLockVulnerabilitySettings()) && (isWaitForResultsEnabled() && isVulnerabilityThresholdEnabled()); } /** @@ -3206,6 +3230,23 @@ public FormValidation doCheckGenerateScaReport(@QueryParameter boolean value, @Q return FormValidation.ok(); } + @POST + public FormValidation doCheckVulnerabilityThresholdEnabled(@QueryParameter boolean value, + @QueryParameter boolean sastEnabled, @QueryParameter boolean vulnerabilityThresholdEnabled, + @AncestorInPath Item item) { + if (item == null) { + return FormValidation.ok(); + } + item.checkPermission(Item.CONFIGURE); + if (!sastEnabled && value) { + vulnerabilityThresholdEnabled = false; + sastEnabled = false; + return FormValidation.error("Enable CxSAST scan to set SAST threshold"); + } + + return FormValidation.ok(); + } + @POST public FormValidation doTestScaSASTConnection(@QueryParameter final String scaSastServerUrl, @QueryParameter final String password, diff --git a/src/main/java/com/checkmarx/jenkins/CxScanResult.java b/src/main/java/com/checkmarx/jenkins/CxScanResult.java index 1da8cef1..2861a576 100644 --- a/src/main/java/com/checkmarx/jenkins/CxScanResult.java +++ b/src/main/java/com/checkmarx/jenkins/CxScanResult.java @@ -173,6 +173,18 @@ public boolean isRemoveAsyncHtml() { return descriptor != null && (!descriptor.isAsyncHtmlRemoval() || !this.scanRanAsynchronous ); } + // Check if neither the current report nor the older report is found + public boolean isEmptyReportAsync() { + try { + String htmlReport = getHtmlReport(); + return htmlReport == null || htmlReport.isEmpty() + || htmlReport.equals("