diff --git a/CHANGELOG.md b/CHANGELOG.md index ab0e46699..36a552f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Snyk Security Changelog +## [2.7.13] +### Added +- Render Snyk Code vulnerabilities using HTML served by the Language Server behind a feature flag. + ## [2.7.12] ### Added - Mark ignored findings as ignored behind a feature flag. diff --git a/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotificationHelper.kt b/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotificationHelper.kt index 553dbbb26..9243db881 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotificationHelper.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotificationHelper.kt @@ -110,4 +110,12 @@ object SnykBalloonNotificationHelper { ) showBalloonForComponent(balloon, component, showAbove) } + fun showErrorBalloonForComponent(message: String, component: Component, showAbove: Boolean = false) { + val balloon = createBalloon( + message, + AllIcons.General.BalloonError, + MessageType.ERROR.popupBackground + ) + showBalloonForComponent(balloon, component, showAbove) + } } diff --git a/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotifications.kt b/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotifications.kt index 702def721..f1b0ac779 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotifications.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/SnykBalloonNotifications.kt @@ -27,6 +27,7 @@ object SnykBalloonNotifications { const val sastForOrgEnablementMessage = "Snyk Code is disabled by your organisation's configuration." const val networkErrorAlertMessage = "Not able to connect to Snyk server." + const val unexpectedErrorAlertMessage = "Something went wrong." private val notificationGroup = NotificationGroupManager.getInstance().getNotificationGroup("Snyk") diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/IssueDescriptionPanelBase.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/IssueDescriptionPanelBase.kt index 7927277ab..88ae70c7f 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/IssueDescriptionPanelBase.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/IssueDescriptionPanelBase.kt @@ -2,17 +2,30 @@ package io.snyk.plugin.ui.toolwindow.panels import com.intellij.ui.IdeBorderFactory import com.intellij.ui.SideBorder +import com.intellij.ui.jcef.JBCefApp +import com.intellij.ui.jcef.JBCefBrowserBuilder +import com.intellij.ui.jcef.JBCefJSQuery import com.intellij.uiDesigner.core.GridLayoutManager import com.intellij.util.ui.JBUI import com.intellij.util.ui.JBUI.Borders +import com.intellij.util.ui.UIUtil import icons.SnykIcons import io.snyk.plugin.Severity +import io.snyk.plugin.pluginSettings import io.snyk.plugin.ui.DescriptionHeaderPanel +import io.snyk.plugin.ui.SnykBalloonNotificationHelper +import io.snyk.plugin.ui.SnykBalloonNotifications import io.snyk.plugin.ui.addSpacer import io.snyk.plugin.ui.baseGridConstraints import io.snyk.plugin.ui.baseGridConstraintsAnchorWest +import io.snyk.plugin.ui.panelGridConstraints import io.snyk.plugin.ui.wrapWithScrollPane +import org.cef.browser.CefBrowser +import org.cef.browser.CefFrame +import org.cef.handler.CefLoadHandlerAdapter +import snyk.common.lsp.LanguageServerWrapper import java.awt.BorderLayout +import java.awt.Dimension import java.awt.Font import java.awt.Insets import javax.swing.JButton @@ -23,18 +36,43 @@ private val EMPTY_PANEL = JBUI.Panels.simplePanel() abstract class IssueDescriptionPanelBase( private val title: String, - private val severity: Severity + private val severity: Severity, + private val details: String? ) : JPanel(BorderLayout()), IssueDescriptionPanel { - /** * **MUST** be invoked in derived class to actually create the UI elements. * Can't be part of constructor due to `state` usage in underling abstract/open methods/props: */ protected fun createUI() { - this.add( - wrapWithScrollPane(descriptionBodyPanel()), - BorderLayout.CENTER - ) + if (pluginSettings().isGlobalIgnoresFeatureEnabled) { + if (!JBCefApp.isSupported()) { + SnykBalloonNotificationHelper.showError("Something went wrong.", null) + return + } + + val cefClient = JBCefApp.getInstance().createClient() + val jbCefBrowser = JBCefBrowserBuilder().setClient(cefClient).setEnableOpenDevToolsMenuItem(true).build() + + val panel = JPanel() + panel.add(jbCefBrowser.component, BorderLayout()) + this.add( + wrapWithScrollPane(panel), + BorderLayout.CENTER + ) + + if (this.details == null) { + SnykBalloonNotificationHelper.showError("Something went wrong.", null) + return + } + + jbCefBrowser.loadHTML(this.details, jbCefBrowser.cefBrowser.url) + + } else { + this.add( + wrapWithScrollPane(descriptionBodyPanel()), + BorderLayout.CENTER + ) + } if (isBottomPanelNeeded) { this.add( bottomPanel(), diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt index e6d83aa27..370d180d6 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt @@ -37,7 +37,11 @@ class SuggestionDescriptionPanel( private val snykCodeFile: SnykCodeFile, private val suggestion: SuggestionForFile, private val suggestionIndex: Int -) : IssueDescriptionPanelBase(title = suggestion.title, severity = suggestion.getSeverityAsEnum()) { +) : IssueDescriptionPanelBase( + title = suggestion.title, + severity = suggestion.getSeverityAsEnum(), + details = null +) { val project = snykCodeFile.project private val suggestionRange: MyTextRange? = suggestion.ranges?.getOrNull(suggestionIndex) diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanelFromLS.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanelFromLS.kt index 9e68b37e1..ea5b7a727 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanelFromLS.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanelFromLS.kt @@ -33,7 +33,11 @@ import kotlin.math.max class SuggestionDescriptionPanelFromLS( snykCodeFile: SnykCodeFile, private val issue: ScanIssue -) : IssueDescriptionPanelBase(title = getIssueTitle(issue), severity = issue.getSeverityAsEnum()) { +) : IssueDescriptionPanelBase( + title = getIssueTitle(issue), + severity = issue.getSeverityAsEnum(), + details = issue.additionalData.details +) { val project = snykCodeFile.project init { diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/VulnerabilityDescriptionPanel.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/VulnerabilityDescriptionPanel.kt index 9ec35659b..8e5cecfcb 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/VulnerabilityDescriptionPanel.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/VulnerabilityDescriptionPanel.kt @@ -20,7 +20,10 @@ import javax.swing.JPanel class VulnerabilityDescriptionPanel( private val groupedVulns: Collection -) : IssueDescriptionPanelBase(title = groupedVulns.first().title, severity = groupedVulns.first().getSeverity()) { +) : IssueDescriptionPanelBase( + title = groupedVulns.first().title, + severity = groupedVulns.first().getSeverity(), + details = null) { private val labelProvider: LabelProvider = LabelProvider() private val vulnerability = groupedVulns.first() diff --git a/src/main/kotlin/snyk/common/lsp/Types.kt b/src/main/kotlin/snyk/common/lsp/Types.kt index 64563370e..07f6c8dee 100644 --- a/src/main/kotlin/snyk/common/lsp/Types.kt +++ b/src/main/kotlin/snyk/common/lsp/Types.kt @@ -201,8 +201,7 @@ data class IssueData( @SerializedName("priorityScore") val priorityScore: Int, @SerializedName("hasAIFix") val hasAIFix: Boolean, @SerializedName("dataFlow") val dataFlow: List, - @SerializedName("isIgnored") val isIgnored: Boolean?, - @SerializedName("ignoreDetails") val ignoreDetails: IgnoreDetails?, + @SerializedName("details") val details: String, ) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -231,6 +230,7 @@ data class IssueData( if (priorityScore != other.priorityScore) return false if (hasAIFix != other.hasAIFix) return false if (dataFlow != other.dataFlow) return false + if (details != other.details) return false return true } @@ -251,6 +251,7 @@ data class IssueData( result = 31 * result + priorityScore result = 31 * result + hasAIFix.hashCode() result = 31 * result + dataFlow.hashCode() + result = 31 * result + details.hashCode() return result } } diff --git a/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt b/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt index 31e9c7ab5..254030189 100644 --- a/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt +++ b/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt @@ -27,10 +27,11 @@ import javax.swing.JTextArea class BaseImageRemediationDetailPanel( private val project: Project, - private val imageIssues: ContainerIssuesForImage + private val imageIssues: ContainerIssuesForImage, ) : IssueDescriptionPanelBase( title = imageIssues.imageName, - severity = imageIssues.getSeverities().maxOrNull() ?: Severity.UNKNOWN + severity = imageIssues.getSeverities().maxOrNull() ?: Severity.UNKNOWN, + details = null ) { private val targetImages: List = getKubernetesImageCache(project) diff --git a/src/main/kotlin/snyk/container/ui/ContainerIssueDetailPanel.kt b/src/main/kotlin/snyk/container/ui/ContainerIssueDetailPanel.kt index aa5ca7e0b..16ac07155 100644 --- a/src/main/kotlin/snyk/container/ui/ContainerIssueDetailPanel.kt +++ b/src/main/kotlin/snyk/container/ui/ContainerIssueDetailPanel.kt @@ -18,7 +18,11 @@ import javax.swing.JPanel class ContainerIssueDetailPanel( private val groupedVulns: Collection -) : IssueDescriptionPanelBase(title = groupedVulns.first().title, severity = groupedVulns.first().getSeverity()) { +) : IssueDescriptionPanelBase( + title = groupedVulns.first().title, + severity = groupedVulns.first().getSeverity(), + details = null +) { private val issue = groupedVulns.first() diff --git a/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt b/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt index eb651d365..ce01ac92d 100644 --- a/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt +++ b/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt @@ -29,7 +29,7 @@ class IacSuggestionDescriptionPanel( val issue: IacIssue, val psiFile: PsiFile?, val project: Project -) : IssueDescriptionPanelBase(title = issue.title, severity = issue.getSeverity()) { +) : IssueDescriptionPanelBase(title = issue.title, severity = issue.getSeverity(), details = null) { private val labelProvider = LabelProvider()