From 58266b6c1a2566578a7e8e6e08589e3c540bea60 Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Fri, 30 Aug 2024 12:45:30 +0200 Subject: [PATCH] chore: cleanup old OSS functionality [IDE-609] (#586) * chore: cleanup after release * fix: compile errors * fix: more cleanup * fix: more compile error fixing * fix: SnykControllerImplTest * fix: remove old annotators from plugin.xml --- .../io/snyk/plugin/SnykPostStartupActivity.kt | 2 +- src/main/kotlin/io/snyk/plugin/Utils.kt | 12 - .../plugin/analytics/AnalyticsScanListener.kt | 17 -- .../io/snyk/plugin/events/SnykScanListener.kt | 5 - .../plugin/services/SnykTaskQueueService.kt | 87 ++---- .../SnykTreeScanTypeFilterActionGroup.kt | 6 +- .../plugin/ui/toolwindow/SnykToolWindow.kt | 28 +- .../ui/toolwindow/SnykToolWindowPanel.kt | 171 +----------- .../ui/toolwindow/SnykTreeCellRenderer.kt | 44 ---- .../nodes/leaf/VulnerabilityTreeNode.kt | 19 -- .../nodes/secondlevel/FileTreeNode.kt | 10 - .../panels/VulnerabilityDescriptionPanel.kt | 247 ------------------ .../snyk/code/annotator/SnykOSSAnnotatorLS.kt | 4 +- .../kotlin/snyk/common/SnykCachedResults.kt | 24 +- .../snyk/common/lsp/LanguageServerWrapper.kt | 14 +- .../snyk/common/lsp/SnykLanguageClient.kt | 4 - .../container/ContainerBulkFileListener.kt | 41 ++- .../kotlin/snyk/oss/OssBulkFileListener.kt | 67 +---- src/main/kotlin/snyk/oss/OssGroupedResult.kt | 7 - src/main/kotlin/snyk/oss/OssResult.kt | 22 -- src/main/kotlin/snyk/oss/OssService.kt | 59 ----- .../kotlin/snyk/oss/OssTextRangeFinder.kt | 18 -- .../snyk/oss/OssVulnerabilitiesForFile.kt | 29 -- src/main/kotlin/snyk/oss/Vulnerability.kt | 41 --- .../snyk/oss/annotator/AnnotatorHelper.kt | 34 --- .../snyk/oss/annotator/OSSBaseAnnotator.kt | 174 ------------ .../snyk/oss/annotator/OSSGoModAnnotator.kt | 36 --- .../snyk/oss/annotator/OSSGradleAnnotator.kt | 75 ------ .../snyk/oss/annotator/OSSMavenAnnotator.kt | 70 ----- .../snyk/oss/annotator/OSSNpmAnnotator.kt | 90 ------- .../resources/META-INF/optional/withGo.xml | 2 - .../resources/META-INF/optional/withJSON.xml | 1 - .../resources/META-INF/optional/withJava.xml | 2 - .../META-INF/optional/withKotlin.xml | 2 +- .../resources/META-INF/optional/withXML.xml | 3 +- .../analytics/AnalyticsScanListenerTest.kt | 7 - .../plugin/cli/ConsoleCommandRunnerTest.kt | 65 +---- .../extensions/SnykControllerImplTest.kt | 41 +-- .../services/SnykTaskQueueServiceTest.kt | 20 +- .../SnykToolWindowPanelIntegTest.kt | 106 ++++---- .../ui/toolwindow/SnykToolWindowPanelTest.kt | 39 --- .../VulnerabilityDescriptionPanelTest.kt | 52 ---- .../snyk/oss/OssBulkFileListenerTest.kt | 76 ------ 43 files changed, 164 insertions(+), 1709 deletions(-) delete mode 100644 src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/leaf/VulnerabilityTreeNode.kt delete mode 100644 src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/secondlevel/FileTreeNode.kt delete mode 100644 src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/VulnerabilityDescriptionPanel.kt delete mode 100644 src/main/kotlin/snyk/oss/OssGroupedResult.kt delete mode 100644 src/main/kotlin/snyk/oss/OssResult.kt delete mode 100644 src/main/kotlin/snyk/oss/OssService.kt delete mode 100644 src/main/kotlin/snyk/oss/OssTextRangeFinder.kt delete mode 100644 src/main/kotlin/snyk/oss/OssVulnerabilitiesForFile.kt delete mode 100644 src/main/kotlin/snyk/oss/Vulnerability.kt delete mode 100644 src/main/kotlin/snyk/oss/annotator/AnnotatorHelper.kt delete mode 100644 src/main/kotlin/snyk/oss/annotator/OSSBaseAnnotator.kt delete mode 100644 src/main/kotlin/snyk/oss/annotator/OSSGoModAnnotator.kt delete mode 100644 src/main/kotlin/snyk/oss/annotator/OSSGradleAnnotator.kt delete mode 100644 src/main/kotlin/snyk/oss/annotator/OSSMavenAnnotator.kt delete mode 100644 src/main/kotlin/snyk/oss/annotator/OSSNpmAnnotator.kt delete mode 100644 src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt delete mode 100644 src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt diff --git a/src/main/kotlin/io/snyk/plugin/SnykPostStartupActivity.kt b/src/main/kotlin/io/snyk/plugin/SnykPostStartupActivity.kt index 63625c891..07538f7cf 100644 --- a/src/main/kotlin/io/snyk/plugin/SnykPostStartupActivity.kt +++ b/src/main/kotlin/io/snyk/plugin/SnykPostStartupActivity.kt @@ -81,7 +81,7 @@ class SnykPostStartupActivity : ProjectActivity { settings.lastTimeFeedbackRequestShown = Date.from(Instant.now()) } - if (isContainerEnabled()) { + if (settings.containerScanEnabled) { getKubernetesImageCache(project)?.cacheKubernetesFileFromProject() } diff --git a/src/main/kotlin/io/snyk/plugin/Utils.kt b/src/main/kotlin/io/snyk/plugin/Utils.kt index f7cada5e8..4cbdc2e06 100644 --- a/src/main/kotlin/io/snyk/plugin/Utils.kt +++ b/src/main/kotlin/io/snyk/plugin/Utils.kt @@ -54,8 +54,6 @@ import snyk.container.ContainerService import snyk.container.KubernetesImageCache import snyk.errorHandler.SentryErrorReporter import snyk.iac.IacScanService -import snyk.oss.OssService -import snyk.oss.OssTextRangeFinder import java.io.File import java.io.FileNotFoundException import java.net.URI @@ -72,8 +70,6 @@ import javax.swing.JComponent private val logger = Logger.getInstance("#io.snyk.plugin.UtilsKt") -fun getOssService(project: Project): OssService? = project.serviceIfNotDisposed() - fun getIacService(project: Project): IacScanService? = project.serviceIfNotDisposed() fun getKubernetesImageCache(project: Project): KubernetesImageCache? = project.serviceIfNotDisposed() @@ -110,8 +106,6 @@ fun isCliInstalled(): Boolean = ApplicationManager.getApplication().isUnitTestMo fun pluginSettings(): SnykApplicationSettingsStateService = getApplicationService() -fun getOssTextRangeFinderService(): OssTextRangeFinder = getApplicationService() - fun getPluginPath() = PathManager.getPluginsPath() + "/snyk-intellij-plugin" fun isProjectSettingsAvailable(project: Project?) = nonNull(project) && !project!!.isDefault @@ -224,14 +218,8 @@ fun controlExternalProcessWithProgressIndicator( checkCancelled.invoke() } -fun isIacEnabled(): Boolean = true - -fun isContainerEnabled(): Boolean = true - fun isFileListenerEnabled(): Boolean = pluginSettings().fileListenerEnabled -fun isSnykOSSLSEnabled(): Boolean = true // TODO: cleanup usage - fun isSnykIaCLSEnabled(): Boolean = false diff --git a/src/main/kotlin/io/snyk/plugin/analytics/AnalyticsScanListener.kt b/src/main/kotlin/io/snyk/plugin/analytics/AnalyticsScanListener.kt index 965cd489d..383c0b86f 100644 --- a/src/main/kotlin/io/snyk/plugin/analytics/AnalyticsScanListener.kt +++ b/src/main/kotlin/io/snyk/plugin/analytics/AnalyticsScanListener.kt @@ -9,7 +9,6 @@ import snyk.common.lsp.LanguageServerWrapper import snyk.common.lsp.commands.ScanDoneEvent import snyk.container.ContainerResult import snyk.iac.IacResult -import snyk.oss.OssResult @Service(Service.Level.PROJECT) class AnalyticsScanListener(val project: Project) { @@ -45,18 +44,6 @@ class AnalyticsScanListener(val project: Project) { start = System.currentTimeMillis() } - override fun scanningOssFinished(ossResult: OssResult) { - val scanDoneEvent = getScanDoneEvent( - System.currentTimeMillis() - start, - "Snyk Open Source", - ossResult.criticalSeveritiesCount(), - ossResult.highSeveritiesCount(), - ossResult.mediumSeveritiesCount(), - ossResult.lowSeveritiesCount() - ) - LanguageServerWrapper.getInstance().sendReportAnalyticsCommand(scanDoneEvent) - } - override fun scanningIacFinished(iacResult: IacResult) { val scanDoneEvent = getScanDoneEvent( System.currentTimeMillis() - start, @@ -81,10 +68,6 @@ class AnalyticsScanListener(val project: Project) { LanguageServerWrapper.getInstance().sendReportAnalyticsCommand(scanDoneEvent) } - override fun scanningOssError(snykError: SnykError) { - // do nothing - } - override fun scanningIacError(snykError: SnykError) { // do nothing } diff --git a/src/main/kotlin/io/snyk/plugin/events/SnykScanListener.kt b/src/main/kotlin/io/snyk/plugin/events/SnykScanListener.kt index bd3c5d12b..51c37e191 100644 --- a/src/main/kotlin/io/snyk/plugin/events/SnykScanListener.kt +++ b/src/main/kotlin/io/snyk/plugin/events/SnykScanListener.kt @@ -4,7 +4,6 @@ import com.intellij.util.messages.Topic import snyk.common.SnykError import snyk.container.ContainerResult import snyk.iac.IacResult -import snyk.oss.OssResult interface SnykScanListener { companion object { @@ -14,12 +13,8 @@ interface SnykScanListener { fun scanningStarted() - fun scanningOssFinished(ossResult: OssResult) - fun scanningIacFinished(iacResult: IacResult) - fun scanningOssError(snykError: SnykError) - fun scanningIacError(snykError: SnykError) fun scanningContainerFinished(containerResult: ContainerResult) diff --git a/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt b/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt index 5875faf55..f8b0e1766 100644 --- a/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt +++ b/src/main/kotlin/io/snyk/plugin/services/SnykTaskQueueService.kt @@ -16,19 +16,15 @@ import io.snyk.plugin.events.SnykSettingsListener import io.snyk.plugin.events.SnykTaskQueueListener import io.snyk.plugin.getContainerService import io.snyk.plugin.getIacService -import io.snyk.plugin.getOssService import io.snyk.plugin.getSnykCachedResults import io.snyk.plugin.getSnykCliDownloaderService import io.snyk.plugin.getSnykToolWindowPanel import io.snyk.plugin.getSyncPublisher import io.snyk.plugin.isCliDownloading import io.snyk.plugin.isCliInstalled -import io.snyk.plugin.isContainerEnabled -import io.snyk.plugin.isIacEnabled import io.snyk.plugin.isOssRunning import io.snyk.plugin.isSnykCodeRunning import io.snyk.plugin.isSnykIaCLSEnabled -import io.snyk.plugin.isSnykOSSLSEnabled import io.snyk.plugin.pluginSettings import io.snyk.plugin.refreshAnnotationsForOpenFiles import io.snyk.plugin.ui.SnykBalloonNotificationHelper @@ -69,29 +65,29 @@ class SnykTaskQueueService(val project: Project) { fun getTaskQueue() = taskQueue fun connectProjectToLanguageServer(project: Project) { - // subscribe to the settings changed topic + // subscribe to the settings changed topic val languageServerWrapper = LanguageServerWrapper.getInstance() getSnykToolWindowPanel(project)?.let { - project.messageBus.connect(it) - .subscribe( - SnykSettingsListener.SNYK_SETTINGS_TOPIC, - object : SnykSettingsListener { - override fun settingsChanged() { - languageServerWrapper.updateConfiguration() - } + project.messageBus.connect(it) + .subscribe( + SnykSettingsListener.SNYK_SETTINGS_TOPIC, + object : SnykSettingsListener { + override fun settingsChanged() { + languageServerWrapper.updateConfiguration() } - ) + } + ) + } + // Try to connect project for up to 30s + for (tries in 1..300) { + if (!languageServerWrapper.isInitialized) { + Thread.sleep(100) + continue } - // Try to connect project for up to 30s - for (tries in 1..300) { - if (!languageServerWrapper.isInitialized) { - Thread.sleep(100) - continue - } - languageServerWrapper.addContentRoots(project) - break - } + languageServerWrapper.addContentRoots(project) + break + } } fun scan(isStartup: Boolean) { @@ -106,22 +102,16 @@ class SnykTaskQueueService(val project: Project) { waitUntilCliDownloadedIfNeeded() indicator.checkCanceled() - if (settings.snykCodeSecurityIssuesScanEnable || settings.snykCodeQualityIssuesScanEnable || isSnykOSSLSEnabled()) { - if (!isStartup) { - LanguageServerWrapper.getInstance().sendScanCommand(project) - } - } - if (settings.ossScanEnable) { - if (!isSnykOSSLSEnabled()) { - scheduleOssScan() - } + if (!isStartup) { + LanguageServerWrapper.getInstance().sendScanCommand(project) } - if (isIacEnabled() && settings.iacScanEnabled) { + + if (settings.iacScanEnabled) { if (!isSnykIaCLSEnabled()) { scheduleIacScan() } } - if (isContainerEnabled() && settings.containerScanEnabled) { + if (settings.containerScanEnabled) { scheduleContainerScan() } } @@ -173,37 +163,6 @@ class SnykTaskQueueService(val project: Project) { }) } - private fun scheduleOssScan() { - taskQueue.run(object : Task.Backgroundable(project, "Snyk Open Source is scanning", true) { - override fun run(indicator: ProgressIndicator) { - if (!isCliInstalled()) return - val snykCachedResults = getSnykCachedResults(project) ?: return - if (snykCachedResults.currentOssResults != null) return - - ossScanProgressIndicator = indicator - scanPublisher?.scanningStarted() - - val ossResult = try { - getOssService(project)?.scan() - } finally { - ossScanProgressIndicator = null - } - if (ossResult == null || project.isDisposed) return - - if (indicator.isCanceled) { - taskQueuePublisher?.stopped(wasOssRunning = true) - } else { - if (ossResult.isSuccessful()) { - scanPublisher?.scanningOssFinished(ossResult) - } else { - ossResult.getFirstError()?.let { scanPublisher?.scanningOssError(it) } - } - } - invokeLater { refreshAnnotationsForOpenFiles(project) } - } - }) - } - private fun scheduleIacScan() { taskQueueIac.run(object : Task.Backgroundable(project, "Snyk Infrastructure as Code is scanning", true) { override fun run(indicator: ProgressIndicator) { diff --git a/src/main/kotlin/io/snyk/plugin/ui/actions/SnykTreeScanTypeFilterActionGroup.kt b/src/main/kotlin/io/snyk/plugin/ui/actions/SnykTreeScanTypeFilterActionGroup.kt index f5a8cb526..4020382f1 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/actions/SnykTreeScanTypeFilterActionGroup.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/actions/SnykTreeScanTypeFilterActionGroup.kt @@ -7,8 +7,6 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.ToggleAction import io.snyk.plugin.events.SnykResultsFilteringListener import io.snyk.plugin.getSyncPublisher -import io.snyk.plugin.isContainerEnabled -import io.snyk.plugin.isIacEnabled import io.snyk.plugin.pluginSettings import io.snyk.plugin.showSettings import io.snyk.plugin.ui.SnykBalloonNotificationHelper @@ -36,8 +34,8 @@ class SnykTreeScanTypeFilterActionGroup : ActionGroup() { createOssScanAction(), createSecurityIssuesScanAction(), createQualityIssuesScanAction(), - if (isIacEnabled()) createIacScanAction() else null, - if (isContainerEnabled()) createContainerScanAction() else null + createIacScanAction(), + createContainerScanAction() ).toTypedArray() private fun createScanFilteringAction( diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindow.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindow.kt index a5fc763cd..0b346ad39 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindow.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindow.kt @@ -13,14 +13,17 @@ import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.project.Project import com.intellij.openapi.ui.SimpleToolWindowPanel import com.intellij.ui.PopupHandler +import io.snyk.plugin.SnykFile import io.snyk.plugin.events.SnykScanListener +import io.snyk.plugin.events.SnykScanListenerLS import io.snyk.plugin.events.SnykTaskQueueListener import io.snyk.plugin.getSnykToolWindowPanel import io.snyk.plugin.ui.expandTreeNodeRecursively import snyk.common.SnykError +import snyk.common.lsp.ScanIssue +import snyk.common.lsp.SnykScanParams import snyk.container.ContainerResult import snyk.iac.IacResult -import snyk.oss.OssResult import javax.swing.JTree import javax.swing.tree.DefaultMutableTreeNode @@ -69,12 +72,8 @@ class SnykToolWindow(private val project: Project) : SimpleToolWindowPanel(false override fun scanningStarted() = updateActionsPresentation() - override fun scanningOssFinished(ossResult: OssResult) = updateActionsPresentation() - override fun scanningIacFinished(iacResult: IacResult) = updateActionsPresentation() - override fun scanningOssError(snykError: SnykError) = updateActionsPresentation() - override fun scanningIacError(snykError: SnykError) = updateActionsPresentation() override fun scanningContainerFinished(containerResult: ContainerResult) = updateActionsPresentation() @@ -82,6 +81,25 @@ class SnykToolWindow(private val project: Project) : SimpleToolWindowPanel(false override fun scanningContainerError(snykError: SnykError) = updateActionsPresentation() }) + project.messageBus.connect(this) + .subscribe(SnykScanListenerLS.SNYK_SCAN_TOPIC, object : SnykScanListenerLS { + override fun scanningSnykCodeFinished() { + updateActionsPresentation() + } + + override fun scanningOssFinished() { + updateActionsPresentation() + } + + override fun scanningError(snykScan: SnykScanParams) { + updateActionsPresentation() + } + + override fun onPublishDiagnostics(product: String, snykFile: SnykFile, issueList: List) = + Unit + }) + + project.messageBus.connect(this) .subscribe(SnykTaskQueueListener.TASK_QUEUE_TOPIC, object : SnykTaskQueueListener { override fun stopped( diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanel.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanel.kt index b72f055e4..c82ac0c9e 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanel.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanel.kt @@ -3,15 +3,12 @@ package io.snyk.plugin.ui.toolwindow import com.intellij.notification.NotificationAction import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.ReadAction import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.ui.SimpleToolWindowPanel import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.psi.PsiManager import com.intellij.ui.OnePixelSplitter import com.intellij.ui.TreeSpeedSearch import com.intellij.ui.TreeUIHelper @@ -27,21 +24,17 @@ import io.snyk.plugin.events.SnykScanListenerLS import io.snyk.plugin.events.SnykSettingsListener import io.snyk.plugin.events.SnykTaskQueueListener import io.snyk.plugin.getKubernetesImageCache -import io.snyk.plugin.getOssTextRangeFinderService import io.snyk.plugin.getSnykCachedResults import io.snyk.plugin.getSnykCachedResultsForProduct import io.snyk.plugin.getSnykCliDownloaderService import io.snyk.plugin.getSnykTaskQueueService import io.snyk.plugin.head import io.snyk.plugin.isCliDownloading -import io.snyk.plugin.isContainerEnabled import io.snyk.plugin.isContainerRunning -import io.snyk.plugin.isIacEnabled import io.snyk.plugin.isIacRunning import io.snyk.plugin.isOssRunning import io.snyk.plugin.isScanRunning import io.snyk.plugin.isSnykCodeRunning -import io.snyk.plugin.isSnykOSSLSEnabled import io.snyk.plugin.navigateToSource import io.snyk.plugin.pluginSettings import io.snyk.plugin.refreshAnnotationsForOpenFiles @@ -53,7 +46,6 @@ import io.snyk.plugin.ui.toolwindow.nodes.DescriptionHolderTreeNode import io.snyk.plugin.ui.toolwindow.nodes.ErrorHolderTreeNode import io.snyk.plugin.ui.toolwindow.nodes.NavigatableToSourceTreeNode import io.snyk.plugin.ui.toolwindow.nodes.leaf.SuggestionTreeNode -import io.snyk.plugin.ui.toolwindow.nodes.leaf.VulnerabilityTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootContainerIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootIacIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootOssTreeNode @@ -61,7 +53,6 @@ import io.snyk.plugin.ui.toolwindow.nodes.root.RootQualityIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootSecurityIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootTreeNodeBase import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.ErrorTreeNode -import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.FileTreeNode import io.snyk.plugin.ui.toolwindow.panels.IssueDescriptionPanel import io.snyk.plugin.ui.toolwindow.panels.SnykAuthPanel import io.snyk.plugin.ui.toolwindow.panels.SnykErrorPanel @@ -69,7 +60,6 @@ import io.snyk.plugin.ui.toolwindow.panels.StatePanel import io.snyk.plugin.ui.toolwindow.panels.TreePanel import io.snyk.plugin.ui.wrapWithScrollPane import org.jetbrains.annotations.TestOnly -import org.jetbrains.concurrency.runAsync import snyk.common.ProductType import snyk.common.SnykError import snyk.common.lsp.LanguageServerWrapper @@ -84,12 +74,7 @@ import snyk.iac.IacResult import snyk.iac.ignorableErrorCodes import snyk.iac.ui.toolwindow.IacFileTreeNode import snyk.iac.ui.toolwindow.IacIssueTreeNode -import snyk.oss.OssResult -import snyk.oss.OssVulnerabilitiesForFile -import snyk.oss.Vulnerability import java.awt.BorderLayout -import java.nio.file.InvalidPathException -import java.nio.file.Paths import java.util.Objects.nonNull import javax.swing.JPanel import javax.swing.JScrollPane @@ -118,8 +103,8 @@ class SnykToolWindowPanel( rootTreeNode.add(rootOssTreeNode) rootTreeNode.add(rootSecurityIssuesTreeNode) rootTreeNode.add(rootQualityIssuesTreeNode) - if (isIacEnabled()) rootTreeNode.add(rootIacIssuesTreeNode) - if (isContainerEnabled()) rootTreeNode.add(rootContainerIssuesTreeNode) + rootTreeNode.add(rootIacIssuesTreeNode) + rootTreeNode.add(rootContainerIssuesTreeNode) Tree(rootTreeNode).apply { this.isRootVisible = false } @@ -187,14 +172,6 @@ class SnykToolWindowPanel( } } - override fun scanningOssFinished(ossResult: OssResult) { - ApplicationManager.getApplication().invokeLater { - displayOssResults(ossResult) - notifyAboutErrorsIfNeeded(ProductType.OSS, ossResult) - refreshAnnotationsForOpenFiles(project) - } - } - override fun scanningIacFinished(iacResult: IacResult) { ApplicationManager.getApplication().invokeLater { displayIacResults(iacResult) @@ -231,26 +208,6 @@ class SnykToolWindowPanel( } } - override fun scanningOssError(snykError: SnykError) { - var ossResultsCount: Int? = null - ApplicationManager.getApplication().invokeLater { - if (snykError.message.contains(NO_OSS_FILES)) { - rootOssTreeNode.originalCliErrorMessage = snykError.message - ossResultsCount = NODE_NOT_SUPPORTED_STATE - } else { - rootOssTreeNode.originalCliErrorMessage = null - SnykBalloonNotificationHelper.showError(snykError.message, project) - if (snykError.message.startsWith(AUTH_FAILED_TEXT)) { - pluginSettings().token = null - } - } - removeAllChildren(listOf(rootOssTreeNode)) - updateTreeRootNodesPresentation(ossResultsCount = ossResultsCount) - chooseMainPanelToDisplay() - refreshAnnotationsForOpenFiles(project) - } - } - override fun scanningIacError(snykError: SnykError) { var iacResultsCount: Int? = null ApplicationManager.getApplication().invokeLater { @@ -300,18 +257,13 @@ class SnykToolWindowPanel( getSnykCachedResultsForProduct(project, ProductType.CODE_SECURITY) ?: return@invokeLater scanListenerLS.displaySnykCodeResults(codeResultsLS) } - if (!isSnykOSSLSEnabled()) { - ApplicationManager.getApplication().invokeLater { - val snykCachedResults = getSnykCachedResults(project) ?: return@invokeLater - snykCachedResults.currentOssResults?.let { displayOssResults(it) } - } - } else { - ApplicationManager.getApplication().invokeLater { - val ossResultsLS = - getSnykCachedResultsForProduct(project, ProductType.OSS) ?: return@invokeLater - scanListenerLS.displayOssResults(ossResultsLS) - } + + ApplicationManager.getApplication().invokeLater { + val ossResultsLS = + getSnykCachedResultsForProduct(project, ProductType.OSS) ?: return@invokeLater + scanListenerLS.displayOssResults(ossResultsLS) } + ApplicationManager.getApplication().invokeLater { val snykCachedResults = getSnykCachedResults(project) ?: return@invokeLater snykCachedResults.currentIacResult?.let { displayIacResults(it) } @@ -423,11 +375,9 @@ class SnykToolWindowPanel( getSnykCachedResults(project)?.cleanCaches() rootOssTreeNode.originalCliErrorMessage = null - if (isContainerEnabled()) { - getKubernetesImageCache(project)?.let { - it.clear() - it.cacheKubernetesFileFromProject() - } + getKubernetesImageCache(project)?.let { + it.clear() + it.cacheKubernetesFileFromProject() } ApplicationManager.getApplication().invokeLater { @@ -684,6 +634,7 @@ class SnykToolWindowPanel( count == 0 -> { NO_ISSUES_FOUND_TEXT } + count > 0 -> ProductType.OSS.getCountText(count, isUniqueCount = true) + addHMLPostfix count == NODE_NOT_SUPPORTED_STATE -> NO_SUPPORTED_PACKAGE_MANAGER_FOUND else -> throw IllegalStateException("ResultsCount is not meaningful") @@ -743,95 +694,6 @@ class SnykToolWindowPanel( revalidate() } - private fun displayOssResults(ossResult: OssResult) { - val userObjectsForExpandedChildren = userObjectsForExpandedNodes(rootOssTreeNode) - val selectedNodeUserObject = TreeUtil.findObjectInPath(vulnerabilitiesTree.selectionPath, Any::class.java) - - rootOssTreeNode.removeAllChildren() - - fun navigateToOssVulnerability( - filePath: String, - vulnerability: Vulnerability?, - ): () -> Unit = - { - runAsync { - var virtualFile: VirtualFile? = null - ReadAction.run { - virtualFile = VirtualFileManager.getInstance().findFileByNioPath(Paths.get(filePath)) - } - val vf = virtualFile - if (vf == null || !vf.isValid) { - return@runAsync - } - - if (vulnerability == null) { - navigateToSource(project, vf, 0) - } else { - ReadAction.run { - val psiFile = PsiManager.getInstance(project).findFile(vf) - val textRange = - psiFile?.let { getOssTextRangeFinderService().findTextRange(it, vulnerability) } - navigateToSource( - project = project, - virtualFile = vf, - selectionStartOffset = textRange?.startOffset ?: 0, - selectionEndOffset = textRange?.endOffset, - ) - } - } - } - } - - val settings = pluginSettings() - if (settings.ossScanEnable && settings.treeFiltering.ossResults) { - ossResult.allCliIssues?.forEach { vulnsForFile -> - if (vulnsForFile.vulnerabilities.isNotEmpty()) { - val ossGroupedResult = vulnsForFile.toGroupedResult() - val fileTreeNode = FileTreeNode(vulnsForFile, project) - rootOssTreeNode.add(fileTreeNode) - - ossGroupedResult.id2vulnerabilities.values - .filter { settings.hasSeverityEnabledAndFiltered(it.head.getSeverity()) } - .sortedByDescending { it.head.getSeverity() } - .forEach { - val navigateToSource = - try { - val filePath = sanitizeNavigationalFilePath(vulnsForFile) - navigateToOssVulnerability(filePath, it.head) - } catch (ignore: InvalidPathException) { - // empty navigation function for invalid path - {} - } - fileTreeNode.add(VulnerabilityTreeNode(it, project, navigateToSource)) - } - } - } - ossResult.errors.forEach { snykError -> - rootOssTreeNode.add( - ErrorTreeNode(snykError, project, navigateToOssVulnerability(snykError.path, null)), - ) - } - } - updateTreeRootNodesPresentation( - ossResultsCount = ossResult.issuesCount, - addHMLPostfix = buildHMLpostfix(ossResult), - ) - - smartReloadRootNode(rootOssTreeNode, userObjectsForExpandedChildren, selectedNodeUserObject) - } - - fun sanitizeNavigationalFilePath(vulnsForFile: OssVulnerabilitiesForFile): String { - val dirPath = vulnsForFile.path - val targetFilePath = vulnsForFile.sanitizedTargetFile - val filePath = - if (Paths.get(targetFilePath).isAbsolute) { - targetFilePath - } else { - Paths.get(dirPath, targetFilePath).toString() - } - return filePath - } - fun displayIacResults(iacResult: IacResult) { val userObjectsForExpandedChildren = userObjectsForExpandedNodes(rootIacIssuesTreeNode) val selectedNodeUserObject = TreeUtil.findObjectInPath(vulnerabilitiesTree.selectionPath, Any::class.java) @@ -1058,15 +920,6 @@ class SnykToolWindowPanel( } } - @Suppress("UNCHECKED_CAST") - fun selectNodeAndDisplayDescription(vulnerability: Vulnerability) = - selectAndDisplayNodeWithIssueDescription { treeNode -> - treeNode is VulnerabilityTreeNode && - (treeNode.userObject as Collection).any { - it == vulnerability - } - } - fun selectNodeAndDisplayDescription(iacIssue: IacIssue) = selectAndDisplayNodeWithIssueDescription { treeNode -> treeNode is IacIssueTreeNode && diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykTreeCellRenderer.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykTreeCellRenderer.kt index d2730a1cc..1faf59d85 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykTreeCellRenderer.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykTreeCellRenderer.kt @@ -13,14 +13,12 @@ import io.snyk.plugin.ui.PackageManagerIconProvider.Companion.getIcon import io.snyk.plugin.ui.getDisabledIcon import io.snyk.plugin.ui.snykCodeAvailabilityPostfix import io.snyk.plugin.ui.toolwindow.nodes.leaf.SuggestionTreeNode -import io.snyk.plugin.ui.toolwindow.nodes.leaf.VulnerabilityTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootContainerIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootIacIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootOssTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootQualityIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.root.RootSecurityIssuesTreeNode import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.ErrorTreeNode -import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.FileTreeNode import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.InfoTreeNode import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.SnykFileTreeNode import snyk.common.ProductType @@ -34,8 +32,6 @@ import snyk.iac.IacIssue import snyk.iac.IacIssuesForFile import snyk.iac.ui.toolwindow.IacFileTreeNode import snyk.iac.ui.toolwindow.IacIssueTreeNode -import snyk.oss.OssVulnerabilitiesForFile -import snyk.oss.Vulnerability import java.util.Locale import javax.swing.Icon import javax.swing.JTree @@ -58,46 +54,6 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { var text: String? = null var attributes = SimpleTextAttributes.REGULAR_ATTRIBUTES when (value) { - is VulnerabilityTreeNode -> { - val vulnerability = (value.userObject as Collection).first() - nodeIcon = SnykIcons.getSeverityIcon(vulnerability.getSeverity()) - text = vulnerability.getPackageNameTitle() - - val snykCachedResults = getSnykCachedResults(value.project) - if (snykCachedResults?.currentOssResults == null) { - attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES - nodeIcon = getDisabledIcon(nodeIcon) - } - } - - is FileTreeNode -> { - val fileVulns = value.userObject as OssVulnerabilitiesForFile - nodeIcon = getIcon(fileVulns.packageManager.lowercase(Locale.getDefault())) - toolTipText = - buildString { - append(fileVulns.relativePath ?: "") - append(fileVulns.sanitizedTargetFile) - append(ProductType.OSS.getCountText(value.childCount)) - } - text = - toolTipText.apply { - if (toolTipText.length > MAX_FILE_TREE_NODE_LENGTH) { - "..." + - this.substring( - this.length - MAX_FILE_TREE_NODE_LENGTH, - this.length, - ) - } - } - - val snykCachedResults = getSnykCachedResults(value.project) - if (snykCachedResults?.currentOssResults == null) { - attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES - text += OBSOLETE_SUFFIX - nodeIcon = getDisabledIcon(nodeIcon) - } - } - is SuggestionTreeNode -> { val issue = value.userObject as ScanIssue nodeIcon = SnykIcons.getSeverityIcon(issue.getSeverityAsEnum()) diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/leaf/VulnerabilityTreeNode.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/leaf/VulnerabilityTreeNode.kt deleted file mode 100644 index 6e7891dd1..000000000 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/leaf/VulnerabilityTreeNode.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.snyk.plugin.ui.toolwindow.nodes.leaf - -import com.intellij.openapi.project.Project -import io.snyk.plugin.ui.toolwindow.nodes.DescriptionHolderTreeNode -import io.snyk.plugin.ui.toolwindow.nodes.NavigatableToSourceTreeNode -import io.snyk.plugin.ui.toolwindow.panels.VulnerabilityDescriptionPanel -import snyk.oss.Vulnerability -import javax.swing.tree.DefaultMutableTreeNode - -class VulnerabilityTreeNode( - private val groupedVulns: Collection, - val project: Project, - override val navigateToSource: () -> Unit -) : DefaultMutableTreeNode(groupedVulns), NavigatableToSourceTreeNode, DescriptionHolderTreeNode { - - override fun getDescriptionPanel(): VulnerabilityDescriptionPanel { - return VulnerabilityDescriptionPanel(groupedVulns) - } -} diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/secondlevel/FileTreeNode.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/secondlevel/FileTreeNode.kt deleted file mode 100644 index b84f4c002..000000000 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/nodes/secondlevel/FileTreeNode.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.snyk.plugin.ui.toolwindow.nodes.secondlevel - -import com.intellij.openapi.project.Project -import snyk.oss.OssVulnerabilitiesForFile -import javax.swing.tree.DefaultMutableTreeNode - -class FileTreeNode( - ossVulnerabilitiesForFile: OssVulnerabilitiesForFile, - val project: Project -) : DefaultMutableTreeNode(ossVulnerabilitiesForFile) 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 deleted file mode 100644 index b2d49a7cb..000000000 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/VulnerabilityDescriptionPanel.kt +++ /dev/null @@ -1,247 +0,0 @@ -package io.snyk.plugin.ui.toolwindow.panels - -import com.intellij.ui.components.ActionLink -import com.intellij.uiDesigner.core.GridLayoutManager -import com.intellij.util.ui.JBUI -import io.snyk.plugin.ui.DescriptionHeaderPanel -import io.snyk.plugin.ui.addRowOfItemsToPanel -import io.snyk.plugin.ui.baseGridConstraintsAnchorWest -import io.snyk.plugin.ui.boldLabel -import io.snyk.plugin.ui.descriptionHeaderPanel -import io.snyk.plugin.ui.getReadOnlyClickableHtmlJEditorPane -import io.snyk.plugin.ui.insertTitleAndResizableTextIntoPanelColumns -import io.snyk.plugin.ui.panelGridConstraints -import io.snyk.plugin.ui.toolwindow.LabelProvider -import org.commonmark.parser.Parser -import org.commonmark.renderer.html.HtmlRenderer -import snyk.oss.Vulnerability -import javax.swing.JLabel -import javax.swing.JPanel - -class VulnerabilityDescriptionPanel( - private val groupedVulns: Collection -) : IssueDescriptionPanelBase( - title = groupedVulns.first().title, - severity = groupedVulns.first().getSeverity() -) { - - private val labelProvider: LabelProvider = LabelProvider() - private val vulnerability = groupedVulns.first() - - init { - createUI() - } - - override fun createMainBodyPanel(): Pair { - val lastRowToAddSpacer = 4 - val panel = JPanel( - GridLayoutManager(lastRowToAddSpacer + 1, 1, JBUI.insets(0, 10, 20, 20), -1, 10) - ).apply { - this.add( - getMainBodyPanel(), - baseGridConstraintsAnchorWest(1, indent = 0) - ) - this.add( - getDetailedPathsPanel(), - panelGridConstraints(2) - ) - this.add( - getOverviewPanel(), - panelGridConstraints(3) - ) - } - return Pair(panel, lastRowToAddSpacer) - } - - private fun getMainBodyPanel(): JPanel { - val panel = JPanel() - panel.layout = GridLayoutManager(11, 2, JBUI.insets(10, 0, 20, 0), 30, -1) - - panel.add( - boldLabel("Vulnerable module:"), - baseGridConstraintsAnchorWest(2, 0) - ) - panel.add( - JLabel(vulnerability.name)/*.apply { this.horizontalAlignment = SwingConstants.LEFT }*/, - baseGridConstraintsAnchorWest(2, 1, indent = 0) - ) - - val introducedThroughListPanel = getIntroducedThroughListPanel() - if (introducedThroughListPanel != null) { - panel.add( - boldLabel("Introduced through:"), - baseGridConstraintsAnchorWest(3, 0) - ) - panel.add( - introducedThroughListPanel, - baseGridConstraintsAnchorWest(3, 1, indent = 0) - ) - } - - val fixedInText = vulnerability.fixedIn?.let { - if (it.isNotEmpty()) { - it.joinToString(prefix = "${vulnerability.name}@", separator = ", @") - } else "Not fixed" - } - if (fixedInText != null) { - panel.add( - boldLabel("Fixed in:"), - baseGridConstraintsAnchorWest(4, 0) - ) - panel.add( - JLabel(fixedInText), - baseGridConstraintsAnchorWest(4, 1, indent = 0) - ) - } - - val exploit = vulnerability.exploit - if (exploit != null) { - panel.add( - boldLabel("Exploit maturity:"), - baseGridConstraintsAnchorWest(5, 0) - ) - panel.add( - JLabel(exploit), - baseGridConstraintsAnchorWest(5, 1, indent = 0) - ) - } - - return panel - } - - private fun getIntroducedThroughListPanel(): JPanel? { - val intros = groupedVulns - .mapNotNull { vulnerability -> - vulnerability.from.let { if (it.size > 1) it[1] else null } - } - .distinct() - - if (intros.isEmpty()) return null - - val panel = JPanel() - val packageManager = groupedVulns.first().packageManager - - panel.layout = GridLayoutManager(1, intros.size * 2, JBUI.emptyInsets(), 0, 0) - - addRowOfItemsToPanel( - panel = panel, - startingColumn = 0, - items = intros.map { item -> labelProvider.getDependencyLabel(packageManager, item) }, - separator = ", ", - firstSeparator = false, - opaqueSeparator = false - ) - return panel - } - - private fun getDetailedPathsPanel(): JPanel { - val detailsPanel = JPanel() - detailsPanel.layout = GridLayoutManager(2, 2, JBUI.emptyInsets(), -1, -1) - - detailsPanel.add( - boldLabel("Detailed paths").apply { - font = font.deriveFont(14f) - }, - baseGridConstraintsAnchorWest( - row = 0 - ) - ) - - detailsPanel.add( - getInnerDetailedPathsPanel(3), - panelGridConstraints( - row = 1 - ) - ) - - return detailsPanel - } - - private fun getInnerDetailedPathsPanel(itemsToShow: Int? = null): JPanel { - val detailsPanel = JPanel() - detailsPanel.layout = GridLayoutManager(groupedVulns.size + 2, 2, JBUI.emptyInsets(), -1, -1) - - groupedVulns - .take(itemsToShow ?: groupedVulns.size) - .forEachIndexed { index, vuln -> - val detailPanel = JPanel() - detailPanel.layout = GridLayoutManager(2, 2, JBUI.emptyInsets(), 30, 0) - - insertTitleAndResizableTextIntoPanelColumns( - panel = detailPanel, - row = 0, - title = "Introduced through:", - htmlText = vuln.from.joinToString(separator = " > ") - ) - - val remediationText = when { - vuln.upgradePath.isEmpty() || vuln.upgradePath.size < 2 -> "none" - else -> "Upgrade to " + vuln.upgradePath[1] - } - insertTitleAndResizableTextIntoPanelColumns( - panel = detailPanel, - row = 1, - title = "Fix:", - htmlText = remediationText - ) - - detailsPanel.add( - detailPanel, - panelGridConstraints( - row = index + 1 - ) - ) - } - - if (itemsToShow != null && itemsToShow < groupedVulns.size) { - val showMoreLabel = ActionLink("...and ${groupedVulns.size - itemsToShow} more") { - detailsPanel.removeAll() - detailsPanel.add( - getInnerDetailedPathsPanel(), - panelGridConstraints( - row = 0 - ) - ) - } - detailsPanel.add( - showMoreLabel, - baseGridConstraintsAnchorWest(groupedVulns.size + 1) - ) - } - - return detailsPanel - } - - private fun getOverviewPanel(): JPanel { - val overviewPanel = JPanel() - overviewPanel.layout = GridLayoutManager(2, 1, JBUI.insetsLeft(5), -1, 0) - - val descriptionPane = getReadOnlyClickableHtmlJEditorPane(getDescriptionAsHtml()) - - overviewPanel.add( - descriptionPane, - panelGridConstraints(1) - ) - return overviewPanel - } - - private fun getDescriptionAsHtml(): String { - val overviewMarkdownStr = vulnerability.description - - val parser = Parser.builder().build() - val document = parser.parse(overviewMarkdownStr) - - val renderer = HtmlRenderer.builder().escapeHtml(true).build() - - return renderer.render(document) - } - - override fun secondRowTitlePanel(): DescriptionHeaderPanel = descriptionHeaderPanel( - issueNaming = if (vulnerability.license == null) "Vulnerability" else "License", - cwes = vulnerability.identifiers?.cwe ?: emptyList(), - cves = vulnerability.identifiers?.cve ?: emptyList(), - cvssScore = vulnerability.cvssScore, - cvssV3 = vulnerability.cvssV3, - id = vulnerability.id - ) -} diff --git a/src/main/kotlin/snyk/code/annotator/SnykOSSAnnotatorLS.kt b/src/main/kotlin/snyk/code/annotator/SnykOSSAnnotatorLS.kt index 02156896b..fe6054190 100644 --- a/src/main/kotlin/snyk/code/annotator/SnykOSSAnnotatorLS.kt +++ b/src/main/kotlin/snyk/code/annotator/SnykOSSAnnotatorLS.kt @@ -4,7 +4,7 @@ import com.intellij.lang.annotation.AnnotationHolder import com.intellij.openapi.util.Disposer import com.intellij.psi.PsiFile import io.snyk.plugin.isOssRunning -import io.snyk.plugin.isSnykOSSLSEnabled +import io.snyk.plugin.pluginSettings import io.snyk.plugin.ui.toolwindow.SnykPluginDisposable import snyk.common.ProductType @@ -19,7 +19,7 @@ class SnykOSSAnnotatorLS : SnykAnnotator(product = ProductType.OSS) { holder: AnnotationHolder, ) { if (disposed) return - if (!isSnykOSSLSEnabled()) return + if (!pluginSettings().ossScanEnable) return if (isOssRunning(psiFile.project)) return super.apply(psiFile, annotationResult, holder) diff --git a/src/main/kotlin/snyk/common/SnykCachedResults.kt b/src/main/kotlin/snyk/common/SnykCachedResults.kt index 181173a06..3f1ceb591 100644 --- a/src/main/kotlin/snyk/common/SnykCachedResults.kt +++ b/src/main/kotlin/snyk/common/SnykCachedResults.kt @@ -19,7 +19,6 @@ import snyk.common.lsp.SnykScanParams import snyk.container.ContainerResult import snyk.container.ContainerService import snyk.iac.IacResult -import snyk.oss.OssResult @Service(Service.Level.PROJECT) class SnykCachedResults( @@ -43,8 +42,6 @@ class SnykCachedResults( val currentSnykCodeResultsLS: MutableMap> = mutableMapOf() val currentOSSResultsLS: MutableMap> = mutableMapOf() - var currentOssResults: OssResult? = null - get() = if (field?.isExpired() == false) field else null val currentContainerResultsLS: MutableMap> = mutableMapOf() var currentContainerResult: ContainerResult? = null @@ -60,7 +57,6 @@ class SnykCachedResults( var currentSnykCodeError: SnykError? = null fun cleanCaches() { - currentOssResults = null currentContainerResult = null currentIacResult = null currentOssError = null @@ -85,10 +81,6 @@ class SnykCachedResults( currentContainerError = null } - override fun scanningOssFinished(ossResult: OssResult) { - currentOssResults = ossResult - } - override fun scanningIacFinished(iacResult: IacResult) { currentIacResult = iacResult } @@ -97,16 +89,6 @@ class SnykCachedResults( currentContainerResult = containerResult } - override fun scanningOssError(snykError: SnykError) { - currentOssResults = null - currentOssError = - when { - snykError.message.startsWith(SnykToolWindowPanel.NO_OSS_FILES) -> null - snykError.message.startsWith(SnykToolWindowPanel.AUTH_FAILED_TEXT) -> null - else -> snykError - } - } - override fun scanningIacError(snykError: SnykError) { currentIacResult = null currentIacError = @@ -142,11 +124,9 @@ class SnykCachedResults( currentContainerError = null } - override fun scanningSnykCodeFinished() { - } + override fun scanningSnykCodeFinished() = Unit - override fun scanningOssFinished() { - } + override fun scanningOssFinished() = Unit override fun scanningError(snykScan: SnykScanParams) { when (snykScan.product) { diff --git a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt index 39a723749..c766b4069 100644 --- a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt +++ b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt @@ -16,10 +16,8 @@ import io.snyk.plugin.getContentRootVirtualFiles import io.snyk.plugin.getSnykTaskQueueService import io.snyk.plugin.getWaitForResultsTimeout import io.snyk.plugin.isSnykIaCLSEnabled -import io.snyk.plugin.isSnykOSSLSEnabled import io.snyk.plugin.pluginSettings import io.snyk.plugin.toLanguageServerURL -import io.snyk.plugin.toVirtualFile import io.snyk.plugin.ui.toolwindow.SnykPluginDisposable import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope @@ -163,7 +161,7 @@ class LanguageServerWrapper( fun shutdown(): Future<*> = executorService.submit { - if (process.isAlive) { + if (::process.isInitialized && process.isAlive) { languageServer.shutdown().get(1, TimeUnit.SECONDS) languageServer.exit() if (process.isAlive) { @@ -196,7 +194,13 @@ class LanguageServerWrapper( val normalizedRoots = mutableSetOf() for (root in contentRoots) { - val pathTrusted = trustService.isPathTrusted(root.toNioPath()) + val pathTrusted = try { + trustService.isPathTrusted(root.toNioPath()) + } catch (e: UnsupportedOperationException) { + // this must be temp filesystem so the path mapping doesn't work + continue + } + if (!pathTrusted) { logger.debug("Path not trusted: ${root.path}") continue @@ -366,7 +370,7 @@ class LanguageServerWrapper( "oauth" } return LanguageServerSettings( - activateSnykOpenSource = (isSnykOSSLSEnabled() && ps.ossScanEnable).toString(), + activateSnykOpenSource = ps.ossScanEnable.toString(), activateSnykCodeSecurity = ps.snykCodeSecurityIssuesScanEnable.toString(), activateSnykCodeQuality = ps.snykCodeQualityIssuesScanEnable.toString(), activateSnykIac = isSnykIaCLSEnabled().toString(), diff --git a/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt b/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt index 646885e80..04832ef75 100644 --- a/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt +++ b/src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt @@ -27,7 +27,6 @@ import io.snyk.plugin.SnykFile import io.snyk.plugin.events.SnykScanListenerLS import io.snyk.plugin.getContentRootVirtualFiles import io.snyk.plugin.getSyncPublisher -import io.snyk.plugin.isSnykOSSLSEnabled import io.snyk.plugin.pluginSettings import io.snyk.plugin.refreshAnnotationsForOpenFiles import io.snyk.plugin.toVirtualFile @@ -206,9 +205,6 @@ class SnykLanguageClient : @JsonNotification(value = "$/snyk.scan") fun snykScan(snykScan: SnykScanParams) { if (disposed) return - if (snykScan.product == "oss" && !isSnykOSSLSEnabled()) { - return - } try { getScanPublishersFor(snykScan.folderPath).forEach { (_, scanPublisher) -> processSnykScan(snykScan, scanPublisher) diff --git a/src/main/kotlin/snyk/container/ContainerBulkFileListener.kt b/src/main/kotlin/snyk/container/ContainerBulkFileListener.kt index 032edf68f..ac28a3f7e 100644 --- a/src/main/kotlin/snyk/container/ContainerBulkFileListener.kt +++ b/src/main/kotlin/snyk/container/ContainerBulkFileListener.kt @@ -9,7 +9,6 @@ import io.snyk.plugin.findPsiFileIgnoringExceptions import io.snyk.plugin.getKubernetesImageCache import io.snyk.plugin.getSnykCachedResults import io.snyk.plugin.getSnykToolWindowPanel -import io.snyk.plugin.isContainerEnabled import io.snyk.plugin.refreshAnnotationsForOpenFiles class ContainerBulkFileListener : SnykBulkFileListener() { @@ -19,36 +18,32 @@ class ContainerBulkFileListener : SnykBulkFileListener() { override fun before(project: Project, virtualFilesAffected: Set) { if (virtualFilesAffected.isEmpty()) return // clean Container cached results for Container related deleted/moved/renamed files - if (isContainerEnabled()) { - val imageCache = getKubernetesImageCache(project) - val kubernetesWorkloadFilesFromCache = imageCache?.getKubernetesWorkloadFilesFromCache() ?: emptySet() - val containerRelatedVirtualFilesAffected = virtualFilesAffected.filter { - kubernetesWorkloadFilesFromCache.contains(it) - } - imageCache?.cleanCache(virtualFilesAffected) - updateContainerCache(containerRelatedVirtualFilesAffected, project) + val imageCache = getKubernetesImageCache(project) + val kubernetesWorkloadFilesFromCache = imageCache?.getKubernetesWorkloadFilesFromCache() ?: emptySet() + val containerRelatedVirtualFilesAffected = virtualFilesAffected.filter { + kubernetesWorkloadFilesFromCache.contains(it) } + imageCache?.cleanCache(virtualFilesAffected) + updateContainerCache(containerRelatedVirtualFilesAffected, project) } override fun after(project: Project, virtualFilesAffected: Set) { if (virtualFilesAffected.isEmpty()) return // update Container cached results for Container related files - if (isContainerEnabled()) { - getKubernetesImageCache(project)?.updateCache(virtualFilesAffected) - val containerRelatedVirtualFilesAffected = virtualFilesAffected.filter { virtualFile -> - val knownContainerIssues: List = - getSnykCachedResults(project)?.currentContainerResult?.allCliIssues ?: emptyList() - val containerFilesCached = knownContainerIssues - .flatMap { it.workloadImages } - .map { it.virtualFile } - // if file was cached before - we should update cache even if it's none k8s file anymore - if (containerFilesCached.contains(virtualFile) || isDotSnykFile(virtualFile)) return@filter true + getKubernetesImageCache(project)?.updateCache(virtualFilesAffected) + val containerRelatedVirtualFilesAffected = virtualFilesAffected.filter { virtualFile -> + val knownContainerIssues: List = + getSnykCachedResults(project)?.currentContainerResult?.allCliIssues ?: emptyList() + val containerFilesCached = knownContainerIssues + .flatMap { it.workloadImages } + .map { it.virtualFile } + // if file was cached before - we should update cache even if it's none k8s file anymore + if (containerFilesCached.contains(virtualFile) || isDotSnykFile(virtualFile)) return@filter true - val psiFile = findPsiFileIgnoringExceptions(virtualFile, project) ?: return@filter false - YAMLImageExtractor.isKubernetes(psiFile) - } - updateContainerCache(containerRelatedVirtualFilesAffected, project) + val psiFile = findPsiFileIgnoringExceptions(virtualFile, project) ?: return@filter false + YAMLImageExtractor.isKubernetes(psiFile) } + updateContainerCache(containerRelatedVirtualFilesAffected, project) } private fun isDotSnykFile(virtualFile: VirtualFile) = virtualFile.name.endsWith(".snyk") diff --git a/src/main/kotlin/snyk/oss/OssBulkFileListener.kt b/src/main/kotlin/snyk/oss/OssBulkFileListener.kt index 8fe9b6544..f6ef8d233 100644 --- a/src/main/kotlin/snyk/oss/OssBulkFileListener.kt +++ b/src/main/kotlin/snyk/oss/OssBulkFileListener.kt @@ -2,12 +2,10 @@ package snyk.oss import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer import com.intellij.ide.impl.ProjectUtil -import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project -import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.newvfs.events.VFileEvent @@ -15,7 +13,6 @@ import com.intellij.openapi.vfs.readText import io.snyk.plugin.SnykBulkFileListener import io.snyk.plugin.SnykFile import io.snyk.plugin.getSnykCachedResults -import io.snyk.plugin.isSnykOSSLSEnabled import io.snyk.plugin.toLanguageServerURL import io.snyk.plugin.toSnykFileSet import org.eclipse.lsp4j.DidSaveTextDocumentParams @@ -23,33 +20,23 @@ import org.eclipse.lsp4j.TextDocumentIdentifier import snyk.common.lsp.LanguageServerWrapper class OssBulkFileListener : SnykBulkFileListener() { - private val log = logger() override fun before( project: Project, virtualFilesAffected: Set, - ) { - if (isSnykOSSLSEnabled()) { - return - } - dropOssCacheIfNeeded(project, virtualFilesAffected) - } + ) = Unit override fun after( project: Project, virtualFilesAffected: Set, ) { - if (isSnykOSSLSEnabled()) { - val filesAffected = toSnykFileSet(project, virtualFilesAffected) - updateCacheAndUI(filesAffected, project) - } - dropOssCacheIfNeeded(project, virtualFilesAffected) + val filesAffected = toSnykFileSet(project, virtualFilesAffected) + updateCacheAndUI(filesAffected, project) } override fun forwardEvents(events: MutableList) { val languageServerWrapper = LanguageServerWrapper.getInstance() - if (!isSnykOSSLSEnabled()) return if (!languageServerWrapper.isInitialized) return val languageServer = languageServerWrapper.languageServer @@ -75,23 +62,6 @@ class OssBulkFileListener : SnykBulkFileListener() { } } - private fun dropOssCacheIfNeeded( - project: Project, - virtualFilesAffected: Set, - ) { - val snykCachedResults = getSnykCachedResults(project) - if (snykCachedResults?.currentOssResults != null) { - val buildFileChanged = - virtualFilesAffected - .filter { scanInvalidatingFiles.contains(it.name) } - .find { ProjectRootManager.getInstance(project).fileIndex.isInContent(it) } - if (buildFileChanged != null) { - snykCachedResults.currentOssResults = null - log.debug("OSS cached results dropped due to changes in: $buildFileChanged") - } - } - } - private fun updateCacheAndUI( filesAffected: Set, project: Project, @@ -103,35 +73,4 @@ class OssBulkFileListener : SnykBulkFileListener() { VirtualFileManager.getInstance().asyncRefresh() DaemonCodeAnalyzer.getInstance(project).restart() } - - companion object { - // see https://github.com/snyk/snyk/blob/master/src/lib/detect.ts#L10 - private val scanInvalidatingFiles = - listOf( - "yarn.lock", - "package-lock.json", - "package.json", - "Gemfile", - "Gemfile.lock", - "pom.xml", - "build.gradle", - "build.gradle.kts", - "build.sbt", - "Pipfile", - "requirements.txt", - "Gopkg.lock", - "go.mod", - "vendor.json", - "project.assets.json", - "project.assets.json", - "packages.config", - "paket.dependencies", - "composer.lock", - "Podfile", - "Podfile.lock", - "pyproject.toml", - "poetry.lock", - ".snyk", - ) - } } diff --git a/src/main/kotlin/snyk/oss/OssGroupedResult.kt b/src/main/kotlin/snyk/oss/OssGroupedResult.kt deleted file mode 100644 index 0fd1b203d..000000000 --- a/src/main/kotlin/snyk/oss/OssGroupedResult.kt +++ /dev/null @@ -1,7 +0,0 @@ -package snyk.oss - -data class OssGroupedResult( - val id2vulnerabilities: Map>, - val uniqueCount: Int, - val pathsCount: Int -) diff --git a/src/main/kotlin/snyk/oss/OssResult.kt b/src/main/kotlin/snyk/oss/OssResult.kt deleted file mode 100644 index 15fcd4c54..000000000 --- a/src/main/kotlin/snyk/oss/OssResult.kt +++ /dev/null @@ -1,22 +0,0 @@ -package snyk.oss - -import io.snyk.plugin.Severity -import io.snyk.plugin.cli.CliResult -import snyk.common.SnykError - -class OssResult( - allOssVulnerabilities: List?, - errors: List = emptyList() -) : CliResult(allOssVulnerabilities, errors) { - - override val issuesCount = allOssVulnerabilities?.sumOf { it.uniqueCount } - - override fun countBySeverity(severity: Severity): Int? { - return allCliIssues?.sumOf { vulnerabilitiesForFile -> - vulnerabilitiesForFile.vulnerabilities - .filter { it.getSeverity() == severity } - .distinctBy { it.id } - .size - } - } -} diff --git a/src/main/kotlin/snyk/oss/OssService.kt b/src/main/kotlin/snyk/oss/OssService.kt deleted file mode 100644 index cb2ba2610..000000000 --- a/src/main/kotlin/snyk/oss/OssService.kt +++ /dev/null @@ -1,59 +0,0 @@ -package snyk.oss - -import com.intellij.openapi.components.Service -import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.LocalFileSystem -import io.snyk.plugin.pluginSettings -import io.snyk.plugin.services.CliAdapter -import snyk.common.RelativePathHelper -import snyk.common.SnykError - -/** - * Wrap work with Snyk CLI for OSS (`test` command). - */ -@Service(Service.Level.PROJECT) -class OssService(project: Project) : CliAdapter(project) { - - fun scan(): OssResult = execute(listOf("test")) - - override fun getProductResult(cliIssues: List?, snykErrors: List): OssResult { - return OssResult(cliIssues, snykErrors) - } - - override fun sanitizeCliIssues(cliIssues: OssVulnerabilitiesForFile): OssVulnerabilitiesForFile { - // .copy() will check nullability of fields - val virtualFile = cliIssues.virtualFile ?: LocalFileSystem.getInstance().findFileByPath(cliIssues.path) - // determine relative path for each issue at scan time - return cliIssues.copy( - vulnerabilities = cliIssues.vulnerabilities.map { it.copy() }, - project = project, - virtualFile = virtualFile, - relativePath = virtualFile?.let { RelativePathHelper().getRelativePath(virtualFile, project) } - ) - } - - override fun getCliIIssuesClass(): Class = OssVulnerabilitiesForFile::class.java - - override fun buildExtraOptions(): List { - val settings = pluginSettings() - val options: MutableList = mutableListOf() - - options.add("--json") - - val additionalParameters = settings.getAdditionalParameters(project) - val hasAllProjectsParam = additionalParameters != null && additionalParameters.contains(ALL_PROJECTS_PARAM) - - if (!hasAllProjectsParam) { - options.add(ALL_PROJECTS_PARAM) - } - - if (additionalParameters != null && additionalParameters.trim().isNotEmpty()) { - options.addAll(additionalParameters.trim().split(" ")) - } - return options - } - - companion object { - const val ALL_PROJECTS_PARAM = "--all-projects" - } -} diff --git a/src/main/kotlin/snyk/oss/OssTextRangeFinder.kt b/src/main/kotlin/snyk/oss/OssTextRangeFinder.kt deleted file mode 100644 index 7cbdf16ec..000000000 --- a/src/main/kotlin/snyk/oss/OssTextRangeFinder.kt +++ /dev/null @@ -1,18 +0,0 @@ -package snyk.oss - -import com.intellij.openapi.components.Service -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiFile - -@Service -class OssTextRangeFinder { - private val availableFinders: MutableList<(psiFile: PsiFile, vulnerability: Vulnerability) -> TextRange> = mutableListOf() - - fun registerFinder(finder: (psiFile: PsiFile, vulnerability: Vulnerability) -> TextRange) { - availableFinders.add(finder) - } - - fun findTextRange(psiFile: PsiFile, vulnerability: Vulnerability): TextRange? = availableFinders.asSequence() - .map { it(psiFile, vulnerability) } - .firstOrNull { it != TextRange.EMPTY_RANGE } -} diff --git a/src/main/kotlin/snyk/oss/OssVulnerabilitiesForFile.kt b/src/main/kotlin/snyk/oss/OssVulnerabilitiesForFile.kt deleted file mode 100644 index 182189517..000000000 --- a/src/main/kotlin/snyk/oss/OssVulnerabilitiesForFile.kt +++ /dev/null @@ -1,29 +0,0 @@ -package snyk.oss - -import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.VirtualFile - -data class OssVulnerabilitiesForFile( - val vulnerabilities: List, - private val displayTargetFile: String, - val packageManager: String, - val path: String, - val remediation: Remediation? = null, - val virtualFile: VirtualFile? = null, - val relativePath: String? = null, - val project: Project? = null -) { - val uniqueCount: Int get() = vulnerabilities.groupBy { it.id }.size - val sanitizedTargetFile: String get() = displayTargetFile.replace("-lock", "") - - fun toGroupedResult(): OssGroupedResult { - val id2vulnerabilities = vulnerabilities.groupBy({ it.id }, { it }) - val uniqueCount = id2vulnerabilities.keys.size - val pathsCount = id2vulnerabilities.values.flatten().size - - return OssGroupedResult(id2vulnerabilities, uniqueCount, pathsCount) - } - - data class Upgrade(val upgradeTo: String) - data class Remediation(val upgrade: Map) -} diff --git a/src/main/kotlin/snyk/oss/Vulnerability.kt b/src/main/kotlin/snyk/oss/Vulnerability.kt deleted file mode 100644 index e6a593e57..000000000 --- a/src/main/kotlin/snyk/oss/Vulnerability.kt +++ /dev/null @@ -1,41 +0,0 @@ -package snyk.oss - -import com.google.gson.annotations.SerializedName -import io.snyk.plugin.Severity - -data class Vulnerability( - val id: String, - val license: String? = null, - val identifiers: Identifiers? = null, - val title: String, - val description: String, - val language: String, - val packageManager: String, - val packageName: String, - private val severity: String, - val name: String, - val version: String, - val exploit: String? = null, - - @SerializedName("CVSSv3") - val cvssV3: String? = null, - val cvssScore: String? = null, - - val fixedIn: List? = null, - val from: List, - val upgradePath: List -) { - val obsolete: Boolean = false - val ignored: Boolean = false - - fun getPackageNameTitle(): String = "$packageName@$version: $title" - - fun getSeverity(): Severity = Severity.getFromName(severity) -} - -data class Identifiers ( - @SerializedName("CWE") - val cwe: List, - @SerializedName("CVE") - val cve: List -) diff --git a/src/main/kotlin/snyk/oss/annotator/AnnotatorHelper.kt b/src/main/kotlin/snyk/oss/annotator/AnnotatorHelper.kt deleted file mode 100644 index 00c01bfcc..000000000 --- a/src/main/kotlin/snyk/oss/annotator/AnnotatorHelper.kt +++ /dev/null @@ -1,34 +0,0 @@ -package snyk.oss.annotator - -import java.util.Locale - -object AnnotatorHelper { - - fun isFileSupported(filePath: String): Boolean = - listOf( - "arn.lock", - "package-lock.json", - "package.json", - "Gemfile", - "Gemfile.lock", - "pom.xml", - "build.gradle", - "build.gradle.kts", - "build.sbt", - "Pipfile", - "requirements.txt", - "Gopkg.lock", - "go.mod", - "vendor/vendor.json", - "obj/project.assets.json", - "project.assets.json", - "packages.config", - "paket.dependencies", - "composer.lock", - "Podfile", - "Podfile.lock", - "poetry.lock", - "mix.exs", - "mix.lock" - ).any { filePath.lowercase(Locale.getDefault()).endsWith(it) } -} diff --git a/src/main/kotlin/snyk/oss/annotator/OSSBaseAnnotator.kt b/src/main/kotlin/snyk/oss/annotator/OSSBaseAnnotator.kt deleted file mode 100644 index 0b8165592..000000000 --- a/src/main/kotlin/snyk/oss/annotator/OSSBaseAnnotator.kt +++ /dev/null @@ -1,174 +0,0 @@ -package snyk.oss.annotator - -import com.intellij.lang.annotation.AnnotationBuilder -import com.intellij.lang.annotation.AnnotationHolder -import com.intellij.lang.annotation.ExternalAnnotator -import com.intellij.openapi.progress.ProgressManager -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiFile -import io.snyk.plugin.Severity -import io.snyk.plugin.getSnykCachedResults -import io.snyk.plugin.isSnykOSSLSEnabled -import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel -import snyk.common.AnnotatorCommon -import snyk.common.intentionactions.AlwaysAvailableReplacementIntentionAction -import snyk.common.intentionactions.ShowDetailsIntentionActionBase -import snyk.oss.OssVulnerabilitiesForFile -import snyk.oss.Vulnerability -import kotlin.math.max - -abstract class OSSBaseAnnotator : ExternalAnnotator() { - // overrides needed for the Annotator to invoke apply(). We don't do anything here - override fun collectInformation(file: PsiFile): PsiFile = file - override fun doAnnotate(psiFile: PsiFile?) { - val filePath = psiFile?.virtualFile?.path ?: return - - if (AnnotatorHelper.isFileSupported(filePath)) { - AnnotatorCommon.prepareAnnotate(psiFile) - } - } - - override fun apply(psiFile: PsiFile, annotationResult: Unit, holder: AnnotationHolder) { - if (isSnykOSSLSEnabled()) return - - val issues = getIssuesForFile(psiFile) ?: return - - val filteredVulns = issues.vulnerabilities - .filter { AnnotatorCommon.isSeverityToShow(it.getSeverity()) } - .distinctBy { getIntroducingPackage(it) + it.id } - - filteredVulns.forEach { vulnerability -> - if (vulnerability.ignored || vulnerability.obsolete) return@forEach - val textRange = textRange(psiFile, vulnerability) - val highlightSeverity = vulnerability.getSeverity().getHighlightSeverity() - if (textRange != TextRange.EMPTY_RANGE) { - val annotationMessage = annotationMessage(vulnerability) - val annotationBuilder = - holder.newAnnotation(highlightSeverity, "Snyk: $annotationMessage").range(textRange) - val fixRange = fixRange(psiFile, vulnerability) - val fixVersion = getFixVersion(issues.remediation, vulnerability) - if (fixRange != TextRange.EMPTY_RANGE && fixVersion.isNotBlank()) { - addQuickFix(psiFile, vulnerability, annotationBuilder, fixRange, fixVersion) - } - annotationBuilder.withFix( - ShowDetailsIntentionAction(annotationMessage, vulnerability) - ) - annotationBuilder.create() - } - } - } - - fun getIssuesForFile(psiFile: PsiFile): OssVulnerabilitiesForFile? { - val ossResult = getSnykCachedResults(psiFile.project)?.currentOssResults ?: return null - val filePath = psiFile.virtualFile?.path ?: return null - if (!AnnotatorHelper.isFileSupported(filePath)) return null - - ProgressManager.checkCanceled() - - return ossResult.allCliIssues - ?.firstOrNull { filePath.endsWith(it.sanitizedTargetFile) } - } - - fun annotationMessage(vulnerability: Vulnerability): String = - "${vulnerability.title} in '${vulnerability.name}' id: ${vulnerability.id}" - - open fun addQuickFix( - psiFile: PsiFile, - vulnerability: Vulnerability, - annotationBuilder: AnnotationBuilder, - textRange: TextRange, - fixVersion: String - ) { - if (fixVersion.isNotBlank()) { - annotationBuilder.withFix( - AlwaysAvailableReplacementIntentionAction( - textRange, - fixVersion - ) - ) - } - } - - open fun getFixVersion(remediation: OssVulnerabilitiesForFile.Remediation?, vulnerability: Vulnerability): String { - val upgrade = getUpgradeProposal(vulnerability, remediation) - return upgrade?.upgradeTo?.split("@")?.get(1) ?: "" - } - - open fun getUpgradeProposal( - vulnerability: Vulnerability, - remediation: OssVulnerabilitiesForFile.Remediation? - ): OssVulnerabilitiesForFile.Upgrade? { - val upgradeKey = getIntroducingPackage(vulnerability) + "@" + getIntroducingPackageVersion(vulnerability) - return remediation?.upgrade?.get(upgradeKey) - } - - open fun textRange(psiFile: PsiFile, vulnerability: Vulnerability): TextRange { - val document = psiFile.viewProvider.document ?: return TextRange.EMPTY_RANGE - val lines = document.text.lines() - var lineStart = 0 - var lineEnd = 0 - var colStart = 0 - var colEnd = 0 - for (i in lines.indices) { - val line = lines[i] - if (lineMatches(psiFile, line, vulnerability)) { - lineStart = i - lineEnd = i - colStart = colStart(line, vulnerability) - colEnd = colEnd(line, vulnerability) - break - } - } - val lineOffSet = document.getLineStartOffset(lineStart) + colStart - val lineOffSetEnd = document.getLineStartOffset(lineEnd) + colEnd - return TextRange.create(lineOffSet, lineOffSetEnd) - } - - open fun fixRange(psiFile: PsiFile, vulnerability: Vulnerability): TextRange = textRange(psiFile, vulnerability) - - open fun colEnd(line: String, vulnerability: Vulnerability): Int { - val versionIndex = line.indexOf(getIntroducingPackageVersion(vulnerability)) - return if (versionIndex == -1) { - line.length - } else { - versionIndex + getIntroducingPackageVersion(vulnerability).length - } - } - - open fun colStart(line: String, vulnerability: Vulnerability) = - max(0, line.indexOf(getIntroducingPackage(vulnerability))) - - open fun getIntroducingPackage(vulnerability: Vulnerability): String { - return if (hasNoIntroducingPackage(vulnerability)) { - vulnerability.packageName - } else { - vulnerability.from[1].split("@")[0] - } - } - - open fun getIntroducingPackageVersion(vulnerability: Vulnerability): String { - return if (hasNoIntroducingPackage(vulnerability)) { - vulnerability.version - } else { - vulnerability.from[1].split("@")[1] - } - } - - private fun hasNoIntroducingPackage(vulnerability: Vulnerability) = - vulnerability.from.isEmpty() || vulnerability.from.size < 2 - - open fun lineMatches(psiFile: PsiFile, line: String, vulnerability: Vulnerability): Boolean = - line.contains(getIntroducingPackage(vulnerability)) - - inner class ShowDetailsIntentionAction( - override val annotationMessage: String, - private val vulnerability: Vulnerability - ) : ShowDetailsIntentionActionBase() { - - override fun selectNodeAndDisplayDescription(toolWindowPanel: SnykToolWindowPanel) { - toolWindowPanel.selectNodeAndDisplayDescription(vulnerability) - } - - override fun getSeverity(): Severity = vulnerability.getSeverity() - } -} diff --git a/src/main/kotlin/snyk/oss/annotator/OSSGoModAnnotator.kt b/src/main/kotlin/snyk/oss/annotator/OSSGoModAnnotator.kt deleted file mode 100644 index 3a2413012..000000000 --- a/src/main/kotlin/snyk/oss/annotator/OSSGoModAnnotator.kt +++ /dev/null @@ -1,36 +0,0 @@ -package snyk.oss.annotator - -import com.intellij.psi.PsiFile -import io.snyk.plugin.getOssTextRangeFinderService -import snyk.oss.OssVulnerabilitiesForFile -import snyk.oss.Vulnerability - -class OSSGoModAnnotator : OSSBaseAnnotator() { - - init { - getOssTextRangeFinderService().registerFinder(this::textRange) - } - - override fun lineMatches(psiFile: PsiFile, line: String, vulnerability: Vulnerability): Boolean { - val fileName = psiFile.virtualFile.path - return fileName.endsWith("go.mod") && !line.endsWith("// indirect") && - super.lineMatches(psiFile, line, vulnerability) - } - - override fun getFixVersion( - remediation: OssVulnerabilitiesForFile.Remediation?, - vulnerability: Vulnerability - ): String { - return super.getFixVersion(remediation, vulnerability).replace("@", " v") - } - - override fun getIntroducingPackage(vulnerability: Vulnerability): String { - return sanitize(super.getIntroducingPackage(vulnerability)) - } - - private fun sanitize(string: String): String { - return string.replace("/pkg/tool", "") - } - - override fun colEnd(line: String, vulnerability: Vulnerability): Int = line.length -} diff --git a/src/main/kotlin/snyk/oss/annotator/OSSGradleAnnotator.kt b/src/main/kotlin/snyk/oss/annotator/OSSGradleAnnotator.kt deleted file mode 100644 index 29845b492..000000000 --- a/src/main/kotlin/snyk/oss/annotator/OSSGradleAnnotator.kt +++ /dev/null @@ -1,75 +0,0 @@ -package snyk.oss.annotator - -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiComment -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiRecursiveElementVisitor -import com.intellij.psi.PsiWhiteSpace -import io.snyk.plugin.getOssTextRangeFinderService -import snyk.oss.Vulnerability - -class OSSGradleAnnotator : OSSBaseAnnotator() { - - init { - getOssTextRangeFinderService().registerFinder(this::textRange) - } - - override fun getIntroducingPackage(vulnerability: Vulnerability): String { - return super.getIntroducingPackage(vulnerability) - .replace("'", "").replace("\"", "") - } - - override fun textRange(psiFile: PsiFile, vulnerability: Vulnerability) = - findTextRanges(psiFile, vulnerability).first - - private fun findTextRanges( - psiFile: PsiFile, - vulnerability: Vulnerability - ): Pair { - if (!psiFile.name.startsWith("build.gradle")) return Pair( - TextRange.EMPTY_RANGE, - TextRange.EMPTY_RANGE - ) - val currentVersion = getIntroducingPackageVersion(vulnerability) - val packageName = getIntroducingPackage(vulnerability) - val visitor = GradleRecursiveVisitor(packageName, currentVersion) - psiFile.accept(visitor) - return Pair(visitor.artifactTextRange, visitor.versionTextRange) - } - - override fun fixRange(psiFile: PsiFile, vulnerability: Vulnerability): TextRange = - findTextRanges(psiFile, vulnerability).second - - internal class GradleRecursiveVisitor( - private val packageName: String, private val version: String - ) : PsiRecursiveElementVisitor() { - - var artifactTextRange: TextRange = TextRange.EMPTY_RANGE - var versionTextRange: TextRange = TextRange.EMPTY_RANGE - - override fun visitElement(element: PsiElement) { - if (isSearchedDependency(element)) { - val endOffset = element.textRange.endOffset - this.artifactTextRange = TextRange(element.textRange.startOffset, endOffset) - val indexOf = element.text.indexOf(version) - if (indexOf > 0) { - versionTextRange = TextRange(element.textRange.startOffset + indexOf, endOffset) - } - return - } - super.visitElement(element) - } - - private fun isSearchedDependency(element: PsiElement): Boolean { - if (element is PsiComment || element is PsiWhiteSpace || element is PsiFile) return false - // no version given - val depGroups = element.text.split(":") - return if (depGroups.size > 2) { - element.textMatches("$packageName:$version") || element.textMatches("'$packageName:$version'") - } else { - element.textMatches(packageName) || element.textMatches("'$packageName'") - } - } - } -} diff --git a/src/main/kotlin/snyk/oss/annotator/OSSMavenAnnotator.kt b/src/main/kotlin/snyk/oss/annotator/OSSMavenAnnotator.kt deleted file mode 100644 index d015487a3..000000000 --- a/src/main/kotlin/snyk/oss/annotator/OSSMavenAnnotator.kt +++ /dev/null @@ -1,70 +0,0 @@ -package snyk.oss.annotator - -import com.intellij.ide.highlighter.XmlFileType -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.XmlRecursiveElementVisitor -import com.intellij.psi.util.siblings -import com.intellij.psi.xml.XmlTag -import io.snyk.plugin.getOssTextRangeFinderService -import snyk.oss.OssVulnerabilitiesForFile -import snyk.oss.Vulnerability - -class OSSMavenAnnotator : OSSBaseAnnotator() { - - init { - getOssTextRangeFinderService().registerFinder(this::textRange) - } - - override fun getIntroducingPackage(vulnerability: Vulnerability): String { - return super.getIntroducingPackage(vulnerability).split(":")[1].replace("\"", "") - } - - override fun textRange(psiFile: PsiFile, vulnerability: Vulnerability): TextRange { - return fixRange(psiFile, vulnerability) - } - - override fun getFixVersion( - remediation: OssVulnerabilitiesForFile.Remediation?, - vulnerability: Vulnerability - ): String { - // we need to use the super class, as we need the group for finding the upgrade - val key = super.getIntroducingPackage(vulnerability) + "@" + super.getIntroducingPackageVersion(vulnerability) - return remediation?.upgrade?.get(key)?.upgradeTo?.split("@")?.get(1) ?: "" - } - - override fun fixRange(psiFile: PsiFile, vulnerability: Vulnerability): TextRange { - if (psiFile.fileType !is XmlFileType || psiFile.name != "pom.xml") return TextRange.EMPTY_RANGE - val currentVersion = getIntroducingPackageVersion(vulnerability) - val artifactName = getIntroducingPackage(vulnerability) - val visitor = MavenRecursiveVisitor(artifactName, currentVersion) - psiFile.accept(visitor) - return visitor.foundTextRange - } - - internal class MavenRecursiveVisitor( - private val artifactName: String, private val artifactVersion: String - ) : XmlRecursiveElementVisitor() { - - var foundTextRange: TextRange = TextRange.EMPTY_RANGE - - override fun visitElement(element: PsiElement) { - if (isSearchedDependency(element)) { - val siblings = element.siblings() - siblings.forEach { - if (it is XmlTag && it.name == "version" && it.value.text == artifactVersion) { - this.foundTextRange = TextRange(it.value.textRange.startOffset, it.value.textRange.endOffset) - return - } - } - } - super.visitElement(element) - } - - private fun isSearchedDependency(element: PsiElement): Boolean { - return element is XmlTag && element.name == "artifactId" && element.value.text == artifactName - && element.parent is XmlTag && (element.parent as XmlTag).name == "dependency" - } - } -} diff --git a/src/main/kotlin/snyk/oss/annotator/OSSNpmAnnotator.kt b/src/main/kotlin/snyk/oss/annotator/OSSNpmAnnotator.kt deleted file mode 100644 index 8445e3cd8..000000000 --- a/src/main/kotlin/snyk/oss/annotator/OSSNpmAnnotator.kt +++ /dev/null @@ -1,90 +0,0 @@ -package snyk.oss.annotator - -import com.intellij.json.JsonFileType -import com.intellij.json.psi.JsonStringLiteral -import com.intellij.json.psi.impl.JsonRecursiveElementVisitor -import com.intellij.lang.annotation.AnnotationBuilder -import com.intellij.openapi.util.TextRange -import com.intellij.psi.PsiComment -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiWhiteSpace -import io.snyk.plugin.getOssTextRangeFinderService -import snyk.common.intentionactions.AlwaysAvailableReplacementIntentionAction -import snyk.oss.OssVulnerabilitiesForFile -import snyk.oss.Vulnerability - -class OSSNpmAnnotator : OSSBaseAnnotator() { - - init { - getOssTextRangeFinderService().registerFinder(this::textRange) - } - - override fun textRange(psiFile: PsiFile, vulnerability: Vulnerability): TextRange { - if (psiFile.fileType !is JsonFileType || psiFile.name != "package.json") return TextRange.EMPTY_RANGE - val packageName = getIntroducingPackage(vulnerability) - val visitor = NpmRecursiveVisitor(packageName) - psiFile.accept(visitor) - return visitor.foundTextRange - } - - override fun addQuickFix( - psiFile: PsiFile, - vulnerability: Vulnerability, - annotationBuilder: AnnotationBuilder, - textRange: TextRange, - fixVersion: String - ) { - if (fixVersion.isNotBlank()) { - val msg = if (psiFile.parent?.findFile("package-lock.json") != null) { - "Please update your package-lock.json to finish fixing the vulnerability." - } else { - "" - } - annotationBuilder.withFix( - AlwaysAvailableReplacementIntentionAction( - textRange, - fixVersion, - message = msg - ) - ) - } - } - - override fun getFixVersion( - remediation: OssVulnerabilitiesForFile.Remediation?, - vulnerability: Vulnerability - ): String { - val upgrade = getUpgradeProposal(vulnerability, remediation) - val split = upgrade?.upgradeTo?.split("@") ?: return "" - return "\"${split[0]}\": \"${split[1]}\"" - } - - internal class NpmRecursiveVisitor(private val artifactName: String) : JsonRecursiveElementVisitor() { - - var foundTextRange: TextRange = TextRange.EMPTY_RANGE - - override fun visitElement(element: PsiElement) { - if (isSearchedDependency(element)) { - val value = element.getNextSiblingIgnoringWhitespace()?.getNextSiblingIgnoringWhitespace() ?: return - this.foundTextRange = TextRange(element.textRange.startOffset, value.textRange.endOffset) - return - } - super.visitElement(element) - } - - private fun PsiElement.getNextSiblingIgnoringWhitespace(): PsiElement? { - var candidate = this.nextSibling - while (candidate is PsiWhiteSpace) { - candidate = candidate.nextSibling - } - return candidate - } - - private fun isSearchedDependency(element: PsiElement): Boolean { - return element !is PsiComment && element !is PsiWhiteSpace && - element is JsonStringLiteral && - element.value == artifactName - } - } -} diff --git a/src/main/resources/META-INF/optional/withGo.xml b/src/main/resources/META-INF/optional/withGo.xml index 142bd28e2..418f6d0aa 100644 --- a/src/main/resources/META-INF/optional/withGo.xml +++ b/src/main/resources/META-INF/optional/withGo.xml @@ -1,6 +1,4 @@ - - diff --git a/src/main/resources/META-INF/optional/withJSON.xml b/src/main/resources/META-INF/optional/withJSON.xml index 9b36b70dd..4a893eae0 100644 --- a/src/main/resources/META-INF/optional/withJSON.xml +++ b/src/main/resources/META-INF/optional/withJSON.xml @@ -1,6 +1,5 @@ - diff --git a/src/main/resources/META-INF/optional/withJava.xml b/src/main/resources/META-INF/optional/withJava.xml index 57c5d7cae..418f6d0aa 100644 --- a/src/main/resources/META-INF/optional/withJava.xml +++ b/src/main/resources/META-INF/optional/withJava.xml @@ -1,6 +1,4 @@ - - diff --git a/src/main/resources/META-INF/optional/withKotlin.xml b/src/main/resources/META-INF/optional/withKotlin.xml index 8995548a6..16f4bd5a1 100644 --- a/src/main/resources/META-INF/optional/withKotlin.xml +++ b/src/main/resources/META-INF/optional/withKotlin.xml @@ -1,5 +1,5 @@ - + diff --git a/src/main/resources/META-INF/optional/withXML.xml b/src/main/resources/META-INF/optional/withXML.xml index 96238d8ea..16f4bd5a1 100644 --- a/src/main/resources/META-INF/optional/withXML.xml +++ b/src/main/resources/META-INF/optional/withXML.xml @@ -1,6 +1,5 @@ - - + diff --git a/src/test/kotlin/io/snyk/plugin/analytics/AnalyticsScanListenerTest.kt b/src/test/kotlin/io/snyk/plugin/analytics/AnalyticsScanListenerTest.kt index 4e0a34d53..883e74831 100644 --- a/src/test/kotlin/io/snyk/plugin/analytics/AnalyticsScanListenerTest.kt +++ b/src/test/kotlin/io/snyk/plugin/analytics/AnalyticsScanListenerTest.kt @@ -107,13 +107,6 @@ class AnalyticsScanListenerTest { verify { languageServerWrapper.sendReportAnalyticsCommand(any()) } } - @Test - fun `testScanListener scanningOssFinished should call language server to report analytics`() { - cut.snykScanListener.scanningOssFinished(mockk(relaxed = true)) - - verify { languageServerWrapper.sendReportAnalyticsCommand(any()) } - } - @Test fun `testScanListener scanningContainerFinished should call language server to report analytics`() { cut.snykScanListener.scanningContainerFinished(mockk(relaxed = true)) diff --git a/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt b/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt index 91dae7a6d..f5c28ee82 100644 --- a/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt +++ b/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt @@ -20,7 +20,6 @@ import io.mockk.verify import io.sentry.protocol.SentryId import io.snyk.plugin.DEFAULT_TIMEOUT_FOR_SCAN_WAITING_MS import io.snyk.plugin.getCliFile -import io.snyk.plugin.getOssService import io.snyk.plugin.getPluginPath import io.snyk.plugin.isCliInstalled import io.snyk.plugin.pluginSettings @@ -28,9 +27,9 @@ import io.snyk.plugin.removeDummyCliFile import io.snyk.plugin.resetSettings import io.snyk.plugin.services.download.SnykCliDownloaderService import io.snyk.plugin.setupDummyCliFile +import org.junit.Ignore import snyk.PLUGIN_ID import snyk.errorHandler.SentryErrorReporter -import snyk.oss.OssService import java.net.URLEncoder import java.util.Locale import java.util.UUID @@ -38,9 +37,6 @@ import java.util.concurrent.TimeUnit class ConsoleCommandRunnerTest : LightPlatformTestCase() { - private val ossService: OssService - get() = getOssService(project) ?: throw IllegalStateException("OSS service should be available") - override fun setUp() { super.setUp() unmockkAll() @@ -266,65 +262,6 @@ class ConsoleCommandRunnerTest : LightPlatformTestCase() { assertEquals("2023.1".length, generalCommandLine.environment["SNYK_INTEGRATION_ENVIRONMENT_VERSION"]?.length) } - @Suppress("SwallowedException") - fun testCommandExecutionRequestWhileCliIsDownloading() { - val cliFile = getCliFile() - cliFile.delete() - mockkStatic("io.snyk.plugin.UtilsKt") - every { isCliInstalled() } returns false - - val progressManager = ProgressManager.getInstance() as CoreProgressManager - val snykCliDownloaderService = service() - var downloadIndicator: ProgressIndicator? = null - - assertFalse("CLI binary should NOT exist at this stage", cliFile.exists()) - progressManager.runProcessWithProgressAsynchronously( - object : Task.Backgroundable(project, "Test CLI download", true) { - override fun run(indicator: ProgressIndicator) { - assertFalse("CLI binary should NOT exist at this stage", cliFile.exists()) - downloadIndicator = indicator - snykCliDownloaderService.downloadLatestRelease(indicator, project) - } - }, - EmptyProgressIndicator() - ) - - assertFalse("CLI binary should NOT exist at this stage", cliFile.exists()) - val testRunFuture = progressManager.runProcessWithProgressAsynchronously( - object : Task.Backgroundable(project, "Test CLI command invocation", true) { - override fun run(indicator: ProgressIndicator) { - while (!snykCliDownloaderService.isCliDownloading()) { - Thread.sleep(10) // lets wait till actual download begin - } - assertTrue( - "Downloading of CLI should be in progress at this stage.", - snykCliDownloaderService.isCliDownloading() - ) - // CLINotExistsException should happen while CLI is not there, - // but downloading and any CLI command is invoked - try { - val commands = ossService.buildCliCommandsList_TEST_ONLY(listOf("test")) - ConsoleCommandRunner().execute(commands, getPluginPath(), "", project) - fail("Should have thrown CliNotExistsException, as the CLI is still downloading.") - } catch (e: CliNotExistsException) { - // this is expected and actually desired - } - } - }, - EmptyProgressIndicator(), - null - ) - - testRunFuture.get(30000, TimeUnit.MILLISECONDS) - // we have to stop CLI download process otherwise partially downloaded CLI file will be visible in other tests - downloadIndicator?.cancel() - while (snykCliDownloaderService.isCliDownloading()) { - Thread.sleep(10) // lets wait till download actually stopped - } - assertFalse(cliFile.exists()) - verify(exactly = 0) { SentryErrorReporter.captureException(any()) } - } - fun testErrorReportedWhenExecutionTimeoutExpire() { val registryValue = Registry.get("snyk.timeout.results.waiting") val defaultValue = registryValue.asInteger() diff --git a/src/test/kotlin/io/snyk/plugin/extensions/SnykControllerImplTest.kt b/src/test/kotlin/io/snyk/plugin/extensions/SnykControllerImplTest.kt index a840c1f05..83c5e605b 100644 --- a/src/test/kotlin/io/snyk/plugin/extensions/SnykControllerImplTest.kt +++ b/src/test/kotlin/io/snyk/plugin/extensions/SnykControllerImplTest.kt @@ -2,42 +2,27 @@ package io.snyk.plugin.extensions import com.intellij.testFramework.LightPlatformTestCase import com.intellij.testFramework.PlatformTestUtil -import com.intellij.testFramework.replaceService import io.mockk.every +import io.mockk.justRun import io.mockk.mockk import io.mockk.mockkObject import io.mockk.mockkStatic import io.mockk.spyk import io.mockk.unmockkAll -import io.snyk.plugin.getOssService -import io.snyk.plugin.getSnykCachedResults +import io.mockk.verify import io.snyk.plugin.getSnykCliDownloaderService -import io.snyk.plugin.isCliInstalled import io.snyk.plugin.pluginSettings -import io.snyk.plugin.removeDummyCliFile -import io.snyk.plugin.resetSettings import io.snyk.plugin.services.download.SnykCliDownloaderService -import org.awaitility.Awaitility.await -import org.junit.Ignore import snyk.common.lsp.LanguageServerWrapper -import snyk.oss.OssResult -import snyk.oss.OssService import snyk.trust.confirmScanningAndSetWorkspaceTrustedStateIfNeeded -import java.util.concurrent.TimeUnit -//TODO rewrite -@Ignore("change to language server") class SnykControllerImplTest : LightPlatformTestCase() { - - private lateinit var ossServiceMock: OssService + private val languageServerWrapper = mockk() private lateinit var downloaderServiceMock: SnykCliDownloaderService override fun setUp() { super.setUp() unmockkAll() - resetSettings(project) - ossServiceMock = mockk(relaxed = true) - project.replaceService(OssService::class.java, ossServiceMock, project) mockkStatic("io.snyk.plugin.UtilsKt") mockkStatic("snyk.trust.TrustedProjectsKt") downloaderServiceMock = spyk(SnykCliDownloaderService()) @@ -45,27 +30,19 @@ class SnykControllerImplTest : LightPlatformTestCase() { every { getSnykCliDownloaderService() } returns downloaderServiceMock every { downloaderServiceMock.isFourDaysPassedSinceLastCheck() } returns false every { confirmScanningAndSetWorkspaceTrustedStateIfNeeded(any()) } returns true - - val languageServerWrapper = mockk(relaxed = true) mockkObject(LanguageServerWrapper.Companion) every { LanguageServerWrapper.getInstance() } returns languageServerWrapper - every { languageServerWrapper.getFeatureFlagStatus(any()) } returns true - + every { languageServerWrapper.isInitialized } returns true + justRun { languageServerWrapper.sendReportAnalyticsCommand(any()) } + justRun { languageServerWrapper.sendScanCommand(any()) } } override fun tearDown() { unmockkAll() - resetSettings(project) - removeDummyCliFile() super.tearDown() } fun testControllerCanTriggerScan() { - mockkStatic("io.snyk.plugin.UtilsKt") - every { isCliInstalled() } returns true - val fakeResult = OssResult(emptyList()) - every { getOssService(project)?.scan() } returns fakeResult - val settings = pluginSettings() settings.ossScanEnable = true settings.snykCodeSecurityIssuesScanEnable = false @@ -73,15 +50,11 @@ class SnykControllerImplTest : LightPlatformTestCase() { settings.iacScanEnabled = false settings.containerScanEnabled = false - getSnykCachedResults(project)?.currentContainerResult = null - val controller = SnykControllerImpl(project) controller.scan() PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue() - await().atMost(2, TimeUnit.SECONDS).until { - getSnykCachedResults(project)?.currentOssResults != null - } + verify { languageServerWrapper.sendScanCommand(project) } } } diff --git a/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt b/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt index 0305c6c67..935321bfd 100644 --- a/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt +++ b/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt @@ -14,12 +14,9 @@ import io.mockk.verify import io.snyk.plugin.getCliFile import io.snyk.plugin.getContainerService import io.snyk.plugin.getIacService -import io.snyk.plugin.getOssService import io.snyk.plugin.getSnykCachedResults import io.snyk.plugin.getSnykCliDownloaderService import io.snyk.plugin.isCliInstalled -import io.snyk.plugin.isContainerEnabled -import io.snyk.plugin.isIacEnabled import io.snyk.plugin.pluginSettings import io.snyk.plugin.removeDummyCliFile import io.snyk.plugin.resetSettings @@ -27,27 +24,23 @@ import io.snyk.plugin.services.download.CliDownloader import io.snyk.plugin.services.download.SnykCliDownloaderService import io.snyk.plugin.setupDummyCliFile import org.awaitility.Awaitility.await +import org.eclipse.lsp4j.services.LanguageServer import snyk.common.lsp.LanguageServerWrapper import snyk.container.ContainerResult import snyk.iac.IacResult -import snyk.oss.OssResult -import snyk.oss.OssService import snyk.trust.confirmScanningAndSetWorkspaceTrustedStateIfNeeded import java.util.concurrent.TimeUnit class SnykTaskQueueServiceTest : LightPlatformTestCase() { - private lateinit var ossServiceMock: OssService private lateinit var downloaderServiceMock: SnykCliDownloaderService + private val lsMock = mockk() override fun setUp() { super.setUp() unmockkAll() resetSettings(project) - ossServiceMock = mockk(relaxed = true) - project.replaceService(OssService::class.java, ossServiceMock, project) - mockkStatic("io.snyk.plugin.UtilsKt") mockkStatic("snyk.trust.TrustedProjectsKt") @@ -59,7 +52,10 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() { every { confirmScanningAndSetWorkspaceTrustedStateIfNeeded(any()) } returns true mockkObject(LanguageServerWrapper.Companion) - every { LanguageServerWrapper.getInstance() } returns mockk(relaxed = true) + val lswMock = mockk(relaxed = true) + every { LanguageServerWrapper.getInstance() } returns lswMock + every { lswMock.languageServer } returns lsMock + every { lswMock.isInitialized } returns true } override fun tearDown() { @@ -111,8 +107,6 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() { } private fun setupAppSettingsForDownloadTests(): SnykApplicationSettingsStateService { - every { getOssService(project)?.scan() } returns OssResult(null) - val settings = pluginSettings() settings.ossScanEnable = true settings.snykCodeSecurityIssuesScanEnable = false @@ -159,7 +153,6 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() { val fakeIacResult = IacResult(emptyList()) mockkStatic("io.snyk.plugin.UtilsKt") - every { isIacEnabled() } returns true every { isCliInstalled() } returns true every { getIacService(project)?.scan() } returns fakeIacResult @@ -171,7 +164,6 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() { fun testContainerScanTriggeredAndProduceResults() { mockkStatic("io.snyk.plugin.UtilsKt") - every { isContainerEnabled() } returns true every { isCliInstalled() } returns true val fakeContainerResult = ContainerResult(emptyList()) every { getContainerService(project)?.scan() } returns fakeContainerResult diff --git a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelIntegTest.kt b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelIntegTest.kt index eeaa92f2f..9ff2b1e19 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelIntegTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelIntegTest.kt @@ -19,10 +19,10 @@ import io.mockk.verify import io.snyk.plugin.Severity import io.snyk.plugin.events.SnykResultsFilteringListener import io.snyk.plugin.events.SnykScanListener +import io.snyk.plugin.events.SnykScanListenerLS import io.snyk.plugin.getContainerService import io.snyk.plugin.getIacService import io.snyk.plugin.getKubernetesImageCache -import io.snyk.plugin.getOssService import io.snyk.plugin.getSnykCachedResults import io.snyk.plugin.getSyncPublisher import io.snyk.plugin.isOssRunning @@ -33,18 +33,15 @@ import io.snyk.plugin.services.SnykTaskQueueService import io.snyk.plugin.setupDummyCliFile import io.snyk.plugin.ui.SnykBalloonNotificationHelper import io.snyk.plugin.ui.actions.SnykTreeMediumSeverityFilterAction -import io.snyk.plugin.ui.toolwindow.nodes.leaf.VulnerabilityTreeNode import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.ErrorTreeNode -import io.snyk.plugin.ui.toolwindow.nodes.secondlevel.FileTreeNode import io.snyk.plugin.ui.toolwindow.panels.SnykErrorPanel -import io.snyk.plugin.ui.toolwindow.panels.VulnerabilityDescriptionPanel import org.eclipse.lsp4j.ExecuteCommandParams import org.eclipse.lsp4j.services.LanguageServer import org.junit.Ignore -import org.junit.Test import snyk.common.SnykError import snyk.common.UIComponentFinder import snyk.common.lsp.LanguageServerWrapper +import snyk.common.lsp.SnykScanParams import snyk.common.lsp.commands.COMMAND_EXECUTE_CLI import snyk.container.ContainerIssue import snyk.container.ContainerIssuesForImage @@ -65,7 +62,6 @@ import snyk.iac.IacSuggestionDescriptionPanel import snyk.iac.IgnoreButtonActionListener import snyk.iac.ui.toolwindow.IacFileTreeNode import snyk.iac.ui.toolwindow.IacIssueTreeNode -import snyk.oss.Vulnerability import snyk.trust.confirmScanningAndSetWorkspaceTrustedStateIfNeeded import java.util.concurrent.CompletableFuture import javax.swing.JButton @@ -91,6 +87,9 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { private val scanPublisher get() = getSyncPublisher(project, SnykScanListener.SNYK_SCAN_TOPIC)!! + private val scanPublisherLS + get() = getSyncPublisher(project, SnykScanListenerLS.SNYK_SCAN_TOPIC)!! + private val fakeApiToken = "fake_token" private val lsMock = mockk(relaxed = true) @@ -158,8 +157,8 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { every { lsMock.workspaceService.executeCommand(param) } returns CompletableFuture.completedFuture(mapOf(Pair("stdOut", ossGoofJson))) - val ossResult = getOssService(project)?.scan()!! - scanPublisher.scanningOssFinished(ossResult) + LanguageServerWrapper.getInstance().sendScanCommand(project) + PlatformTestUtil.waitWhileBusy(toolWindowPanel.getTree()) } @@ -252,11 +251,12 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { fun `test when no OSS supported file found should display special text (not error) in node and description`() { mockkObject(SnykBalloonNotificationHelper) - val snykError = SnykError(SnykToolWindowPanel.NO_OSS_FILES, project.basePath.toString()) - val snykErrorControl = SnykError("control", project.basePath.toString()) + val snykError = + SnykScanParams("failed", "oss", project.basePath!!, emptyList(), SnykToolWindowPanel.NO_OSS_FILES) + val snykErrorControl = SnykScanParams("failed", "oss", project.basePath!!, emptyList(), "control") - scanPublisher.scanningOssError(snykErrorControl) - scanPublisher.scanningOssError(snykError) + scanPublisherLS.scanningError(snykErrorControl) + scanPublisherLS.scanningError(snykError) PlatformTestUtil.dispatchAllEventsInIdeEventQueue() val rootOssTreeNode = toolWindowPanel.getRootOssIssuesTreeNode() @@ -265,7 +265,7 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { SnykBalloonNotificationHelper.showError(any(), project) } assertTrue(getSnykCachedResults(project)?.currentOssError == null) - assertTrue(getSnykCachedResults(project)?.currentOssResults == null) + assertTrue(getSnykCachedResults(project)?.currentOSSResultsLS?.isEmpty() ?: false) val cliErrorMessage = rootOssTreeNode.originalCliErrorMessage assertTrue(cliErrorMessage != null && cliErrorMessage.startsWith(SnykToolWindowPanel.NO_OSS_FILES)) // node check @@ -285,7 +285,6 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { assertTrue(jEditorPane.text.contains(SnykToolWindowPanel.NO_OSS_FILES)) } - @Test fun `test when no IAC supported file found should display special text (not error) in node and description`() { mockkObject(SnykBalloonNotificationHelper) @@ -322,7 +321,6 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { assertTrue(jEditorPane.text.contains(SnykToolWindowPanel.NO_IAC_FILES)) } - @Test fun `test should ignore IaC failures in IaC scan results (no issues found)`() { mockkObject(SnykBalloonNotificationHelper) val jsonError = SnykError("Failed to parse JSON file", project.basePath.toString(), 1021) @@ -439,18 +437,27 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { fun `test OSS scan should redirect to Auth panel if token is invalid`() { mockkObject(SnykBalloonNotificationHelper) - val snykErrorControl = SnykError("control", project.basePath.toString()) - val snykError = SnykError("Authentication failed. Please check the API token on ", project.basePath.toString()) - scanPublisher.scanningOssError(snykErrorControl) - scanPublisher.scanningOssError(snykError) + val snykError = + SnykScanParams( + "failed", + "oss", + project.basePath!!, + emptyList(), + "Authentication failed. Please check the API token on " + ) + val snykErrorControl = SnykScanParams("failed", "oss", project.basePath!!, emptyList(), "control") + + scanPublisherLS.scanningError(snykErrorControl) + scanPublisherLS.scanningError(snykError) + PlatformTestUtil.dispatchAllEventsInIdeEventQueue() verify(exactly = 1, timeout = 2000) { - SnykBalloonNotificationHelper.showError(snykErrorControl.message, project) + SnykBalloonNotificationHelper.showError(snykErrorControl.errorMessage!!, project) } verify(exactly = 1, timeout = 2000) { - SnykBalloonNotificationHelper.showError(snykError.message, project) + SnykBalloonNotificationHelper.showError(snykError.errorMessage!!, project) } assertTrue(getSnykCachedResults(project)?.currentOssError == null) assertEquals( @@ -505,12 +512,20 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { } fun `test should display '(error)' in OSS root tree node when result is empty and error occurs`() { - val snykError = SnykError("an error", project.basePath.toString()) - scanPublisher.scanningOssError(snykError) + val snykError = + SnykScanParams("failed", "oss", project.basePath!!, emptyList(), "an error") + + scanPublisherLS.scanningError(snykError) + PlatformTestUtil.dispatchAllEventsInIdeEventQueue() - assertTrue(getSnykCachedResults(project)?.currentOssError == snykError) - assertTrue(getSnykCachedResults(project)?.currentOssResults == null) + assertTrue( + getSnykCachedResults(project)?.currentOssError == SnykError( + snykError.errorMessage!!, + project.basePath!! + ) + ) + assertTrue(getSnykCachedResults(project)?.currentOSSResultsLS?.isEmpty() ?: false) assertEquals( SnykToolWindowPanel.OSS_ROOT_TEXT + " (error)", toolWindowPanel.getRootOssIssuesTreeNode().userObject @@ -523,7 +538,7 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { PlatformTestUtil.dispatchAllEventsInIdeEventQueue() toolWindowPanel.updateTreeRootNodesPresentation(null, 0, 0, 0) - assertTrue(getSnykCachedResults(project)?.currentOssResults == null) + assertTrue(getSnykCachedResults(project)?.currentOSSResultsLS?.isEmpty() ?: false) assertEquals( SnykToolWindowPanel.OSS_ROOT_TEXT + " (scanning...)", toolWindowPanel.getRootOssIssuesTreeNode().userObject @@ -843,9 +858,19 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { setOf("ignored_image_name") val containerService = ContainerService(project) - val param = ExecuteCommandParams(COMMAND_EXECUTE_CLI, listOf(project.basePath, "container", "test", "ignored_image_name", "--json")) + val param = ExecuteCommandParams( + COMMAND_EXECUTE_CLI, + listOf(project.basePath, "container", "test", "ignored_image_name", "--json") + ) - every { lsMock.workspaceService.executeCommand(param) } returns CompletableFuture.completedFuture(mapOf(Pair("stdOut", containerResultJson))) + every { lsMock.workspaceService.executeCommand(param) } returns CompletableFuture.completedFuture( + mapOf( + Pair( + "stdOut", + containerResultJson + ) + ) + ) val containerResult = containerService.scan() setUpContainerTest(containerResult) @@ -901,31 +926,6 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { assertNotNull("IacSuggestionDescriptionPanel should not be null", iacDescriptionPanel) } - fun `test OSS node selected and Description shown on external request`() { - prepareTreeWithFakeOssResults() - - val rootOssIssuesTreeNode = toolWindowPanel.getRootOssIssuesTreeNode() - val firstOssFileNode = rootOssIssuesTreeNode.firstChild as FileTreeNode - val firstOssIssueNode = firstOssFileNode.firstChild as VulnerabilityTreeNode - val groupedVulns = firstOssIssueNode.userObject as Collection - val vulnerability = groupedVulns.first() - - // actual test run - toolWindowPanel.selectNodeAndDisplayDescription(vulnerability) - waitWhileTreeBusy() - - // Assertions - val selectedNodeUserObject = TreeUtil.findObjectInPath(toolWindowPanel.getTree().selectionPath, Any::class.java) - assertEquals(groupedVulns, selectedNodeUserObject) - - val vulnerabilityDescriptionPanel = - UIComponentFinder.getComponentByName( - toolWindowPanel.getDescriptionPanel(), - VulnerabilityDescriptionPanel::class - ) - assertNotNull("VulnerabilityDescriptionPanel should not be null", vulnerabilityDescriptionPanel) - } - fun `test Container node selected and Description shown on external request`() { // prepare Tree with fake Container results setUpContainerTest(null) diff --git a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelTest.kt b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelTest.kt index 1976aaa1a..b920408f0 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelTest.kt @@ -20,7 +20,6 @@ import org.junit.Test import snyk.UIComponentFinder import snyk.common.lsp.LanguageServerWrapper import snyk.common.lsp.SnykLanguageClient -import snyk.oss.OssVulnerabilitiesForFile import java.awt.Container import java.io.File import java.util.concurrent.CompletableFuture @@ -99,44 +98,6 @@ class SnykToolWindowPanelTest : LightPlatform4TestCase() { assertNotNull(treePanel) } - @Test - fun `sanitizeNavigationalFilePath should return a valid filepath when given abs path in displayTargetFile`() { - val tempFile = File.createTempFile("package", ".json") - tempFile.deleteOnExit() - val absPath = tempFile.toPath().toAbsolutePath() - val vulnsForFile = - OssVulnerabilitiesForFile( - displayTargetFile = absPath.toString(), - path = absPath.parent.toString(), - packageManager = "npm", - vulnerabilities = emptyList() - ) - cut = SnykToolWindowPanel(project) - - val filePath = cut.sanitizeNavigationalFilePath(vulnsForFile) - - assertEquals(absPath.toString(), filePath) - } - - @Test - fun `sanitizeNavigationalFilePath should return a valid filepath when given rel path in displayTargetFile`() { - val tempFile = File.createTempFile("package", ".json") - tempFile.deleteOnExit() - val absPath = tempFile.toPath().toAbsolutePath() - val vulnsForFile = - OssVulnerabilitiesForFile( - displayTargetFile = tempFile.name, - path = absPath.parent.toString(), - packageManager = "npm", - vulnerabilities = emptyList() - ) - cut = SnykToolWindowPanel(project) - - val filePath = cut.sanitizeNavigationalFilePath(vulnsForFile) - - assertEquals(absPath.toString(), filePath) - } - @Test fun `should not display onboarding panel and run scan directly`() { every { settings.token } returns "test-token" diff --git a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt deleted file mode 100644 index 2f7cda0a4..000000000 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.snyk.plugin.ui.toolwindow - -import com.google.gson.Gson -import com.intellij.ui.components.ActionLink -import io.snyk.plugin.ui.toolwindow.panels.VulnerabilityDescriptionPanel -import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.assertNotNull -import org.junit.Before -import org.junit.Test -import snyk.UIComponentFinder.getJButtonByText -import snyk.oss.Vulnerability -import java.io.FileReader - -class VulnerabilityDescriptionPanelTest { - private lateinit var cut: VulnerabilityDescriptionPanel - private lateinit var vulnerability: Vulnerability - - @Before - fun setup() { - val gson = Gson() - val json = FileReader("src/test/resources/npm-test-vulnerability.json") - vulnerability = gson.fromJson(json, Vulnerability::class.java) - cut = VulnerabilityDescriptionPanel(listOf(vulnerability)) - } - - @Test - fun `constructor should build panel with npm introduced-from info as link label`() { - val introducingDependency = vulnerability.from[1] - val actual = getJButtonByText(cut, introducingDependency) - assertNotNull(actual) - assertEquals(ActionLink::class, actual!!::class) - } - - @Test - fun `constructor should build panel with non-npm introduced-from info as plain text label`() { - val introducingDependency = vulnerability.from[1] - vulnerability = vulnerability.copy(packageManager = "not npm!") - cut = VulnerabilityDescriptionPanel(listOf(vulnerability)) - val actual = getJButtonByText(cut, introducingDependency) - assertNotNull(actual) - assertEquals(ActionLink::class, actual!!::class) - } - - @Test - fun `constructor should build panel with all CWEs as link labels`() { - val cwes = vulnerability.identifiers!!.cwe - cwes.forEach { cwe -> - val actual = getJButtonByText(cut, cwe) - assertNotNull("Expected to find label for $cwe, but was null", actual) - } - } -} diff --git a/src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt b/src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt deleted file mode 100644 index bef9afa32..000000000 --- a/src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -package snyk.oss - -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.fileEditor.FileDocumentManager -import com.intellij.openapi.roots.ProjectRootManager -import com.intellij.psi.PsiDocumentManager -import com.intellij.testFramework.PsiTestUtil -import com.intellij.testFramework.fixtures.BasePlatformTestCase -import io.mockk.mockk -import io.mockk.unmockkAll -import io.snyk.plugin.getSnykCachedResults -import io.snyk.plugin.resetSettings -import org.eclipse.lsp4j.services.LanguageServer -import org.junit.Test -import snyk.common.lsp.LanguageServerWrapper - -class OssBulkFileListenerTest : BasePlatformTestCase() { - private val lsMock = mockk(relaxed = true) - override fun setUp() { - super.setUp() - unmockkAll() - resetSettings(project) - val languageServerWrapper = LanguageServerWrapper.getInstance() - languageServerWrapper.languageServer = lsMock - languageServerWrapper.isInitialized = true - } - - override fun tearDown() { - resetSettings(project) - unmockkAll() - try { - super.tearDown() - } catch (ignore: Exception) { - // nothing to do - } - } - - @Test - fun `test currentOssResults should be dropped when build file changed`() { - val fakeOssResult = OssResult(null) - getSnykCachedResults(project)?.currentOssResults = fakeOssResult - - myFixture.configureByText("package.json", "main project file") - - assertNull( - "cached OssResult should be dropped after project build file changed", - getSnykCachedResults(project)?.currentOssResults - ) - } - - @Test - fun `test keep currentOssResults when out-of-project build file content changed`() { - val fakeOssResult = OssResult(null) - - val file = myFixture.addFileToProject("exclude/package.json", "") - val module = ProjectRootManager.getInstance(project).fileIndex.getModuleForFile(file.virtualFile)!! - PsiTestUtil.addExcludedRoot(module, file.virtualFile.parent) - - getSnykCachedResults(project)?.currentOssResults = fakeOssResult - - // change and save excluded file to trigger BulkFileListener to proceed events - ApplicationManager.getApplication().runWriteAction { - PsiDocumentManager.getInstance(project).getDocument(file)?.setText("updated content") - } - FileDocumentManager.getInstance().saveAllDocuments() - - // dispose virtual pointer manually created before - PsiTestUtil.removeExcludedRoot(module, file.virtualFile.parent) - - assertEquals( - "cached OssResult should NOT be dropped after NON project build file changed", - fakeOssResult, - getSnykCachedResults(project)?.currentOssResults - ) - } -}