Skip to content

Commit

Permalink
Merge branch 'master' into feat/render-ignore-settings
Browse files Browse the repository at this point in the history
  • Loading branch information
cat2608 committed Mar 21, 2024
2 parents e280404 + 73d18d2 commit 8c8861a
Show file tree
Hide file tree
Showing 19 changed files with 102 additions and 58 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
# Snyk Changelog
# Snyk Security Changelog

## [2.7.10]
### Fixed
- (LS Preview) Fix content root handling for Snyk Code scans

## [2.7.9]
### Fixed
- fix: shortened plugin name to just Snyk Security
- (LS Preview) Fix long-running UI operation to run outside of UI thread
- Remove duplicated annotations in Snyk Code

## [2.8.1]
### Added
Expand All @@ -10,6 +20,7 @@

## [2.7.8]
### Fixed
- (LS Preview) UI freezes and initialization errors caused by CodeVision and Code annotations
- (LS Preview) check trust for content root before triggering Snyk Code scans

## [2.7.7]
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pluginGroup=io.snyk.intellij
pluginName=Snyk Security - Code, Open Source, Container, IaC Configurations
pluginName=Snyk Security

# for insight into build numbers and IntelliJ Platform versions
# see https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class SnykTaskQueueService(val project: Project) {
fun connectProjectToLanguageServer(project: Project) {
synchronized(LanguageServerWrapper) {
val wrapper = LanguageServerWrapper.getInstance()
wrapper.ensureLanguageServerInitialized()
val added = wrapper.getWorkspaceFolders(project)
wrapper.updateWorkspaceFolders(added, emptySet())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,12 @@ class SnykCodeBulkFileListener : SnykBulkFileListener() {
}

override fun forwardEvents(events: MutableList<out VFileEvent>) {
val languageServerWrapper = LanguageServerWrapper.getInstance()

if (!isSnykCodeLSEnabled()) return
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()
val languageServer = LanguageServerWrapper.getInstance().languageServer
if (!languageServerWrapper.ensureLanguageServerInitialized()) return

val languageServer = languageServerWrapper.languageServer
for (event in events) {
if (event.file == null || !event.isFromSave) continue
val file = event.file!!
Expand Down
6 changes: 5 additions & 1 deletion src/main/kotlin/snyk/code/annotator/SnykCodeAnnotatorLS.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import icons.SnykIcons
import io.snyk.plugin.Severity
import io.snyk.plugin.getSnykCachedResults
import io.snyk.plugin.isSnykCodeLSEnabled
import io.snyk.plugin.isSnykCodeRunning
import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel
import org.eclipse.lsp4j.CodeAction
import org.eclipse.lsp4j.CodeActionContext
Expand Down Expand Up @@ -43,7 +44,6 @@ class SnykCodeAnnotatorLS : ExternalAnnotator<PsiFile, Unit>() {

// overrides needed for the Annotator to invoke apply(). We don't do anything here
override fun collectInformation(file: PsiFile): PsiFile {
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()
return file
}

Expand All @@ -53,8 +53,12 @@ class SnykCodeAnnotatorLS : ExternalAnnotator<PsiFile, Unit>() {

override fun apply(psiFile: PsiFile, annotationResult: Unit, holder: AnnotationHolder) {
if (!isSnykCodeLSEnabled()) return
if (!LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()) return
if (isSnykCodeRunning(psiFile.project)) return

getIssuesForFile(psiFile)
.filter { AnnotatorCommon.isSeverityToShow(it.getSeverityAsEnum()) }
.distinctBy { it.id }
.sortedBy { it.title }
.forEach { issue ->
val highlightSeverity = issue.getSeverityAsEnum().getHighlightSeverity()
Expand Down
26 changes: 18 additions & 8 deletions src/main/kotlin/snyk/common/AnnotatorCommon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import io.snyk.plugin.events.SnykProductsOrSeverityListener
import io.snyk.plugin.events.SnykSettingsListener
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.refreshAnnotationsForOpenFiles
import snyk.common.lsp.LanguageServerWrapper

object AnnotatorCommon {
val logger = logger<AnnotatorCommon>()

fun prepareAnnotate(psiFile: PsiFile?) {
logger.debug("Preparing annotation for $psiFile")

// trigger LS initialization if not already done, we consciously don't check the result here
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()

// todo: review later if any way to provide up-to-date context for CLI scans is available
// force saving here will break some user's workflow: https://github.com/snyk/snyk-intellij-plugin/issues/324
}
Expand All @@ -25,16 +30,21 @@ object AnnotatorCommon {
logger.debug("Initializing annotations refresh listener")
project.messageBus.connect()
.subscribe(
SnykProductsOrSeverityListener.SNYK_ENABLEMENT_TOPIC, object : SnykProductsOrSeverityListener {
override fun enablementChanged() {
refreshAnnotationsForOpenFiles(project)
SnykProductsOrSeverityListener.SNYK_ENABLEMENT_TOPIC,
object : SnykProductsOrSeverityListener {
override fun enablementChanged() {
refreshAnnotationsForOpenFiles(project)
}
}
})
)
project.messageBus.connect()
.subscribe(SnykSettingsListener.SNYK_SETTINGS_TOPIC, object : SnykSettingsListener {
override fun settingsChanged() {
refreshAnnotationsForOpenFiles(project)
.subscribe(
SnykSettingsListener.SNYK_SETTINGS_TOPIC,
object : SnykSettingsListener {
override fun settingsChanged() {
refreshAnnotationsForOpenFiles(project)
}
}
})
)
}
}
6 changes: 5 additions & 1 deletion src/main/kotlin/snyk/common/lsp/LSCodeVisionProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import icons.SnykIcons
import io.snyk.plugin.isSnykCodeLSEnabled
import io.snyk.plugin.isSnykCodeRunning
import org.eclipse.lsp4j.CodeLens
import org.eclipse.lsp4j.CodeLensParams
import org.eclipse.lsp4j.ExecuteCommandParams
Expand All @@ -43,8 +44,11 @@ class LSCodeVisionProvider : CodeVisionProvider<Unit> {
}

override fun computeCodeVision(editor: Editor, uiData: Unit): CodeVisionState {
if (editor.project == null) return CodeVisionState.READY_EMPTY
if (!isSnykCodeLSEnabled()) return CodeVisionState.READY_EMPTY
LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()
if (!LanguageServerWrapper.getInstance().ensureLanguageServerInitialized()) return CodeVisionState.READY_EMPTY
if (isSnykCodeRunning(editor.project!!)) return CodeVisionState.READY_EMPTY

return ReadAction.compute<CodeVisionState, RuntimeException> {
val project = editor.project ?: return@compute CodeVisionState.READY_EMPTY
val document = editor.document
Expand Down
53 changes: 39 additions & 14 deletions src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.util.io.toNioPathOrNull
import com.intellij.openapi.vfs.VirtualFile
import io.snyk.plugin.getCliFile
import io.snyk.plugin.getContentRootVirtualFiles
import io.snyk.plugin.getUserAgentString
import io.snyk.plugin.isSnykCodeLSEnabled
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.ui.SnykBalloonNotificationHelper
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -59,7 +59,6 @@ class LanguageServerWrapper(
/**
* The language client is used to receive messages from LS
*/
@Suppress("MemberVisibilityCanBePrivate")
lateinit var languageClient: SnykLanguageClient

/**
Expand Down Expand Up @@ -90,7 +89,7 @@ class LanguageServerWrapper(
internal fun initialize() {
if (lsPath.toNioPathOrNull()?.exists() == false) {
val message = "Snyk Language Server not found. Please make sure the Snyk CLI is installed at $lsPath."
SnykBalloonNotificationHelper.showError(message, null)
logger.warn(message)
return
}
try {
Expand Down Expand Up @@ -138,8 +137,36 @@ class LanguageServerWrapper(
return workspaceFolders.toList()
}

fun getWorkspaceFolders(project: Project) =
project.getContentRootVirtualFiles().mapNotNull { WorkspaceFolder(it.url, it.name) }.toSet()
fun getWorkspaceFolders(project: Project): Set<WorkspaceFolder> {
val normalizedRoots = getTrustedContentRoots(project)
return normalizedRoots.map { WorkspaceFolder(it.url, it.name) }.toSet()
}

private fun getTrustedContentRoots(project: Project): MutableSet<VirtualFile> {
// 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 trustService = service<WorkspaceTrustService>()
val normalizedRoots = mutableSetOf<VirtualFile>()

for (root in contentRoots) {
val pathTrusted = trustService.isPathTrusted(root.toNioPath())
if (!pathTrusted) {
logger.debug("Path not trusted: ${root.path}")
continue
}

var add = true
for (normalizedRoot in normalizedRoots) {
if (!root.path.startsWith(normalizedRoot.path)) continue
add = false
break
}
if (add) normalizedRoots.add(root)
}
logger.debug("Normalized content roots: $normalizedRoots")
return normalizedRoots
}

fun sendInitializeMessage() {
val workspaceFolders = determineWorkspaceFolders()
Expand Down Expand Up @@ -182,7 +209,7 @@ class LanguageServerWrapper(

fun updateWorkspaceFolders(added: Set<WorkspaceFolder>, removed: Set<WorkspaceFolder>) {
try {
ensureLanguageServerInitialized()
if (!ensureLanguageServerInitialized()) return
val params = DidChangeWorkspaceFoldersParams()
params.event = WorkspaceFoldersChangeEvent(added.toList(), removed.toList())
languageServer.workspaceService.didChangeWorkspaceFolders(params)
Expand All @@ -191,17 +218,18 @@ class LanguageServerWrapper(
}
}

fun ensureLanguageServerInitialized() {
fun ensureLanguageServerInitialized(): Boolean {
while (isInitializing) {
Thread.sleep(DEFAULT_SLEEP_TIME)
}
if (!isInitialized) {
initialize()
}
return isInitialized
}

fun sendReportAnalyticsCommand(scanDoneEvent: ScanDoneEvent) {
ensureLanguageServerInitialized()
if (!ensureLanguageServerInitialized()) return
try {
val eventString = gson.toJson(scanDoneEvent)
val param = ExecuteCommandParams()
Expand All @@ -214,17 +242,14 @@ class LanguageServerWrapper(
}

fun sendScanCommand() {
ensureLanguageServerInitialized()
if (!ensureLanguageServerInitialized()) return
val project = ProjectUtil.getActiveProject()
if (project == null) {
logger.warn("No active project found, not sending scan command.")
return
}
project.getContentRootVirtualFiles().filterNotNull().toSet().forEach {
val trustService = service<WorkspaceTrustService>()
if (trustService.isPathTrusted(it.toNioPath())) {
sendFolderScanCommand(it.path)
}
getTrustedContentRoots(project).forEach {
sendFolderScanCommand(it.path)
}
}

Expand Down
16 changes: 10 additions & 6 deletions src/main/kotlin/snyk/common/lsp/SnykLanguageClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ import org.eclipse.lsp4j.WorkDoneProgressKind.report
import org.eclipse.lsp4j.WorkDoneProgressReport
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification
import org.eclipse.lsp4j.services.LanguageClient
import org.jetbrains.kotlin.idea.util.application.executeOnPooledThread
import snyk.common.ProductType
import snyk.common.SnykCodeFileIssueComparator
import snyk.trust.WorkspaceTrustService
import java.util.Collections
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit

class SnykLanguageClient : LanguageClient {
Expand Down Expand Up @@ -186,6 +188,12 @@ class SnykLanguageClient : LanguageClient {
val map = processedIssues
.groupBy { it.filePath }
.mapNotNull { (file, issues) -> SnykCodeFile(project, file.toVirtualFile()) to issues.sorted() }
.map {
// initialize all calculated values before they are needed, so we don't have to do it in the UI thread
it.first.relativePath
it.second.forEach { i -> i.textRange }
it
}
.filter { it.second.isNotEmpty() }
.toMap()
return map.toSortedMap(SnykCodeFileIssueComparator(map))
Expand Down Expand Up @@ -239,9 +247,7 @@ class SnykLanguageClient : LanguageClient {
}

report -> {
while (progresses[token] == null) {
Thread.sleep(1000)
}
executeOnPooledThread { while (progresses[token] == null) { Thread.sleep(1000) } }.get(5, TimeUnit.SECONDS)
val indicator = progresses[token] ?: return
val report: WorkDoneProgressReport = workDoneProgressNotification as WorkDoneProgressReport
indicator.text = report.message
Expand All @@ -253,9 +259,7 @@ class SnykLanguageClient : LanguageClient {
}

end -> {
while (progresses[token] == null) {
Thread.sleep(1000)
}
executeOnPooledThread { while (progresses[token] == null) { Thread.sleep(1000) } }.get(5, TimeUnit.SECONDS)
val indicator = progresses[token] ?: return
val workDoneProgressEnd = workDoneProgressNotification as WorkDoneProgressEnd
indicator.text = workDoneProgressEnd.message
Expand Down
2 changes: 0 additions & 2 deletions src/main/resources/META-INF/optional/withCsharp.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="C#" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="C#" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
4 changes: 0 additions & 4 deletions src/main/resources/META-INF/optional/withGo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,5 @@
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="vgo" implementationClass="snyk.oss.annotator.OSSGoModAnnotator"/>
<externalAnnotator language="go" implementationClass="snyk.oss.annotator.OSSGoModAnnotator"/>
<externalAnnotator language="vgo" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="vgo" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
<externalAnnotator language="go" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="go" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
2 changes: 0 additions & 2 deletions src/main/resources/META-INF/optional/withHTML.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="HTML" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="HTML" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
4 changes: 0 additions & 4 deletions src/main/resources/META-INF/optional/withJava.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,5 @@
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="JAVA" implementationClass="snyk.oss.annotator.OSSMavenAnnotator"/>
<externalAnnotator language="Groovy" implementationClass="snyk.oss.annotator.OSSGradleAnnotator"/>
<externalAnnotator language="JAVA" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="JAVA" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
<externalAnnotator language="Groovy" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="Groovy" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
2 changes: 0 additions & 2 deletions src/main/resources/META-INF/optional/withJavaScript.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="JavaScript" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="JavaScript" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
2 changes: 0 additions & 2 deletions src/main/resources/META-INF/optional/withKotlin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="kotlin" implementationClass="snyk.oss.annotator.OSSGradleAnnotator"/>
<externalAnnotator language="kotlin" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="kotlin" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
2 changes: 0 additions & 2 deletions src/main/resources/META-INF/optional/withPHP.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="PHP" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="PHP" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
2 changes: 0 additions & 2 deletions src/main/resources/META-INF/optional/withPython.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<externalAnnotator language="Python" implementationClass="snyk.code.annotator.SnykCodeAnnotatorLS"/>
<externalAnnotator language="Python" implementationClass="snyk.code.annotator.SnykCodeAnnotator"/>
</extensions>
</idea-plugin>
Loading

0 comments on commit 8c8861a

Please sign in to comment.