diff --git a/src/main/java/com/checkmarx/intellij/Resource.java b/src/main/java/com/checkmarx/intellij/Resource.java index 05abca52..f91156ec 100644 --- a/src/main/java/com/checkmarx/intellij/Resource.java +++ b/src/main/java/com/checkmarx/intellij/Resource.java @@ -12,6 +12,7 @@ public enum Resource { ASCA_CHECKBOX, ASCA_DESCRIPTION, ASCA_SCAN_WARNING, + ASCA_STARTED_MSG, VALIDATE_BUTTON, VALIDATE_IN_PROGRESS, VALIDATE_SUCCESS, diff --git a/src/main/java/com/checkmarx/intellij/inspections/AscaInspection.java b/src/main/java/com/checkmarx/intellij/inspections/AscaInspection.java index ac0b9309..9321e744 100644 --- a/src/main/java/com/checkmarx/intellij/inspections/AscaInspection.java +++ b/src/main/java/com/checkmarx/intellij/inspections/AscaInspection.java @@ -9,7 +9,6 @@ import com.intellij.codeInspection.*; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.Strings; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; @@ -24,6 +23,8 @@ public class AscaInspection extends LocalInspectionTool { private final GlobalSettingsState settings = GlobalSettingsState.getInstance(); private Map severityToHighlightMap; + public static String ASCA_INSPECTION_ID = "ASCA"; + /** * Checks the file for ASCA issues. @@ -94,13 +95,32 @@ private ProblemDescriptor[] createProblemDescriptors(@NotNull PsiFile file, @Not */ private ProblemDescriptor createProblemDescriptor(@NotNull PsiFile file, @NotNull InspectionManager manager, ScanDetail detail, Document document, int lineNumber, boolean isOnTheFly) { TextRange problemRange = getTextRangeForLine(document, lineNumber); - String description = Strings.join(detail.getRuleName(), " - ", detail.getRemediationAdvise()); + String description = formatDescription(detail.getRuleName(), detail.getRemediationAdvise()); ProblemHighlightType highlightType = determineHighlightType(detail); return manager.createProblemDescriptor( file, problemRange, description, highlightType, isOnTheFly, new AscaQuickFix(detail)); } + public String formatDescription(String ruleName, String remediationAdvise) { + return String.format( + "%s - %s
%s", + escapeHtml(ruleName), escapeHtml(remediationAdvise), escapeHtml(ASCA_INSPECTION_ID) + ); + } + + // Helper method to escape HTML special characters for safety + private String escapeHtml(String text) { + if (text == null) { + return ""; + } + return text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'"); + } + /** * Gets the text range for a specific line in the document. * diff --git a/src/main/java/com/checkmarx/intellij/inspections/quickfixes/AscaQuickFix.java b/src/main/java/com/checkmarx/intellij/inspections/quickfixes/AscaQuickFix.java index ad82c362..bc1ddb60 100644 --- a/src/main/java/com/checkmarx/intellij/inspections/quickfixes/AscaQuickFix.java +++ b/src/main/java/com/checkmarx/intellij/inspections/quickfixes/AscaQuickFix.java @@ -1,5 +1,6 @@ package com.checkmarx.intellij.inspections.quickfixes; +import com.checkmarx.intellij.Constants; import com.intellij.notification.Notification; import com.intellij.notification.NotificationGroupManager; import com.intellij.notification.NotificationType; @@ -14,6 +15,8 @@ import java.awt.*; import java.awt.datatransfer.StringSelection; +import static com.checkmarx.intellij.inspections.AscaInspection.ASCA_INSPECTION_ID; + /** * Quick fix implementation for ASCA issues. */ @@ -75,12 +78,39 @@ private void showNotification(Project project, String message, NotificationType final String FIX_PROMPT_COPY_FAIL_MSG = "Fix prompt copied"; ApplicationManager.getApplication().invokeLater(() -> { Notification notification = NotificationGroupManager.getInstance() - .getNotificationGroup("Checkmarx.Notifications") + .getNotificationGroup(Constants.NOTIFICATION_GROUP_ID) .createNotification(FIX_PROMPT_COPY_FAIL_MSG, message, type); notification.notify(project); }); } + public String stripHtml(String htmlText) { + if (htmlText == null) { + return ""; + } + // Remove HTML tags + String plainText = htmlText.replaceAll("<[^>]*>", ""); + + // Remove "ASCA" suffix, if it exists + if (plainText.endsWith(ASCA_INSPECTION_ID)) { + plainText = plainText.substring(0, plainText.length() - 4).trim(); // Remove "ASCA" and trim any trailing space + } + + return unescapeHtml(plainText); + } + + private String unescapeHtml(String text) { + if (text == null) { + return ""; + } + return text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace(""", "\"") + .replace("'", "'"); + } + + /** * Generates a fix prompt based on the problematic line and description. * @@ -93,7 +123,7 @@ private String generateFixPrompt(String problematicLine, String description) { "Code snippet with potential issue:\n%s\n\n" + "Issue description:\n%s\n\n" + "Provide a fix to make this code safer and more secure."; - return String.format(FIX_PROMPT, problematicLine.trim(), description.trim() + return String.format(FIX_PROMPT, problematicLine.trim(), stripHtml(description.trim()) ); } diff --git a/src/main/resources/messages/CxBundle.properties b/src/main/resources/messages/CxBundle.properties index bd50159e..d021b9dd 100644 --- a/src/main/resources/messages/CxBundle.properties +++ b/src/main/resources/messages/CxBundle.properties @@ -8,6 +8,7 @@ VALIDATE_IN_PROGRESS=Validating... ASCA_DESCRIPTION=Checkmarx AI Secure Coding Assistant (ASCA): Activate ASCA ASCA_CHECKBOX=Scan your file as you code ASCA_SCAN_WARNING=ASCA Warning: {0} +ASCA_STARTED_MSG=AI Secure Coding Assistant started. VALIDATE_SUCCESS=Successfully authenticated to Checkmarx One server VALIDATE_FAIL=Failed authentication: {0} VALIDATE_ERROR=Error in authentication diff --git a/src/test/java/com/checkmarx/intellij/standard/commands/TestScanAsca.java b/src/test/java/com/checkmarx/intellij/standard/commands/TestScanAsca.java index a84d16e6..23533f04 100644 --- a/src/test/java/com/checkmarx/intellij/standard/commands/TestScanAsca.java +++ b/src/test/java/com/checkmarx/intellij/standard/commands/TestScanAsca.java @@ -2,6 +2,7 @@ import com.checkmarx.ast.asca.ScanResult; import com.checkmarx.intellij.ASCA.AscaService; +import com.checkmarx.intellij.Constants; import com.checkmarx.intellij.standard.BaseTest; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; @@ -68,7 +69,7 @@ public void testRunAscaScan_FileWithNoVulnerabilities_Success() { public void testRunAscaScan_FileWithoutExtension_Fail() { PsiFile psiFile = createPsiFileFromPath("src/test/java/com/checkmarx/intellij/standard/data/file"); Project project = ProjectManager.getInstance().getDefaultProject(); - ScanResult ascaResult = ascaService.runAscaScan(psiFile, project, true, "Jetbrains"); + ScanResult ascaResult = ascaService.runAscaScan(psiFile, project, true, Constants.JET_BRAINS_AGENT_NAME); assert ascaResult != null; Assertions.assertNull(ascaResult.getScanDetails()); diff --git a/src/test/java/com/checkmarx/intellij/ui/BaseUITest.java b/src/test/java/com/checkmarx/intellij/ui/BaseUITest.java index 44067202..55096a71 100644 --- a/src/test/java/com/checkmarx/intellij/ui/BaseUITest.java +++ b/src/test/java/com/checkmarx/intellij/ui/BaseUITest.java @@ -12,6 +12,7 @@ import com.intellij.remoterobot.utils.RepeatUtilsKt; import com.intellij.remoterobot.utils.WaitForConditionTimeoutException; import org.apache.commons.lang3.StringUtils; +import org.assertj.swing.fixture.JCheckBoxFixture; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Assertions; @@ -101,7 +102,7 @@ private static void trustClonedProject() { } } - private static void setField(String fieldName, String value) { + static void setField(String fieldName, String value) { log("Setting field " + fieldName); @Language("XPath") String fieldXpath = String.format(FIELD_NAME, fieldName); waitFor(() -> hasAnyComponent(fieldXpath) && find(fieldXpath).isShowing()); @@ -165,7 +166,7 @@ protected static void testASTConnection(boolean validCredentials) { } } - private static void openSettings() { + static void openSettings() { waitFor(() -> { focusCxWindow(); if (hasAnyComponent(SETTINGS_ACTION)) { @@ -279,7 +280,7 @@ && hasAnyComponent(NO_SCAN_SELECTED) }); } - private static void focusCxWindow() { + static void focusCxWindow() { boolean cxPluginOpened = find(BASE_LABEL).hasText("Checkmarx"); System.out.println("Plugin opened: " + cxPluginOpened); diff --git a/src/test/java/com/checkmarx/intellij/ui/TestAsca.java b/src/test/java/com/checkmarx/intellij/ui/TestAsca.java new file mode 100644 index 00000000..17754727 --- /dev/null +++ b/src/test/java/com/checkmarx/intellij/ui/TestAsca.java @@ -0,0 +1,34 @@ +package com.checkmarx.intellij.ui; + +import com.checkmarx.intellij.ASCA.AscaService; +import com.checkmarx.intellij.Constants; +import com.checkmarx.intellij.Environment; +import org.junit.jupiter.api.Assertions; + +import static com.checkmarx.intellij.ui.utils.RemoteRobotUtils.click; +import static com.checkmarx.intellij.ui.utils.RemoteRobotUtils.hasAnyComponent; +import static com.checkmarx.intellij.ui.utils.Xpath.*; +import static com.checkmarx.intellij.ui.utils.Xpath.CANCEL_SCAN_BTN; + +public class TestAsca extends BaseUITest{ + public static void testASTAscaWithValidateConnections(boolean validCredentials) { + openSettings(); + + setField(Constants.FIELD_NAME_API_KEY, validCredentials ? Environment.API_KEY : "invalidAPIKey"); + setField(Constants.FIELD_NAME_ADDITIONAL_PARAMETERS, "--debug"); + + + click(VALIDATE_BUTTON); + + waitFor(() -> !hasAnyComponent(ASCA_INSTALL_SUCCESS)); + + Assertions.assertTrue(hasAnyComponent(ASCA_INSTALL_SUCCESS)); + click(OK_BTN); + // Ensure that start scan button and cancel scan button are visible with valid credentials + waitFor(() -> { + focusCxWindow(); + return hasAnyComponent(START_SCAN_BTN) && hasAnyComponent(CANCEL_SCAN_BTN); + }); + } + +} diff --git a/src/test/java/com/checkmarx/intellij/ui/utils/Xpath.java b/src/test/java/com/checkmarx/intellij/ui/utils/Xpath.java index 7fa57c39..adbbbb89 100644 --- a/src/test/java/com/checkmarx/intellij/ui/utils/Xpath.java +++ b/src/test/java/com/checkmarx/intellij/ui/utils/Xpath.java @@ -164,4 +164,7 @@ public class Xpath { public static final String HAS_SELECTION = "//div[@class='ActionButtonWithText' and starts-with(@visible_text,'%s: ')]"; @Language("XPath") public static final String SCAN_ID_SELECTION = "//div[@class='ActionButtonWithText' and substring(@visible_text, string-length(@visible_text) - string-length('%s') + 1) = '%s']"; + @Language("XPath") + public + static final String ASCA_INSTALL_SUCCESS = "//div[@accessiblename.key='AI Secure Coding Assistant started.']"; }