Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.cycode.plugin.cli.models.scanResult

interface ScanDetectionDetailsBase {
fun getFilepath(): String

/**
* Gets the line number.
* @return The 1-based line number (first line is 1, not 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@ import java.awt.GridBagConstraints
import javax.swing.JButton
import javax.swing.JComponent
import javax.swing.JPanel
import javax.swing.SwingUtilities

open class CardActions {
private val gbc = GridBagConstraints()
private val panel: JPanel = JPanel(FlowLayout(FlowLayout.RIGHT))
private val buttons: MutableMap<String, ButtonInfo> = mutableMapOf()

private data class ButtonInfo(
val button: JButton,
val originalText: String,
val inProgressText: String
)

init {
gbc.insets = JBUI.insets(2)
Expand All @@ -21,10 +29,79 @@ open class CardActions {
)
}

fun addActionButton(text: String, onClick: () -> Unit) {
panel.add(JButton(text).apply {
addActionListener { onClick() }
}, gbc)
fun addActionButton(
id: String,
text: String,
onClick: () -> Unit,
async: Boolean = false,
inProgressText: String = "$text..."
): JButton {
val button = JButton(text).apply {
addActionListener {
disableButton(id)
if (async) {
// For async operations, onClick handles its own threading and re-enabling
onClick()
} else {
// For sync operations, run in a background thread to avoid blocking EDT
Thread {
try {
onClick()
} finally {
SwingUtilities.invokeLater {
enableButton(id)
}
}
}.start()
}
}
}
buttons[id] = ButtonInfo(button, text, inProgressText)
panel.add(button, gbc)
return button
}

fun removeButton(id: String) {
buttons[id]?.let { buttonInfo ->
panel.remove(buttonInfo.button)
buttons.remove(id)
panel.revalidate()
panel.repaint()
}
}

fun showButton(id: String) {
buttons[id]?.button?.isVisible = true
panel.revalidate()
panel.repaint()
}

fun hideButton(id: String) {
buttons[id]?.button?.isVisible = false
panel.revalidate()
panel.repaint()
}

fun getButton(id: String): JButton? {
return buttons[id]?.button
}

fun enableButton(id: String) {
buttons[id]?.let { buttonInfo ->
SwingUtilities.invokeLater {
buttonInfo.button.isEnabled = true
buttonInfo.button.text = buttonInfo.originalText
}
}
}

fun disableButton(id: String) {
buttons[id]?.let { buttonInfo ->
SwingUtilities.invokeLater {
buttonInfo.button.isEnabled = false
buttonInfo.button.text = buttonInfo.inProgressText
}
}
}

fun getContent(): JComponent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,55 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa
import com.cycode.plugin.services.cycode
import com.intellij.openapi.project.Project
import javax.swing.JComponent
import javax.swing.SwingUtilities

class IacActions(val project: Project) : CardActions() {

companion object {
private const val GENERATE_AI_REMEDIATION_ID = "generate_ai_remediation"
private const val APPLY_AI_REMEDIATION_ID = "apply_ai_remediation"
}

fun addContent(detection: IacDetection, aiRemediationComponent: CardHtmlSummary): JComponent {
addActionButton(CycodeBundle.message("generateAiRemediationBtn"), onClick = {
cycode(project).getAiRemediation(detection.id) { remediationResult ->
aiRemediationComponent.setHtmlContent(convertMarkdownToHtml(remediationResult.remediation))
}
})
val generateButtonText = CycodeBundle.message("generateAiRemediationBtn")
val applyButtonText = CycodeBundle.message("applyAiRemediationBtn")

addActionButton(
id = GENERATE_AI_REMEDIATION_ID,
text = generateButtonText,
onClick = {
cycode(project).getAiRemediation(
detectionId = detection.id,
onSuccess = { remediationResult ->
aiRemediationComponent.setHtmlContent(convertMarkdownToHtml(remediationResult.remediation))

SwingUtilities.invokeLater {
hideButton(GENERATE_AI_REMEDIATION_ID)

if (remediationResult.isFixAvailable) {
showButton(APPLY_AI_REMEDIATION_ID)
}
}
},
onFailure = {
enableButton(GENERATE_AI_REMEDIATION_ID)
}
)
},
async = true,
inProgressText = CycodeBundle.message("generateAiRemediationBtnInProgress")
)

addActionButton(
id = APPLY_AI_REMEDIATION_ID,
text = applyButtonText,
onClick = {
// TODO: Implement actual apply fix logic
Thread.sleep(3000)
},
inProgressText = CycodeBundle.message("applyAiRemediationBtnInProgress")
)
hideButton(APPLY_AI_REMEDIATION_ID)

return getContent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,55 @@ import com.cycode.plugin.components.toolWindow.components.violationCardContentTa
import com.cycode.plugin.services.cycode
import com.intellij.openapi.project.Project
import javax.swing.JComponent
import javax.swing.SwingUtilities

class SastActions(val project: Project) : CardActions() {

companion object {
private const val GENERATE_AI_REMEDIATION_ID = "generate_ai_remediation"
private const val APPLY_AI_REMEDIATION_ID = "apply_ai_remediation"
}

fun addContent(detection: SastDetection, aiRemediationComponent: CardHtmlSummary): JComponent {
addActionButton(CycodeBundle.message("generateAiRemediationBtn"), onClick = {
cycode(project).getAiRemediation(detection.id) { remediationResult ->
aiRemediationComponent.setHtmlContent(convertMarkdownToHtml(remediationResult.remediation))
}
})
val generateButtonText = CycodeBundle.message("generateAiRemediationBtn")
val applyButtonText = CycodeBundle.message("applyAiRemediationBtn")

addActionButton(
id = GENERATE_AI_REMEDIATION_ID,
text = generateButtonText,
onClick = {
cycode(project).getAiRemediation(
detectionId = detection.id,
onSuccess = { remediationResult ->
aiRemediationComponent.setHtmlContent(convertMarkdownToHtml(remediationResult.remediation))

SwingUtilities.invokeLater {
hideButton(GENERATE_AI_REMEDIATION_ID)

if (remediationResult.isFixAvailable) {
showButton(APPLY_AI_REMEDIATION_ID)
}
}
},
onFailure = {
enableButton(GENERATE_AI_REMEDIATION_ID)
}
)
},
async = true,
inProgressText = CycodeBundle.message("generateAiRemediationBtnInProgress")
)

addActionButton(
id = APPLY_AI_REMEDIATION_ID,
text = applyButtonText,
onClick = {
// TODO: Implement actual apply fix logic
Thread.sleep(3000)
},
inProgressText = CycodeBundle.message("applyAiRemediationBtnInProgress")
)
hideButton(APPLY_AI_REMEDIATION_ID)

return getContent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,24 @@ import com.intellij.openapi.project.Project
import javax.swing.JComponent

class ScaActions(val project: Project) : CardActions() {

companion object {
private const val IGNORE_VIOLATION_ID = "ignore_violation"
}

fun addContent(detection: ScaDetection): JComponent {
if (detection.detectionDetails.alert?.cveIdentifier != null) {
addActionButton(CycodeBundle.message("violationCardIgnoreViolationBtn"), onClick = {
cycode(project).applyIgnoreFromFileAnnotation(
CliScanType.Sca,
CliIgnoreType.CVE,
detection.detectionDetails.alert.cveIdentifier
)
})
addActionButton(
id = IGNORE_VIOLATION_ID,
text = CycodeBundle.message("violationCardIgnoreViolationBtn"),
onClick = {
cycode(project).applyIgnoreFromFileAnnotation(
CliScanType.Sca,
CliIgnoreType.CVE,
detection.detectionDetails.alert.cveIdentifier
)
}
)
}

return getContent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,25 @@ import com.intellij.openapi.project.Project
import javax.swing.JComponent

class SecretActions(val project: Project) : CardActions() {

companion object {
private const val IGNORE_VIOLATION_ID = "ignore_violation"
}

fun addContent(detection: SecretDetection): JComponent {
addActionButton(CycodeBundle.message("violationCardIgnoreViolationBtn"), onClick = {
if (detection.detectionDetails.detectedValue != null) {
cycode(project).applyIgnoreFromFileAnnotation(
CliScanType.Secret,
CliIgnoreType.VALUE,
detection.detectionDetails.detectedValue!!
)
addActionButton(
id = IGNORE_VIOLATION_ID,
text = CycodeBundle.message("violationCardIgnoreViolationBtn"),
onClick = {
if (detection.detectionDetails.detectedValue != null) {
cycode(project).applyIgnoreFromFileAnnotation(
CliScanType.Secret,
CliIgnoreType.VALUE,
detection.detectionDetails.detectedValue!!
)
}
}
})
)

return getContent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,20 @@ class CycodeService(val project: Project) : Disposable {
}
}

fun getAiRemediation(detectionId: String, onSuccess: (AiRemediationResultData) -> Unit) {
fun getAiRemediation(
detectionId: String,
onSuccess: (AiRemediationResultData) -> Unit,
onFailure: () -> 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)
} else {
onFailure()
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/messages/CycodeBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ violationCardCompanyGuidelinesTitle=Company Guidelines
violationCardCycodeGuidelinesTitle=Cycode Guidelines
violationCardAiRemediationTitle=AI Remediation
generateAiRemediationBtn=Generate AI Remediation
applyAiRemediationBtn=Apply AI suggested fix
generateAiRemediationBtnInProgress=Generating...
applyAiRemediationBtnInProgress=Applying...
violationCardIgnoreViolationBtn=Ignore this violation
# sca violation card
scaViolationCardShortSummary=<html>{0} | {1}</html>
Expand Down