diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java index dd8c6e6..79f171c 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxConstants.java @@ -82,4 +82,13 @@ public final class CxConstants { static final String SUB_CMD_CONTAINERS_REALTIME = "containers-realtime"; static final String SUB_CMD_MASK = "mask"; static final String RESULT_FILE = "--result-file"; + static final String CMD_TELEMETRY = "telemetry"; + static final String SUB_CMD_TELEMETRY_AI = "ai"; + static final String AI_PROVIDER = "--ai-provider"; + static final String TYPE = "--type"; + static final String SUB_TYPE = "--sub-type"; + static final String PROBLEM_SEVERITY = "--problem-severity"; + static final String SCAN_TYPE_FLAG = "--scan-type"; + static final String STATUS = "--status"; + static final String TOTAL_COUNT = "--total-count"; } diff --git a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java index c49aa87..386133a 100644 --- a/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java +++ b/src/main/java/com/checkmarx/ast/wrapper/CxWrapper.java @@ -409,7 +409,7 @@ 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) + public T realtimeScan(@NonNull String subCommand, @NonNull String sourcePath, String containerTool, 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); @@ -418,6 +418,10 @@ public T realtimeScan(@NonNull String subCommand, @NonNull String sourcePath arguments.add(subCommand); arguments.add(CxConstants.SOURCE); arguments.add(sourcePath); + if(StringUtils.isNotBlank(containerTool)){ + arguments.add(CxConstants.ENGINE); + arguments.add(containerTool); + } if (StringUtils.isNotBlank(ignoredFilePath)) { arguments.add(CxConstants.IGNORED_FILE_PATH); arguments.add(ignoredFilePath); @@ -428,27 +432,26 @@ public T realtimeScan(@NonNull String subCommand, @NonNull String sourcePath // 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); + return realtimeScan(CxConstants.SUB_CMD_OSS_REALTIME, sourcePath,"", ignoredFilePath, OssRealtimeResults::fromLine); } // IAC Realtime - public IacRealtimeResults iacRealtimeScan(@NonNull String sourcePath, String ignoredFilePath) + public IacRealtimeResults iacRealtimeScan(@NonNull String sourcePath,String containerTool, String ignoredFilePath) throws IOException, InterruptedException, CxException { - return realtimeScan(CxConstants.SUB_CMD_IAC_REALTIME, sourcePath, ignoredFilePath, IacRealtimeResults::fromLine); + return realtimeScan(CxConstants.SUB_CMD_IAC_REALTIME, sourcePath,containerTool, 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); + 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); + return realtimeScan(CxConstants.SUB_CMD_CONTAINERS_REALTIME, sourcePath, "",ignoredFilePath, ContainersRealtimeResults::fromLine); } public KicsRemediation kicsRemediate(@NonNull String resultsFile, String kicsFile, String engine,String similarityIds) @@ -534,6 +537,54 @@ public MaskResult maskSecrets(@NonNull String filePath) throws CxException, IOEx return Execution.executeCommand(withConfigArguments(arguments), logger, MaskResult::fromLine); } + /** + * Executes telemetry AI command to collect telemetry data for user interactions related to AI features. + * + * @param aiProvider AI provider name (e.g., "Copilot") + * @param agent Agent name (e.g., "Jetbrains") + * @param eventType Event type (e.g., "click") + * @param subType Event subtype (e.g., "ast-results.viewPackageDetails") + * @param engine Engine type (e.g., "secrets") + * @param problemSeverity Severity level (e.g., "high") + * @param scanType Type of scan + * @param status Status information + * @param totalCount Number count + * @return Command output as string + * @throws IOException if I/O error occurs + * @throws InterruptedException if command execution is interrupted + * @throws CxException if CLI command fails + */ + public String telemetryAIEvent(String aiProvider, String agent, String eventType, String subType, + String engine, String problemSeverity, String scanType, String status, + Integer totalCount) throws IOException, InterruptedException, CxException { + this.logger.info("Executing telemetry AI event with provider: {}, type: {}, subType: {}", + aiProvider, eventType, subType); + + List arguments = new ArrayList<>(); + arguments.add(CxConstants.CMD_TELEMETRY); + arguments.add(CxConstants.SUB_CMD_TELEMETRY_AI); + arguments.add(CxConstants.AI_PROVIDER); + arguments.add(aiProvider); + arguments.add(CxConstants.AGENT); + arguments.add(agent); + arguments.add(CxConstants.TYPE); + arguments.add(eventType); + arguments.add(CxConstants.SUB_TYPE); + arguments.add(subType); + arguments.add(CxConstants.ENGINE); + arguments.add(engine); + arguments.add(CxConstants.PROBLEM_SEVERITY); + arguments.add(problemSeverity); + arguments.add(CxConstants.SCAN_TYPE_FLAG); + arguments.add(scanType); + arguments.add(CxConstants.STATUS); + arguments.add(status); + arguments.add(CxConstants.TOTAL_COUNT); + arguments.add(totalCount.toString()); + + return Execution.executeCommand(withConfigArguments(arguments), logger, line -> line); + } + private int getIndexOfBfLNode(List bflNodes, List resultNodes) { int bflNodeNotFound = -1; diff --git a/src/test/java/com/checkmarx/ast/TelemetryTest.java b/src/test/java/com/checkmarx/ast/TelemetryTest.java new file mode 100644 index 0000000..a014aa6 --- /dev/null +++ b/src/test/java/com/checkmarx/ast/TelemetryTest.java @@ -0,0 +1,67 @@ +package com.checkmarx.ast; + +import com.checkmarx.ast.wrapper.CxException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +/** + * Telemetry AI event test cases covering various parameter scenarios. + */ +class TelemetryTest extends BaseTest { + + @Test + void testTelemetryAIEventSuccessfulCaseWithMinimalParametersAiLog() throws CxException, IOException, InterruptedException { + // Test case: AI logging with specific parameters and some empty values + Assertions.assertDoesNotThrow(() -> { + String result = wrapper.telemetryAIEvent( + "Cursor", // aiProvider + "Cursos", // agent + "click", // eventType + "ast-results.viewPackageDetails", // subType + "secrets", // engine + "high", // problemSeverity + "", // scanType (empty) + "", // status (empty) + 0 // totalCount + ); + }, "Telemetry AI event should execute successfully"); + } + + @Test + void testTelemetryAIEventSuccessfulCaseWithMinimalParametersDetectionLog() throws CxException, IOException, InterruptedException { + // Test case: Detection logging with most parameters empty and specific scan data + Assertions.assertDoesNotThrow(() -> { + String result = wrapper.telemetryAIEvent( + "", // aiProvider (empty) + "", // agent (empty) + "", // eventType (empty) + "", // subType (empty) + "", // engine (empty) + "", // problemSeverity (empty) + "asca", // scanType + "Critical", // status + 10 // totalCount + ); + }, "Telemetry AI event should execute successfully for detection log"); + } + + @Test + void testTelemetryAIEventSuccessfulCaseWithEdgeCaseParameters() throws CxException, IOException, InterruptedException { + // Test case: Edge case with minimal required parameters + Assertions.assertDoesNotThrow(() -> { + String result = wrapper.telemetryAIEvent( + "test-provider", // aiProvider (minimal value) + "java-wrapper", // agent (minimal value) + "", // eventType (empty) + "", // subType (empty) + "", // engine (empty) + "", // problemSeverity (empty) + "", // scanType (empty) + "", // status (empty) + 0 // totalCount + ); + }, "Telemetry AI event should execute successfully for edge case"); + } +}