diff --git a/CHANGELOG.md b/CHANGELOG.md index 11e5b25..92ca8df 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ ## [Unreleased] +## [2.2.0] - 2024-12-11 + +- Add AI remediations for IaC and SAST +- Fix "Path to executable" field applying in the settings + ## [2.1.0] - 2024-10-07 - Add sync flow for Secrets and IaC @@ -125,6 +130,8 @@ The first public release of the plugin. +[2.2.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v2.2.0 + [2.1.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v2.1.0 [2.0.1]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v2.0.1 @@ -175,4 +182,4 @@ The first public release of the plugin. [1.0.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.0.0 -[Unreleased]: https://github.com/cycodehq/intellij-platform-plugin/compare/v2.1.0...HEAD +[Unreleased]: https://github.com/cycodehq/intellij-platform-plugin/compare/v2.2.0...HEAD diff --git a/gradle.properties b/gradle.properties index 5b9ca75..60c3a5d 100755 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginGroup = com.cycode.plugin pluginName = Cycode pluginRepositoryUrl = https://github.com/cycodehq/intellij-platform-plugin # SemVer format -> https://semver.org -pluginVersion = 2.1.0 +pluginVersion = 2.2.0 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 231 diff --git a/src/main/kotlin/com/cycode/plugin/Consts.kt b/src/main/kotlin/com/cycode/plugin/Consts.kt index bf25409..7cb2d4c 100644 --- a/src/main/kotlin/com/cycode/plugin/Consts.kt +++ b/src/main/kotlin/com/cycode/plugin/Consts.kt @@ -27,7 +27,7 @@ class Consts { companion object { val PLUGIN_PATH = PathManager.getPluginsPath() + "/cycode-intellij-platform-plugin" val DEFAULT_CLI_PATH = getDefaultCliPath() - const val REQUIRED_CLI_VERSION = "1.11.0" + const val REQUIRED_CLI_VERSION = "2.1.0" const val CYCODE_DOMAIN = "cycode.com" diff --git a/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt b/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt index 45bf4d6..8817f5f 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt @@ -27,7 +27,7 @@ class CliOSProcessHandler(commandLine: GeneralCommandLine) : OSProcessHandler(co } -class CliWrapper(val executablePath: String, val workDirectory: String? = null) { +class CliWrapper(val workDirectory: String? = null) { val pluginSettings = pluginSettings() var mapper: ObjectMapper = jacksonObjectMapper() @@ -42,7 +42,7 @@ class CliWrapper(val executablePath: String, val workDirectory: String? = null) ): CliResult { val commandLine = GeneralCommandLine() commandLine.charset = Charset.forName("UTF-8") - commandLine.exePath = executablePath + commandLine.exePath = pluginSettings.cliPath if (workDirectory != null) { commandLine.workDirectory = File(workDirectory) diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/AiRemediationResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/AiRemediationResult.kt new file mode 100644 index 0000000..4084057 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/cli/models/AiRemediationResult.kt @@ -0,0 +1,12 @@ +package com.cycode.plugin.cli.models + +data class AiRemediationResult( + val result: Boolean, + val message: String, + val data: AiRemediationResultData? = null, +) + +data class AiRemediationResultData( + val remediation: String, + val isFixAvailable: Boolean, +) diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/AuthCheckResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/AuthCheckResult.kt deleted file mode 100644 index 168c3be..0000000 --- a/src/main/kotlin/com/cycode/plugin/cli/models/AuthCheckResult.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.cycode.plugin.cli.models - -data class AuthCheckResult( - val result: Boolean, - val message: String, - val data: AuthCheckResultData? = null, -) - -data class AuthCheckResultData( - val userId: String, - val tenantId: String, -) diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/StatusResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/StatusResult.kt new file mode 100644 index 0000000..51e82ba --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/cli/models/StatusResult.kt @@ -0,0 +1,19 @@ +package com.cycode.plugin.cli.models + +data class SupportedModulesStatus( + // TODO(MarshalX): respect enabled/disabled scanning modules + val secretScanning: Boolean, + val scaScanning: Boolean, + val iacScanning: Boolean, + val sastScanning: Boolean, + val aiLargeLanguageModel: Boolean, +) + +data class StatusResult( + val program: String, + val version: String, + val isAuthenticated: Boolean, + val userId: String?, + val tenantId: String?, + val supportedModules: SupportedModulesStatus, +) diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/VersionResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/VersionResult.kt deleted file mode 100644 index cbcb6d6..0000000 --- a/src/main/kotlin/com/cycode/plugin/cli/models/VersionResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.cycode.plugin.cli.models - -data class VersionResult( - val name: String, - val version: String, -) diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/DetectionBase.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/DetectionBase.kt index 6283eba..78f0136 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/DetectionBase.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/DetectionBase.kt @@ -1,6 +1,7 @@ package com.cycode.plugin.cli.models.scanResult interface DetectionBase { + val id: String val severity: String val detectionDetails: ScanDetectionDetailsBase diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt index 9f939f6..be03041 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt @@ -4,9 +4,10 @@ import com.cycode.plugin.CycodeBundle import com.cycode.plugin.cli.models.scanResult.DetectionBase data class IacDetection( - val message: String, - override val detectionDetails: IacDetectionDetails, + override val id: String, override val severity: String, + override val detectionDetails: IacDetectionDetails, + val message: String, val type: String, val detectionRuleId: String, // UUID val detectionTypeId: String, // UUID diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sast/SastDetection.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sast/SastDetection.kt index 505f5f0..0a10d76 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sast/SastDetection.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sast/SastDetection.kt @@ -4,9 +4,10 @@ import com.cycode.plugin.CycodeBundle import com.cycode.plugin.cli.models.scanResult.DetectionBase data class SastDetection( - val message: String, - override val detectionDetails: SastDetectionDetails, + override val id: String, override val severity: String, + override val detectionDetails: SastDetectionDetails, + val message: String, val type: String, val detectionRuleId: String, // UUID val detectionTypeId: String, // UUID diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaDetection.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaDetection.kt index 9e72e0e..df3239a 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaDetection.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaDetection.kt @@ -4,9 +4,10 @@ import com.cycode.plugin.CycodeBundle import com.cycode.plugin.cli.models.scanResult.DetectionBase data class ScaDetection( - val message: String, - override val detectionDetails: ScaDetectionDetails, + override val id: String, override val severity: String, + override val detectionDetails: ScaDetectionDetails, + val message: String, val type: String, val detectionRuleId: String, val detectionTypeId: String, diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretDetection.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretDetection.kt index bf8c767..53156e1 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretDetection.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretDetection.kt @@ -6,9 +6,10 @@ import com.cycode.plugin.cli.models.scanResult.DetectionBase const val IDE_ENTRY_LINE_NUMBER = 1 data class SecretDetection( - val message: String, - override val detectionDetails: SecretDetectionDetails, + override val id: String, override val severity: String, + override val detectionDetails: SecretDetectionDetails, + val message: String, val type: String, val detectionRuleId: String, // UUID val detectionTypeId: String, // UUID diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/authContentTab/AuthContentTab.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/authContentTab/AuthContentTab.kt index 6389ffa..9bcebb3 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/authContentTab/AuthContentTab.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/authContentTab/AuthContentTab.kt @@ -18,27 +18,20 @@ class AuthContentTab : Component() { return BorderedPanel().apply { add(JPanel().apply { layout = GridBagLayout() - add(add(JPanel().apply { - add(createClickableLabel(CycodeBundle.message("cliReqInfoLabel"))) - }), GridBagConstraints().apply { - gridy = 0 - insets = JBUI.insetsBottom(10) - anchor = GridBagConstraints.NORTHWEST - }) add(JButton(CycodeBundle.message("authBtn")).apply { addActionListener { this.setEnabled(false) service.startAuth() } }, GridBagConstraints().apply { - gridy = 1 + gridy = 0 insets = JBUI.insetsBottom(10) fill = GridBagConstraints.HORIZONTAL }) add(add(JPanel().apply { add(createClickableLabel(CycodeBundle.message("howToUseLabel"))) }), GridBagConstraints().apply { - gridy = 2 + gridy = 1 anchor = GridBagConstraints.NORTHWEST }) }, BorderLayout.NORTH) diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt index 92d4bf1..17d58f9 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt @@ -1,6 +1,7 @@ package com.cycode.plugin.components.toolWindow.components.cycodeActionToolBar.actions import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.cli.CliScanType import com.cycode.plugin.services.cycode import com.cycode.plugin.services.pluginState import com.intellij.icons.AllIcons @@ -27,10 +28,10 @@ class RunAllAction : val project = e.project ?: return val service = cycode(project) - service.startSecretScanForCurrentProject() - service.startScaScanForCurrentProject() - service.startIacScanForCurrentProject() - service.startSastScanForCurrentProject() + service.startScanForCurrentProject(CliScanType.Secret) + service.startScanForCurrentProject(CliScanType.Sca) + service.startScanForCurrentProject(CliScanType.Iac) + service.startScanForCurrentProject(CliScanType.Sast) } override fun update(e: AnActionEvent) { diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt index db3e175..f1a3690 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt @@ -1,6 +1,7 @@ package com.cycode.plugin.components.toolWindow.components.scanContentTab import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.cli.CliScanType import com.cycode.plugin.components.Component import com.cycode.plugin.components.common.createClickableLabel import com.cycode.plugin.services.CycodeService @@ -29,22 +30,22 @@ class ScanContentTab : Component() { addComponentToPanel(createClickableLabel(CycodeBundle.message("scanTabTitleLabel"))) addComponentToPanel( JButton(CycodeBundle.message("scanTabSecretsBtn")).apply { - addActionListener { service.startSecretScanForCurrentProject() } + addActionListener { service.startScanForCurrentProject(CliScanType.Secret) } }, ) addComponentToPanel( JButton(CycodeBundle.message("scanTabScaBtn")).apply { - addActionListener { service.startScaScanForCurrentProject() } + addActionListener { service.startScanForCurrentProject(CliScanType.Sca) } }, ) addComponentToPanel( JButton(CycodeBundle.message("scanTabIacBtn")).apply { - addActionListener { service.startIacScanForCurrentProject() } + addActionListener { service.startScanForCurrentProject(CliScanType.Iac) } }, ) addComponentToPanel( JButton(CycodeBundle.message("scanTabSastBtn")).apply { - addActionListener { service.startSastScanForCurrentProject() } + addActionListener { service.startScanForCurrentProject(CliScanType.Sast) } }, ) diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt index d9c4004..5cce7bb 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt @@ -123,8 +123,8 @@ class TreeView( val card = when (detection) { is SecretDetection -> SecretViolationCardContentTab(project).getContent(detection) is ScaDetection -> ScaViolationCardContentTab().getContent(detection) - is IacDetection -> IacViolationCardContentTab().getContent(detection) - is SastDetection -> SastViolationCardContentTab().getContent(detection) + is IacDetection -> IacViolationCardContentTab(project).getContent(detection) + is SastDetection -> SastViolationCardContentTab(project).getContent(detection) else -> return } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt index 1ae3789..3a3686b 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt @@ -1,6 +1,7 @@ package com.cycode.plugin.components.toolWindow.components.treeView.components.detectionNodeContextMenu import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.cli.CliScanType import com.cycode.plugin.components.toolWindow.components.treeView.TreeView import com.cycode.plugin.components.toolWindow.components.treeView.nodes.* import com.cycode.plugin.components.toolWindow.components.treeView.openDetectionInFile @@ -69,33 +70,33 @@ class DetectionNodeContextMenu( // FIXME(MarshalX): add some key field instead of abusing name? when (node.name) { - CycodeBundle.message("secretDisplayName") -> service.startSecretScanForCurrentProject() - CycodeBundle.message("scaDisplayName") -> service.startScaScanForCurrentProject() - CycodeBundle.message("iacDisplayName") -> service.startIacScanForCurrentProject() - CycodeBundle.message("sastDisplayName") -> service.startSastScanForCurrentProject() + CycodeBundle.message("secretDisplayName") -> service.startScanForCurrentProject(CliScanType.Secret) + CycodeBundle.message("scaDisplayName") -> service.startScanForCurrentProject(CliScanType.Sca) + CycodeBundle.message("iacDisplayName") -> service.startScanForCurrentProject(CliScanType.Iac) + CycodeBundle.message("sastDisplayName") -> service.startScanForCurrentProject(CliScanType.Sast) } } private fun onRescanOptionClicked() { when (val node = getUnknownNode()) { - is SecretDetectionNode -> service.startPathSecretScan( - node.detection.detectionDetails.getFilepath(), - onDemand = true + is SecretDetectionNode -> service.startScan( + CliScanType.Secret, + listOf(node.detection.detectionDetails.getFilepath()), ) - is ScaDetectionNode -> service.startPathScaScan( - node.detection.detectionDetails.getFilepath(), - onDemand = true + is ScaDetectionNode -> service.startScan( + CliScanType.Sca, + listOf(node.detection.detectionDetails.getFilepath()), ) - is IacDetectionNode -> service.startPathIacScan( - node.detection.detectionDetails.getFilepath(), - onDemand = true + is IacDetectionNode -> service.startScan( + CliScanType.Iac, + listOf(node.detection.detectionDetails.getFilepath()), ) - is SastDetectionNode -> service.startPathSastScan( - node.detection.detectionDetails.getFilepath(), - onDemand = true + is SastDetectionNode -> service.startScan( + CliScanType.Sast, + listOf(node.detection.detectionDetails.getFilepath()), ) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/common/htmlSummary/CardHtmlSummary.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/common/htmlSummary/CardHtmlSummary.kt index a82d54c..3c62284 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/common/htmlSummary/CardHtmlSummary.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/common/htmlSummary/CardHtmlSummary.kt @@ -13,60 +13,63 @@ import javax.swing.JEditorPane import javax.swing.JPanel import javax.swing.text.DefaultCaret -open class CardHtmlSummary { - fun getContent(panelTitle: String, htmlSummary: String? = null): JComponent { - val editorPane = JEditorPane().apply { - contentType = "text/html" - isEditable = false - isOpaque = false - background = UIUtil.TRANSPARENT_COLOR - } +open class CardHtmlSummary(panelTitle: String) { + private val editorPane = JEditorPane().apply { + contentType = "text/html" + isEditable = false + isOpaque = false + background = UIUtil.TRANSPARENT_COLOR + } + private val mainPanel = JPanel(GridBagLayout()).apply { + add( + editorPane, + GridBagConstraints().apply { + fill = GridBagConstraints.BOTH + anchor = GridBagConstraints.NORTHWEST + weightx = 1.0 + weighty = 1.0 + gridy = 1 + } + ) + } + private val labelComponent = JBLabel(panelTitle).apply { + font = font.deriveFont(18f).deriveFont(JBFont.BOLD) + } + private val labelComponentConstraints = GridBagConstraints().apply { + fill = GridBagConstraints.HORIZONTAL + anchor = GridBagConstraints.NORTHWEST + weightx = 1.0 + weighty = 0.0 + gridy = 0 + insets = JBUI.insetsBottom(5) + } + init { editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true) editorPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE) (editorPane.caret as DefaultCaret).updatePolicy = DefaultCaret.NEVER_UPDATE editorPane.editorKit = HTMLEditorKitBuilder.simple() - editorPane.text = htmlSummary + editorPane.border = null + } + fun getContent(htmlSummary: String? = null): JComponent { + setHtmlContent(htmlSummary) + return mainPanel + } + + fun setHtmlContent(htmlSummary: String? = null) { + editorPane.text = htmlSummary // reset scroll position to top editorPane.caretPosition = 0 - editorPane.border = null - - val panel = JPanel(GridBagLayout()).apply { - add( - editorPane, - GridBagConstraints().apply { - fill = GridBagConstraints.BOTH - anchor = GridBagConstraints.NORTHWEST - weightx = 1.0 - weighty = 1.0 - gridy = 1 - } - ) - } - - if (!htmlSummary.isNullOrBlank()) { + if (htmlSummary.isNullOrBlank()) { // we don't want to show the label if there is no description // editor pane still required to acquire the space on the card panel - - panel.add( - JBLabel(panelTitle).apply { - font = font.deriveFont(18f).deriveFont(JBFont.BOLD) - }, - GridBagConstraints().apply { - fill = GridBagConstraints.HORIZONTAL - anchor = GridBagConstraints.NORTHWEST - weightx = 1.0 - weighty = 0.0 - gridy = 0 - insets = JBUI.insetsBottom(5) - } - ) + mainPanel.remove(labelComponent) + } else { + mainPanel.add(labelComponent, labelComponentConstraints) } - - return panel } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/IacViolationCardContentTab.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/IacViolationCardContentTab.kt index 00dd71a..34ec7b9 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/IacViolationCardContentTab.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/IacViolationCardContentTab.kt @@ -1,17 +1,24 @@ package com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab +import com.cycode.plugin.CycodeBundle import com.cycode.plugin.cli.models.scanResult.iac.IacDetection import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.CommonViolationCardContentTab +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.htmlSummary.CardHtmlSummary +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.actions.IacActions import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.companyGuidelines.IacCompanyGuidelines import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.cycodeGuidelines.IacCycodeGuidelines import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.header.IacHeader import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.shortSummary.IacShortSummary import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.summary.IacSummary import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.title.IacTitle +import com.cycode.plugin.services.pluginState +import com.intellij.openapi.project.Project import javax.swing.JComponent -class IacViolationCardContentTab : CommonViolationCardContentTab() { +class IacViolationCardContentTab(val project: Project) : CommonViolationCardContentTab() { fun getContent(detection: IacDetection): JComponent { + val pluginState = pluginState() + val titlePanel = IacTitle().getContent(detection) val shortSummaryPanel = IacShortSummary().getContent(detection) val headerContentPanel = IacHeader().addContent(detection) @@ -19,15 +26,22 @@ class IacViolationCardContentTab : CommonViolationCardContentTab() { val companyGuidelines = IacCompanyGuidelines().getContent(detection) val cycodeGuidelines = IacCycodeGuidelines().getContent(detection) - return getContent( - listOf( - titlePanel, - shortSummaryPanel, - headerContentPanel, - summaryPanel, - companyGuidelines, - cycodeGuidelines, - ) + val componentsToRender = mutableListOf( + titlePanel, + shortSummaryPanel, + headerContentPanel, + summaryPanel, + companyGuidelines, + cycodeGuidelines, ) + + if (pluginState.isAiLargeLanguageModelEnabled) { + val aiRemediationComponent = CardHtmlSummary(CycodeBundle.message("violationCardAiRemediationTitle")) + val actionsPanel = IacActions(project).addContent(detection, aiRemediationComponent) + componentsToRender.add(aiRemediationComponent.getContent()) + componentsToRender.add(actionsPanel) + } + + return getContent(componentsToRender) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/actions/IacActions.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/actions/IacActions.kt new file mode 100644 index 0000000..ba24085 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/actions/IacActions.kt @@ -0,0 +1,22 @@ +package com.cycode.plugin.components.toolWindow.components.violationCardContentTab.iacViolationCardContentTab.components.actions + +import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.cli.models.scanResult.iac.IacDetection +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.actions.CardActions +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.htmlSummary.CardHtmlSummary +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml +import com.cycode.plugin.services.cycode +import com.intellij.openapi.project.Project +import javax.swing.JComponent + +class IacActions(val project: Project) : CardActions() { + fun addContent(detection: IacDetection, aiRemediationComponent: CardHtmlSummary): JComponent { + addActionButton(CycodeBundle.message("generateAiRemediationBtn"), onClick = { + cycode(project).getAiRemediation(detection.id) { remediationResult -> + aiRemediationComponent.setHtmlContent(convertMarkdownToHtml(remediationResult.remediation)) + } + }) + + return getContent() + } +} diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/companyGuidelines/IacCompanyGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/companyGuidelines/IacCompanyGuidelines.kt index a2c7bae..170f092 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/companyGuidelines/IacCompanyGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/companyGuidelines/IacCompanyGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class IacCompanyGuidelines : CardHtmlSummary() { +class IacCompanyGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCompanyGuidelinesTitle")) { private fun getCustomGuidelines(detection: IacDetection): String? { val descriptionMarkdown = detection.detectionDetails.customRemediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: IacDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCompanyGuidelinesTitle"), - getCustomGuidelines(detection) - ) + return getContent(getCustomGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/cycodeGuidelines/IacCycodeGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/cycodeGuidelines/IacCycodeGuidelines.kt index c78d01b..09effcc 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/cycodeGuidelines/IacCycodeGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/iacViolationCardContentTab/components/cycodeGuidelines/IacCycodeGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class IacCycodeGuidelines : CardHtmlSummary() { +class IacCycodeGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCycodeGuidelinesTitle")) { private fun getCycodeGuidelines(detection: IacDetection): String? { val descriptionMarkdown = detection.detectionDetails.remediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: IacDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCycodeGuidelinesTitle"), - getCycodeGuidelines(detection) - ) + return getContent(getCycodeGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/SastViolationCardContentTab.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/SastViolationCardContentTab.kt index 3e1b09e..994b9d5 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/SastViolationCardContentTab.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/SastViolationCardContentTab.kt @@ -1,17 +1,24 @@ package com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab +import com.cycode.plugin.CycodeBundle import com.cycode.plugin.cli.models.scanResult.sast.SastDetection import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.CommonViolationCardContentTab +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.htmlSummary.CardHtmlSummary +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.actions.SastActions import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.companyGuidelines.SastCompanyGuidelines import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.cycodeGuidelines.SastCycodeGuidelines import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.header.SastHeader import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.shortSummary.SastShortSummary import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.summary.SastSummary import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.title.SastTitle +import com.cycode.plugin.services.pluginState +import com.intellij.openapi.project.Project import javax.swing.JComponent -class SastViolationCardContentTab : CommonViolationCardContentTab() { +class SastViolationCardContentTab(val project: Project) : CommonViolationCardContentTab() { fun getContent(detection: SastDetection): JComponent { + val pluginState = pluginState() + val titlePanel = SastTitle().getContent(detection) val shortSummaryPanel = SastShortSummary().getContent(detection) val headerContentPanel = SastHeader().addContent(detection) @@ -19,15 +26,22 @@ class SastViolationCardContentTab : CommonViolationCardContentTab() { val companyGuidelines = SastCompanyGuidelines().getContent(detection) val cycodeGuidelines = SastCycodeGuidelines().getContent(detection) - return getContent( - listOf( - titlePanel, - shortSummaryPanel, - headerContentPanel, - summaryPanel, - companyGuidelines, - cycodeGuidelines, - ) + val componentsToRender = mutableListOf( + titlePanel, + shortSummaryPanel, + headerContentPanel, + summaryPanel, + companyGuidelines, + cycodeGuidelines, ) + + if (pluginState.isAiLargeLanguageModelEnabled) { + val aiRemediationComponent = CardHtmlSummary(CycodeBundle.message("violationCardAiRemediationTitle")) + val actionsPanel = SastActions(project).addContent(detection, aiRemediationComponent) + componentsToRender.add(aiRemediationComponent.getContent()) + componentsToRender.add(actionsPanel) + } + + return getContent(componentsToRender) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/actions/SastActions.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/actions/SastActions.kt new file mode 100644 index 0000000..db6fca1 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/actions/SastActions.kt @@ -0,0 +1,22 @@ +package com.cycode.plugin.components.toolWindow.components.violationCardContentTab.sastViolationCardContentTab.components.actions + +import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.cli.models.scanResult.sast.SastDetection +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.actions.CardActions +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.common.htmlSummary.CardHtmlSummary +import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml +import com.cycode.plugin.services.cycode +import com.intellij.openapi.project.Project +import javax.swing.JComponent + +class SastActions(val project: Project) : CardActions() { + fun addContent(detection: SastDetection, aiRemediationComponent: CardHtmlSummary): JComponent { + addActionButton(CycodeBundle.message("generateAiRemediationBtn"), onClick = { + cycode(project).getAiRemediation(detection.id) { remediationResult -> + aiRemediationComponent.setHtmlContent(convertMarkdownToHtml(remediationResult.remediation)) + } + }) + + return getContent() + } +} diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/companyGuidelines/SastCompanyGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/companyGuidelines/SastCompanyGuidelines.kt index 5e9d9ff..929498e 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/companyGuidelines/SastCompanyGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/companyGuidelines/SastCompanyGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class SastCompanyGuidelines : CardHtmlSummary() { +class SastCompanyGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCompanyGuidelinesTitle")) { private fun getCustomGuidelines(detection: SastDetection): String? { val descriptionMarkdown = detection.detectionDetails.customRemediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: SastDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCompanyGuidelinesTitle"), - getCustomGuidelines(detection) - ) + return getContent(getCustomGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/cycodeGuidelines/SastCycodeGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/cycodeGuidelines/SastCycodeGuidelines.kt index ffba3a8..0946651 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/cycodeGuidelines/SastCycodeGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/cycodeGuidelines/SastCycodeGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class SastCycodeGuidelines : CardHtmlSummary() { +class SastCycodeGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCycodeGuidelinesTitle")) { private fun getCycodeGuidelines(detection: SastDetection): String? { val descriptionMarkdown = detection.detectionDetails.remediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: SastDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCycodeGuidelinesTitle"), - getCycodeGuidelines(detection) - ) + return getContent(getCycodeGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/summary/SastSummary.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/summary/SastSummary.kt index 5f65f2e..dea4950 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/summary/SastSummary.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/sastViolationCardContentTab/components/summary/SastSummary.kt @@ -6,12 +6,12 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class SastSummary : CardHtmlSummary() { +class SastSummary : CardHtmlSummary(CycodeBundle.message("violationCardSummaryTitle")) { private fun getSummary(detection: SastDetection): String { return convertMarkdownToHtml(detection.detectionDetails.description) } fun getContent(detection: SastDetection): JComponent { - return getContent(CycodeBundle.message("violationCardSummaryTitle"), getSummary(detection)) + return getContent(getSummary(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/companyGuidelines/ScaCompanyGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/companyGuidelines/ScaCompanyGuidelines.kt index 935132e..429b41e 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/companyGuidelines/ScaCompanyGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/companyGuidelines/ScaCompanyGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class ScaCompanyGuidelines : CardHtmlSummary() { +class ScaCompanyGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCompanyGuidelinesTitle")) { private fun getCustomGuidelines(detection: ScaDetection): String? { val descriptionMarkdown = detection.detectionDetails.customRemediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: ScaDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCompanyGuidelinesTitle"), - getCustomGuidelines(detection) - ) + return getContent(getCustomGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/cycodeGuidelines/ScaCycodeGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/cycodeGuidelines/ScaCycodeGuidelines.kt index ae67849..73f4aef 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/cycodeGuidelines/ScaCycodeGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/cycodeGuidelines/ScaCycodeGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class ScaCycodeGuidelines : CardHtmlSummary() { +class ScaCycodeGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCycodeGuidelinesTitle")) { private fun getCycodeGuidelines(detection: ScaDetection): String? { val descriptionMarkdown = detection.detectionDetails.remediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: ScaDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCycodeGuidelinesTitle"), - getCycodeGuidelines(detection) - ) + return getContent(getCycodeGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/summary/ScaSummary.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/summary/ScaSummary.kt index 14ec54a..97ea1f4 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/summary/ScaSummary.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/scaViolationCardContentTab/components/summary/ScaSummary.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class ScaSummary : CardHtmlSummary() { +class ScaSummary : CardHtmlSummary(CycodeBundle.message("violationCardSummaryTitle")) { private fun getDescription(detection: ScaDetection): String? { val descriptionMarkdown = detection.detectionDetails.alert?.description ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: ScaDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardSummaryTitle"), - getDescription(detection) - ) + return getContent(getDescription(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/companyGuidelines/SecretCompanyGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/companyGuidelines/SecretCompanyGuidelines.kt index e67a877..4882b01 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/companyGuidelines/SecretCompanyGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/companyGuidelines/SecretCompanyGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class SecretCompanyGuidelines : CardHtmlSummary() { +class SecretCompanyGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCompanyGuidelinesTitle")) { private fun getCustomGuidelines(detection: SecretDetection): String? { val descriptionMarkdown = detection.detectionDetails.customRemediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: SecretDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCompanyGuidelinesTitle"), - getCustomGuidelines(detection) - ) + return getContent(getCustomGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/cycodeGuidelines/SecretCycodeGuidelines.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/cycodeGuidelines/SecretCycodeGuidelines.kt index 176d457..e8cb165 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/cycodeGuidelines/SecretCycodeGuidelines.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/violationCardContentTab/secretViolationCardContentTab/components/cycodeGuidelines/SecretCycodeGuidelines.kt @@ -6,16 +6,13 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa import com.cycode.plugin.components.toolWindow.components.violationCardContentTab.convertMarkdownToHtml import javax.swing.JComponent -class SecretCycodeGuidelines : CardHtmlSummary() { +class SecretCycodeGuidelines : CardHtmlSummary(CycodeBundle.message("violationCardCycodeGuidelinesTitle")) { private fun getCycodeGuidelines(detection: SecretDetection): String? { val descriptionMarkdown = detection.detectionDetails.remediationGuidelines ?: return null return convertMarkdownToHtml(descriptionMarkdown) } fun getContent(detection: SecretDetection): JComponent { - return getContent( - CycodeBundle.message("violationCardCycodeGuidelinesTitle"), - getCycodeGuidelines(detection) - ) + return getContent(getCycodeGuidelines(detection)) } } diff --git a/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt b/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt index c3dcf19..2fa2cd8 100644 --- a/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt +++ b/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt @@ -1,6 +1,7 @@ package com.cycode.plugin.listeners import com.cycode.plugin.Consts +import com.cycode.plugin.cli.CliScanType import com.cycode.plugin.cli.isSupportedIacFile import com.cycode.plugin.cli.isSupportedPackageFile import com.cycode.plugin.services.cycode @@ -37,17 +38,17 @@ class FileSaveListener(private val project: Project) : FileDocumentManagerListen } if (pathsToScan.isNotEmpty()) { - service.startPathSecretScan(pathsToScan) + service.startScan(CliScanType.Secret, pathsToScan, onDemand = false) } val scaPathsToScan = excludeNonScaRelatedPaths(pathsToScan) if (scaPathsToScan.isNotEmpty()) { - service.startPathScaScan(scaPathsToScan) + service.startScan(CliScanType.Sca, scaPathsToScan, onDemand = false) } val iacPathsToScan = excludeNonIacRelatedPaths(pathsToScan) if (iacPathsToScan.isNotEmpty()) { - service.startPathIacScan(iacPathsToScan) + service.startScan(CliScanType.Iac, iacPathsToScan, onDemand = false) } } diff --git a/src/main/kotlin/com/cycode/plugin/services/CliService.kt b/src/main/kotlin/com/cycode/plugin/services/CliService.kt index d93740b..1f5e4f6 100644 --- a/src/main/kotlin/com/cycode/plugin/services/CliService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/CliService.kt @@ -2,9 +2,10 @@ package com.cycode.plugin.services import com.cycode.plugin.CycodeBundle import com.cycode.plugin.cli.* -import com.cycode.plugin.cli.models.AuthCheckResult +import com.cycode.plugin.cli.models.AiRemediationResult +import com.cycode.plugin.cli.models.AiRemediationResultData import com.cycode.plugin.cli.models.AuthResult -import com.cycode.plugin.cli.models.VersionResult +import com.cycode.plugin.cli.models.StatusResult import com.cycode.plugin.cli.models.scanResult.ScanResultBase import com.cycode.plugin.cli.models.scanResult.iac.IacDetection import com.cycode.plugin.cli.models.scanResult.iac.IacScanResult @@ -27,11 +28,8 @@ typealias TaskCancelledCallback = (() -> Boolean)? @Service(Service.Level.PROJECT) class CliService(private val project: Project) { private val pluginState = pluginState() - private val pluginSettings = pluginSettings() - private val scanResults = scanResults(project) - - private val cli = CliWrapper(pluginSettings.cliPath, getProjectRootDirectory()) + private val cli = CliWrapper(getProjectRootDirectory()) fun getProjectRootDirectory(): String? { val modules = ModuleManager.getInstance(project).modules @@ -92,53 +90,36 @@ class CliService(private val project: Project) { return result } - fun healthCheck(cancelledCallback: TaskCancelledCallback = null): Boolean { - val result: CliResult = + fun syncStatus(cancelledCallback: TaskCancelledCallback = null) { + val result: CliResult = cli.executeCommand( - "version", + "status", cancelledCallback = cancelledCallback ) val processedResult = processResult(result) - if (processedResult is CliResult.Success) { - pluginState.cliInstalled = true - pluginState.cliVer = processedResult.result.version - return true + if (processedResult !is CliResult.Success) { + resetPluginCLiState() + return } - resetPluginCLiState() - return false - } - - fun checkAuth(cancelledCallback: TaskCancelledCallback = null): Boolean { - val result: CliResult = - cli.executeCommand( - "auth", - "check", - cancelledCallback = cancelledCallback - ) - - val processedResult = processResult(result) - if (processedResult is CliResult.Success) { - pluginState.cliInstalled = true - pluginState.cliAuthed = processedResult.result.result - if (!pluginState.cliAuthed) { - showErrorNotification(CycodeBundle.message("checkAuthErrorNotification")) - } + pluginState.cliInstalled = true + pluginState.cliVer = processedResult.result.version + pluginState.cliAuthed = processedResult.result.isAuthenticated + pluginState.isAiLargeLanguageModelEnabled = processedResult.result.supportedModules.aiLargeLanguageModel - val sentryData = processedResult.result.data - if (sentryData != null) { - SentryInit.setupScope(sentryData.userId, sentryData.tenantId) + if (!pluginState.cliAuthed) { + showErrorNotification(CycodeBundle.message("checkAuthErrorNotification")) + } else { + if (processedResult.result.userId != null && processedResult.result.tenantId != null) { + SentryInit.setupScope(processedResult.result.userId, processedResult.result.tenantId) } - - return pluginState.cliAuthed } - resetPluginCLiState() - return false + return } - fun doAuth(cancelledCallback: TaskCancelledCallback = null): Boolean { + fun startAuth(cancelledCallback: TaskCancelledCallback = null): Boolean { val result: CliResult = cli.executeCommand( "auth", @@ -233,7 +214,7 @@ class CliService(private val project: Project) { ) { val results = scanPaths(paths, CliScanType.Secret, cancelledCallback) if (results == null) { - thisLogger().warn("Failed to scan paths: $paths") + thisLogger().warn("Failed to scan Secret paths: $paths") return } @@ -251,7 +232,7 @@ class CliService(private val project: Project) { fun scanPathsSca(paths: List, onDemand: Boolean = true, cancelledCallback: TaskCancelledCallback = null) { val results = scanPaths(paths, CliScanType.Sca, cancelledCallback) if (results == null) { - thisLogger().warn("Failed to scan paths: $paths") + thisLogger().warn("Failed to scan SCA paths: $paths") return } @@ -281,7 +262,7 @@ class CliService(private val project: Project) { fun scanPathsIac(paths: List, onDemand: Boolean = true, cancelledCallback: TaskCancelledCallback = null) { var results = scanPaths(paths, CliScanType.Iac, cancelledCallback) if (results == null) { - thisLogger().warn("Failed to scan paths: $paths") + thisLogger().warn("Failed to IaC scan paths: $paths") return } @@ -307,7 +288,7 @@ class CliService(private val project: Project) { fun scanPathsSast(paths: List, onDemand: Boolean = true, cancelledCallback: TaskCancelledCallback = null) { val results = scanPaths(paths, CliScanType.Sast, cancelledCallback) if (results == null) { - thisLogger().warn("Failed to scan paths: $paths") + thisLogger().warn("Failed to SAST scan paths: $paths") return } @@ -321,4 +302,30 @@ class CliService(private val project: Project) { scanResults.setSastResults(results) rerunAnnotators() } + + fun getAiRemediation( + detectionId: String, + cancelledCallback: TaskCancelledCallback = null + ): AiRemediationResultData? { + val result: CliResult = + cli.executeCommand( + "ai_remediation", + detectionId, + cancelledCallback = cancelledCallback + ) + + val processedResult = processResult(result) + if (processedResult !is CliResult.Success) { + thisLogger().warn("Failed to get AI remediation for detection: $detectionId") + return null + } + + if (!processedResult.result.result || processedResult.result.data?.remediation == null) { + thisLogger().warn("AI remediation is not available for detection: $detectionId") + showErrorNotification(CycodeBundle.message("aiRemediationNotAvailableNotification")) + return null + } + + return processedResult.result.data + } } diff --git a/src/main/kotlin/com/cycode/plugin/services/CycodePersistentStateService.kt b/src/main/kotlin/com/cycode/plugin/services/CycodePersistentStateService.kt index a1cf109..e0b738f 100644 --- a/src/main/kotlin/com/cycode/plugin/services/CycodePersistentStateService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/CycodePersistentStateService.kt @@ -20,6 +20,7 @@ class CycodePersistentStateService : PersistentStateComponent? = null var cliLastUpdateCheckedAt: Long? = null + var isAiLargeLanguageModelEnabled: Boolean = false override fun getState(): CycodePersistentStateService { return this @@ -35,5 +36,6 @@ class CycodePersistentStateService : PersistentStateComponent runBackgroundTask( + title: String, + canBeCancelled: Boolean = true, + task: (ProgressIndicator) -> T, + ) { + thisLogger().debug("Create background task: $title") + object : Task.Backgroundable(project, title, canBeCancelled) { override fun run(indicator: ProgressIndicator) { - // we are using lock of download service because it shared per application - // the current service is per project so, we can't create a lock here - synchronized(cliDownloadService.initCliLock) { - cliDownloadService.initCli() - - // required to know CLI version. - // we don't have a universal command that will cover the auth state and CLI version yet - cliService.healthCheck() - - cliService.checkAuth() - updateToolWindowState(project) - } + thisLogger().debug("Run background task: $title") + task(indicator) + thisLogger().debug("Finish background task: $title") } }.queue() + thisLogger().debug("Background task queued: $title") } - fun startAuth() { - object : Task.Backgroundable(project, CycodeBundle.message("authProcessing"), true) { - override fun run(indicator: ProgressIndicator) { - if (!pluginState.cliAuthed) { - cliService.doAuth { indicator.isCanceled } - cliService.checkAuth() - - updateToolWindowStateForAllProjects() - } + fun installCliIfNeededAndCheckAuthentication() { + runBackgroundTask(CycodeBundle.message("pluginLoading"), canBeCancelled = false) { _ -> + thisLogger().debug("Check CLI installation and authentication") + // we are using lock of download service because it shared per application + // the current service is per project so, we can't create a lock here + synchronized(cliDownloadService.initCliLock) { + cliDownloadService.initCli() + cliService.syncStatus() + updateToolWindowState(project) } - }.queue() + } } - fun startPathSecretScan(path: String, onDemand: Boolean = false) { - startPathSecretScan(listOf(path), onDemand = onDemand) + fun startAuth() { + runBackgroundTask(CycodeBundle.message("authProcessing")) { indicator -> + if (!pluginState.cliAuthed) { + cliService.startAuth { indicator.isCanceled } + cliService.syncStatus() + updateToolWindowStateForAllProjects() + } + } } - fun startPathSecretScan(pathsToScan: List, onDemand: Boolean = false) { - object : Task.Backgroundable(project, CycodeBundle.message("secretScanning"), true) { - override fun run(indicator: ProgressIndicator) { - if (!pluginState.cliAuthed) { - return - } - - thisLogger().debug("[Secret] Start scanning paths: $pathsToScan") - cliService.scanPathsSecrets( - pathsToScan, - onDemand = onDemand, - cancelledCallback = { indicator.isCanceled }) - thisLogger().debug("[Secret] Finish scanning paths: $pathsToScan") - } - }.queue() + private fun getBackgroundScanningLabel(scanType: CliScanType) = when (scanType) { + CliScanType.Secret -> CycodeBundle.message("secretScanning") + CliScanType.Sca -> CycodeBundle.message("scaScanning") + CliScanType.Iac -> CycodeBundle.message("iacScanning") + CliScanType.Sast -> CycodeBundle.message("sastScanning") } - fun startPathScaScan(path: String, onDemand: Boolean = false) { - startPathScaScan(listOf(path), onDemand = onDemand) + fun startScanForCurrentProject(scanType: CliScanType) { + val projectRoot = cliService.getProjectRootDirectory() + if (projectRoot == null) { + CycodeNotifier.notifyInfo(project, CycodeBundle.message("noProjectRootErrorNotification")) + return + } + + // the only way to run the entire project scans is by pressing the button + // so this is on demand scan + startScan(scanType, listOf(projectRoot)) } - fun startPathScaScan(pathsToScan: List, onDemand: Boolean = false) { - object : Task.Backgroundable(project, CycodeBundle.message("scaScanning"), true) { - override fun run(indicator: ProgressIndicator) { - if (!pluginState.cliAuthed) { - return - } - - thisLogger().debug("[SCA] Start scanning paths: $pathsToScan") - cliService.scanPathsSca( - pathsToScan, - onDemand = onDemand, - cancelledCallback = { indicator.isCanceled } - ) - thisLogger().debug("[SCA] Finish scanning paths: $pathsToScan") + fun startScan(scanType: CliScanType, pathsToScan: List, onDemand: Boolean = true) { + runBackgroundTask(getBackgroundScanningLabel(scanType)) { indicator -> + if (!pluginState.cliAuthed) { + CycodeNotifier.notifyInfo(project, CycodeBundle.message("authorizationRequiredNotification")) + return@runBackgroundTask } - }.queue() - } - fun startPathIacScan(path: String, onDemand: Boolean = false) { - startPathIacScan(listOf(path), onDemand = onDemand) - } + thisLogger().debug("[$scanType] Start scanning paths: $pathsToScan") - fun startPathIacScan(pathsToScan: List, onDemand: Boolean = false) { - object : Task.Backgroundable(project, CycodeBundle.message("iacScanning"), true) { - override fun run(indicator: ProgressIndicator) { - if (!pluginState.cliAuthed) { - return - } - - thisLogger().debug("[IAC] Start scanning paths: $pathsToScan") - cliService.scanPathsIac( - pathsToScan, - onDemand = onDemand, - cancelledCallback = { indicator.isCanceled } - ) - thisLogger().debug("[IAC] Finish scanning paths: $pathsToScan") + val cancelledCallback = { indicator.isCanceled } + when (scanType) { + CliScanType.Secret -> cliService.scanPathsSecrets(pathsToScan, onDemand, cancelledCallback) + CliScanType.Sca -> cliService.scanPathsSca(pathsToScan, onDemand, cancelledCallback) + CliScanType.Iac -> cliService.scanPathsIac(pathsToScan, onDemand, cancelledCallback) + CliScanType.Sast -> cliService.scanPathsSast(pathsToScan, onDemand, cancelledCallback) } - }.queue() - } - fun startPathSastScan(path: String, onDemand: Boolean = false) { - startPathSastScan(listOf(path), onDemand = onDemand) + thisLogger().debug("[$scanType] Finish scanning paths: $pathsToScan") + } } - private fun startPathSastScan(pathsToScan: List, onDemand: Boolean = false) { - object : Task.Backgroundable(project, CycodeBundle.message("sastScanning"), true) { - override fun run(indicator: ProgressIndicator) { - if (!pluginState.cliAuthed) { - return - } - - thisLogger().debug("[SAST] Start scanning paths: $pathsToScan") - cliService.scanPathsSast( - pathsToScan, - onDemand = onDemand, - cancelledCallback = { indicator.isCanceled } - ) - thisLogger().debug("[SAST] Finish scanning paths: $pathsToScan") + fun getAiRemediation(detectionId: String, onSuccess: (AiRemediationResultData) -> Unit) { + runBackgroundTask(CycodeBundle.message("aiRemediationGenerating")) { indicator -> + thisLogger().debug("[AI REMEDIATION] Start generating remediation for $detectionId") + val aiRemediation = cliService.getAiRemediation(detectionId) + thisLogger().debug("[AI REMEDIATION] Finish generating remediation for $detectionId") + + if (aiRemediation != null) { + onSuccess(aiRemediation) } - }.queue() + } } private fun mapTypeToOptionName(type: CliIgnoreType): String { @@ -168,62 +141,21 @@ class CycodeService(val project: Project) : Disposable { // we are removing is from UI first to show how it's blazing fast and then apply it in the background applyIgnoreInUi(type, value) - object : Task.Backgroundable(project, CycodeBundle.message("ignoresApplying"), true) { - override fun run(indicator: ProgressIndicator) { - if (!pluginState.cliAuthed) { - return - } - - cliService.ignore( - scanType.name.lowercase(), - mapTypeToOptionName(type), - value, - cancelledCallback = { indicator.isCanceled }) + runBackgroundTask(CycodeBundle.message("ignoresApplying"), canBeCancelled = false) { indicator -> + if (!pluginState.cliAuthed) { + return@runBackgroundTask } - }.queue() - } - - fun startSecretScanForCurrentProject() { - val projectRoot = cliService.getProjectRootDirectory() - if (projectRoot == null) { - CycodeNotifier.notifyInfo(project, CycodeBundle.message("noProjectRootErrorNotification")) - return - } - startPathSecretScan(projectRoot, onDemand = true) - } - - fun startScaScanForCurrentProject() { - val projectRoot = cliService.getProjectRootDirectory() - if (projectRoot == null) { - CycodeNotifier.notifyInfo(project, CycodeBundle.message("noProjectRootErrorNotification")) - return + cliService.ignore( + scanType.name.lowercase(), + mapTypeToOptionName(type), + value, + cancelledCallback = { indicator.isCanceled } + ) } - - startPathScaScan(projectRoot, onDemand = true) - } - - fun startIacScanForCurrentProject() { - val projectRoot = cliService.getProjectRootDirectory() - if (projectRoot == null) { - CycodeNotifier.notifyInfo(project, CycodeBundle.message("noProjectRootErrorNotification")) - return - } - - startPathIacScan(projectRoot, onDemand = true) - } - - fun startSastScanForCurrentProject() { - val projectRoot = cliService.getProjectRootDirectory() - if (projectRoot == null) { - CycodeNotifier.notifyInfo(project, CycodeBundle.message("noProjectRootErrorNotification")) - return - } - - startPathSastScan(projectRoot, onDemand = true) } override fun dispose() { CycodeToolWindowFactory.TabManager.removeTab(project) } -} +} \ No newline at end of file diff --git a/src/main/resources/messages/CycodeBundle.properties b/src/main/resources/messages/CycodeBundle.properties index a89db85..d01436f 100755 --- a/src/main/resources/messages/CycodeBundle.properties +++ b/src/main/resources/messages/CycodeBundle.properties @@ -31,8 +31,10 @@ missingCStandardLibraryErrorNotification=Plugin requires C standard library (lib unknownErrorNotification=Unknown error occurred. Please try again noOpenFileErrorNotification=Cycode scans the file that is currently opened. Please open a file and try again noProjectRootErrorNotification=Cycode scans the project that is currently opened. Please open a project and try again +authorizationRequiredNotification=You are not authenticated in Cycode. Please authenticate scanFileResultNotification=Cycode has detected {0} {1} issues in your file. Check out your "Cycode" tab to analyze. scanFileNoResultNotification=No {0} issues were found. +aiRemediationNotAvailableNotification=AI remediation is not available for this detection openProblemsTabAction=Open "Cycode" tab # tool window toolWindowId=Cycode @@ -54,7 +56,6 @@ loadingTabLoadingText=\ # auth content tab authBtn=Authenticate -cliReqInfoLabel=Cycode extension requires pre-installed Cycode CLI # scan content tab scanTabTitleLabel=Ready to scan. scanTabOnSaveTip=To easily scan your edited files, enable Scan on Save in settings (enabled by default). @@ -71,6 +72,7 @@ iacScanning=Cycode is scanning files for Infrastructure As Code... sastScanning=Cycode is scanning files for Code Security... ignoresApplying=Cycode is applying ignores... sentryReporting=Cycode is reporting the problem... +aiRemediationGenerating=Cycode is generating AI remediation... # settings menu settingsCliSectionTitle=Cycode CLI settings settingsOnPremiseSectionTitle=On-premise settings @@ -101,6 +103,8 @@ openViolationCardOption=Open Violation Card violationCardSummaryTitle=Summary violationCardCompanyGuidelinesTitle=Company Guidelines violationCardCycodeGuidelinesTitle=Cycode Guidelines +violationCardAiRemediationTitle=AI Remediation +generateAiRemediationBtn=Generate AI Remediation # sca violation card scaViolationCardShortSummary={0} | {1} scaViolationCardHeaderPackageField=Package: