diff --git a/pom.xml b/pom.xml index f167102..9e8faa7 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ com.fasterxml.jackson.core jackson-databind - 2.16.1 + 2.21.1 org.projectlombok diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java index ea2205b..6067544 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java @@ -43,6 +43,7 @@ public final class CxConstants { static final String SCAN_ID = "--scan-id"; static final String PROJECT_ID = "--project-id"; static final String SIMILARITY_ID = "--similarity-id"; + static final String VULNERABILITY_IDENTIFIERS = "--vulnerability-identifiers"; static final String QUERY_ID = "--query-id"; static final String STATE = "--state"; static final String COMMENT = "--comment"; diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java index 9e3c05f..6e73560 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java @@ -29,14 +29,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; -import static com.checkmarx.ast.wrapper.Execution.*; public class CxWrapper { @@ -170,12 +168,46 @@ public List triageShow(@NonNull UUID projectId, String similarityId, arguments.add(similarityId); arguments.add(CxConstants.SCAN_TYPE); arguments.add(scanType); - arguments.addAll(jsonArguments()); return Execution.executeCommand(withConfigArguments(arguments), logger, Predicate::listFromLine, Predicate::validator); } + /** + * SCA-specific triage show command. + */ + public List triageScaShow(@NonNull UUID projectId, String vulnerabilities, String scanType) + throws IOException, InterruptedException, CxException { + this.logger.info("Executing 'triage show' command using the CLI for SCA."); + + if (StringUtils.isBlank(vulnerabilities)) { + this.logger.warn("Skipping SCA triage show: no vulnerability identifiers were provided."); + return Collections.emptyList(); + } + + List arguments = new ArrayList<>(); + arguments.add(CxConstants.CMD_TRIAGE); + arguments.add(CxConstants.SUB_CMD_SHOW); + arguments.add(CxConstants.SCAN_TYPE); + arguments.add(scanType); + arguments.add(CxConstants.VULNERABILITY_IDENTIFIERS); + arguments.add(vulnerabilities); + arguments.add(CxConstants.PROJECT_ID); + arguments.add(projectId.toString()); + arguments.addAll(jsonArguments()); + + try { + return Execution.executeCommand(withConfigArguments(arguments), logger, Predicate::listFromLine, Predicate::validator); + } catch (CxException e) { + String message = e.getMessage(); + if (message != null && message.contains("Failed to get SCA predicate result")) { + this.logger.info("No SCA triage history found for vulnerability identifiers: {}", vulnerabilities); + return Collections.emptyList(); + } + throw e; + } + } + public List triageGetStates(boolean all) throws IOException, InterruptedException, CxException { this.logger.info("Executing 'triage get-states' command using the CLI."); @@ -224,6 +256,39 @@ public void triageUpdate(@NonNull UUID projectId, String similarityId, String sc Execution.executeCommand(withConfigArguments(arguments), logger, line -> null); } + /** + * SCA-specific triage update command. + */ + public void triageScaUpdate(@NonNull UUID projectId, + String state, + String comment, + String vulnerabilities, + String scanType) + throws IOException, InterruptedException, CxException { + this.logger.info("Executing 'triage update' command using the CLI for SCA."); + + if (StringUtils.isBlank(vulnerabilities)) { + this.logger.warn("Skipping SCA triage update: no vulnerability identifiers were provided."); + return; + } + + List arguments = new ArrayList<>(); + arguments.add(CxConstants.CMD_TRIAGE); + arguments.add(CxConstants.SUB_CMD_UPDATE); + arguments.add(CxConstants.SCAN_TYPE); + arguments.add(scanType); + arguments.add(CxConstants.VULNERABILITY_IDENTIFIERS); + arguments.add(vulnerabilities); + arguments.add(CxConstants.STATE); + arguments.add(state); + arguments.add(CxConstants.COMMENT); + arguments.add(comment); + arguments.add(CxConstants.PROJECT_ID); + arguments.add(projectId.toString()); + + Execution.executeCommand(withConfigArguments(arguments), logger, line -> null); + } + public Project projectShow(@NonNull UUID projectId) throws IOException, InterruptedException, CxException { this.logger.info("Retrieving the details for project id: {}", projectId); diff --git a/src/test/java/com/checkmarx/ast/PredicateTest.java b/src/test/java/com/checkmarx/ast/PredicateTest.java index 7eb2f9c..ce1d05c 100644 --- a/src/test/java/com/checkmarx/ast/PredicateTest.java +++ b/src/test/java/com/checkmarx/ast/PredicateTest.java @@ -7,7 +7,7 @@ import com.checkmarx.ast.scan.Scan; import com.checkmarx.ast.wrapper.CxConstants; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import java.util.List; @@ -52,4 +52,45 @@ void testGetStates() throws Exception { List states = wrapper.triageGetStates(false); Assertions.assertNotNull(states); } + + @Test + void testScaTriage() throws Exception { + // Automatically find a completed scan that has SCA results + List scans = wrapper.scanList("statuses=Completed"); + + Scan scaScan = null; + Result scaResult = null; + + for (Scan scan : scans) { + Results results = wrapper.results(UUID.fromString(scan.getId())); + scaResult = results.getResults().stream() + .filter(res -> res.getType().equalsIgnoreCase("sca")) + .findFirst() + .orElse(null); + if (scaResult != null) { + scaScan = scan; + break; + } + } + + Assumptions.assumeTrue(scaScan != null, "Skipping: no completed scan with SCA results found"); + + String packageIdentifier = scaResult.getData().getPackageIdentifier(); + int firstDash = packageIdentifier.indexOf('-'); + int lastDash = packageIdentifier.lastIndexOf('-'); + String vulnerabilities = String.format("packagename=%s,packageversion=%s,vulnerabilityId=%s,packagemanager=%s", + packageIdentifier.substring(firstDash + 1, lastDash), + packageIdentifier.substring(lastDash + 1), + scaResult.getVulnerabilityDetails().getCveName(), + packageIdentifier.substring(0, firstDash).toLowerCase()); + + List predicates = wrapper.triageScaShow(UUID.fromString(scaScan.getProjectId()), vulnerabilities, scaResult.getType()); + Assertions.assertNotNull(predicates); + + try { + wrapper.triageScaUpdate(UUID.fromString(scaScan.getProjectId()), TO_VERIFY, "Edited via Java Wrapper", vulnerabilities, scaResult.getType()); + } catch (Exception e) { + Assertions.fail("SCA triage update failed. Should not throw exception"); + } + } }