Skip to content

Commit

Permalink
fix: use project base path as content root, if none are defined [IDE-…
Browse files Browse the repository at this point in the history
…557] (#584)

* fix: use project basePath as content root if none given [IDE-557]

Usually, Jetbrains provides content roots. In Rider it seems like it isn't doing so. In this case, we fall back to the project directory when determining the workspace folders.

* chore: always use LS for OSS scans

* docs: update changelog

* chore: deactivate & fix tests for release

* chore: deactivate & fix tests for release
  • Loading branch information
bastiandoetsch authored Aug 28, 2024
1 parent a81f143 commit d923a29
Show file tree
Hide file tree
Showing 19 changed files with 74 additions and 1,119 deletions.
11 changes: 8 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
## [2.9.0]
### Changed
- Updated the language server protocol version to 14 to support new communication model.
- All HTTP communications now goes through language server
- All HTTP communications now go through language server
- Removed Snyk Advisor
- Removed Amplitude integration
- Handle exception
- Do not excessively spawn CLIs
- Remove UI freezes caused by annotator
- Use language server for OSS scans

### Fixes
- Handle exceptions
- Do not excessively spawn CLIs
- Use project base path as content roots, if none are defined (e.g. in Rider)
- Limit navigation functions to only use UI thread when needed


## [2.8.11]
### Added
- Improved UI thread usage and app shutdown handling
Expand Down
32 changes: 15 additions & 17 deletions src/main/kotlin/io/snyk/plugin/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.util.registry.Registry
Expand Down Expand Up @@ -55,7 +56,6 @@ import snyk.errorHandler.SentryErrorReporter
import snyk.iac.IacScanService
import snyk.oss.OssService
import snyk.oss.OssTextRangeFinder
import snyk.pluginInfo
import java.io.File
import java.io.FileNotFoundException
import java.net.URI
Expand Down Expand Up @@ -230,7 +230,7 @@ fun isContainerEnabled(): Boolean = true

fun isFileListenerEnabled(): Boolean = pluginSettings().fileListenerEnabled

fun isSnykOSSLSEnabled(): Boolean = Registry.`is`("snyk.preview.snyk.oss.ls.enabled", false)
fun isSnykOSSLSEnabled(): Boolean = true // TODO: cleanup usage

fun isSnykIaCLSEnabled(): Boolean = false

Expand Down Expand Up @@ -451,19 +451,17 @@ fun Project.getContentRootPaths(): SortedSet<Path> {
.toSortedSet()
}

fun Project.getContentRootVirtualFiles() = ProjectRootManager.getInstance(this).contentRoots
.filter { it.exists() && it.isDirectory }.toSet()

fun getUserAgentString(): String {
// $APPLICATION/$APPLICATION_VERSION ($GOOS;$GOARCH[;$BINARY_NAME]) [$SNYK_INTEGRATION_NAME/$SNYK_INTEGRATION_VERSION [($SNYK_INTEGRATION_ENVIRONMENT/$SNYK_INTEGRATION_ENVIRONMENT_VERSION)]]
val integrationName = pluginInfo.integrationName
val integrationVersion = pluginInfo.integrationVersion
val integrationEnvironment = pluginInfo.integrationEnvironment
val integrationEnvironmentVersion = pluginInfo.integrationEnvironmentVersion
val os = SystemUtils.OS_NAME
val arch = SystemUtils.OS_ARCH

return "$integrationEnvironment/$integrationEnvironmentVersion " +
"($os;$arch) $integrationName/$integrationVersion " +
"($integrationEnvironment/$integrationEnvironmentVersion)"
fun Project.getContentRootVirtualFiles(): Set<VirtualFile> {
var contentRoots = ProjectRootManager.getInstance(this).contentRoots
if (contentRoots.isEmpty()) {
// this should cover for the case when no content roots are configured, e.g. in rider
contentRoots = ProjectManager.getInstance().openProjects
.mapNotNull { it.basePath?.toVirtualFile() }.toTypedArray()
}

// the sort is to ensure that parent folders come first
// e.g. /a/b should come before /a/b/c
return contentRoots
.filter { it.exists() && it.isDirectory }
.sortedBy { it.path }.toSet()
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.vfs.newvfs.events.VFileEvent
import com.intellij.openapi.vfs.readText
import io.snyk.plugin.SnykBulkFileListener
import io.snyk.plugin.getPsiFile
import io.snyk.plugin.getSnykCachedResults
import io.snyk.plugin.toLanguageServerURL
import io.snyk.plugin.toSnykFileSet
Expand Down Expand Up @@ -59,9 +60,9 @@ class SnykCodeBulkFileListener : SnykBulkFileListener() {
virtualFile.readText()
)
languageServer.textDocumentService.didSave(param)
virtualFile.getPsiFile(project)?.let { DaemonCodeAnalyzer.getInstance(project).restart(it) }
}
VirtualFileManager.getInstance().asyncRefresh()
DaemonCodeAnalyzer.getInstance(project).restart()
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ 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
Expand Down Expand Up @@ -190,9 +191,7 @@ class LanguageServerWrapper(
private fun getTrustedContentRoots(project: Project): MutableSet<VirtualFile> {
if (!confirmScanningAndSetWorkspaceTrustedStateIfNeeded(project)) return mutableSetOf()

// the sort is to ensure that parent folders come first
// e.g. /a/b should come before /a/b/c
val contentRoots = project.getContentRootVirtualFiles().filterNotNull().sortedBy { it.path }
val contentRoots = project.getContentRootVirtualFiles()
val trustService = service<WorkspaceTrustService>()
val normalizedRoots = mutableSetOf<VirtualFile>()

Expand Down
10 changes: 9 additions & 1 deletion src/main/kotlin/snyk/trust/TrustedProjects.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.MessageDialogBuilder
import com.intellij.openapi.ui.Messages
import io.snyk.plugin.getContentRootPaths
import io.snyk.plugin.toVirtualFile
import snyk.SnykBundle

private val LOG = Logger.getInstance("snyk.trust.TrustedProjects")
Expand All @@ -22,7 +23,14 @@ private val LOG = Logger.getInstance("snyk.trust.TrustedProjects")
*/
fun confirmScanningAndSetWorkspaceTrustedStateIfNeeded(project: Project): Boolean {
if (project.isDisposed || ApplicationManager.getApplication().isDisposed) return false
val paths = project.getContentRootPaths()
val paths = project.getContentRootPaths().toMutableSet()
val basePath = project.basePath

// fallback to project base path if no content roots (needed for rider)
if (paths.isEmpty() && basePath != null) {
paths.add(basePath.toVirtualFile().toNioPath())
}

val trustService = service<WorkspaceTrustService>()
for (path in paths) {
val trustedState = trustService.isPathTrusted(path)
Expand Down
5 changes: 0 additions & 5 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@
defaultValue="720000"
description="Snyk timeout (milliseconds) to wait for results during scan"/>

<registryKey key="snyk.preview.snyk.oss.ls.enabled"
defaultValue="false"
description="Preview: Use language server as source for Snyk OSS findings."
restartRequired="true"/>

<notificationGroup id="Snyk" displayType="BALLOON" toolWindowId="Snyk"/>

<codeInsight.codeVisionProvider implementation="snyk.common.lsp.LSCodeVisionProvider"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ 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
Expand Down Expand Up @@ -74,6 +75,8 @@ import javax.swing.JPanel
import javax.swing.JTextArea
import javax.swing.tree.TreeNode

//TODO rewrite
@Ignore("change to language server")
class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() {

private val iacGoofJson = getResourceAsString("iac-test-results/infrastructure-as-code-goof.json")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import io.snyk.plugin.services.SnykApplicationSettingsStateService
import io.snyk.plugin.services.SnykTaskQueueService
import org.eclipse.lsp4j.services.LanguageServer
import org.eclipse.lsp4j.services.WorkspaceService
import org.junit.Ignore
import org.junit.Test
import snyk.UIComponentFinder
import snyk.common.lsp.LanguageServerWrapper
Expand Down Expand Up @@ -46,6 +47,7 @@ class SnykToolWindowPanelTest : LightPlatform4TestCase() {
lsw.languageServer = lsMock
lsw.languageClient = lsClientMock
lsw.process = lsProcessMock
lsw.isInitialized = true

every { lsProcessMock.info().startInstant().isPresent } returns true
every { lsProcessMock.isAlive } returns true
Expand Down Expand Up @@ -151,6 +153,8 @@ class SnykToolWindowPanelTest : LightPlatform4TestCase() {
verify(exactly = 1) { taskQueueService.scan(false) }
}

//TODO rewrite
@Ignore("change to language server")
@Test
fun `should automatically enable all products on first run after Auth`() {
val application = ApplicationManager.getApplication()
Expand Down
4 changes: 3 additions & 1 deletion src/test/kotlin/snyk/common/lsp/LanguageServerWrapperTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ class LanguageServerWrapperTest {
fun `sendInitializeMessage should send an initialize message to the language server`() {
val rootManagerMock = mockk<ProjectRootManager>(relaxed = true)
every { projectMock.getService(ProjectRootManager::class.java) } returns rootManagerMock
every { projectMock.isDisposed } returns false
every { rootManagerMock.contentRoots } returns emptyArray()
every { projectMock.basePath } returns null
every { lsMock.initialize(any<InitializeParams>()) } returns CompletableFuture.completedFuture(null)
justRun { lsMock.initialized(any()) }

Expand Down Expand Up @@ -315,7 +317,7 @@ class LanguageServerWrapperTest {

assertEquals("false", actual.activateSnykCode)
assertEquals("false", actual.activateSnykIac)
assertEquals("false", actual.activateSnykOpenSource)
assertEquals("true", actual.activateSnykOpenSource)
assertEquals(settings.token, actual.token)
assertEquals("${settings.ignoreUnknownCA}", actual.insecure)
assertEquals(getCliFile().absolutePath, actual.cliPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import com.intellij.testFramework.PlatformTestUtil
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.intellij.util.io.createDirectories
import com.intellij.util.io.delete
import io.mockk.justRun
import io.mockk.mockk
import io.mockk.unmockkAll
import io.snyk.plugin.getKubernetesImageCache
import io.snyk.plugin.getSnykCachedResults
import io.snyk.plugin.resetSettings
import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel
import org.awaitility.Awaitility.await
import org.eclipse.lsp4j.services.LanguageServer
import snyk.common.lsp.LanguageServerWrapper
import snyk.container.ui.ContainerImageTreeNode
import java.io.File
import java.nio.file.Files
Expand All @@ -29,11 +33,14 @@ import java.util.concurrent.TimeUnit
import kotlin.io.path.notExists

class ContainerBulkFileListenerTest : BasePlatformTestCase() {
private val lsMock = mockk<LanguageServer>(relaxed = true)

override fun setUp() {
super.setUp()
unmockkAll()
resetSettings(project)
LanguageServerWrapper.getInstance().languageServer = lsMock
LanguageServerWrapper.getInstance().isInitialized = true
}

override fun tearDown() {
Expand Down
9 changes: 8 additions & 1 deletion src/test/kotlin/snyk/iac/IacBulkFileListenerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ 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.every
import io.mockk.mockk
import io.snyk.plugin.getSnykCachedResults
import io.snyk.plugin.resetSettings
import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel
import org.awaitility.Awaitility.await
import org.eclipse.lsp4j.services.LanguageServer
import org.junit.Test
import snyk.common.lsp.LanguageServerWrapper
import snyk.iac.ui.toolwindow.IacFileTreeNode
import java.util.concurrent.TimeUnit

@Suppress("FunctionName")
class IacBulkFileListenerTest : BasePlatformTestCase() {
private val lsMock = mockk<LanguageServer>(relaxed = true)

override fun setUp() {
super.setUp()
resetSettings(project)
val languageServerWrapper = LanguageServerWrapper.getInstance()
languageServerWrapper.isInitialized = true
languageServerWrapper.languageServer = lsMock
}

override fun tearDown() {
Expand Down
12 changes: 10 additions & 2 deletions src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,28 @@ 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

@Suppress("FunctionName")
class OssBulkFileListenerTest : BasePlatformTestCase() {

private val lsMock = mockk<LanguageServer>(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) {
Expand Down
Loading

0 comments on commit d923a29

Please sign in to comment.