diff --git a/src/main/java/com/checkmarx/ast/predicate/CustomState.java b/src/main/java/com/checkmarx/ast/predicate/CustomState.java new file mode 100644 index 00000000..e7d6177f --- /dev/null +++ b/src/main/java/com/checkmarx/ast/predicate/CustomState.java @@ -0,0 +1,37 @@ +package com.checkmarx.ast.predicate; + +import com.checkmarx.ast.utils.JsonParser; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.type.TypeFactory; +import lombok.Value; + +import java.util.List; + +@Value +@JsonDeserialize() +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class CustomState { + Integer id; + String name; + String type; + + public CustomState(@JsonProperty("id") Integer id, + @JsonProperty("name") String name, + @JsonProperty("type") String type) { + this.id = id; + this.name = name; + this.type = type; + } + + public static T fromLine(String line) { + return JsonParser.parse(line, TypeFactory.defaultInstance().constructType(CustomState.class)); + } + + public static List listFromLine(String line) { + return JsonParser.parse(line, TypeFactory.defaultInstance().constructCollectionType(List.class, CustomState.class)); + } +} diff --git a/src/main/java/com/checkmarx/ast/predicate/Predicate.java b/src/main/java/com/checkmarx/ast/predicate/Predicate.java index 4a5a6373..2806391a 100644 --- a/src/main/java/com/checkmarx/ast/predicate/Predicate.java +++ b/src/main/java/com/checkmarx/ast/predicate/Predicate.java @@ -12,6 +12,7 @@ import org.apache.commons.lang3.StringUtils; import java.io.IOException; +import java.lang.reflect.Field; import java.util.List; @Value @@ -29,13 +30,14 @@ public class Predicate { String createdBy; String createdAt; String updatedAt; + String stateId; @JsonCreator public Predicate(@JsonProperty("ID") String id, @JsonProperty("SimilarityID") String similarityId, @JsonProperty("ProjectID") String projectId, @JsonProperty("State") String state, @JsonProperty("Severity") String severity, @JsonProperty("Comment") String comment, @JsonProperty("CreatedBy") String createdBy, @JsonProperty("CreatedAt") String createdAt, - @JsonProperty("UpdatedAt") String updatedAt) { + @JsonProperty("UpdatedAt") String updatedAt, @JsonProperty("StateId") String stateId) { this.id = id; this.similarityId = similarityId; this.projectId = projectId; @@ -45,6 +47,7 @@ public Predicate(@JsonProperty("ID") String id, @JsonProperty("SimilarityID") St this.createdBy = createdBy; this.createdAt = createdAt; this.updatedAt = updatedAt; + this.stateId = stateId; } public static T fromLine(String line) { @@ -68,6 +71,22 @@ protected static T parse(String line, JavaType type) { return result; } + public static boolean validator(List arguments, Object parsedLine) { + { + for (Field field : parsedLine.getClass().getDeclaredFields()) { + field.setAccessible(true); + try { + if (field.get(parsedLine) == null && !field.getName().equals("stateId")) { + return false; + } + } catch (IllegalAccessException e) { + return false; + } + } + return true; + } + } + private static boolean isValidJSON(final String json) { try { final ObjectMapper mapper = new ObjectMapper(); diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java index a192b603..889bbb65 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java @@ -32,6 +32,8 @@ public final class CxConstants { static final String SUB_CMD_CANCEL = "cancel"; static final String CMD_TRIAGE = "triage"; static final String SUB_CMD_UPDATE = "update"; + static final String SUB_CMD_GET_STATES = "get-states"; + static final String ALL_STATES_FLAG = "--all"; static final String CMD_RESULT = "results"; static final String FORMAT = "--format"; static final String SCAN_INFO_FORMAT = "--scan-info-format"; @@ -44,6 +46,7 @@ public final class CxConstants { static final String STATE = "--state"; static final String COMMENT = "--comment"; static final String SEVERITY = "--severity"; + static final String CUSTOM_STATE_ID = "--custom-state-id"; static final String REPORT_FORMAT = "--report-format"; static final String OUTPUT_NAME = "--output-name"; static final String OUTPUT_PATH = "--output-path"; diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java index c72e1bd5..d9c10fd4 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java @@ -4,6 +4,7 @@ import com.checkmarx.ast.codebashing.CodeBashing; import com.checkmarx.ast.kicsRealtimeResults.KicsRealtimeResults; import com.checkmarx.ast.learnMore.LearnMore; +import com.checkmarx.ast.predicate.CustomState; import com.checkmarx.ast.predicate.Predicate; import com.checkmarx.ast.project.Project; import com.checkmarx.ast.remediation.KicsRemediation; @@ -23,6 +24,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; +import java.lang.reflect.Field; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -161,12 +163,32 @@ public List triageShow(@NonNull UUID projectId, String similarityId, arguments.addAll(jsonArguments()); - return Execution.executeCommand(withConfigArguments(arguments), logger, Predicate::listFromLine); + return Execution.executeCommand(withConfigArguments(arguments), logger, Predicate::listFromLine, Predicate::validator); + } + + public List triageGetStates(boolean all) throws IOException, InterruptedException, CxException { + this.logger.info("Executing 'triage get-states' command using the CLI."); + + List arguments = new ArrayList<>(); + arguments.add(CxConstants.CMD_TRIAGE); + arguments.add(CxConstants.SUB_CMD_GET_STATES); + if (all) { + arguments.add(CxConstants.ALL_STATES_FLAG); + } + + return Execution.executeCommand(withConfigArguments(arguments), logger, CustomState::listFromLine); } public void triageUpdate(@NonNull UUID projectId, String similarityId, String scanType, String state, String comment, String severity) throws IOException, InterruptedException, CxException { + triageUpdate(projectId, similarityId, scanType, state, comment, severity, null); + } + + public void triageUpdate(@NonNull UUID projectId, String similarityId, String scanType, String state, String comment, String severity, String customStateId) throws IOException, InterruptedException, CxException { this.logger.info("Executing 'triage update' command using the CLI."); - this.logger.info("Updating the similarityId {} with state {} and severity {}.", similarityId, state, severity); + this.logger.info("Updating the similarityId {} with state {} with customStateId {} and severity {}.", similarityId, state, customStateId, severity); + + boolean emptyState = state == null || state.isEmpty(); + boolean emptyCustomStateId = customStateId == null || customStateId.isEmpty(); List arguments = new ArrayList<>(); arguments.add(CxConstants.CMD_TRIAGE); @@ -179,6 +201,10 @@ public void triageUpdate(@NonNull UUID projectId, String similarityId, String sc arguments.add(scanType); arguments.add(CxConstants.STATE); arguments.add(state); + if (!emptyCustomStateId) { + arguments.add(CxConstants.CUSTOM_STATE_ID); + arguments.add(customStateId); + } if (!StringUtils.isBlank(comment)) { arguments.add(CxConstants.COMMENT); arguments.add(comment); @@ -232,7 +258,9 @@ public ScanResult ScanAsca(String fileSource, boolean ascaLatestVersion, String appendAgentToArguments(agent, arguments); - return Execution.executeCommand(withConfigArguments(arguments), logger, ScanResult::fromLine); + return Execution.executeCommand(withConfigArguments(arguments), logger, ScanResult::fromLine, + (args, ignored) -> + (args.size() >= 3 && args.get(1).equals(CxConstants.CMD_SCAN) && args.get(2).equals(CxConstants.SUB_CMD_ASCA))); } private static void appendAgentToArguments(String agent, List arguments) { diff --git a/src/main/java/com/checkmarx/ast/wrapper/Execution.java b/src/main/java/com/checkmarx/ast/wrapper/Execution.java index 56aa48ef..a60101c0 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/Execution.java +++ b/src/main/java/com/checkmarx/ast/wrapper/Execution.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.function.BiFunction; import java.util.function.Function; public final class Execution { @@ -42,34 +43,37 @@ static T executeCommand(List arguments, Logger logger, Function lineParser) throws IOException, InterruptedException, CxException { + return executeCommand(arguments, logger, lineParser, Execution::areAllFieldsNotNull); + } + + static T executeCommand(List arguments, + Logger logger, + Function lineParser, + BiFunction, T, Boolean> customValidator) + throws IOException, InterruptedException, CxException { Process process = buildProcess(arguments); try (BufferedReader br = getReader(process)) { T executionResult = null; String line; - StringBuilder stringBuilder = new StringBuilder(); + StringBuilder output = new StringBuilder(); while ((line = br.readLine()) != null) { logger.info(line); - stringBuilder.append(line).append(LINE_SEPARATOR); + output.append(line).append(LINE_SEPARATOR); T parsedLine = lineParser.apply(line); if (parsedLine != null) { - if (areAllFieldsNotNull(parsedLine) || isAscaRequest(arguments)) { - executionResult = parsedLine; - } + executionResult = customValidator.apply(arguments, parsedLine) ? parsedLine : null; } } process.waitFor(); if (process.exitValue() != 0) { - throw new CxException(process.exitValue(), stringBuilder.toString()); + throw new CxException(process.exitValue(), output.toString()); } return executionResult; } } - public static boolean isAscaRequest(List arguments) { - return (arguments.size() >= 3 && arguments.get(1).equals("scan") && arguments.get(2).equals("asca")); - } - private static boolean areAllFieldsNotNull(Object obj) { + private static boolean areAllFieldsNotNull(List arguments, Object obj) { for (Field field : obj.getClass().getDeclaredFields()) { field.setAccessible(true); try { diff --git a/src/test/java/com/checkmarx/ast/PredicateTest.java b/src/test/java/com/checkmarx/ast/PredicateTest.java index 9004402b..55d6b268 100644 --- a/src/test/java/com/checkmarx/ast/PredicateTest.java +++ b/src/test/java/com/checkmarx/ast/PredicateTest.java @@ -5,7 +5,9 @@ import com.checkmarx.ast.results.result.Result; import com.checkmarx.ast.scan.Scan; import com.checkmarx.ast.wrapper.CxConstants; +import org.junit.Ignore; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.util.List; @@ -44,4 +46,11 @@ void testTriage() throws Exception { Assertions.fail("Triage update failed. Should not throw exception"); } } + + @Test + @Disabled("Ignore this tests until get states api will be in production") + void testGetStates() throws Exception { + List states = wrapper.triageGetStates(false); + Assertions.assertNotNull(states); + } }