From c41d0108ac76106b92d44320b2b78ec5835998a0 Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Wed, 21 Feb 2024 14:08:05 +0100 Subject: [PATCH] fix: source dataflow from new issue data field --- .../SuggestionDescriptionPanelFromLS.kt | 46 +++++++++---------- .../snyk/common/lsp/SnykLanguageClient.kt | 5 +- src/main/kotlin/snyk/common/lsp/Types.kt | 15 +++++- 3 files changed, 37 insertions(+), 29 deletions(-) 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 b427adc82..1de7c6c9c 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 @@ -27,6 +27,7 @@ import io.snyk.plugin.ui.panelGridConstraints import io.snyk.plugin.ui.toolwindow.ReportFalsePositiveDialog import io.snyk.plugin.ui.toolwindow.ReportFalsePositiveDialog.Companion.FALSE_POSITIVE_REPORTED_TEXT import io.snyk.plugin.ui.toolwindow.ReportFalsePositiveDialog.Companion.REPORT_FALSE_POSITIVE_TEXT +import snyk.common.lsp.DataFlow import snyk.common.lsp.MarkerPosition import snyk.common.lsp.ScanIssue import java.awt.Color @@ -85,8 +86,8 @@ class SuggestionDescriptionPanelFromLS( return panel } - private fun codeLine(range: MarkerPosition, file: SnykCodeFile?): JTextArea { - val component = JTextArea(getLineOfCode(range, file)) + private fun codeLine(content: String): JTextArea { + val component = JTextArea(content) component.font = io.snyk.plugin.ui.getFont(-1, 14, component.font) component.isEditable = false return component @@ -129,44 +130,39 @@ class SuggestionDescriptionPanelFromLS( } private fun dataFlowPanel(): JPanel? { - val markers = issue.additionalData.markers?.let { marker -> - marker.map { it.pos } - .filter { it.isNotEmpty() } - .flatten() - .distinctBy { it.file + it.rows?.firstOrNull() } - } ?: emptyList() + val dataFlow = issue.additionalData.dataFlow val panel = JPanel() - panel.layout = GridLayoutManager(1 + markers.size, 1, Insets(0, 0, 0, 0), -1, 5) + panel.layout = GridLayoutManager(1 + dataFlow.size, 1, Insets(0, 0, 0, 0), -1, 5) panel.add( - defaultFontLabel("Data Flow - ${markers.size} step${if (markers.size > 1) "s" else ""}", true), + defaultFontLabel("Data Flow - ${dataFlow.size} step${if (dataFlow.size > 1) "s" else ""}", true), baseGridConstraintsAnchorWest(0) ) panel.add( - stepsPanel(markers), + stepsPanel(dataFlow), baseGridConstraintsAnchorWest(1) ) return panel } - private fun stepsPanel(markers: List): JPanel { + private fun stepsPanel(dataflow: List): JPanel { val panel = JPanel() - panel.layout = GridLayoutManager(markers.size, 1, Insets(0, 0, 0, 0), 0, 0) + panel.layout = GridLayoutManager(dataflow.size, 1, Insets(0, 0, 0, 0), 0, 0) panel.background = UIUtil.getTextFieldBackground() - val maxFilenameLength = markers.asSequence() - .filter { it.file.isNotEmpty() } - .map { it.file.substringAfterLast('/', "").length } + val maxFilenameLength = dataflow.asSequence() + .filter { it.filePath.isNotEmpty() } + .map { it.filePath.substringAfterLast('/', "").length } .maxOrNull() ?: 0 val allStepPanels = mutableListOf() - markers.forEachIndexed { index, markerRange -> + dataflow.forEachIndexed { index, flow -> val stepPanel = stepPanel( index = index, - markerRange = markerRange, + flow = flow, maxFilenameLength = max(snykCodeFile.virtualFile.name.length, maxFilenameLength), allStepPanels = allStepPanels ) @@ -187,7 +183,7 @@ class SuggestionDescriptionPanelFromLS( private fun stepPanel( index: Int, - markerRange: MarkerPosition, + flow: DataFlow, maxFilenameLength: Int, allStepPanels: MutableList ): JPanel { @@ -199,7 +195,7 @@ class SuggestionDescriptionPanelFromLS( val fileName = snykCodeFile.virtualFile.name - val lineNumber = markerRange.rows?.get(0)?.plus(1) + val lineNumber = flow.flowRange.start.line + 1 val positionLinkText = "$fileName:$lineNumber".padEnd(maxFilenameLength + 5, ' ') val positionLabel = linkLabel( @@ -213,10 +209,10 @@ class SuggestionDescriptionPanelFromLS( if (!file.isValid) return@linkLabel val document = file.getDocument() - val lineStartOffset = document?.getLineStartOffset(markerRange.rows?.firstOrNull() ?: 0) ?: 0 - val startOffset = lineStartOffset + (markerRange.cols?.firstOrNull() ?: 0) - val lineEndOffset = document?.getLineStartOffset(markerRange.rows?.lastOrNull() ?: 0) ?: 0 - val endOffset = lineEndOffset + (markerRange.cols?.lastOrNull() ?: 0) - 1 + val lineStartOffset = document?.getLineStartOffset(flow.flowRange.start.line) ?: 0 + val startOffset = lineStartOffset + (flow.flowRange.start.character) + val lineEndOffset = document?.getLineStartOffset(flow.flowRange.end.line) ?: 0 + val endOffset = lineEndOffset + flow.flowRange.end.character - 1 navigateToSource(project, snykCodeFile.virtualFile, startOffset, endOffset) @@ -227,7 +223,7 @@ class SuggestionDescriptionPanelFromLS( } stepPanel.add(positionLabel, baseGridConstraintsAnchorWest(0, indent = 1)) - val codeLine = codeLine(markerRange, snykCodeFile) + val codeLine = codeLine(flow.content) codeLine.isOpaque = false stepPanel.add( codeLine, diff --git a/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt b/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt index 45b635fbd..ef42d86eb 100644 --- a/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt +++ b/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt @@ -38,6 +38,7 @@ import org.eclipse.lsp4j.WorkDoneProgressKind.report import org.eclipse.lsp4j.WorkDoneProgressReport import org.eclipse.lsp4j.jsonrpc.services.JsonNotification import org.eclipse.lsp4j.services.LanguageClient +import snyk.common.SnykCodeFileIssueComparator import snyk.trust.WorkspaceTrustService import java.util.Collections import java.util.concurrent.ArrayBlockingQueue @@ -147,12 +148,12 @@ class SnykLanguageClient : LanguageClient { private fun getSnykCodeResult(project: Project, snykScan: SnykScanParams): Map> { check(snykScan.product == "code") { "Expected Snyk Code scan result" } - return snykScan.issues + val map = snykScan.issues .groupBy { it.virtualFile } .map { (file, issues) -> SnykCodeFile(project, file!!) to issues.sorted() } .filter { it.second.isNotEmpty() } .toMap() - .toSortedMap { o1, o2 -> o1.virtualFile.path.compareTo(o2.virtualFile.path) } + return map.toSortedMap(SnykCodeFileIssueComparator(map)) } override fun createProgress(params: WorkDoneProgressCreateParams?): CompletableFuture { diff --git a/src/main/kotlin/snyk/common/lsp/Types.kt b/src/main/kotlin/snyk/common/lsp/Types.kt index 11f62fd02..d175e0d93 100644 --- a/src/main/kotlin/snyk/common/lsp/Types.kt +++ b/src/main/kotlin/snyk/common/lsp/Types.kt @@ -160,8 +160,16 @@ data class MarkerPosition( result = 31 * result + file.hashCode() return result } + } +data class DataFlow( + @SerializedName("position") val position: Int, + @SerializedName("filePath") val filePath: String, + @SerializedName("flowRange") val flowRange: Range, + @SerializedName("content") val content: String +) + data class IssueData( @SerializedName("message") val message: String, @SerializedName("leadURL") val leadURL: String?, @@ -176,7 +184,8 @@ data class IssueData( @SerializedName("rows") val rows: Point, @SerializedName("isSecurityType") val isSecurityType: Boolean, @SerializedName("priorityScore") val priorityScore: Int, - @SerializedName("hasAIFix") val hasAIFix: Boolean + @SerializedName("hasAIFix") val hasAIFix: Boolean, + @SerializedName("dataFlow") val dataFlow: List, ) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -204,6 +213,7 @@ data class IssueData( if (isSecurityType != other.isSecurityType) return false if (priorityScore != other.priorityScore) return false if (hasAIFix != other.hasAIFix) return false + if (dataFlow != other.dataFlow) return false return true } @@ -223,10 +233,11 @@ data class IssueData( result = 31 * result + isSecurityType.hashCode() result = 31 * result + priorityScore result = 31 * result + hasAIFix.hashCode() + result = 31 * result + dataFlow.hashCode() return result } } -data class HasAuthenticatedParam (@SerializedName("token") val token: String?) +data class HasAuthenticatedParam(@SerializedName("token") val token: String?) data class SnykTrustedFoldersParams(@SerializedName("trustedFolders") val trustedFolders: List)