diff --git a/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeImage.java b/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeImage.java new file mode 100644 index 00000000..7a6a33a6 --- /dev/null +++ b/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeImage.java @@ -0,0 +1,40 @@ +package com.checkmarx.ast.containersrealtime; + +import com.checkmarx.ast.realtime.RealtimeLocation; +import com.fasterxml.jackson.annotation.JsonCreator; +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 lombok.Value; + +import java.util.Collections; +import java.util.List; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ContainersRealtimeImage { + @JsonProperty("ImageName") String imageName; + @JsonProperty("ImageTag") String imageTag; + @JsonProperty("FilePath") String filePath; + @JsonProperty("Locations") List locations; + @JsonProperty("Status") String status; + @JsonProperty("Vulnerabilities") List vulnerabilities; + + @JsonCreator + public ContainersRealtimeImage(@JsonProperty("ImageName") String imageName, + @JsonProperty("ImageTag") String imageTag, + @JsonProperty("FilePath") String filePath, + @JsonProperty("Locations") List locations, + @JsonProperty("Status") String status, + @JsonProperty("Vulnerabilities") List vulnerabilities) { + this.imageName = imageName; + this.imageTag = imageTag; + this.filePath = filePath; + this.locations = locations == null ? Collections.emptyList() : locations; + this.status = status; + this.vulnerabilities = vulnerabilities == null ? Collections.emptyList() : vulnerabilities; + } +} \ No newline at end of file diff --git a/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeResults.java b/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeResults.java new file mode 100644 index 00000000..f9054ffe --- /dev/null +++ b/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeResults.java @@ -0,0 +1,53 @@ +package com.checkmarx.ast.containersrealtime; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Value; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.List; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ContainersRealtimeResults { + private static final Logger log = LoggerFactory.getLogger(ContainersRealtimeResults.class); + + @JsonProperty("Images") List images; + + @JsonCreator + public ContainersRealtimeResults(@JsonProperty("Images") List images) { + this.images = images; + } + + public static ContainersRealtimeResults fromLine(String line) { + if (StringUtils.isBlank(line)) { + return null; + } + try { + if (line.contains("\"Images\"") && isValidJSON(line)) { + return new ObjectMapper().readValue(line, ContainersRealtimeResults.class); + } + } catch (IOException e) { + log.debug("Failed to parse containers realtime line: {}", line, e); + } + return null; + } + + private static boolean isValidJSON(String json) { + try { + new ObjectMapper().readTree(json); + return true; + } catch (IOException e) { + return false; + } + } +} diff --git a/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeVulnerability.java b/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeVulnerability.java new file mode 100644 index 00000000..cabe4b68 --- /dev/null +++ b/src/main/java/com/checkmarx/ast/containersrealtime/ContainersRealtimeVulnerability.java @@ -0,0 +1,24 @@ +package com.checkmarx.ast.containersrealtime; + +import com.fasterxml.jackson.annotation.JsonCreator; +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 lombok.Value; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class ContainersRealtimeVulnerability { + @JsonProperty("CVE") String cve; + @JsonProperty("Severity") String severity; + + @JsonCreator + public ContainersRealtimeVulnerability(@JsonProperty("CVE") String cve, + @JsonProperty("Severity") String severity) { + this.cve = cve; + this.severity = severity; + } +} \ No newline at end of file diff --git a/src/main/java/com/checkmarx/ast/iacrealtime/IacRealtimeResults.java b/src/main/java/com/checkmarx/ast/iacrealtime/IacRealtimeResults.java new file mode 100644 index 00000000..0afc103f --- /dev/null +++ b/src/main/java/com/checkmarx/ast/iacrealtime/IacRealtimeResults.java @@ -0,0 +1,98 @@ +package com.checkmarx.ast.iacrealtime; + +import com.checkmarx.ast.realtime.RealtimeLocation; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Value; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class IacRealtimeResults { + private static final Logger log = LoggerFactory.getLogger(IacRealtimeResults.class); + @JsonProperty("Results") List results; // Normalized list (array or single object) + + @JsonCreator + public IacRealtimeResults(@JsonProperty("Results") List results) { + this.results = results == null ? Collections.emptyList() : results; + } + + @Value + @JsonDeserialize + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Issue { + @JsonProperty("Title") String title; + @JsonProperty("Description") String description; + @JsonProperty("SimilarityID") String similarityId; + @JsonProperty("FilePath") String filePath; + @JsonProperty("Severity") String severity; + @JsonProperty("ExpectedValue") String expectedValue; + @JsonProperty("ActualValue") String actualValue; + @JsonProperty("Locations") List locations; + + @JsonCreator + public Issue(@JsonProperty("Title") String title, + @JsonProperty("Description") String description, + @JsonProperty("SimilarityID") String similarityId, + @JsonProperty("FilePath") String filePath, + @JsonProperty("Severity") String severity, + @JsonProperty("ExpectedValue") String expectedValue, + @JsonProperty("ActualValue") String actualValue, + @JsonProperty("Locations") List locations) { + this.title = title; + this.description = description; + this.similarityId = similarityId; + this.filePath = filePath; + this.severity = severity; + this.expectedValue = expectedValue; + this.actualValue = actualValue; + this.locations = locations == null ? Collections.emptyList() : locations; + } + } + + public static IacRealtimeResults fromLine(String line) { + if (StringUtils.isBlank(line)) { + return null; + } + try { + if (!isValidJSON(line)) { + return null; + } + ObjectMapper mapper = new ObjectMapper(); + String trimmed = line.trim(); + if (trimmed.startsWith("[")) { + List list = mapper.readValue(trimmed, mapper.getTypeFactory().constructCollectionType(List.class, Issue.class)); + return new IacRealtimeResults(list == null ? Collections.emptyList() : list); + } + if (trimmed.startsWith("{")) { + Issue single = mapper.readValue(trimmed, Issue.class); + return new IacRealtimeResults(Collections.singletonList(single)); + } + } catch (IOException e) { + log.debug("Failed to parse iac realtime JSON line: {}", line, e); + } + return null; + } + + private static boolean isValidJSON(String json) { + try { + new ObjectMapper().readTree(json); + return true; + } catch (IOException e) { + return false; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/checkmarx/ast/mask/MaskResult.java b/src/main/java/com/checkmarx/ast/mask/MaskResult.java new file mode 100644 index 00000000..80d9e8bb --- /dev/null +++ b/src/main/java/com/checkmarx/ast/mask/MaskResult.java @@ -0,0 +1,37 @@ +package com.checkmarx.ast.mask; + +import com.checkmarx.ast.utils.JsonParser; +import com.fasterxml.jackson.annotation.JsonCreator; +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.EqualsAndHashCode; +import lombok.ToString; +import lombok.Value; + +import java.util.List; + +@Value +@EqualsAndHashCode() +@JsonDeserialize() +@ToString(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class MaskResult { + + List maskedSecrets; + String maskedFile; + + @JsonCreator + public MaskResult(@JsonProperty("maskedSecrets") List maskedSecrets, + @JsonProperty("maskedFile") String maskedFile) { + this.maskedSecrets = maskedSecrets; + this.maskedFile = maskedFile; + } + + public static MaskResult fromLine(String line) { + return JsonParser.parse(line, TypeFactory.defaultInstance().constructType(MaskResult.class)); + } +} diff --git a/src/main/java/com/checkmarx/ast/mask/MaskedSecret.java b/src/main/java/com/checkmarx/ast/mask/MaskedSecret.java new file mode 100644 index 00000000..a18e8827 --- /dev/null +++ b/src/main/java/com/checkmarx/ast/mask/MaskedSecret.java @@ -0,0 +1,32 @@ +package com.checkmarx.ast.mask; + +import com.fasterxml.jackson.annotation.JsonCreator; +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 lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.Value; + +@Value +@EqualsAndHashCode() +@JsonDeserialize() +@ToString(callSuper = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class MaskedSecret { + + String masked; + String secret; + int line; + + @JsonCreator + public MaskedSecret(@JsonProperty("masked") String masked, + @JsonProperty("secret") String secret, + @JsonProperty("line") int line) { + this.masked = masked; + this.secret = secret; + this.line = line; + } +} diff --git a/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeResults.java b/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeResults.java new file mode 100644 index 00000000..7141370f --- /dev/null +++ b/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeResults.java @@ -0,0 +1,55 @@ +package com.checkmarx.ast.ossrealtime; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Value; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class OssRealtimeResults { + private static final Logger log = LoggerFactory.getLogger(OssRealtimeResults.class); + + @JsonProperty("Packages") List packages; + + @JsonCreator + public OssRealtimeResults(@JsonProperty("Packages") List packages) { + this.packages = packages == null ? Collections.emptyList() : packages; + } + + public static OssRealtimeResults fromLine(String line) { + if (StringUtils.isBlank(line)) { + return null; + } + try { + if (isValidJSON(line) && line.contains("\"Packages\"")) { + return new ObjectMapper().readValue(line, OssRealtimeResults.class); + } + } catch (IOException e) { + log.debug("Failed to parse oss realtime line: {}", line, e); + } + return null; + } + + private static boolean isValidJSON(String json) { + try { + new ObjectMapper().readTree(json); + return true; + } catch (IOException e) { + return false; + } + } +} + diff --git a/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeScanPackage.java b/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeScanPackage.java new file mode 100644 index 00000000..d4b32771 --- /dev/null +++ b/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeScanPackage.java @@ -0,0 +1,44 @@ +package com.checkmarx.ast.ossrealtime; + +import com.checkmarx.ast.realtime.RealtimeLocation; +import com.fasterxml.jackson.annotation.JsonCreator; +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 lombok.Value; + +import java.util.Collections; +import java.util.List; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class OssRealtimeScanPackage { + @JsonProperty("PackageManager") String packageManager; + @JsonProperty("PackageName") String packageName; + @JsonProperty("PackageVersion") String packageVersion; + @JsonProperty("FilePath") String filePath; + @JsonProperty("Locations") List locations; + @JsonProperty("Status") String status; + @JsonProperty("Vulnerabilities") List vulnerabilities; + + @JsonCreator + public OssRealtimeScanPackage(@JsonProperty("PackageManager") String packageManager, + @JsonProperty("PackageName") String packageName, + @JsonProperty("PackageVersion") String packageVersion, + @JsonProperty("FilePath") String filePath, + @JsonProperty("Locations") List locations, + @JsonProperty("Status") String status, + @JsonProperty("Vulnerabilities") List vulnerabilities) { + this.packageManager = packageManager; + this.packageName = packageName; + this.packageVersion = packageVersion; + this.filePath = filePath; + this.locations = locations == null ? Collections.emptyList() : locations; + this.status = status; + this.vulnerabilities = vulnerabilities == null ? Collections.emptyList() : vulnerabilities; + } +} + diff --git a/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeVulnerability.java b/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeVulnerability.java new file mode 100644 index 00000000..ac89b368 --- /dev/null +++ b/src/main/java/com/checkmarx/ast/ossrealtime/OssRealtimeVulnerability.java @@ -0,0 +1,31 @@ +package com.checkmarx.ast.ossrealtime; + +import com.fasterxml.jackson.annotation.JsonCreator; +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 lombok.Value; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class OssRealtimeVulnerability { + @JsonProperty("CVE") String cve; + @JsonProperty("Severity") String severity; + @JsonProperty("Description") String description; + @JsonProperty("FixVersion") String fixVersion; + + @JsonCreator + public OssRealtimeVulnerability(@JsonProperty("CVE") String cve, + @JsonProperty("Severity") String severity, + @JsonProperty("Description") String description, + @JsonProperty("FixVersion") String fixVersion) { + this.cve = cve; + this.severity = severity; + this.description = description; + this.fixVersion = fixVersion; + } +} + diff --git a/src/main/java/com/checkmarx/ast/realtime/RealtimeLocation.java b/src/main/java/com/checkmarx/ast/realtime/RealtimeLocation.java new file mode 100644 index 00000000..277fe13d --- /dev/null +++ b/src/main/java/com/checkmarx/ast/realtime/RealtimeLocation.java @@ -0,0 +1,28 @@ +package com.checkmarx.ast.realtime; + +import com.fasterxml.jackson.annotation.JsonCreator; +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 lombok.Value; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class RealtimeLocation { + @JsonProperty("Line") int line; + @JsonProperty("StartIndex") int startIndex; + @JsonProperty("EndIndex") int endIndex; + + @JsonCreator + public RealtimeLocation(@JsonProperty("Line") int line, + @JsonProperty("StartIndex") int startIndex, + @JsonProperty("EndIndex") int endIndex) { + this.line = line; + this.startIndex = startIndex; + this.endIndex = endIndex; + } +} + diff --git a/src/main/java/com/checkmarx/ast/secretsrealtime/SecretsRealtimeResults.java b/src/main/java/com/checkmarx/ast/secretsrealtime/SecretsRealtimeResults.java new file mode 100644 index 00000000..ef19050c --- /dev/null +++ b/src/main/java/com/checkmarx/ast/secretsrealtime/SecretsRealtimeResults.java @@ -0,0 +1,94 @@ +package com.checkmarx.ast.secretsrealtime; + +import com.checkmarx.ast.realtime.RealtimeLocation; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Value; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +@Value +@JsonDeserialize +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class SecretsRealtimeResults { + private static final Logger log = LoggerFactory.getLogger(SecretsRealtimeResults.class); + + @JsonProperty("Secrets") List secrets; + + @JsonCreator + public SecretsRealtimeResults(@JsonProperty("Secrets") List secrets) { + this.secrets = secrets == null ? Collections.emptyList() : secrets; + } + + @Value + @JsonDeserialize + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Secret { + @JsonProperty("Title") String title; + @JsonProperty("Description") String description; + @JsonProperty("SecretValue") String secretValue; + @JsonProperty("FilePath") String filePath; + @JsonProperty("Severity") String severity; + @JsonProperty("Locations") List locations; + + @JsonCreator + public Secret(@JsonProperty("Title") String title, + @JsonProperty("Description") String description, + @JsonProperty("SecretValue") String secretValue, + @JsonProperty("FilePath") String filePath, + @JsonProperty("Severity") String severity, + @JsonProperty("Locations") List locations) { + this.title = title; + this.description = description; + this.secretValue = secretValue; + this.filePath = filePath; + this.severity = severity; + this.locations = locations == null ? Collections.emptyList() : locations; + } + } + + public static SecretsRealtimeResults fromLine(String line) { + if (StringUtils.isBlank(line)) { + return null; + } + try { + if (!isValidJSON(line)) { + return null; + } + ObjectMapper mapper = new ObjectMapper(); + String trimmed = line.trim(); + if (trimmed.startsWith("[")) { + List list = mapper.readValue(trimmed, mapper.getTypeFactory().constructCollectionType(List.class, Secret.class)); + return new SecretsRealtimeResults(list); + } + if (trimmed.startsWith("{")) { + Secret single = mapper.readValue(trimmed, Secret.class); + return new SecretsRealtimeResults(Collections.singletonList(single)); + } + } catch (IOException e) { + log.debug("Failed to parse secrets realtime JSON line: {}", line, e); + } + return null; + } + + private static boolean isValidJSON(String json) { + try { + new ObjectMapper().readTree(json); + return true; + } catch (IOException e) { + return false; + } + } +} + diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java index 5f1b8f62..dd8c6e69 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java @@ -74,4 +74,12 @@ public final class CxConstants { static final String SUB_CMD_LEARN_MORE = "learn-more"; static final String SUB_CMD_TENANT = "tenant"; static final String IDE_SCANS_KEY = "scan.config.plugins.ideScans"; + static final String AI_MCP_SERVER_KEY = "scan.config.plugins.aiMcpServer"; + static final String IGNORED_FILE_PATH = "--ignored-file-path"; + static final String SUB_CMD_OSS_REALTIME = "oss-realtime"; + static final String SUB_CMD_IAC_REALTIME = "iac-realtime"; + static final String SUB_CMD_SECRETS_REALTIME = "secrets-realtime"; + static final String SUB_CMD_CONTAINERS_REALTIME = "containers-realtime"; + static final String SUB_CMD_MASK = "mask"; + static final String RESULT_FILE = "--result-file"; } diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java index 38640d81..c49aa875 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java @@ -4,6 +4,12 @@ import com.checkmarx.ast.codebashing.CodeBashing; import com.checkmarx.ast.kicsRealtimeResults.KicsRealtimeResults; import com.checkmarx.ast.learnMore.LearnMore; +import com.checkmarx.ast.mask.MaskResult; +import com.checkmarx.ast.ossrealtime.OssRealtimeResults; +import com.checkmarx.ast.secretsrealtime.SecretsRealtimeResults; + +import com.checkmarx.ast.iacrealtime.IacRealtimeResults; +import com.checkmarx.ast.containersrealtime.ContainersRealtimeResults; import com.checkmarx.ast.predicate.CustomState; import com.checkmarx.ast.predicate.Predicate; import com.checkmarx.ast.project.Project; @@ -24,7 +30,6 @@ 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; @@ -396,7 +401,7 @@ public KicsRealtimeResults kicsRealtimeScan(@NonNull String fileSources, String arguments.add(fileSources); arguments.add(CxConstants.ADDITONAL_PARAMS); arguments.add(additionalParams); - if (engine.length() > 0) { + if (!engine.isEmpty()) { arguments.add(CxConstants.ENGINE); arguments.add(engine); } @@ -404,6 +409,48 @@ public KicsRealtimeResults kicsRealtimeScan(@NonNull String fileSources, String return Execution.executeCommand(withConfigArguments(arguments), logger, KicsRealtimeResults::fromLine); } + public T realtimeScan(@NonNull String subCommand, @NonNull String sourcePath, String ignoredFilePath, java.util.function.Function resultParser) + throws IOException, InterruptedException, CxException { + this.logger.info("Executing 'scan {}' command using the CLI.", subCommand); + this.logger.info("Source: {} IgnoredFilePath: {}", sourcePath, ignoredFilePath); + List arguments = new ArrayList<>(); + arguments.add(CxConstants.CMD_SCAN); + arguments.add(subCommand); + arguments.add(CxConstants.SOURCE); + arguments.add(sourcePath); + if (StringUtils.isNotBlank(ignoredFilePath)) { + arguments.add(CxConstants.IGNORED_FILE_PATH); + arguments.add(ignoredFilePath); + } + return Execution.executeCommand(withConfigArguments(arguments), logger, resultParser); + } + + // OSS Realtime + public OssRealtimeResults ossRealtimeScan(@NonNull String sourcePath, String ignoredFilePath) + throws IOException, InterruptedException, CxException { + return realtimeScan(CxConstants.SUB_CMD_OSS_REALTIME, sourcePath, ignoredFilePath, OssRealtimeResults::fromLine); + } + + // IAC Realtime + public IacRealtimeResults iacRealtimeScan(@NonNull String sourcePath, String ignoredFilePath) + throws IOException, InterruptedException, CxException { + return realtimeScan(CxConstants.SUB_CMD_IAC_REALTIME, sourcePath, ignoredFilePath, IacRealtimeResults::fromLine); + } + + // Secrets Realtime + public SecretsRealtimeResults secretsRealtimeScan(@NonNull String sourcePath, String ignoredFilePath) + throws IOException, InterruptedException, CxException { + return realtimeScan(CxConstants.SUB_CMD_SECRETS_REALTIME, sourcePath, ignoredFilePath, SecretsRealtimeResults::fromLine); + } + + + + // Containers Realtime + public ContainersRealtimeResults containersRealtimeScan(@NonNull String sourcePath, String ignoredFilePath) + throws IOException, InterruptedException, CxException { + return realtimeScan(CxConstants.SUB_CMD_CONTAINERS_REALTIME, sourcePath, ignoredFilePath, ContainersRealtimeResults::fromLine); + } + public KicsRemediation kicsRemediate(@NonNull String resultsFile, String kicsFile, String engine,String similarityIds) throws IOException, InterruptedException, CxException { this.logger.info("Executing 'remediation kics' command using the CLI."); @@ -418,11 +465,11 @@ public KicsRemediation kicsRemediate(@NonNull String resultsFile, String kicsFil arguments.add(resultsFile); arguments.add(CxConstants.KICS_REMEDIATION_KICS_FILE); arguments.add(kicsFile); - if (engine.length() > 0) { + if (!engine.isEmpty()) { arguments.add(CxConstants.ENGINE); arguments.add(engine); } - if (similarityIds.length() > 0) { + if (!similarityIds.isEmpty()) { arguments.add(CxConstants.KICS_REMEDIATION_SIMILARITY); arguments.add(similarityIds); } @@ -455,6 +502,18 @@ public boolean ideScansEnabled() throws CxException, IOException, InterruptedExc .orElse(false); } + public boolean aiMcpServerEnabled() throws CxException, IOException, InterruptedException { + List tenantSettings = tenantSettings(); + if (tenantSettings == null) { + throw new CxException(1, "Unable to parse tenant settings"); + } + return tenantSettings.stream() + .filter(t -> t.getKey().equals(CxConstants.AI_MCP_SERVER_KEY)) + .findFirst() + .map(t -> Boolean.parseBoolean(t.getValue())) + .orElse(false); + } + public List tenantSettings() throws CxException, IOException, InterruptedException { List arguments = jsonArguments(); @@ -464,6 +523,17 @@ public List tenantSettings() throws CxException, IOException, Int return Execution.executeCommand(withConfigArguments(arguments), logger, TenantSetting::listFromLine); } + public MaskResult maskSecrets(@NonNull String filePath) throws CxException, IOException, InterruptedException { + List arguments = new ArrayList<>(); + + arguments.add(CxConstants.CMD_UTILS); + arguments.add(CxConstants.SUB_CMD_MASK); + arguments.add(CxConstants.RESULT_FILE); + arguments.add(filePath); + + return Execution.executeCommand(withConfigArguments(arguments), logger, MaskResult::fromLine); + } + private int getIndexOfBfLNode(List bflNodes, List resultNodes) { int bflNodeNotFound = -1; diff --git a/src/main/java/com/checkmarx/ast/wrapper/Execution.java b/src/main/java/com/checkmarx/ast/wrapper/Execution.java index 9d45cac3..c233ff2d 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/Execution.java +++ b/src/main/java/com/checkmarx/ast/wrapper/Execution.java @@ -57,7 +57,7 @@ static T executeCommand(List arguments, String line; StringBuilder output = new StringBuilder(); while ((line = br.readLine()) != null) { - logger.info(line); + logger.debug(line); output.append(line).append(LINE_SEPARATOR); T parsedLine = lineParser.apply(line); if (parsedLine != null) { @@ -98,7 +98,7 @@ static String executeCommand(List arguments, String line; StringBuilder stringBuilder = new StringBuilder(); while ((line = br.readLine()) != null) { - logger.info(line); + logger.debug(line); stringBuilder.append(line).append(LINE_SEPARATOR); } process.waitFor(); diff --git a/src/test/java/com/checkmarx/ast/ContainersRealtimeResultsTest.java b/src/test/java/com/checkmarx/ast/ContainersRealtimeResultsTest.java new file mode 100644 index 00000000..6911e4af --- /dev/null +++ b/src/test/java/com/checkmarx/ast/ContainersRealtimeResultsTest.java @@ -0,0 +1,241 @@ +package com.checkmarx.ast; + +import com.checkmarx.ast.containersrealtime.ContainersRealtimeImage; +import com.checkmarx.ast.containersrealtime.ContainersRealtimeResults; +import com.checkmarx.ast.containersrealtime.ContainersRealtimeVulnerability; +import com.checkmarx.ast.wrapper.CxException; +import org.junit.jupiter.api.*; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration and unit tests for Container Realtime scanner functionality. + * Tests the complete workflow: CLI invocation -> JSON parsing -> domain object mapping. + * Integration tests use Dockerfile as the scan target and are assumption-guarded for CI/local flexibility. + */ +class ContainersRealtimeResultsTest extends BaseTest { + + private boolean isCliConfigured() { + return Optional.ofNullable(getConfig().getPathToExecutable()).filter(s -> !s.isEmpty()).isPresent(); + } + + /* ------------------------------------------------------ */ + /* Integration tests for Container Realtime scanning */ + /* ------------------------------------------------------ */ + + /** + * Tests basic container realtime scan functionality on Dockerfile. + * Verifies that the scan returns a valid results object with detected container images. + * This test validates the end-to-end workflow from CLI execution to domain object creation. + */ + @Test + @DisplayName("Basic container scan on Dockerfile returns detected images") + void basicContainerRealtimeScan() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String dockerfilePath = "src/test/resources/Dockerfile"; + Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)), "Dockerfile not found - cannot test container scanning"); + + ContainersRealtimeResults results = wrapper.containersRealtimeScan(dockerfilePath, ""); + + assertNotNull(results, "Scan should return non-null results"); + assertNotNull(results.getImages(), "Images list should be initialized"); + + // Verify that if images are detected, they have proper structure + if (!results.getImages().isEmpty()) { + results.getImages().forEach(image -> { + assertNotNull(image.getImageName(), "Image name should be populated"); + assertNotNull(image.getVulnerabilities(), "Vulnerabilities list should be initialized"); + }); + } + } + + /** + * Tests container scan with ignore file functionality. + * Verifies that providing an ignore file doesn't break the scanning process + * and produces consistent or reduced results compared to baseline scan. + */ + @Test + @DisplayName("Container scan with ignore file works correctly") + void containerRealtimeScanWithIgnoreFile() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String dockerfilePath = "src/test/resources/Dockerfile"; + String ignoreFile = "src/test/resources/ignored-packages.json"; + Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)) && Files.exists(Paths.get(ignoreFile)), + "Required test resources missing - cannot test ignore functionality"); + + ContainersRealtimeResults baseline = wrapper.containersRealtimeScan(dockerfilePath, ""); + ContainersRealtimeResults filtered = wrapper.containersRealtimeScan(dockerfilePath, ignoreFile); + + assertNotNull(baseline, "Baseline scan should return results"); + assertNotNull(filtered, "Filtered scan should return results"); + + // Ignore file should not increase the number of detected issues + if (baseline.getImages() != null && filtered.getImages() != null) { + assertTrue(filtered.getImages().size() <= baseline.getImages().size(), + "Filtered scan should not have more images than baseline"); + } + } + + /** + * Tests scan consistency by running the same container scan multiple times. + * Verifies that repeated scans of the same Dockerfile produce stable, deterministic results. + * This is important for CI/CD pipelines where consistent results are crucial. + */ + @Test + @DisplayName("Repeated container scans produce consistent results") + void containerRealtimeScanConsistency() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String dockerfilePath = "src/test/resources/Dockerfile"; + Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)), "Dockerfile not found - cannot test consistency"); + + ContainersRealtimeResults firstScan = wrapper.containersRealtimeScan(dockerfilePath, ""); + ContainersRealtimeResults secondScan = wrapper.containersRealtimeScan(dockerfilePath, ""); + + assertNotNull(firstScan, "First scan should return results"); + assertNotNull(secondScan, "Second scan should return results"); + + // Compare image counts for consistency + int firstImageCount = (firstScan.getImages() != null) ? firstScan.getImages().size() : 0; + int secondImageCount = (secondScan.getImages() != null) ? secondScan.getImages().size() : 0; + + assertEquals(firstImageCount, secondImageCount, + "Image count should be consistent across multiple scans"); + } + + /** + * Tests domain object mapping for container scan results. + * Verifies that JSON responses are properly parsed into domain objects + * and all expected fields are correctly mapped and initialized. + */ + @Test + @DisplayName("Container domain objects are properly mapped from scan results") + void containerDomainObjectMapping() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String dockerfilePath = "src/test/resources/Dockerfile"; + Assumptions.assumeTrue(Files.exists(Paths.get(dockerfilePath)), "Dockerfile not found - cannot test mapping"); + + ContainersRealtimeResults results = wrapper.containersRealtimeScan(dockerfilePath, ""); + assertNotNull(results, "Scan results should not be null"); + + // If images are detected, validate their structure + if (results.getImages() != null && !results.getImages().isEmpty()) { + ContainersRealtimeImage sampleImage = results.getImages().get(0); + + // Verify core image fields are mapped correctly + assertNotNull(sampleImage.getImageName(), "Image name should always be present"); + assertNotNull(sampleImage.getVulnerabilities(), "Vulnerabilities list should be initialized"); + + // If vulnerabilities exist, validate their structure + if (!sampleImage.getVulnerabilities().isEmpty()) { + ContainersRealtimeVulnerability sampleVuln = sampleImage.getVulnerabilities().get(0); + // CVE and Severity are the core fields that should be present + assertTrue(sampleVuln.getCve() != null || sampleVuln.getSeverity() != null, + "Vulnerability should have at least CVE or Severity information"); + } + } + } + + /** + * Tests error handling when scanning a non-existent file. + * Verifies that the scanner properly throws a CxException with meaningful error message + * when provided with invalid file paths, demonstrating proper error handling. + */ + @Test + @DisplayName("Container scan throws appropriate exception for non-existent file") + void containerScanHandlesInvalidPath() { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + + // Test with a non-existent file path + String invalidPath = "src/test/resources/NonExistentDockerfile"; + + // The CLI should throw a CxException with a meaningful error message for invalid paths + CxException exception = assertThrows(CxException.class, () -> + wrapper.containersRealtimeScan(invalidPath, "") + ); + + // Verify the exception contains information about the invalid file path + String errorMessage = exception.getMessage(); + assertNotNull(errorMessage, "Exception should contain an error message"); + assertTrue(errorMessage.contains("invalid file path") || errorMessage.contains("file") || errorMessage.contains("path"), + "Exception message should indicate the issue is related to file path: " + errorMessage); + } + + /* ------------------------------------------------------ */ + /* Unit tests for JSON parsing robustness */ + /* ------------------------------------------------------ */ + + /** + * Tests JSON parsing with valid container scan response. + * Verifies that well-formed JSON is correctly parsed into domain objects. + */ + @Test + @DisplayName("Valid JSON parsing creates correct domain objects") + void testFromLineWithValidJson() { + String json = "{" + + "\"Images\": [" + + " {" + + " \"ImageName\": \"nginx:latest\"," + + " \"Vulnerabilities\": [" + + " {" + + " \"CVE\": \"CVE-2021-2345\"," + + " \"Severity\": \"High\"" + + " }" + + " ]" + + " }" + + "]" + + "}"; + ContainersRealtimeResults results = ContainersRealtimeResults.fromLine(json); + assertNotNull(results); + assertEquals(1, results.getImages().size()); + ContainersRealtimeImage image = results.getImages().get(0); + assertEquals("nginx:latest", image.getImageName()); + assertEquals(1, image.getVulnerabilities().size()); + ContainersRealtimeVulnerability vulnerability = image.getVulnerabilities().get(0); + assertEquals("CVE-2021-2345", vulnerability.getCve()); + assertEquals("High", vulnerability.getSeverity()); + } + + /** + * Tests parsing robustness with malformed JSON. + * Verifies that the parser gracefully handles various edge cases. + */ + @Test + @DisplayName("Malformed JSON is handled gracefully") + void testFromLineWithEdgeCases() { + // Missing Images key + assertNull(ContainersRealtimeResults.fromLine("{\"some_other_key\": \"some_value\"}")); + + // Invalid JSON structure + assertNull(ContainersRealtimeResults.fromLine("{\"Images\": [}")); + + // Blank/null inputs + assertNull(ContainersRealtimeResults.fromLine("")); + assertNull(ContainersRealtimeResults.fromLine(" ")); + assertNull(ContainersRealtimeResults.fromLine(null)); + } + + /** + * Tests parsing with empty or null image arrays. + * Verifies that empty results are handled correctly. + */ + @Test + @DisplayName("Empty and null image arrays are handled correctly") + void testFromLineWithEmptyResults() { + // Empty images array + String emptyJson = "{\"Images\": []}"; + ContainersRealtimeResults emptyResults = ContainersRealtimeResults.fromLine(emptyJson); + assertNotNull(emptyResults); + assertTrue(emptyResults.getImages().isEmpty()); + + // Null images + String nullJson = "{\"Images\": null}"; + ContainersRealtimeResults nullResults = ContainersRealtimeResults.fromLine(nullJson); + assertNotNull(nullResults); + assertNull(nullResults.getImages()); + } +} + diff --git a/src/test/java/com/checkmarx/ast/IacRealtimeResultsTest.java b/src/test/java/com/checkmarx/ast/IacRealtimeResultsTest.java new file mode 100644 index 00000000..ce7c8b7f --- /dev/null +++ b/src/test/java/com/checkmarx/ast/IacRealtimeResultsTest.java @@ -0,0 +1,60 @@ +package com.checkmarx.ast; + +import com.checkmarx.ast.iacrealtime.IacRealtimeResults; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +class IacRealtimeResultsTest { + + @Test + void testFromLineWithValidJsonArray() { + String json = "[" + + " {" + + " \"Title\": \"My Issue\"," + + " \"Severity\": \"High\"" + + " }" + + "]"; + IacRealtimeResults results = IacRealtimeResults.fromLine(json); + assertNotNull(results); + assertEquals(1, results.getResults().size()); + IacRealtimeResults.Issue issue = results.getResults().get(0); + assertEquals("My Issue", issue.getTitle()); + assertEquals("High", issue.getSeverity()); + } + + @Test + void testFromLineWithValidJsonObject() { + String json = "{" + + " \"Title\": \"My Single Issue\"," + + " \"Severity\": \"Medium\"" + + "}"; + IacRealtimeResults results = IacRealtimeResults.fromLine(json); + assertNotNull(results); + assertEquals(1, results.getResults().size()); + IacRealtimeResults.Issue issue = results.getResults().get(0); + assertEquals("My Single Issue", issue.getTitle()); + assertEquals("Medium", issue.getSeverity()); + } + + @Test + void testFromLineWithEmptyJsonArray() { + String json = "[]"; + IacRealtimeResults results = IacRealtimeResults.fromLine(json); + assertNotNull(results); + assertTrue(results.getResults().isEmpty()); + } + + @Test + void testFromLineWithBlankLine() { + assertNull(IacRealtimeResults.fromLine("")); + assertNull(IacRealtimeResults.fromLine(" ")); + assertNull(IacRealtimeResults.fromLine(null)); + } + + @Test + void testFromLineWithInvalidJson() { + String json = "[{]"; + assertNull(IacRealtimeResults.fromLine(json)); + } +} + diff --git a/src/test/java/com/checkmarx/ast/MaskTest.java b/src/test/java/com/checkmarx/ast/MaskTest.java new file mode 100644 index 00000000..ad188d21 --- /dev/null +++ b/src/test/java/com/checkmarx/ast/MaskTest.java @@ -0,0 +1,105 @@ +package com.checkmarx.ast; + +import com.checkmarx.ast.mask.MaskResult; +import com.checkmarx.ast.mask.MaskedSecret; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class MaskTest extends BaseTest { + + private static final String RESULTS_FILE = "target/test-classes/results.json"; + private static final String SECRETS_REALTIME_FILE = "target/test-classes/Secrets-realtime.json"; + + @Test + void testMaskSecretsWithFileContainingSecrets() throws Exception { + // Tests CLI execution with file containing actual secrets and validates masking behavior + MaskResult result = wrapper.maskSecrets(SECRETS_REALTIME_FILE); + + Assertions.assertNotNull(result); + Assertions.assertNotNull(result.getMaskedFile()); + Assertions.assertNotNull(result.getMaskedSecrets()); + Assertions.assertFalse(result.getMaskedSecrets().isEmpty()); + + MaskedSecret secret = result.getMaskedSecrets().get(0); + Assertions.assertNotNull(secret.getMasked()); + Assertions.assertNotNull(secret.getSecret()); + Assertions.assertEquals(5, secret.getLine()); + Assertions.assertTrue(secret.getMasked().contains("") || secret.getMasked().contains("\\u003cmasked\\u003e")); + Assertions.assertTrue(secret.getSecret().contains("-----BEGIN RSA PRIVATE KEY-----")); + Assertions.assertTrue(secret.getSecret().length() > secret.getMasked().length()); + } + + @Test + void testMaskSecretsWithFileContainingNoSecrets() throws Exception { + // Tests CLI execution with file containing no secrets + MaskResult result = wrapper.maskSecrets(RESULTS_FILE); + + Assertions.assertNotNull(result); + Assertions.assertNotNull(result.getMaskedFile()); + Assertions.assertFalse(result.getMaskedFile().isEmpty()); + } + + @Test + void testMaskSecretsErrorHandling() { + // Tests CLI error handling for invalid inputs + Assertions.assertThrows(Exception.class, () -> wrapper.maskSecrets(null)); + Assertions.assertThrows(Exception.class, () -> wrapper.maskSecrets("non-existent-file.json")); + Assertions.assertDoesNotThrow(() -> wrapper.maskSecrets(RESULTS_FILE)); + } + + @Test + void testMaskSecretsResponseParsing() throws Exception { + // Tests CLI response structure and JSON parsing functionality + MaskResult result = wrapper.maskSecrets(SECRETS_REALTIME_FILE); + + Assertions.assertNotNull(result); + Assertions.assertNotNull(result.getMaskedSecrets()); + Assertions.assertFalse(result.getMaskedSecrets().isEmpty()); + + MaskedSecret secret = result.getMaskedSecrets().get(0); + Assertions.assertNotNull(secret.getMasked()); + Assertions.assertNotNull(secret.getSecret()); + Assertions.assertTrue(secret.getLine() >= 0); + + Assertions.assertNull(MaskResult.fromLine("")); + Assertions.assertNull(MaskResult.fromLine("{invalid json}")); + Assertions.assertNull(MaskResult.fromLine(null)); + } + + @Test + void testMaskSecretsObjectBehavior() throws Exception { + // Tests object equality, serialization and consistency with CLI responses + MaskResult result1 = wrapper.maskSecrets(SECRETS_REALTIME_FILE); + MaskResult result2 = wrapper.maskSecrets(SECRETS_REALTIME_FILE); + + Assertions.assertEquals(result1.getMaskedFile(), result2.getMaskedFile()); + Assertions.assertNotNull(result1.toString()); + Assertions.assertTrue(result1.toString().contains("MaskResult")); + + if (result1.getMaskedSecrets() != null && !result1.getMaskedSecrets().isEmpty()) { + MaskedSecret secret1 = result1.getMaskedSecrets().get(0); + MaskedSecret secret2 = result2.getMaskedSecrets().get(0); + + Assertions.assertEquals(secret1.getMasked(), secret2.getMasked()); + Assertions.assertEquals(secret1.getSecret(), secret2.getSecret()); + Assertions.assertEquals(secret1.getLine(), secret2.getLine()); + Assertions.assertEquals(secret1.hashCode(), secret2.hashCode()); + Assertions.assertEquals(secret1, secret1); + Assertions.assertNotEquals(secret1, null); + + String toString = secret1.toString(); + Assertions.assertNotNull(toString); + Assertions.assertTrue(toString.contains("MaskedSecret")); + } + + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(result1); + MaskResult deserialized = mapper.readValue(json, MaskResult.class); + + Assertions.assertEquals(result1.getMaskedFile(), deserialized.getMaskedFile()); + if (result1.getMaskedSecrets() != null) { + Assertions.assertEquals(result1.getMaskedSecrets().size(), deserialized.getMaskedSecrets().size()); + } + } +} diff --git a/src/test/java/com/checkmarx/ast/OssRealtimeParsingTest.java b/src/test/java/com/checkmarx/ast/OssRealtimeParsingTest.java new file mode 100644 index 00000000..6ea0fea0 --- /dev/null +++ b/src/test/java/com/checkmarx/ast/OssRealtimeParsingTest.java @@ -0,0 +1,157 @@ +package com.checkmarx.ast; + +import com.checkmarx.ast.ossrealtime.OssRealtimeResults; +import com.checkmarx.ast.ossrealtime.OssRealtimeScanPackage; +import org.junit.jupiter.api.*; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration tests for OSS Realtime scanner functionality. + * Tests the complete workflow: CLI invocation -> JSON parsing -> domain object mapping. + * All tests use pom.xml as the scan target and are assumption-guarded for CI/local flexibility. + */ +class OssRealtimeParsingTest extends BaseTest { + + private boolean isCliConfigured() { + return Optional.ofNullable(getConfig().getPathToExecutable()).filter(s -> !s.isEmpty()).isPresent(); + } + + /** + * Tests basic OSS realtime scan functionality on pom.xml. + * Verifies that the scan returns a valid results object with detected Maven dependencies. + */ + @Test + @DisplayName("Basic OSS scan on pom.xml returns Maven dependencies") + void basicOssRealtimeScan() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + + OssRealtimeResults results = wrapper.ossRealtimeScan("pom.xml", ""); + + assertNotNull(results, "Scan should return non-null results"); + assertFalse(results.getPackages().isEmpty(), "Should detect Maven dependencies in pom.xml"); + + // Verify each package has required fields populated + results.getPackages().forEach(pkg -> { + assertNotNull(pkg.getPackageName(), "Package name should be populated"); + assertNotNull(pkg.getStatus(), "Package status should be populated"); + }); + } + + /** + * Tests OSS scan with ignore file functionality. + * Verifies that providing an ignore file reduces or maintains the package count compared to baseline scan. + */ + @Test + @DisplayName("OSS scan with ignore file filters packages correctly") + void ossRealtimeScanWithIgnoreFile() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String ignoreFile = "src/test/resources/ignored-packages.json"; + Assumptions.assumeTrue(Files.exists(Paths.get(ignoreFile)), "Ignore file not found - cannot test ignore functionality"); + + OssRealtimeResults baseline = wrapper.ossRealtimeScan("pom.xml", ""); + OssRealtimeResults filtered = wrapper.ossRealtimeScan("pom.xml", ignoreFile); + + assertNotNull(baseline, "Baseline scan should return results"); + assertNotNull(filtered, "Filtered scan should return results"); + assertTrue(filtered.getPackages().size() <= baseline.getPackages().size(), + "Filtered scan should have same or fewer packages than baseline"); + } + + /** + * Diagnostic test to see what package names are actually detected by the OSS scanner. + * This helps identify the correct package names for ignore file testing. + */ + @Test + @DisplayName("Display detected package names for diagnostic purposes") + void diagnosticPackageNames() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + + OssRealtimeResults results = wrapper.ossRealtimeScan("pom.xml", ""); + assertFalse(results.getPackages().isEmpty(), "Should have packages for diagnostic"); + + // Print package names for debugging (will show in test output) + System.out.println("Detected package names:"); + results.getPackages().forEach(pkg -> + System.out.println(" - " + pkg.getPackageName() + " (Manager: " + pkg.getPackageManager() + ")") + ); + + // This test always passes - it's just for information gathering + assertTrue(true, "Diagnostic test completed"); + } + + /** + * Tests that specific packages listed in ignore file are actually excluded from scan results. + * Uses a more flexible approach to find packages that can be ignored. + */ + @Test + @DisplayName("Ignore file excludes detected packages correctly") + void ignoreFileExcludesPackages() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String ignoreFile = "src/test/resources/ignored-packages.json"; + Assumptions.assumeTrue(Files.exists(Paths.get(ignoreFile)), "Ignore file not found - cannot test ignore functionality"); + + OssRealtimeResults baseline = wrapper.ossRealtimeScan("pom.xml", ""); + OssRealtimeResults filtered = wrapper.ossRealtimeScan("pom.xml", ignoreFile); + + // Look for common Maven packages that might be detected + String[] commonPackageNames = {"jackson-databind", "commons-lang3", "json-simple", "slf4j-simple", "junit-jupiter"}; + + boolean foundIgnoredPackage = false; + for (String packageName : commonPackageNames) { + boolean inBaseline = baseline.getPackages().stream() + .anyMatch(pkg -> packageName.equalsIgnoreCase(pkg.getPackageName())); + boolean inFiltered = filtered.getPackages().stream() + .anyMatch(pkg -> packageName.equalsIgnoreCase(pkg.getPackageName())); + + if (inBaseline && !inFiltered) { + foundIgnoredPackage = true; + System.out.println("Successfully filtered out package: " + packageName); + break; + } + } + assertTrue(filtered.getPackages().size() <= baseline.getPackages().size(), + "Filtered scan should not have more packages than baseline"); + } + + /** + * Tests scan consistency by running the same scan multiple times. + * Verifies that repeated scans of the same source produce stable, deterministic results. + */ + @Test + @DisplayName("Repeated OSS scans produce consistent results") + void ossRealtimeScanConsistency() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + + OssRealtimeResults firstScan = wrapper.ossRealtimeScan("pom.xml", ""); + OssRealtimeResults secondScan = wrapper.ossRealtimeScan("pom.xml", ""); + + assertEquals(firstScan.getPackages().size(), secondScan.getPackages().size(), + "Package count should be consistent across multiple scans"); + } + + /** + * Tests domain object mapping by verifying all expected package fields are properly populated. + * Ensures the JSON to POJO conversion works correctly for all package attributes. + */ + @Test + @DisplayName("Package domain objects are properly mapped from scan results") + void packageDomainObjectMapping() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + + OssRealtimeResults results = wrapper.ossRealtimeScan("pom.xml", ""); + assertFalse(results.getPackages().isEmpty(), "Should have packages to validate mapping"); + + OssRealtimeScanPackage samplePackage = results.getPackages().get(0); + + // Verify core package fields are mapped (some may be null based on scan results) + assertNotNull(samplePackage.getPackageName(), "Package name should always be present"); + assertNotNull(samplePackage.getStatus(), "Package status should always be present"); + assertNotNull(samplePackage.getLocations(), "Locations list should be initialized (may be empty)"); + assertNotNull(samplePackage.getVulnerabilities(), "Vulnerabilities list should be initialized (may be empty)"); + } +} diff --git a/src/test/java/com/checkmarx/ast/ScanTest.java b/src/test/java/com/checkmarx/ast/ScanTest.java index fb0b3b3b..5e31b337 100644 --- a/src/test/java/com/checkmarx/ast/ScanTest.java +++ b/src/test/java/com/checkmarx/ast/ScanTest.java @@ -3,7 +3,9 @@ import com.checkmarx.ast.asca.ScanDetail; import com.checkmarx.ast.asca.ScanResult; import com.checkmarx.ast.kicsRealtimeResults.KicsRealtimeResults; +import com.checkmarx.ast.ossrealtime.OssRealtimeResults; import com.checkmarx.ast.scan.Scan; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -92,4 +94,17 @@ void testKicsRealtimeScan() throws Exception { Assertions.assertTrue(scan.getResults().size() >= 1); } + @Test + void testOssRealtimeScanWithIgnoredFile() throws Exception { + Assumptions.assumeTrue(getConfig().getPathToExecutable() != null && !getConfig().getPathToExecutable().isEmpty(), "PATH_TO_EXECUTABLE not set"); + + String source = "pom.xml"; + String ignoreFile = "src/test/resources/ignored-packages.json"; + + OssRealtimeResults results = wrapper.ossRealtimeScan(source, ignoreFile); + + Assertions.assertNotNull(results); + Assertions.assertNotNull(results.getPackages()); + } + } diff --git a/src/test/java/com/checkmarx/ast/SecretsRealtimeResultsTest.java b/src/test/java/com/checkmarx/ast/SecretsRealtimeResultsTest.java new file mode 100644 index 00000000..a662d0f1 --- /dev/null +++ b/src/test/java/com/checkmarx/ast/SecretsRealtimeResultsTest.java @@ -0,0 +1,293 @@ +package com.checkmarx.ast; + +import com.checkmarx.ast.realtime.RealtimeLocation; +import com.checkmarx.ast.secretsrealtime.SecretsRealtimeResults; +import com.checkmarx.ast.wrapper.CxException; +import org.junit.jupiter.api.*; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Integration and unit tests for Secrets Realtime scanner functionality. + * Tests the complete workflow: CLI invocation -> JSON parsing -> domain object mapping. + * Integration tests use python-vul-file.py as the scan target and are assumption-guarded for CI/local flexibility. + */ +class SecretsRealtimeResultsTest extends BaseTest { + + private boolean isCliConfigured() { + return Optional.ofNullable(getConfig().getPathToExecutable()).filter(s -> !s.isEmpty()).isPresent(); + } + + /* ------------------------------------------------------ */ + /* Integration tests for Secrets Realtime scanning */ + /* ------------------------------------------------------ */ + + /** + * Tests basic secrets realtime scan functionality on a vulnerable Python file. + * Verifies that the scan returns a valid results object and can detect hardcoded secrets + * such as passwords and credentials embedded in the source code. + */ + @Test + @DisplayName("Basic secrets scan on python file returns detected secrets") + void basicSecretsRealtimeScan() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String pythonFile = "src/test/resources/python-vul-file.py"; + Assumptions.assumeTrue(Files.exists(Paths.get(pythonFile)), "Python vulnerable file not found - cannot test secrets scanning"); + + SecretsRealtimeResults results = wrapper.secretsRealtimeScan(pythonFile, ""); + + assertNotNull(results, "Scan should return non-null results"); + assertNotNull(results.getSecrets(), "Secrets list should be initialized"); + + // The python file contains hardcoded credentials, so we expect some secrets to be found + if (!results.getSecrets().isEmpty()) { + results.getSecrets().forEach(secret -> { + assertNotNull(secret.getTitle(), "Secret title should be populated"); + assertNotNull(secret.getFilePath(), "Secret file path should be populated"); + assertNotNull(secret.getLocations(), "Secret locations should be initialized"); + }); + } + } + + /** + * Tests secrets scan with ignore file functionality. + * Verifies that providing an ignore file doesn't break the scanning process + * and produces consistent or reduced results compared to baseline scan. + */ + @Test + @DisplayName("Secrets scan with ignore file works correctly") + void secretsRealtimeScanWithIgnoreFile() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String pythonFile = "src/test/resources/python-vul-file.py"; + String ignoreFile = "src/test/resources/ignored-packages.json"; + Assumptions.assumeTrue(Files.exists(Paths.get(pythonFile)) && Files.exists(Paths.get(ignoreFile)), + "Required test resources missing - cannot test ignore functionality"); + + SecretsRealtimeResults baseline = wrapper.secretsRealtimeScan(pythonFile, ""); + SecretsRealtimeResults filtered = wrapper.secretsRealtimeScan(pythonFile, ignoreFile); + + assertNotNull(baseline, "Baseline scan should return results"); + assertNotNull(filtered, "Filtered scan should return results"); + + // Ignore file should not increase the number of detected secrets + assertTrue(filtered.getSecrets().size() <= baseline.getSecrets().size(), + "Filtered scan should not have more secrets than baseline"); + } + + /** + * Tests scan consistency by running the same secrets scan multiple times. + * Verifies that repeated scans of the same file produce stable, deterministic results. + * This is crucial for ensuring reliable CI/CD pipeline integration. + */ + @Test + @DisplayName("Repeated secrets scans produce consistent results") + void secretsRealtimeScanConsistency() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String pythonFile = "src/test/resources/python-vul-file.py"; + Assumptions.assumeTrue(Files.exists(Paths.get(pythonFile)), "Python file not found - cannot test consistency"); + + SecretsRealtimeResults firstScan = wrapper.secretsRealtimeScan(pythonFile, ""); + SecretsRealtimeResults secondScan = wrapper.secretsRealtimeScan(pythonFile, ""); + + assertNotNull(firstScan, "First scan should return results"); + assertNotNull(secondScan, "Second scan should return results"); + + // Compare secret counts for consistency + assertEquals(firstScan.getSecrets().size(), secondScan.getSecrets().size(), + "Secret count should be consistent across multiple scans"); + } + + /** + * Tests domain object mapping for secrets scan results. + * Verifies that JSON responses are properly parsed into domain objects + * and all expected fields (title, description, severity, locations) are correctly mapped. + */ + @Test + @DisplayName("Secret domain objects are properly mapped from scan results") + void secretDomainObjectMapping() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String pythonFile = "src/test/resources/python-vul-file.py"; + Assumptions.assumeTrue(Files.exists(Paths.get(pythonFile)), "Python file not found - cannot test mapping"); + + SecretsRealtimeResults results = wrapper.secretsRealtimeScan(pythonFile, ""); + assertNotNull(results, "Scan results should not be null"); + + // If secrets are detected, validate their structure + if (!results.getSecrets().isEmpty()) { + SecretsRealtimeResults.Secret sampleSecret = results.getSecrets().get(0); + + // Verify core secret fields are mapped correctly + assertNotNull(sampleSecret.getTitle(), "Secret title should always be present"); + assertNotNull(sampleSecret.getFilePath(), "Secret file path should always be present"); + assertNotNull(sampleSecret.getLocations(), "Locations list should be initialized"); + + // Verify locations have proper structure if they exist + if (!sampleSecret.getLocations().isEmpty()) { + RealtimeLocation sampleLocation = sampleSecret.getLocations().get(0); + assertTrue(sampleLocation.getLine() > 0, "Line number should be positive"); + } + } + } + + /** + * Tests secrets scanning on a clean file that should not contain secrets. + * Verifies that the scanner correctly identifies files without secrets + * and returns empty results without errors. + */ + @Test + @DisplayName("Secrets scan on clean file returns empty results") + void secretsScanOnCleanFile() throws Exception { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + String cleanFile = "src/test/resources/csharp-no-vul.cs"; + Assumptions.assumeTrue(Files.exists(Paths.get(cleanFile)), "Clean C# file not found - cannot test clean scan"); + + SecretsRealtimeResults results = wrapper.secretsRealtimeScan(cleanFile, ""); + assertNotNull(results, "Scan results should not be null even for clean files"); + + // Clean file should have no secrets or very few false positives + assertTrue(results.getSecrets().size() <= 2, + "Clean file should have no or minimal secrets detected"); + } + + /** + * Tests error handling when scanning a non-existent file. + * Verifies that the scanner properly throws a CxException with meaningful error message + * when provided with invalid file paths, demonstrating proper error handling. + */ + @Test + @DisplayName("Secrets scan throws appropriate exception for non-existent file") + void secretsScanHandlesInvalidPath() { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + + // Test with a non-existent file path + String invalidPath = "src/test/resources/NonExistentFile.py"; + + // The CLI should throw a CxException with a meaningful error message for invalid paths + CxException exception = assertThrows(CxException.class, () -> + wrapper.secretsRealtimeScan(invalidPath, "") + ); + + // Verify the exception contains information about the invalid file path + String errorMessage = exception.getMessage(); + assertNotNull(errorMessage, "Exception should contain an error message"); + assertTrue(errorMessage.contains("invalid file path") || errorMessage.contains("file") || errorMessage.contains("path"), + "Exception message should indicate the issue is related to file path: " + errorMessage); + } + + /** + * Tests secrets scanning across multiple file types. + * Verifies that the scanner can handle different file extensions and formats + * without crashing and produces appropriate results for each file type. + */ + @Test + @DisplayName("Secrets scan handles multiple file types correctly") + void secretsScanMultipleFileTypes() { + Assumptions.assumeTrue(isCliConfigured(), "PATH_TO_EXECUTABLE not configured - skipping integration test"); + + String[] testFiles = { + "src/test/resources/python-vul-file.py", + "src/test/resources/csharp-file.cs", + "src/test/resources/Dockerfile" + }; + + for (String filePath : testFiles) { + if (Files.exists(Paths.get(filePath))) { + assertDoesNotThrow(() -> { + SecretsRealtimeResults results = wrapper.secretsRealtimeScan(filePath, ""); + assertNotNull(results, "Results should not be null for file: " + filePath); + }, "Scanner should handle file type gracefully: " + filePath); + } + } + } + + + /* ------------------------------------------------------ */ + /* Unit tests for JSON parsing robustness */ + /* ------------------------------------------------------ */ + + /** + * Tests JSON parsing with valid secrets scan response containing array format. + * Verifies that well-formed JSON arrays are correctly parsed into domain objects. + */ + @Test + @DisplayName("Valid JSON array parsing creates correct domain objects") + void testFromLineWithJsonArray() { + String json = "[" + + "{" + + "\"Title\":\"Hardcoded AWS Access Key\"," + + "\"Description\":\"An AWS access key is hardcoded in the source code. This is a security risk.\"," + + "\"SecretValue\":\"AKIAIOSFODNN7EXAMPLE\"," + + "\"FilePath\":\"/path/to/file.py\"," + + "\"Severity\":\"HIGH\"," + + "\"Locations\":[{\"StartLine\":10,\"StartColumn\":5,\"EndLine\":10,\"EndColumn\":25}]" + + "}" + + "]"; + SecretsRealtimeResults results = SecretsRealtimeResults.fromLine(json); + assertNotNull(results); + assertEquals(1, results.getSecrets().size()); + SecretsRealtimeResults.Secret secret = results.getSecrets().get(0); + assertEquals("Hardcoded AWS Access Key", secret.getTitle()); + assertEquals("An AWS access key is hardcoded in the source code. This is a security risk.", secret.getDescription()); + assertEquals("AKIAIOSFODNN7EXAMPLE", secret.getSecretValue()); + assertEquals("/path/to/file.py", secret.getFilePath()); + assertEquals("HIGH", secret.getSeverity()); + assertEquals(1, secret.getLocations().size()); + } + + /** + * Tests JSON parsing with valid secrets scan response containing single object format. + * Verifies that single JSON objects are correctly parsed into domain objects. + */ + @Test + @DisplayName("Valid JSON object parsing creates correct domain objects") + void testFromLineWithJsonObject() { + String json = "{" + + "\"Title\":\"Hardcoded AWS Access Key\"," + + "\"Description\":\"An AWS access key is hardcoded in the source code. This is a security risk.\"," + + "\"SecretValue\":\"AKIAIOSFODNN7EXAMPLE\"," + + "\"FilePath\":\"/path/to/file.py\"," + + "\"Severity\":\"HIGH\"," + + "\"Locations\":[{\"StartLine\":10,\"StartColumn\":5,\"EndLine\":10,\"EndColumn\":25}]" + + "}"; + SecretsRealtimeResults results = SecretsRealtimeResults.fromLine(json); + assertNotNull(results); + assertEquals(1, results.getSecrets().size()); + SecretsRealtimeResults.Secret secret = results.getSecrets().get(0); + assertEquals("Hardcoded AWS Access Key", secret.getTitle()); + } + + /** + * Tests parsing robustness with malformed JSON and edge cases. + * Verifies that the parser gracefully handles various invalid input scenarios. + */ + @Test + @DisplayName("Malformed JSON and edge cases are handled gracefully") + void testFromLineWithEdgeCases() { + // Blank/null inputs + assertNull(SecretsRealtimeResults.fromLine("")); + assertNull(SecretsRealtimeResults.fromLine(" ")); + assertNull(SecretsRealtimeResults.fromLine(null)); + + // Invalid JSON structures + assertNull(SecretsRealtimeResults.fromLine("{")); + assertNull(SecretsRealtimeResults.fromLine("not a json")); + } + + /** + * Tests parsing with empty results. + * Verifies that empty JSON arrays are handled correctly and produce valid empty results. + */ + @Test + @DisplayName("Empty JSON arrays are handled correctly") + void testFromLineWithEmptyResults() { + String emptyJson = "[]"; + SecretsRealtimeResults results = SecretsRealtimeResults.fromLine(emptyJson); + assertNotNull(results); + assertTrue(results.getSecrets().isEmpty()); + } +} + diff --git a/src/test/java/com/checkmarx/ast/TenantTest.java b/src/test/java/com/checkmarx/ast/TenantTest.java index b9ac752c..7f49da16 100644 --- a/src/test/java/com/checkmarx/ast/TenantTest.java +++ b/src/test/java/com/checkmarx/ast/TenantTest.java @@ -11,11 +11,17 @@ public class TenantTest extends BaseTest { @Test void testTenantSettings() throws Exception { List tenantSettings = wrapper.tenantSettings(); - Assertions.assertTrue(tenantSettings.size() > 0); + Assertions.assertFalse(tenantSettings.isEmpty()); } @Test void testIdeScansEnabled() { Assertions.assertDoesNotThrow(() -> wrapper.ideScansEnabled()); } + + @Test + void testAiMcpServerEnabled() throws Exception { + boolean enabled = Assertions.assertDoesNotThrow(() -> wrapper.aiMcpServerEnabled()); + Assertions.assertTrue(enabled, "AI MCP Server flag expected to be true"); + } } diff --git a/src/test/resources/Secrets-realtime.json b/src/test/resources/Secrets-realtime.json new file mode 100644 index 00000000..5acdd938 --- /dev/null +++ b/src/test/resources/Secrets-realtime.json @@ -0,0 +1,147 @@ +[ + { + "Title": "private-key", + "Description": "Identified a Private Key, which may compromise cryptographic security and sensitive data encryption.", + "SecretValue": "-----BEGIN RSA PRIVATE KEY-----MIIEowIBAAKCAQEAl5X22d9tXl2Bz1b+3mWsAouoBiQhrDS3GxFAdpJFkKF6Wst+Vl1mfshTd+gF2kHXTzLMdxsUM2AS8laG2xeIhLe07FhhOtQGSoAOjHD++K53MBcOD/mDVOlPhNOWAc3qWfa3R7ohxUJq8lvy1OErw7qlQv9U+xABUJtHbJtMn/dDBs1Fy6MUgO6TOtEwzQaTaGpWh3NmGaUu3KQuaekHZnzlYd8mc1XkztXrph0XxZPq43Gg8RVh25fCrcA+7iAkqa4MNL/5gsatzzjP7KRdx1IP0pnM6F/cwoMClmuV7FFhKoP3KFwt8SXglZnqLrCNK6DzrYU5bRaszIxYc8egywIDAQABAoIBADGaq2rkiF+m7cGx0Dlqv/0dQmCwFizKG1lKLfQfLZCEpwtrJ+6PJek7GMVWMgQYI6MRFoOrYtLlD44p7ntnmg8EJrpouXiMxXo/qYMfvvAV937PLJThq65vosvuiVoRziyeZZ+dM0vfzit9F1u+S5oDS+0+rMpzlFqSVa8eqtZ1i4wmVwKXF35FqHyzhXgKmVNXUy/JCiftJYF2hpe2zXAuGQKTD5x3v52GX+cUsRPPAO5GhJbDnwhbL8i06gLDc7xwfaTJbebvOKHT1F2AW/RhoW+BpJke86beea8DM7KR8RYjobgdf66fadosa27u8qqpcdtyzYMqLc3GuTeYL/ECgYEA36KgaxlWNCuQhLOLPZ/2fzebD1SCI7HsPtbiM+mSbWTGBnSPX36b1XLJkynZrAFKYoe9zx9ksqlrBHWG7PC3FLeIO9ExV9BsGFGjzDpkENteArcO5eLfSMkfXIXNlWwhb8m8DPjdnK2pihs3vVN0OMIPeYu9mAXst0CcR8vQuJ0CgYEArYYBgmAyWfRs8exU2F7vQoG8B4mykTOQ9J91Js72WiHaqt+z4NdGJrFr5VkXCZdbBf3J/PfEPVFT3ud/dAm2PZ40TfNQJTa4pwBhuyCozZff1Qm+X5NdzvHkLePFex6wxUgupVwr/W9UOVU7gRFB/hziSLnlglfpgwxA9j9tfocCgYEAqH69YzQp0RDpyDIGvR2i+WMJ/1jq3L4Xg5kfwYFAhA+jbAWyaH7aJs5ftfOYP5KRWv9vMXkzw7EGIsvyJt+O8Zr+mCMbjFBKwV/xi9SKxHCjumP2Y5q2JP70FB/0L5rS7okOmK+BOaVW0emD66/PJ1x/kFKLPNlp6wBRP37++bkCgYAgrkdkfaeeB4npOmB0a9TWCscWCFoIPNUFLW8MAxikuxGK8xzWsNS2ft3aUSAkn0v2YekD6sob3lBUf/ciLJ4VFtG1CKlEiPzX/xto+eqw5fSzE+W17HRTgH1AI1DTMmGKlmCqpiRm0+vh7GqLkWuDZ386wUA3f0UseEdX2XROywKBgE1Xer2yEZtLlrubHgVAKVrz2u5ZCEPzDkLEDhOxX2h1dP19TWvm2Sr6Fm1QL8lez52YhCW/xJHsr8S4Kka3Ntbm9+ZfhhATTICTpqIicqeAq/Iiw++7UgCk1gZPW1hnlDHYRdmI4Dr6j5aUBFLl4Bj2nedH+1L1Eo8EXfnjm0pi-----END RSA PRIVATE KEY-----", + "FilePath": "C:\\Users\\XYZ\\GitHub_Repo\\JavaVulnerableLab\\CxAppMonDeploy.pem", + "Severity": "High", + "Locations": [ + { + "Line": 0, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 1, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 2, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 3, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 4, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 5, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 6, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 7, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 8, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 9, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 10, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 11, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 12, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 13, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 14, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 15, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 16, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 17, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 18, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 19, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 20, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 21, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 22, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 23, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 24, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 25, + "StartIndex": 1, + "EndIndex": 30 + }, + { + "Line": 26, + "StartIndex": 1, + "EndIndex": 30 + } + ] + } +] + diff --git a/src/test/resources/ignored-packages.json b/src/test/resources/ignored-packages.json new file mode 100644 index 00000000..5ec153d5 --- /dev/null +++ b/src/test/resources/ignored-packages.json @@ -0,0 +1,5 @@ +[ + {"name": "jackson-databind", "version": "*"}, + {"name": "commons-lang3", "version": "*"}, + {"name": "json-simple", "version": "*"} +]