From e8aa1aa45724f461c510dc4f7304e69acfed78e4 Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Wed, 8 Jan 2025 14:27:53 +0100 Subject: [PATCH 1/7] fix: ensure workspace folder scans are only sent when folder configured --- .../snyk/plugin/SnykProjectManagerListener.kt | 2 +- .../snyk/common/lsp/LanguageServerWrapper.kt | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/io/snyk/plugin/SnykProjectManagerListener.kt b/src/main/kotlin/io/snyk/plugin/SnykProjectManagerListener.kt index df8f4296f..8bbd8f6eb 100644 --- a/src/main/kotlin/io/snyk/plugin/SnykProjectManagerListener.kt +++ b/src/main/kotlin/io/snyk/plugin/SnykProjectManagerListener.kt @@ -24,7 +24,7 @@ class SnykProjectManagerListener : ProjectManagerListener { threadPool.submit { val ls = LanguageServerWrapper.getInstance() if (ls.isInitialized) { - ls.updateWorkspaceFolders(emptySet(), ls.getWorkspaceFolders(project)) + ls.updateWorkspaceFolders(emptySet(), ls.getWorkspaceFoldersFromRoots(project)) } }.get(TIMEOUT, TimeUnit.SECONDS) } catch (ignored: Exception) { diff --git a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt index b48ab0963..8ef28cdf0 100644 --- a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt +++ b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt @@ -19,6 +19,7 @@ import io.snyk.plugin.getWaitForResultsTimeout import io.snyk.plugin.pluginSettings import io.snyk.plugin.runInBackground import io.snyk.plugin.toLanguageServerURL +import io.snyk.plugin.toVirtualFile import io.snyk.plugin.ui.toolwindow.SnykPluginDisposable import org.eclipse.lsp4j.ClientCapabilities import org.eclipse.lsp4j.ClientInfo @@ -66,6 +67,8 @@ import snyk.common.lsp.settings.SeverityFilter import snyk.pluginInfo import snyk.trust.WorkspaceTrustService import snyk.trust.confirmScanningAndSetWorkspaceTrustedStateIfNeeded +import java.io.FileNotFoundException +import java.util.Collections import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -85,6 +88,7 @@ class LanguageServerWrapper( private var authenticatedUser: Map? = null private var initializeResult: InitializeResult? = null private val gson = Gson() + private val configuredWorkspaceFolders: MutableSet = Collections.synchronizedSet(mutableSetOf()) private var disposed = false get() { return ApplicationManager.getApplication().isDisposed || field @@ -231,7 +235,7 @@ class LanguageServerWrapper( private fun lsIsAlive() = ::process.isInitialized && process.isAlive - fun getWorkspaceFolders(project: Project): Set { + fun getWorkspaceFoldersFromRoots(project: Project): Set { if (disposed || project.isDisposed) return emptySet() val normalizedRoots = getTrustedContentRoots(project) return normalizedRoots.map { WorkspaceFolder(it.toLanguageServerURL(), it.name) }.toSet() @@ -316,8 +320,15 @@ class LanguageServerWrapper( try { if (!ensureLanguageServerInitialized()) return val params = DidChangeWorkspaceFoldersParams() - params.event = WorkspaceFoldersChangeEvent(added.toList(), removed.toList()) - languageServer.workspaceService.didChangeWorkspaceFolders(params) + params.event = WorkspaceFoldersChangeEvent( + added.filter { !configuredWorkspaceFolders.contains(it) }, + removed.filter { configuredWorkspaceFolders.contains(it) }, + ) + if (params.event.added.size > 0 || params.event.removed.size > 0) { + languageServer.workspaceService.didChangeWorkspaceFolders(params) + configuredWorkspaceFolders.removeAll(removed) + configuredWorkspaceFolders.addAll(added) + } } catch (e: Exception) { logger.error(e) } @@ -424,10 +435,14 @@ class LanguageServerWrapper( if (notAuthenticated()) return if (DumbService.getInstance(project).isDumb) return try { + val folderUri = folder.toVirtualFile().toLanguageServerURL() + if (!configuredWorkspaceFolders.any { it.uri == folderUri }) return val param = ExecuteCommandParams() param.command = COMMAND_WORKSPACE_FOLDER_SCAN param.arguments = listOf(folder) languageServer.workspaceService.executeCommand(param) + } catch (e: FileNotFoundException) { + logger.debug("File not found: $folder") } catch (e: Exception) { logger.error("error calling scan command from language server. re-initializing", e) restart() @@ -577,7 +592,7 @@ class LanguageServerWrapper( ensureLanguageServerInitialized() ensureLanguageServerProtocolVersion(project) updateConfiguration() - val added = getWorkspaceFolders(project) + val added = getWorkspaceFoldersFromRoots(project) updateWorkspaceFolders(added, emptySet()) } From f06cb8e81cc4475764be44a1db28eda79abada80 Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Wed, 8 Jan 2025 14:33:03 +0100 Subject: [PATCH 2/7] fix: don't run scan on all configuration changes --- .../snyk/plugin/services/SnykCliAuthenticationService.kt | 2 +- .../plugin/settings/SnykProjectSettingsConfigurable.kt | 4 ++-- .../io/snyk/plugin/ui/BranchChooserComboboxDialog.kt | 2 +- src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt | 7 ++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt b/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt index 1ae80659e..7dc7be929 100644 --- a/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt +++ b/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt @@ -41,7 +41,7 @@ class SnykCliAuthenticationService( if (getCliFile().exists()) { executeAuthCommand() } - LanguageServerWrapper.getInstance().updateConfiguration() + LanguageServerWrapper.getInstance().updateConfiguration(false) } private fun downloadCliIfNeeded() { diff --git a/src/main/kotlin/io/snyk/plugin/settings/SnykProjectSettingsConfigurable.kt b/src/main/kotlin/io/snyk/plugin/settings/SnykProjectSettingsConfigurable.kt index f1e4af5a3..445032acd 100644 --- a/src/main/kotlin/io/snyk/plugin/settings/SnykProjectSettingsConfigurable.kt +++ b/src/main/kotlin/io/snyk/plugin/settings/SnykProjectSettingsConfigurable.kt @@ -20,8 +20,8 @@ import io.snyk.plugin.isUrlValid import io.snyk.plugin.pluginSettings import io.snyk.plugin.ui.SnykBalloonNotificationHelper import io.snyk.plugin.ui.SnykSettingsDialog -import snyk.common.lsp.settings.FolderConfigSettings import snyk.common.lsp.LanguageServerWrapper +import snyk.common.lsp.settings.FolderConfigSettings import javax.swing.JComponent class SnykProjectSettingsConfigurable( @@ -126,7 +126,7 @@ class SnykProjectSettingsConfigurable( } languageServerWrapper.refreshFeatureFlags() - languageServerWrapper.updateConfiguration() + languageServerWrapper.updateConfiguration(true) } if (rescanNeeded) { diff --git a/src/main/kotlin/io/snyk/plugin/ui/BranchChooserComboboxDialog.kt b/src/main/kotlin/io/snyk/plugin/ui/BranchChooserComboboxDialog.kt index 493b2d9fa..469dae04b 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/BranchChooserComboboxDialog.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/BranchChooserComboboxDialog.kt @@ -67,7 +67,7 @@ class BranchChooserComboBoxDialog(val project: Project) : DialogWrapper(true) { folderConfigSettings.addFolderConfig(folderConfig.copy(baseBranch = baseBranch)) } runInBackground("Snyk: updating configuration") { - LanguageServerWrapper.getInstance().updateConfiguration() + LanguageServerWrapper.getInstance().updateConfiguration(true) } } diff --git a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt index 8ef28cdf0..46e5b0a38 100644 --- a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt +++ b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt @@ -376,6 +376,7 @@ class LanguageServerWrapper( if (notAuthenticated()) return DumbService.getInstance(project).runWhenSmart { getTrustedContentRoots(project).forEach { + addContentRoots(project) sendFolderScanCommand(it.path, project) } } @@ -492,12 +493,12 @@ class LanguageServerWrapper( ) } - fun updateConfiguration() { + fun updateConfiguration(runScan: Boolean = false) { if (!ensureLanguageServerInitialized()) return val params = DidChangeConfigurationParams(getSettings()) languageServer.workspaceService.didChangeConfiguration(params) - if (pluginSettings().scanOnSave) { + if (runScan && pluginSettings().scanOnSave) { ProjectManager.getInstance().openProjects.forEach { sendScanCommand(it) } } } @@ -591,7 +592,7 @@ class LanguageServerWrapper( if (disposed || project.isDisposed) return ensureLanguageServerInitialized() ensureLanguageServerProtocolVersion(project) - updateConfiguration() + updateConfiguration(false) val added = getWorkspaceFoldersFromRoots(project) updateWorkspaceFolders(added, emptySet()) } From a5fd609e3cda337ab49ba6cef7b24121821774f7 Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Wed, 8 Jan 2025 15:27:33 +0100 Subject: [PATCH 3/7] fix: tests --- src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt | 7 ++++--- .../kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt | 9 +++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt index 46e5b0a38..4d6e2c8cb 100644 --- a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt +++ b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt @@ -19,7 +19,6 @@ import io.snyk.plugin.getWaitForResultsTimeout import io.snyk.plugin.pluginSettings import io.snyk.plugin.runInBackground import io.snyk.plugin.toLanguageServerURL -import io.snyk.plugin.toVirtualFile import io.snyk.plugin.ui.toolwindow.SnykPluginDisposable import org.eclipse.lsp4j.ClientCapabilities import org.eclipse.lsp4j.ClientInfo @@ -68,6 +67,7 @@ import snyk.pluginInfo import snyk.trust.WorkspaceTrustService import snyk.trust.confirmScanningAndSetWorkspaceTrustedStateIfNeeded import java.io.FileNotFoundException +import java.net.URI import java.util.Collections import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -88,7 +88,8 @@ class LanguageServerWrapper( private var authenticatedUser: Map? = null private var initializeResult: InitializeResult? = null private val gson = Gson() - private val configuredWorkspaceFolders: MutableSet = Collections.synchronizedSet(mutableSetOf()) + // internal for test set up + internal val configuredWorkspaceFolders: MutableSet = Collections.synchronizedSet(mutableSetOf()) private var disposed = false get() { return ApplicationManager.getApplication().isDisposed || field @@ -436,7 +437,7 @@ class LanguageServerWrapper( if (notAuthenticated()) return if (DumbService.getInstance(project).isDumb) return try { - val folderUri = folder.toVirtualFile().toLanguageServerURL() + val folderUri = URI.create(folder).toASCIIString() if (!configuredWorkspaceFolders.any { it.uri == folderUri }) return val param = ExecuteCommandParams() param.command = COMMAND_WORKSPACE_FOLDER_SCAN diff --git a/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt b/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt index b8aacc044..5ff0a3127 100644 --- a/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt +++ b/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt @@ -23,6 +23,7 @@ import junit.framework.TestCase.fail import org.eclipse.lsp4j.DidChangeConfigurationParams import org.eclipse.lsp4j.ExecuteCommandParams import org.eclipse.lsp4j.InitializeParams +import org.eclipse.lsp4j.WorkspaceFolder import org.eclipse.lsp4j.services.LanguageServer import org.junit.After import org.junit.Before @@ -31,6 +32,7 @@ import snyk.common.lsp.analytics.ScanDoneEvent import snyk.common.lsp.settings.FolderConfigSettings import snyk.pluginInfo import snyk.trust.WorkspaceTrustService +import java.net.URI import java.util.concurrent.CompletableFuture class LanguageServerWrapperTest { @@ -218,7 +220,10 @@ class LanguageServerWrapperTest { lsMock.workspaceService.executeCommand(any()) } returns CompletableFuture.completedFuture(null) - cut.sendFolderScanCommand("testFolder", projectMock) + val folder = "testFolder" + cut.configuredWorkspaceFolders.add(WorkspaceFolder(URI(folder).toString(), folder)) + + cut.sendFolderScanCommand(folder, projectMock) verify { lsMock.workspaceService.executeCommand(any()) } } @@ -263,7 +268,7 @@ class LanguageServerWrapperTest { every { dumbServiceMock.isDumb } returns false settings.scanOnSave = true - cut.updateConfiguration() + cut.updateConfiguration(true) verify { lsMock.workspaceService.didChangeConfiguration(any()) } From 5bf7e16b5fef939cde2be1328e61a25461cd67cf Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Wed, 8 Jan 2025 15:51:55 +0100 Subject: [PATCH 4/7] fix: URI in workspace folder filter --- src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt index 4d6e2c8cb..2b66f83b1 100644 --- a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt +++ b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt @@ -63,11 +63,13 @@ import snyk.common.lsp.commands.SNYK_GENERATE_ISSUE_DESCRIPTION import snyk.common.lsp.progress.ProgressManager import snyk.common.lsp.settings.LanguageServerSettings import snyk.common.lsp.settings.SeverityFilter +import snyk.common.removeTrailingSlashesIfPresent import snyk.pluginInfo import snyk.trust.WorkspaceTrustService import snyk.trust.confirmScanningAndSetWorkspaceTrustedStateIfNeeded import java.io.FileNotFoundException import java.net.URI +import java.nio.file.Paths import java.util.Collections import java.util.concurrent.ExecutorService import java.util.concurrent.Executors @@ -437,8 +439,8 @@ class LanguageServerWrapper( if (notAuthenticated()) return if (DumbService.getInstance(project).isDumb) return try { - val folderUri = URI.create(folder).toASCIIString() - if (!configuredWorkspaceFolders.any { it.uri == folderUri }) return + val folderUri = Paths.get(folder).toUri().toASCIIString().removeTrailingSlashesIfPresent() + if (!configuredWorkspaceFolders.any { it.uri.removeTrailingSlashesIfPresent() == folderUri }) return val param = ExecuteCommandParams() param.command = COMMAND_WORKSPACE_FOLDER_SCAN param.arguments = listOf(folder) From cb3b20ed606929cc6ffe0cf935b9aff2661e5aa7 Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Wed, 8 Jan 2025 15:54:12 +0100 Subject: [PATCH 5/7] fix: test error --- src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt b/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt index 5ff0a3127..da05962ae 100644 --- a/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt +++ b/src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt @@ -32,7 +32,7 @@ import snyk.common.lsp.analytics.ScanDoneEvent import snyk.common.lsp.settings.FolderConfigSettings import snyk.pluginInfo import snyk.trust.WorkspaceTrustService -import java.net.URI +import java.nio.file.Paths import java.util.concurrent.CompletableFuture class LanguageServerWrapperTest { @@ -221,7 +221,7 @@ class LanguageServerWrapperTest { } returns CompletableFuture.completedFuture(null) val folder = "testFolder" - cut.configuredWorkspaceFolders.add(WorkspaceFolder(URI(folder).toString(), folder)) + cut.configuredWorkspaceFolders.add(WorkspaceFolder(Paths.get(folder).toUri().toASCIIString(), folder)) cut.sendFolderScanCommand(folder, projectMock) From dd949a4fe628a6074990b3efdf32b4e772bca4bd Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Wed, 8 Jan 2025 15:55:04 +0100 Subject: [PATCH 6/7] docs: CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f17f1fd54..1a8f5a13f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Added a description of custom endpoints to settings dialog. - Add option to ignore IaC issues ### Fixed +- only ask to scan folders that are known to language server - folder-specific configs are availabe on opening projects, not only on restart of the IDE - display open source issues in Rider. Previously, as the project.assets.json is in a derived folder, it was filtered. - correctly display and update base branch name for Net New Issues From 97bc793dd1a819c7698c5db565fecd0a078ec5cb Mon Sep 17 00:00:00 2001 From: Bastian Doetsch Date: Wed, 8 Jan 2025 16:00:20 +0100 Subject: [PATCH 7/7] fix: update gitleaksignore --- .gitleaksignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitleaksignore b/.gitleaksignore index fc51e5e68..852be63c6 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -53,3 +53,4 @@ db2662b39d5195aeda376109d21d1530c194711e:src/main/scala/io/snyk/plugin/client/Ap 372778ad27f3293b03c96eed86dccfe4d6d8c6f4:src/main/scala/io/snyk/plugin/ApiClient.scala:generic-api-key:14 372778ad27f3293b03c96eed86dccfe4d6d8c6f4:src/main/scala/io/snyk/plugin/ApiClient.scala:generic-api-key:17 372778ad27f3293b03c96eed86dccfe4d6d8c6f4:src/main/scala/io/snyk/plugin/ApiClient.scala:generic-api-key:20 +67244ecb074381e16902870e29d9f734cc611fd8:src/main/scala/io/snyk/plugin/metrics/SegmentApi.scala:generic-api-key:56