Skip to content
Merged
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
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# libraries
annotations = "24.0.1"
jackson = "2.15.2"
markdown = "0.6.1"

# plugins
dokka = "1.9.10"
Expand All @@ -13,6 +14,7 @@ kover = "0.7.3"
[libraries]
annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" }
jackson = { group = "com.fasterxml.jackson.module", name = "jackson-module-kotlin", version.ref = "jackson" }
markdown = { group = "org.jetbrains", name = "markdown", version.ref = "markdown" }

[plugins]
changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ data class ScaDetectionDetails(
val line: Int,
val lineInFile: Int,
val dependencyPaths: String,
val licence: String?,
val license: String?,
val packageName: String,
val packageVersion: String,
val vulnerabilityDescription: String?,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.cycode.plugin.components.common

import com.intellij.ide.BrowserUtil
import com.cycode.plugin.components.openURL
import com.intellij.ui.HyperlinkLabel

fun createClickableLabel(htmlContent: String): HyperlinkLabel {
Expand All @@ -24,7 +24,3 @@ fun extractUrlFromHtml(html: String): String? {
val match = regex.find(html)
return match?.groupValues?.getOrNull(1)
}

fun openURL(url: String) {
BrowserUtil.browse(url)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab

import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection
import com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.header.ScaHeader
import com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.shortSummary.ScaShortSummary
import com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.summary.ScaSummary
import com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.title.ScaTitle
import com.intellij.util.ui.JBUI
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import javax.swing.JComponent
import javax.swing.JPanel

class ScaViolationCardContentTab {
fun getContent(detection: ScaDetection): JComponent {
val titlePanel = ScaTitle().getContent(detection)
val shortSummaryPanel = ScaShortSummary().getContent(detection)
val headerContentPanel = ScaHeader().getContent(detection)
val summaryPanel = ScaSummary().getContent(detection)

val gbc = GridBagConstraints()
gbc.fill = GridBagConstraints.BOTH
gbc.anchor = GridBagConstraints.NORTHWEST
gbc.gridx = 0
gbc.weightx = 1.0
gbc.weighty = 0.0
gbc.insets = JBUI.insetsBottom(10)

val panel = JPanel(GridBagLayout())

gbc.gridy++
panel.add(titlePanel, gbc)

gbc.gridy++
panel.add(shortSummaryPanel, gbc)

gbc.gridy++
panel.add(headerContentPanel, gbc)

gbc.gridy++
panel.add(summaryPanel, gbc.apply {
weighty = 1.0 // Expand the summary panel to fill the remaining space
})

// FIXME(MarshalX): something gives left border already. that's why left != right here
panel.border = JBUI.Borders.empty(10, 2, 0, 10)

return panel
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.header

import com.cycode.plugin.CycodeBundle
import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection
import com.intellij.ui.JBColor
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.JBUI
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel

class ScaHeader {
private val gbc = GridBagConstraints()
private val panel: JPanel = JPanel(GridBagLayout())

private fun addHeader(label: String, value: String) {
gbc.gridy++

panel.add(JLabel(label), gbc.apply {
gridx = 0
weightx = 0.125
})
panel.add(JBLabel(value).apply { setAllowAutoWrapping(true); setCopyable(true) }, gbc.apply {
gridx = 1
weightx = 0.875
anchor = GridBagConstraints.NORTHWEST
})
}

fun getContent(detection: ScaDetection): JComponent {
gbc.fill = GridBagConstraints.HORIZONTAL
gbc.anchor = GridBagConstraints.NORTHWEST
gbc.insets = JBUI.insets(2)

addHeader(CycodeBundle.message("scaViolationCardHeaderPackageField"), detection.detectionDetails.packageName)
addHeader(CycodeBundle.message("scaViolationCardHeaderVersionField"), detection.detectionDetails.packageVersion)

if (detection.detectionDetails.alert != null) {
val patchedVersion = detection.detectionDetails.alert.firstPatchedVersion
?: CycodeBundle.message("scaViolationCardHeaderPatchedVersionDefaultValue")
addHeader(CycodeBundle.message("scaViolationCardHeaderPatchedVersionField"), patchedVersion)
}

if (detection.detectionDetails.dependencyPaths.isNotEmpty()) {
addHeader(
CycodeBundle.message("scaViolationCardHeaderDependencyPathField"),
detection.detectionDetails.dependencyPaths
)
}

if (detection.detectionDetails.alert == null) {
// if non-permissive-license
addHeader(
CycodeBundle.message("scaViolationCardHeaderLicenseField"),
detection.detectionDetails.license ?: CycodeBundle.message("scaViolationCardHeaderLicenseDefaultValue")
)
}

panel.border = JBUI.Borders.compound(
JBUI.Borders.customLine(JBColor.GRAY, 1, 0, 1, 0),
JBUI.Borders.empty(10, 0)
)

return panel
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.shortSummary

import com.cycode.plugin.CycodeBundle
import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection
import com.intellij.ui.components.JBLabel
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import javax.swing.JComponent
import javax.swing.JPanel

class ScaShortSummary {
fun getContent(detection: ScaDetection): JComponent {
val gbc = GridBagConstraints()
gbc.fill = GridBagConstraints.NONE
gbc.anchor = GridBagConstraints.NORTHWEST
gbc.weightx = 1.0

val panel = JPanel(GridBagLayout())

if (detection.detectionDetails.alert != null) {
val cwe = detection.detectionDetails.vulnerabilityId
val severity = detection.severity
val shortSummary = CycodeBundle.message("scaViolationCardShortSummary", severity, cwe ?: "")

panel.add(JBLabel(shortSummary).apply { setAllowAutoWrapping(true); setCopyable(true) }, gbc)
}

return panel
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.summary

import com.cycode.plugin.CycodeBundle
import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection
import com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.convertMarkdownToHtml
import com.intellij.ui.BrowserHyperlinkListener
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.JBFont
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import javax.swing.JComponent
import javax.swing.JEditorPane
import javax.swing.JPanel
import javax.swing.JScrollPane
import javax.swing.text.DefaultCaret

class ScaSummary {
private fun getDescription(detection: ScaDetection): String {
val descriptionMarkdown = detection.detectionDetails.alert?.description ?: ""
return convertMarkdownToHtml(descriptionMarkdown)
}

fun getContent(detection: ScaDetection): JComponent {
val description = getDescription(detection)

val editorPane = JEditorPane().apply {
contentType = "text/html"
isEditable = false
isOpaque = false
background = UIUtil.TRANSPARENT_COLOR
}

editorPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true)
editorPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE)

(editorPane.caret as DefaultCaret).updatePolicy = DefaultCaret.NEVER_UPDATE

editorPane.editorKit = UIUtil.getHTMLEditorKit()
editorPane.text = description

// reset scroll position to top
editorPane.caretPosition = 0

editorPane.border = null

val panel = JPanel(GridBagLayout()).apply {
add(
JScrollPane(
editorPane,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
).apply { border = null },
GridBagConstraints().apply {
fill = GridBagConstraints.BOTH
anchor = GridBagConstraints.NORTHWEST
weightx = 1.0
weighty = 1.0
gridy = 1
}
)
}

if (detection.detectionDetails.alert?.description != null) {
// we don't want to show the summary label if there is no description
// editor pane still required to acquire the space on the card panel

panel.add(
JBLabel(CycodeBundle.message("scaViolationCardSummaryTitle")).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)
}
)
}

return panel
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.components.title

import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection
import com.cycode.plugin.icons.PluginIcons
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.JBUI
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel

class ScaTitle {
private fun getSeverityIcon(detection: ScaDetection): JLabel {
val icon = when (detection.severity.toLowerCase()) {
"critical" -> PluginIcons.CARD_SEVERITY_CRITICAL
"high" -> PluginIcons.CARD_SEVERITY_HIGH
"medium" -> PluginIcons.CARD_SEVERITY_MEDIUM
"low" -> PluginIcons.CARD_SEVERITY_LOW
else -> PluginIcons.CARD_SEVERITY_INFO
}

return JLabel(icon)
}

fun getContent(detection: ScaDetection): JComponent {
val gbc = GridBagConstraints()
gbc.fill = GridBagConstraints.BOTH
gbc.anchor = GridBagConstraints.NORTHWEST
gbc.gridy = 0

val panel = JPanel(GridBagLayout())

val severityIcon = getSeverityIcon(detection)
val titleMessage = detection.detectionDetails.alert?.summary ?: detection.message
val title = JBLabel(titleMessage).apply {
setAllowAutoWrapping(true)
setCopyable(true)
font = font.deriveFont(18f)
}

panel.add(severityIcon, gbc.apply {
gridx = 0
weightx = 0.0
anchor = GridBagConstraints.CENTER
insets = JBUI.insets(5)
})
panel.add(title, gbc.apply {
gridx = 1
weightx = 1.0
})

return panel
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab

import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.html.HtmlGenerator
import org.intellij.markdown.parser.MarkdownParser

fun convertMarkdownToHtml(markdown: String): String {
val flavour = CommonMarkFlavourDescriptor()
val parsedTree = MarkdownParser(flavour).buildMarkdownTreeFromString(markdown)
return HtmlGenerator(markdown, parsedTree, flavour).generateHtml()
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import com.cycode.plugin.cli.models.scanResult.DetectionBase
import com.cycode.plugin.cli.models.scanResult.ScanResultBase
import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection
import com.cycode.plugin.cli.models.scanResult.secret.SecretDetection
import com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.ScaViolationCardContentTab
import com.cycode.plugin.components.toolWindow.components.treeView.components.detectionNodeContextMenu.DetectionNodeContextMenu
import com.cycode.plugin.components.toolWindow.components.treeView.nodes.*
import com.cycode.plugin.icons.PluginIcons
import com.cycode.plugin.services.scanResults
Expand Down Expand Up @@ -64,7 +66,6 @@ class TreeView(

splitPane.firstComponent = treeView
splitPane.isShowDividerControls = true
splitPane.isShowDividerIcon = true

if (defaultRightPane != null) {
splitPane.secondComponent = defaultRightPane
Expand All @@ -90,22 +91,24 @@ class TreeView(
}

private fun createMouseListeners(): MouseAdapter {
val treeView = this

return object : MouseAdapter() {
override fun mousePressed(e: MouseEvent) {
val selRow: Int = tree.getRowForLocation(e.x, e.y)
val selPath: TreePath? = tree.getPathForLocation(e.x, e.y)
if (selRow != -1 && selPath != null) {
// single right mouse click
if (e.button == MouseEvent.BUTTON3 && e.clickCount == 1) {
DetectionNodeContextMenu(project, selPath).showPopup(e)
DetectionNodeContextMenu(treeView, project, selPath).showPopup(e)
}
}
}
}
}

private fun displayScaViolationCard(node: ScaDetectionNode) {
// not implemented yet
fun displayScaViolationCard(node: ScaDetectionNode) {
replaceRightPanel(ScaViolationCardContentTab().getContent(node.detection))
}

private fun convertSeverityToIcon(severity: String): Icon {
Expand Down
Loading