diff --git a/build.gradle.kts b/build.gradle.kts index 9834f9336..efb390f6c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ fun properties(key: String) = project.findProperty(key).toString() plugins { id("org.jetbrains.changelog") version "2.1.2" - id("org.jetbrains.intellij") version "1.16.0" + id("org.jetbrains.intellij") version "1.16.1" id("org.jetbrains.kotlin.jvm") version "1.9.0" id("io.gitlab.arturbosch.detekt") version ("1.23.1") id("pl.allegro.tech.build.axion-release") version "1.13.6" diff --git a/gradle.properties b/gradle.properties index 237851769..bbdbe2645 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,17 +3,17 @@ pluginName=Snyk Security - Code, Open Source, Container, IaC Configurations # for insight into build numbers and IntelliJ Platform versions # see https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html -pluginSinceBuild=231 +pluginSinceBuild=233 pluginUntilBuild=233.* -platformVersion=2023.1 +platformVersion=2023.3 platformDownloadSources=true # plugin dependencies (comma-separated) # example: platformPlugins = com.intellij.java, org.jetbrains.plugins.yaml # see https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html -platformPlugins=org.intellij.plugins.hcl:231.8109.91,org.jetbrains.plugins.yaml,org.jetbrains.kotlin,com.intellij.java,org.intellij.groovy +platformPlugins=org.intellij.plugins.hcl:233.11799.172,org.jetbrains.plugins.yaml,org.jetbrains.kotlin,com.intellij.java,org.intellij.groovy # list of versions for which to check the plugin for api compatibility -pluginVerifierIdeVersions=2023.1,2023.2,2023.3 +pluginVerifierIdeVersions=2023.3 localIdeDirectory= # opt-out flag for bundling Kotlin standard library # see https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library diff --git a/src/main/kotlin/io/snyk/plugin/Utils.kt b/src/main/kotlin/io/snyk/plugin/Utils.kt index cbd1a19e5..401eac264 100644 --- a/src/main/kotlin/io/snyk/plugin/Utils.kt +++ b/src/main/kotlin/io/snyk/plugin/Utils.kt @@ -39,7 +39,7 @@ import io.snyk.plugin.snykcode.core.RunUtils import io.snyk.plugin.ui.SnykBalloonNotificationHelper import io.snyk.plugin.ui.toolwindow.SnykToolWindowFactory import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel -import org.apache.commons.lang.SystemUtils +import org.apache.commons.lang3.SystemUtils import snyk.advisor.AdvisorService import snyk.advisor.AdvisorServiceImpl import snyk.advisor.SnykAdvisorModel diff --git a/src/main/kotlin/io/snyk/plugin/cli/ConsoleCommandRunner.kt b/src/main/kotlin/io/snyk/plugin/cli/ConsoleCommandRunner.kt index e75d1d5cc..70bd701fd 100644 --- a/src/main/kotlin/io/snyk/plugin/cli/ConsoleCommandRunner.kt +++ b/src/main/kotlin/io/snyk/plugin/cli/ConsoleCommandRunner.kt @@ -10,18 +10,11 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.openapi.util.Key -import com.intellij.util.net.HttpConfigurable import io.snyk.plugin.controlExternalProcessWithProgressIndicator import io.snyk.plugin.getWaitForResultsTimeout -import io.snyk.plugin.pluginSettings import io.snyk.plugin.ui.SnykBalloonNotificationHelper -import snyk.common.getEndpointUrl -import snyk.common.isFedramp -import snyk.common.isOauth +import snyk.common.EnvironmentHelper import snyk.errorHandler.SentryErrorReporter -import snyk.pluginInfo -import java.net.URI -import java.net.URLEncoder import java.nio.charset.Charset open class ConsoleCommandRunner { @@ -90,54 +83,10 @@ open class ConsoleCommandRunner { * Setup environment variables for CLI. */ fun setupCliEnvironmentVariables(commandLine: GeneralCommandLine, apiToken: String) { - val endpoint = getEndpointUrl() - - val oauthEnabledEnvVar = "INTERNAL_SNYK_OAUTH_ENABLED" - val oauthEnvVar = "INTERNAL_OAUTH_TOKEN_STORAGE" - val snykTokenEnvVar = "SNYK_TOKEN" - - val endpointURI = URI(endpoint) - val oauthEnabled = endpointURI.isOauth() - if (oauthEnabled) { - commandLine.environment[oauthEnabledEnvVar] = "1" - commandLine.environment.remove(snykTokenEnvVar) - } else { - commandLine.environment.remove(oauthEnvVar) - commandLine.environment.remove(oauthEnabledEnvVar) - } - - if (apiToken.isNotEmpty()) { - if (oauthEnabled) { - commandLine.environment[oauthEnvVar] = apiToken - } else { - commandLine.environment[snykTokenEnvVar] = apiToken - } - } - - commandLine.environment["SNYK_API"] = endpoint - - if (!pluginSettings().usageAnalyticsEnabled || endpointURI.isFedramp()) { - commandLine.environment["SNYK_CFG_DISABLE_ANALYTICS"] = "1" - } - - commandLine.environment["SNYK_INTEGRATION_NAME"] = pluginInfo.integrationName - commandLine.environment["SNYK_INTEGRATION_VERSION"] = pluginInfo.integrationVersion - commandLine.environment["SNYK_INTEGRATION_ENVIRONMENT"] = pluginInfo.integrationEnvironment - commandLine.environment["SNYK_INTEGRATION_ENVIRONMENT_VERSION"] = pluginInfo.integrationEnvironmentVersion - val proxySettings = HttpConfigurable.getInstance() - val proxyHost = proxySettings.PROXY_HOST - if (proxySettings != null && proxySettings.USE_HTTP_PROXY && proxyHost.isNotEmpty()) { - val authentication = if (proxySettings.PROXY_AUTHENTICATION) { - val auth = proxySettings.getPromptedAuthentication(proxyHost, "Snyk: Please enter your proxy password") - if (auth == null) "" else auth.userName.urlEncode() + ":" + String(auth.password).urlEncode() + "@" - } else "" - commandLine.environment["http_proxy"] = "http://$authentication$proxyHost:${proxySettings.PROXY_PORT}" - commandLine.environment["https_proxy"] = "http://$authentication$proxyHost:${proxySettings.PROXY_PORT}" - } + val environment = commandLine.environment + EnvironmentHelper.updateEnvironment(environment, apiToken) } - private fun String.urlEncode() = URLEncoder.encode(this, "UTF-8") - companion object { const val PROCESS_CANCELLED_BY_USER = "PROCESS_CANCELLED_BY_USER" } diff --git a/src/main/kotlin/io/snyk/plugin/net/TokenInterceptor.kt b/src/main/kotlin/io/snyk/plugin/net/TokenInterceptor.kt index 3dd67f616..5920f9ffe 100644 --- a/src/main/kotlin/io/snyk/plugin/net/TokenInterceptor.kt +++ b/src/main/kotlin/io/snyk/plugin/net/TokenInterceptor.kt @@ -7,7 +7,7 @@ import io.snyk.plugin.getWhoamiService import io.snyk.plugin.pluginSettings import okhttp3.Interceptor import okhttp3.Response -import org.apache.commons.lang.SystemUtils +import org.apache.commons.lang3.SystemUtils import snyk.common.needsSnykToken import snyk.pluginInfo import java.time.OffsetDateTime diff --git a/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt b/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt index 48d96d221..5b94d19ab 100644 --- a/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt +++ b/src/main/kotlin/io/snyk/plugin/services/SnykCliAuthenticationService.kt @@ -20,7 +20,7 @@ import io.snyk.plugin.getSnykCliDownloaderService import io.snyk.plugin.pluginSettings import io.snyk.plugin.ui.SnykBalloonNotificationHelper import io.snyk.plugin.ui.getReadOnlyClickableHtmlJEditorPane -import org.apache.commons.lang.StringEscapeUtils.escapeHtml +import org.apache.commons.text.StringEscapeUtils.escapeHtml4 import snyk.common.getEndpointUrl import snyk.common.isOauth import java.awt.BorderLayout @@ -35,7 +35,7 @@ import javax.swing.JPanel import javax.swing.JProgressBar import javax.swing.ScrollPaneConstants -@Service +@Service(Service.Level.PROJECT) class SnykCliAuthenticationService(val project: Project) { private val logger = logger() @@ -78,7 +78,7 @@ class SnykCliAuthenticationService(val project: Project) { } val finalOutput = getConsoleCommandRunner().execute(commands, getPluginPath(), "", project) { line -> if (line.startsWith("https://")) { - val htmlLink = escapeHtml(line.removeLineEnd()) + val htmlLink = escapeHtml4(line.removeLineEnd()) val htmlText = """ We are now redirecting you to our auth page, go ahead and log in.

@@ -119,7 +119,7 @@ class SnykCliAuthenticationService(val project: Project) { token = getConfigApiOutput.removeLineEnd() } ProgressManager.getInstance().runProcessWithProgressSynchronously( - getConfigApiTask, "Get Snyk API token", true, null + getConfigApiTask, "Get Snyk API Token", true, null ) } @@ -142,13 +142,13 @@ class SnykCliAuthenticationService(val project: Project) { class AuthDialog : DialogWrapper(true) { var onCancel: () -> Unit = {} - private val viewer = getReadOnlyClickableHtmlJEditorPane("Initializing authentication...") + private val viewer = getReadOnlyClickableHtmlJEditorPane("Initializing Authentication...") val copyUrlAction = CopyUrlAction() init { super.init() copyUrlAction.isEnabled = false - title = "Authenticating Snyk plugin" + title = "Authenticating Snyk Plugin" } override fun createCenterPanel(): JComponent { diff --git a/src/main/kotlin/io/snyk/plugin/snykcode/core/SnykCodeFile.kt b/src/main/kotlin/io/snyk/plugin/snykcode/core/SnykCodeFile.kt index ea46fcdce..e9c4f74f2 100644 --- a/src/main/kotlin/io/snyk/plugin/snykcode/core/SnykCodeFile.kt +++ b/src/main/kotlin/io/snyk/plugin/snykcode/core/SnykCodeFile.kt @@ -1,6 +1,19 @@ package io.snyk.plugin.snykcode.core +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Iconable import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.idea.core.util.toPsiFile +import snyk.common.RelativePathHelper +import javax.swing.Icon -data class SnykCodeFile(val project: Project, val virtualFile: VirtualFile) +data class SnykCodeFile(val project: Project, val virtualFile: VirtualFile) { + var icon: Icon? = null + val relativePath = RelativePathHelper().getRelativePath(virtualFile, project) + init { + ApplicationManager.getApplication().runReadAction { + virtualFile.toPsiFile(project)?.getIcon(Iconable.ICON_FLAG_READ_STATUS) + } + } +} diff --git a/src/main/kotlin/io/snyk/plugin/ui/UIUtils.kt b/src/main/kotlin/io/snyk/plugin/ui/UIUtils.kt index d6accd5fb..655716b01 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/UIUtils.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/UIUtils.kt @@ -5,17 +5,17 @@ import com.intellij.openapi.util.IconLoader import com.intellij.ui.BrowserHyperlinkListener import com.intellij.ui.ColorUtil import com.intellij.ui.ScrollPaneFactory +import com.intellij.ui.components.ActionLink import com.intellij.uiDesigner.core.GridConstraints import com.intellij.uiDesigner.core.GridLayoutManager import com.intellij.uiDesigner.core.Spacer import com.intellij.util.Alarm import com.intellij.util.ui.HTMLEditorKitBuilder -import com.intellij.util.ui.JBHtmlEditorKit import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import io.snyk.plugin.pluginSettings import io.snyk.plugin.ui.toolwindow.LabelProvider -import org.apache.commons.lang.StringEscapeUtils +import org.apache.commons.text.StringEscapeUtils import snyk.common.isSnykCodeAvailable import java.awt.Color import java.awt.Container @@ -242,7 +242,7 @@ fun descriptionHeaderPanel( cvssV3: String? = null, id: String? = null, idUrl: String? = null, - customLabels: List = emptyList() + customLabels: List = emptyList() ): DescriptionHeaderPanel { val panel = DescriptionHeaderPanel() val font14 = getFont(-1, 14, panel.font) @@ -297,7 +297,7 @@ class DescriptionHeaderPanel : JPanel() fun addRowOfItemsToPanel( panel: JPanel, startingColumn: Int, - items: List, + items: List, separator: String = " | ", firstSeparator: Boolean = true, opaqueSeparator: Boolean = true, @@ -353,12 +353,13 @@ fun wrapWithScrollPane(panel: JPanel): JScrollPane { } fun txtToHtml(s: String): String { - val escapedHtml = StringEscapeUtils.escapeHtml(s) + val escapedHtml = StringEscapeUtils.escapeHtml4(s) val newLineConverted = escapedHtml .replace("\n", "
") .replace("\t", "     ") // html link converter "stolen" from https://stackoverflow.com/a/12053940/7577274 - val str = "(?i)\\b((?:https?://|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\".,<>?«»“”‘’]))" + val str = + "(?i)\\b((?:https?://|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\".,<>?«»“”‘’]))" val patt: Pattern = Pattern.compile(str) val matcher: Matcher = patt.matcher(newLineConverted) return matcher.replaceAll("$1") diff --git a/src/main/kotlin/io/snyk/plugin/ui/settings/ScanTypesPanel.kt b/src/main/kotlin/io/snyk/plugin/ui/settings/ScanTypesPanel.kt index 7370c659f..3e0376aa4 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/settings/ScanTypesPanel.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/settings/ScanTypesPanel.kt @@ -8,14 +8,11 @@ import com.intellij.openapi.ui.popup.Balloon import com.intellij.ui.HyperlinkLabel import com.intellij.ui.components.ActionLink import com.intellij.ui.components.JBCheckBox -import com.intellij.ui.layout.panel import com.intellij.util.Alarm import com.intellij.util.ui.JBUI import com.intellij.util.ui.UIUtil import io.snyk.plugin.getKubernetesImageCache import io.snyk.plugin.getSnykApiService -import io.snyk.plugin.isContainerEnabled -import io.snyk.plugin.isIacEnabled import io.snyk.plugin.net.CliConfigSettings import io.snyk.plugin.net.ClientException import io.snyk.plugin.pluginSettings @@ -26,6 +23,7 @@ import snyk.common.ProductType import snyk.common.isSnykCodeAvailable import snyk.common.toSnykCodeSettingsUrl import java.awt.Component +import java.awt.event.ItemEvent import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.JLabel @@ -53,144 +51,93 @@ class ScanTypesPanel( } } - private var currentOssScanEnabled = settings.ossScanEnable - private var currentCodeSecurityScanEnabled = settings.snykCodeSecurityIssuesScanEnable - private var currentCodeQualityScanEnabled = settings.snykCodeQualityIssuesScanEnable - private var currentIacScanPanelEnabled = settings.iacScanEnabled - private var currentContainerScanEnabled = settings.containerScanEnabled - - val codeAlertPanel = panel { + val codeAlertPanel = com.intellij.ui.dsl.builder.panel { row { - cell { - snykCodeAlertHyperLinkLabel() - .withLargeLeftGap() - snykCodeReCheckLinkLabel() - .withLargeLeftGap() - } + cell(snykCodeAlertHyperLinkLabel) + cell(snykCodeReCheckLinkLabel) } }.apply { border = JBUI.Borders.empty() this.isVisible = false } - val panel = panel { + val panel = com.intellij.ui.dsl.builder.panel { row { - cell { - checkBox( - text = ProductType.OSS.productSelectionName, - getter = { settings.ossScanEnable }, - setter = { settings.ossScanEnable = it }, - comment = cliScanComments - ).component.apply { - this.addItemListener { - isLastProductDisabling(this, currentOssScanEnabled) - currentOssScanEnabled = this.isSelected - } - name = ProductType.OSS.toString() - } + checkBox(ProductType.OSS.productSelectionName).applyToComponent { + name = text + cliScanComments?.let { comment(it) } label("").component.convertIntoHelpHintLabel(ProductType.OSS.description) + isSelected = settings.ossScanEnable + this.addItemListener { + correctLastProductDisabled(it) + settings.ossScanEnable = this.isSelected + } } } row { - cell { - checkBox( - text = ProductType.ADVISOR.productSelectionName, - getter = { settings.advisorEnable }, - setter = { settings.advisorEnable = it } - ) - label("").component.convertIntoHelpHintLabel( - "Discover the health (maintenance, community, popularity & security)\n" + - "status of your open source packages" - ) + checkBox(ProductType.ADVISOR.productSelectionName).applyToComponent { + name = text + label("").component.convertIntoHelpHintLabel(ProductType.ADVISOR.description) + isSelected = settings.advisorEnable + this.addItemListener { + correctLastProductDisabled(it) + settings.advisorEnable = this.isSelected + } } } - if (isIacEnabled()) { - row { - cell { - checkBox( - text = ProductType.IAC.productSelectionName, - getter = { settings.iacScanEnabled }, - setter = { settings.iacScanEnabled = it } - ).component.apply { - this.addItemListener { - isLastProductDisabling(this, currentIacScanPanelEnabled) - currentIacScanPanelEnabled = this.isSelected - } - name = ProductType.IAC.toString() - } - label("").component.convertIntoHelpHintLabel(ProductType.IAC.description) + row { + checkBox(ProductType.IAC.productSelectionName).applyToComponent { + name = text + label("").component.convertIntoHelpHintLabel(ProductType.IAC.description) + isSelected = settings.iacScanEnabled + this.addItemListener { + correctLastProductDisabled(it) + settings.iacScanEnabled = this.isSelected } } } - if (isContainerEnabled()) { - row { - cell { - checkBox( - text = ProductType.CONTAINER.productSelectionName, - getter = { settings.containerScanEnabled }, - setter = { enabled -> - settings.containerScanEnabled = enabled - val imagesCache = getKubernetesImageCache(project) - if (enabled) imagesCache?.scanProjectForKubernetesFiles() else imagesCache?.clear() - } - ).component.apply { - this.addItemListener { - isLastProductDisabling(this, currentContainerScanEnabled) - currentContainerScanEnabled = this.isSelected - } - name = ProductType.CONTAINER.toString() - } - label("").component.convertIntoHelpHintLabel(ProductType.CONTAINER.description) + row { + checkBox(ProductType.CONTAINER.productSelectionName).applyToComponent { + name = text + label("").component.convertIntoHelpHintLabel(ProductType.CONTAINER.description) + isSelected = settings.containerScanEnabled + this.addItemListener { + correctLastProductDisabled(it) + settings.containerScanEnabled = this.isSelected + val imagesCache = getKubernetesImageCache(project) + if (this.isSelected) imagesCache?.scanProjectForKubernetesFiles() else imagesCache?.clear() } } } row { - cell { - codeSecurityCheckbox = checkBox( - text = ProductType.CODE_SECURITY.productSelectionName, - getter = { settings.snykCodeSecurityIssuesScanEnable }, - setter = { settings.snykCodeSecurityIssuesScanEnable = it } - ) - .component.apply { - this.addItemListener { - isLastProductDisabling(this, currentCodeSecurityScanEnabled) - currentCodeSecurityScanEnabled = this.isSelected - } - name = ProductType.CODE_SECURITY.toString() - } + checkBox(ProductType.CODE_SECURITY.productSelectionName).applyToComponent { + name = text + codeSecurityCheckbox = this + isSelected = settings.snykCodeSecurityIssuesScanEnable label("").component.convertIntoHelpHintLabel(ProductType.CODE_SECURITY.description) - - if (!simplifyForOnboardPanel) { - codeQualityCheckbox = checkBox( - text = ProductType.CODE_QUALITY.productSelectionName, - getter = { settings.snykCodeQualityIssuesScanEnable }, - setter = { settings.snykCodeQualityIssuesScanEnable = it } - ) - .withLargeLeftGap() - .component.apply { - this.addItemListener { - isLastProductDisabling(this, currentCodeQualityScanEnabled) - currentCodeQualityScanEnabled = this.isSelected - } - name = ProductType.CODE_QUALITY.toString() - } - label("").component.convertIntoHelpHintLabel(ProductType.CODE_QUALITY.description) + this.addItemListener { + correctLastProductDisabled(it) + settings.snykCodeSecurityIssuesScanEnable = this.isSelected + snykCodeComment?.isVisible = shouldSnykCodeCommentBeVisible() + } + } + checkBox(ProductType.CODE_QUALITY.productSelectionName).applyToComponent { + name = text + codeQualityCheckbox = this + isSelected = settings.snykCodeQualityIssuesScanEnable + label("").component.convertIntoHelpHintLabel(ProductType.CODE_QUALITY.description) + this.addItemListener { + correctLastProductDisabled(it) + settings.snykCodeQualityIssuesScanEnable = this.isSelected + snykCodeComment?.isVisible = shouldSnykCodeCommentBeVisible() } } } row { snykCodeComment = label("") - .withLargeLeftGap() .component.apply { foreground = UIUtil.getContextHelpForeground() } - - codeSecurityCheckbox?.addItemListener { - snykCodeComment?.isVisible = shouldSnykCodeCommentBeVisible() - } - codeQualityCheckbox?.addItemListener { - snykCodeComment?.isVisible = shouldSnykCodeCommentBeVisible() - } runBackgroundableTask("Checking Snyk Code enablement in organisation", project, true) { checkSastEnabled() } @@ -200,6 +147,11 @@ class ScanTypesPanel( border = JBUI.Borders.empty(2) } + private fun JBCheckBox.correctLastProductDisabled(it: ItemEvent) { + val deselected = it.stateChange == ItemEvent.DESELECTED + isLastProductDisabling(this, deselected) + } + private fun JLabel.convertIntoHelpHintLabel(text: String) { icon = AllIcons.General.ContextHelp addMouseListener(ShowHintMouseAdapter(this, text)) @@ -221,7 +173,7 @@ class ScanTypesPanel( return } - var sastSettingsError: String = "" + var sastSettingsError = "" val sastCliConfigSettings: CliConfigSettings? = try { val sastSettings = getSnykApiService().getSastSettings() settings.sastSettingsError = false @@ -244,15 +196,13 @@ class ScanTypesPanel( ) if (snykCodeAvailable) { setSnykCodeComment(progressMessage = "Checking if Snyk Code enabled for organisation...") { - when (settings.sastOnServerEnabled) { true -> { doShowFilesToUpload() } false -> { - settings.snykCodeSecurityIssuesScanEnable = false - settings.snykCodeQualityIssuesScanEnable = false + disableSnykCode() showSnykCodeAlert( message = "Snyk Code is disabled by your organisation's configuration: ", linkText = "Snyk > Settings > Snyk Code", @@ -267,8 +217,7 @@ class ScanTypesPanel( } null -> { - settings.snykCodeSecurityIssuesScanEnable = false - settings.snykCodeQualityIssuesScanEnable = false + disableSnykCode() showSnykCodeAlert( message = "Not able to connect to Snyk server. Check your connection and network settings." ) @@ -279,6 +228,13 @@ class ScanTypesPanel( } } + private fun disableSnykCode() { + codeSecurityCheckbox?.isSelected = false + codeQualityCheckbox?.isSelected = false + settings.snykCodeSecurityIssuesScanEnable = false + settings.snykCodeQualityIssuesScanEnable = false + } + private fun doShowFilesToUpload() { setSnykCodeAvailability(true) showSnykCodeAlert("") @@ -339,7 +295,7 @@ class ScanTypesPanel( } else { codeAlertPanel.isVisible = showAlert // todo: change to setTextWithHyperlink() after move to sinceId >= 211 - snykCodeAlertHyperLinkLabel.setHyperlinkText(message, linkText, "") + snykCodeAlertHyperLinkLabel.setTextWithHyperlink("$message$linkText") snykCodeAlertHyperLinkLabel.setHyperlinkTarget(url) if (runOnClick == null) { currentHyperlinkListener?.let { snykCodeAlertHyperLinkLabel.removeHyperlinkListener(it) } @@ -358,24 +314,23 @@ class ScanTypesPanel( val enabled = (settings.sastOnServerEnabled == true) && available codeSecurityCheckbox?.let { it.isEnabled = enabled - it.isSelected = enabled && settings.snykCodeSecurityIssuesScanEnable } codeQualityCheckbox?.let { it.isEnabled = enabled - it.isSelected = enabled && settings.snykCodeQualityIssuesScanEnable } } - private fun isLastProductDisabling(component: JBCheckBox, wasEnabled: Boolean): Boolean { + private fun isLastProductDisabling(component: JBCheckBox, deselected: Boolean): Boolean { val onlyOneEnabled = arrayOf( - currentOssScanEnabled, - currentCodeSecurityScanEnabled, - currentCodeQualityScanEnabled, - currentIacScanPanelEnabled, - currentContainerScanEnabled + settings.ossScanEnable, + settings.snykCodeSecurityIssuesScanEnable, + settings.snykCodeQualityIssuesScanEnable, + settings.iacScanEnabled, + settings.containerScanEnabled, + settings.advisorEnable, ).count { it } == 1 - if (onlyOneEnabled && wasEnabled) { + if (onlyOneEnabled && deselected) { component.isSelected = true SnykBalloonNotificationHelper.showWarnBalloonForComponent( "At least one Scan type should be enabled", diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/LabelProvider.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/LabelProvider.kt index 9da4690ff..a7b319ffb 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/LabelProvider.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/LabelProvider.kt @@ -1,60 +1,59 @@ package io.snyk.plugin.ui.toolwindow import com.intellij.ide.BrowserUtil -import com.intellij.ui.components.labels.LinkLabel +import com.intellij.ui.components.ActionLink +import java.awt.event.ActionEvent +import java.awt.event.ActionListener import java.net.URL -import javax.swing.JLabel class LabelProvider { companion object { - const val npmBaseUrl = "https://app.snyk.io/test/npm" - const val cweBaseUrl = "https://cwe.mitre.org/data/definitions" - const val vulnerabilityBaseUrl = "https://snyk.io/vuln" - const val cvssBaseUrl = "https://www.first.org/cvss/calculator/3.1" - const val cveBaseUrl = "https://cve.mitre.org/cgi-bin/cvename.cgi?name" + const val NPM_BASE_URL = "https://app.snyk.io/test/npm" + const val CWE_BASE_URL = "https://cwe.mitre.org/data/definitions" + const val VULNERABILITY_BASE_URL = "https://snyk.io/vuln" + const val CVSS_BASE_URL = "https://www.first.org/cvss/calculator/3.1" + const val CVE_BASE_URL = "https://cve.mitre.org/cgi-bin/cvename.cgi?name" } - class OpenLinkAction(val url: URL) : Runnable { - override fun run() { + class OpenLinkAction(val url: URL) : ActionListener { + override fun actionPerformed(e: ActionEvent?) { BrowserUtil.open(url.toExternalForm()) } } - fun getDependencyLabel(packageManager: String, packageName: String): JLabel { + fun getDependencyLabel(packageManager: String, packageName: String): ActionLink { return if (packageManager != "npm") { - JLabel(packageName) + ActionLink(packageName) } else { - val url = URL("$npmBaseUrl/$packageName") - return createLinkLabel(url, packageName) + val url = URL("$NPM_BASE_URL/$packageName") + return createActionLink(url, packageName) } } - fun getCWELabel(cwe: String): LinkLabel<*> { - return createLinkLabel(URL("$cweBaseUrl/${cwe.removePrefix("CWE-")}.html"), cwe) + fun getCWELabel(cwe: String): ActionLink { + return createActionLink(URL("$CWE_BASE_URL/${cwe.removePrefix("CWE-")}.html"), cwe) } - fun getVulnerabilityLabel(id: String, idUrl: String? = null): JLabel { - val url = idUrl ?: "$vulnerabilityBaseUrl/$id" - return createLinkLabel(URL(url), id.toUpperCase()) + fun getVulnerabilityLabel(id: String, idUrl: String? = null): ActionLink { + val url = idUrl ?: "$VULNERABILITY_BASE_URL/$id" + return createActionLink(URL(url), id.uppercase()) } - fun getCVSSLabel(text: String, id: String): JLabel { - return createLinkLabel(URL("$cvssBaseUrl#$id"), text) + fun getCVSSLabel(text: String, id: String): ActionLink { + return createActionLink(URL("$CVSS_BASE_URL#$id"), text) } - fun getCVELabel(cve: String): JLabel { - return createLinkLabel(URL("$cveBaseUrl=$cve"), cve) + fun getCVELabel(cve: String): ActionLink { + return createActionLink(URL("$CVE_BASE_URL=$cve"), cve) } - fun createLinkLabel( + fun createActionLink( url: URL, text: String, customToolTipText: String = "Click to open description in the Browser" - ): LinkLabel<*> { + ): ActionLink { val openLinkAction = OpenLinkAction(url) - return LinkLabel.create(text, openLinkAction).apply { - toolTipText = customToolTipText - } + return ActionLink(text, openLinkAction).apply { toolTipText = customToolTipText } } } 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 3175989a6..9cd84d211 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanel.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanel.kt @@ -8,10 +8,10 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.fileEditor.FileDocumentManager 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 @@ -695,7 +695,6 @@ class SnykToolWindowPanel(val project: Project) : JPanel(), Disposable { ossResult.allCliIssues?.forEach { vulnsForFile -> if (vulnsForFile.vulnerabilities.isNotEmpty()) { val ossGroupedResult = vulnsForFile.toGroupedResult() - val fileTreeNode = FileTreeNode(vulnsForFile, project) rootOssTreeNode.add(fileTreeNode) @@ -813,17 +812,9 @@ class SnykToolWindowPanel(val project: Project) : JPanel(), Disposable { rootIacIssuesTreeNode.removeAllChildren() - fun navigateToIaCIssue(filePath: String, issueLine: Int): () -> Unit = { - val virtualFile = VirtualFileManager.getInstance().findFileByNioPath(Paths.get(filePath)) - if (virtualFile != null && virtualFile.isValid) { - val document = FileDocumentManager.getInstance().getDocument(virtualFile) - if (document != null) { - val candidate = issueLine - 1 // to 1-based count used in the editor - val lineNumber = if (0 <= candidate && candidate < document.lineCount) candidate else 0 - val lineStartOffset = document.getLineStartOffset(lineNumber) - - navigateToSource(project, virtualFile, lineStartOffset) - } + fun navigateToIaCIssue(virtualFile: VirtualFile?, lineStartOffset: Int): () -> Unit = { + if (virtualFile?.isValid == true) { + navigateToSource(project, virtualFile, lineStartOffset) } } @@ -839,8 +830,8 @@ class SnykToolWindowPanel(val project: Project) : JPanel(), Disposable { .sortedByDescending { it.getSeverity() } .forEach { val navigateToSource = navigateToIaCIssue( - iacVulnerabilitiesForFile.targetFilePath, - it.lineNumber + iacVulnerabilitiesForFile.virtualFile, + it.lineStartOffset ) fileTreeNode.add(IacIssueTreeNode(it, project, navigateToSource)) } @@ -848,7 +839,7 @@ class SnykToolWindowPanel(val project: Project) : JPanel(), Disposable { } iacResult.getVisibleErrors().forEach { snykError -> rootIacIssuesTreeNode.add( - ErrorTreeNode(snykError, project, navigateToIaCIssue(snykError.path, 0)) + ErrorTreeNode(snykError, project, navigateToIaCIssue(snykError.virtualFile, 0)) ) } } @@ -867,28 +858,32 @@ class SnykToolWindowPanel(val project: Project) : JPanel(), Disposable { rootContainerIssuesTreeNode.removeAllChildren() - fun navigateToImage(imageName: String): () -> Unit = { - val targetImage = getKubernetesImageCache(project) - ?.getKubernetesWorkloadImages() - ?.find { it.image == imageName } - val virtualFile = targetImage?.virtualFile - val line = targetImage?.lineNumber?.let { it - 1 } // to 1-based count used in the editor - if (virtualFile != null && virtualFile.isValid && line != null) { - val document = FileDocumentManager.getInstance().getDocument(virtualFile) - if (document != null) { - val lineNumber = if (0 <= line && line < document.lineCount) line else 0 - val lineStartOffset = document.getLineStartOffset(lineNumber) - navigateToSource(project, virtualFile, lineStartOffset) - } + fun navigateToImage(issuesForImage: ContainerIssuesForImage?, virtualFile: VirtualFile?): () -> Unit = { + val image = issuesForImage?.workloadImages?.getOrNull(0) + + var file = virtualFile + if (image != null) { + file = image.virtualFile + } + + if (file?.isValid == true) { + navigateToSource(project, file, image?.lineStartOffset ?: 0) } } val settings = pluginSettings() if (settings.containerScanEnabled && settings.treeFiltering.containerResults) { containerResult.allCliIssues?.forEach { issuesForImage -> + val image = issuesForImage.workloadImages.getOrNull(0) + val virtualFile = image?.virtualFile if (issuesForImage.vulnerabilities.isNotEmpty()) { val imageTreeNode = - ContainerImageTreeNode(issuesForImage, project, navigateToImage(issuesForImage.imageName)) + ContainerImageTreeNode( + issuesForImage, project, navigateToImage( + issuesForImage, + virtualFile + ) + ) rootContainerIssuesTreeNode.add(imageTreeNode) issuesForImage.groupedVulnsById.values @@ -896,14 +891,19 @@ class SnykToolWindowPanel(val project: Project) : JPanel(), Disposable { .sortedByDescending { it.head.getSeverity() } .forEach { imageTreeNode.add( - ContainerIssueTreeNode(it, project, navigateToImage(issuesForImage.imageName)) + ContainerIssueTreeNode( + it, project, navigateToImage( + issuesForImage, + virtualFile + ) + ) ) } } } containerResult.errors.forEach { snykError -> rootContainerIssuesTreeNode.add( - ErrorTreeNode(snykError, project, navigateToImage(snykError.path)) + ErrorTreeNode(snykError, project, navigateToImage(null, snykError.virtualFile)) ) } } 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 4abc07b77..91bdf833f 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykTreeCellRenderer.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/SnykTreeCellRenderer.kt @@ -2,8 +2,6 @@ package io.snyk.plugin.ui.toolwindow import ai.deepcode.javaclient.core.SuggestionForFile import com.intellij.icons.AllIcons -import com.intellij.ide.util.gotoByName.GotoFileCellRenderer -import com.intellij.openapi.util.Iconable import com.intellij.ui.ColoredTreeCellRenderer import com.intellij.ui.SimpleTextAttributes import com.intellij.util.ui.UIUtil @@ -11,10 +9,9 @@ import icons.SnykIcons import io.snyk.plugin.getSnykCachedResults import io.snyk.plugin.pluginSettings import io.snyk.plugin.snykcode.core.AnalysisData -import io.snyk.plugin.snykcode.core.PDU import io.snyk.plugin.snykcode.core.SnykCodeFile import io.snyk.plugin.snykcode.getSeverityAsEnum -import io.snyk.plugin.ui.PackageManagerIconProvider +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 @@ -40,7 +37,7 @@ import snyk.iac.ui.toolwindow.IacFileTreeNode import snyk.iac.ui.toolwindow.IacIssueTreeNode import snyk.oss.OssVulnerabilitiesForFile import snyk.oss.Vulnerability -import java.util.* +import java.util.Locale import javax.swing.Icon import javax.swing.JTree @@ -69,15 +66,13 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { is FileTreeNode -> { val fileVulns = value.userObject as OssVulnerabilitiesForFile - nodeIcon = PackageManagerIconProvider.getIcon(fileVulns.packageManager.lowercase(Locale.getDefault())) - val relativePath = fileVulns.virtualFile?.let { - GotoFileCellRenderer.getRelativePath( - fileVulns.virtualFile, value.project - ) - } ?: "" + nodeIcon = getIcon(fileVulns.packageManager.lowercase(Locale.getDefault())) toolTipText = - relativePath + fileVulns.sanitizedTargetFile + ProductType.OSS.getCountText(value.childCount) - + buildString { + append(fileVulns.relativePath ?: "") + append(fileVulns.sanitizedTargetFile) + append(ProductType.OSS.getCountText(value.childCount)) + } text = toolTipText.letIf(toolTipText.length > MAX_FILE_TREE_NODE_LENGTH) { "..." + it.substring( it.length - MAX_FILE_TREE_NODE_LENGTH, it.length @@ -109,10 +104,12 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { is SnykCodeFileTreeNode -> { val (file, productType) = value.userObject as Pair + val relativePath = file.relativePath toolTipText = - GotoFileCellRenderer.getRelativePath(file.virtualFile, file.project) + productType.getCountText( - value.childCount - ) + buildString { + append(relativePath) + append(productType.getCountText(value.childCount)) + } text = toolTipText.letIf(toolTipText.length > MAX_FILE_TREE_NODE_LENGTH) { "..." + it.substring( @@ -120,8 +117,7 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { ) } - val psiFile = PDU.toPsiFile(file) - nodeIcon = psiFile?.getIcon(Iconable.ICON_FLAG_READ_STATUS) + nodeIcon = file.icon if (!AnalysisData.instance.isFileInCache(file)) { attributes = SimpleTextAttributes.GRAYED_ATTRIBUTES text += OBSOLETE_SUFFIX @@ -131,15 +127,15 @@ class SnykTreeCellRenderer : ColoredTreeCellRenderer() { is IacFileTreeNode -> { val iacVulnerabilitiesForFile = value.userObject as IacIssuesForFile - nodeIcon = PackageManagerIconProvider.getIcon( + nodeIcon = getIcon( iacVulnerabilitiesForFile.packageManager.lowercase(Locale.getDefault()) ) - val relativePath = iacVulnerabilitiesForFile.virtualFile?.let { - GotoFileCellRenderer.getRelativePath( - iacVulnerabilitiesForFile.virtualFile, value.project - ) - } ?: iacVulnerabilitiesForFile.targetFilePath - toolTipText = relativePath + ProductType.IAC.getCountText(value.childCount) + val relativePath = + iacVulnerabilitiesForFile.relativePath ?: iacVulnerabilitiesForFile.targetFilePath + toolTipText = buildString { + append(relativePath) + append(ProductType.IAC.getCountText(value.childCount)) + } text = toolTipText.letIf(toolTipText.length > MAX_FILE_TREE_NODE_LENGTH) { "..." + it.substring( diff --git a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt index 398c5e660..a89701e41 100644 --- a/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt +++ b/src/main/kotlin/io/snyk/plugin/ui/toolwindow/panels/SuggestionDescriptionPanel.kt @@ -217,10 +217,7 @@ class SuggestionDescriptionPanel( val paddedStepNumber = (index + 1).toString().padStart(2, ' ') - val fileToNavigate = if (markerRange.file.isNullOrEmpty()) snykCodeFile else { - PDU.instance.getFileByDeepcodedPath(markerRange.file, project)?.let { PDU.toSnykCodeFile(it) } - } - val fileName = fileToNavigate?.virtualFile?.name ?: markerRange.file + val fileName = snykCodeFile.virtualFile.name val positionLinkText = "$fileName:${markerRange.startRow}".padEnd(maxFilenameLength + 5, ' ') @@ -231,9 +228,9 @@ class SuggestionDescriptionPanel( toolTipText = "Click to show in the Editor", customFont = JTextArea().font ) { - if (fileToNavigate == null || !fileToNavigate.virtualFile.isValid) return@linkLabel + if (!snykCodeFile.virtualFile.isValid) return@linkLabel - navigateToSource(project, fileToNavigate.virtualFile, markerRange.start, markerRange.end) + navigateToSource(project, snykCodeFile.virtualFile, markerRange.start, markerRange.end) allStepPanels.forEach { it.background = UIUtil.getTextFieldBackground() @@ -242,7 +239,7 @@ class SuggestionDescriptionPanel( } stepPanel.add(positionLabel, baseGridConstraintsAnchorWest(0, indent = 1)) - val codeLine = codeLine(markerRange, fileToNavigate) + val codeLine = codeLine(markerRange, snykCodeFile) codeLine.isOpaque = false stepPanel.add( codeLine, diff --git a/src/main/kotlin/snyk/advisor/AdvisorEditorFactoryListener.kt b/src/main/kotlin/snyk/advisor/AdvisorEditorFactoryListener.kt index 97708185a..7008dceb1 100644 --- a/src/main/kotlin/snyk/advisor/AdvisorEditorFactoryListener.kt +++ b/src/main/kotlin/snyk/advisor/AdvisorEditorFactoryListener.kt @@ -6,13 +6,11 @@ import com.intellij.openapi.editor.ex.EditorEx import com.intellij.psi.PsiCompiledElement import com.intellij.psi.PsiDocumentManager -class AdvisorEditorFactoryListener: EditorFactoryListener { +class AdvisorEditorFactoryListener : EditorFactoryListener { override fun editorCreated(event: EditorFactoryEvent) { - val editor = event.editor - // sanity checks, examples taken from com.intellij.codeInsight.preview.ImageOrColorPreviewManager.registerListeners if (editor.isOneLineMode) { return } @@ -31,9 +29,8 @@ class AdvisorEditorFactoryListener: EditorFactoryListener { return } - val advisorScoreProvider = AdvisorScoreProvider(editor) + val advisorScoreProvider = AdvisorScoreProvider(editor, psiFile) editor.registerLineExtensionPainter(advisorScoreProvider::getLineExtensions) - } } diff --git a/src/main/kotlin/snyk/advisor/AdvisorScoreProvider.kt b/src/main/kotlin/snyk/advisor/AdvisorScoreProvider.kt index 7e338217d..dcc155f74 100644 --- a/src/main/kotlin/snyk/advisor/AdvisorScoreProvider.kt +++ b/src/main/kotlin/snyk/advisor/AdvisorScoreProvider.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.editor.event.VisibleAreaListener import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.editor.markup.TextAttributes import com.intellij.openapi.ui.popup.Balloon +import com.intellij.psi.PsiFile import com.intellij.ui.Gray import com.intellij.ui.JBColor import io.snyk.plugin.analytics.getEcosystem @@ -25,16 +26,17 @@ import java.awt.Color import java.awt.Cursor import java.awt.Font -class AdvisorScoreProvider( - private val editor: Editor -) { - private val scoredLines: MutableMap> = mutableMapOf() +private const val darkModeTextAttribute = 0x3d8065 + +class AdvisorScoreProvider(private val editor: Editor, private val psiFile: PsiFile) { + private var packageManager: AdvisorPackageManager? = null + + private val scoredLines: MutableMap> private var selectedScore: Int? = null private var currentBalloon: Balloon? = null private var currentCursor: Cursor? = null private var editorListener: AdvisorEditorListener? = null - - private val packageNameProvider = PackageNameProvider(editor) + var packageNameProvider: PackageNameProvider? = null fun getLineExtensions(lineNumber: Int): Collection { val settings = pluginSettings() @@ -42,7 +44,7 @@ class AdvisorScoreProvider( return resetAndReturnEmptyList() } - val (packageName, packageManager) = packageNameProvider.getPackageName(lineNumber) + val (packageName, packageManager) = packageNameProvider?.getPackageName(lineNumber) ?: return resetAndReturnEmptyList() val info = packageName?.let { @@ -153,11 +155,8 @@ class AdvisorScoreProvider( // not over Text in LineExtension val lineLength = e.editor.document.getLineEndOffset(line) - e.editor.document.getLineStartOffset(line) - if (e.logicalPosition.column < lineLength + LINE_EXTENSION_PREFIX.length || - e.logicalPosition.column > lineLength + LINE_EXTENSION_LENGTH - ) return false - - return true + return !(e.logicalPosition.column < lineLength + LINE_EXTENSION_PREFIX.length || + e.logicalPosition.column > lineLength + LINE_EXTENSION_LENGTH) } private fun cleanCacheForRemovedLines() { @@ -188,7 +187,9 @@ class AdvisorScoreProvider( .globalScheme.getAttributes(DefaultLanguageHighlighterColors.BLOCK_COMMENT) return if (attributes == null || attributes.foregroundColor == null) { TextAttributes( - JBColor { if (EditorColorsManager.getInstance().isDarkEditor) Color(0x3d8065) else Gray._135 }, + JBColor.lazy { + JBColor(Gray._135, Color(darkModeTextAttribute)) + }, null, null, null, @@ -209,4 +210,14 @@ class AdvisorScoreProvider( fontType = fontType xor Font.BOLD } } + + init { + this.scoredLines = mutableMapOf() + this.packageManager = when (psiFile.virtualFile.name) { + "package.json" -> AdvisorPackageManager.NPM + "requirements.txt" -> AdvisorPackageManager.PYTHON + else -> null + } + this.packageManager?.let { this.packageNameProvider = PackageNameProvider(editor, it) } + } } diff --git a/src/main/kotlin/snyk/advisor/PackageNameProvider.kt b/src/main/kotlin/snyk/advisor/PackageNameProvider.kt index 60eb939a4..7b3eb5c4f 100644 --- a/src/main/kotlin/snyk/advisor/PackageNameProvider.kt +++ b/src/main/kotlin/snyk/advisor/PackageNameProvider.kt @@ -2,12 +2,10 @@ package snyk.advisor import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.editor.Editor -import com.intellij.psi.PsiCompiledElement -import com.intellij.psi.PsiDocumentManager import snyk.advisor.buildsystems.NpmSupport import snyk.advisor.buildsystems.PythonSupport -class PackageNameProvider(private val editor: Editor) { +class PackageNameProvider(private val editor: Editor, private val packageManager: AdvisorPackageManager) { fun getPackageName(lineNumber: Int): Pair? { // sanity checks, examples taken from ImageOrColorPreviewManager.registerListeners @@ -15,18 +13,12 @@ class PackageNameProvider(private val editor: Editor) { if (project == null || project.isDisposed) { return null } - val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) - if (psiFile == null || psiFile.virtualFile == null || psiFile is PsiCompiledElement) { - return null - } - val packageManager = when (psiFile.virtualFile.name) { - "package.json" -> AdvisorPackageManager.NPM - "requirements.txt" -> AdvisorPackageManager.PYTHON - else -> return null - } if (lineNumber < 0 || (editor.document.lineCount - 1) < lineNumber) { - log.warn("Line number $lineNumber is out of range [0:${editor.document.lineCount - 1}] at ${psiFile.virtualFile.path}") + log.warn( + "Line number $lineNumber is out of range " + + "[0:${editor.document.lineCount - 1}] at ${editor.virtualFile.path}" + ) return Pair(null, packageManager) } val packageName = when (packageManager) { diff --git a/src/main/kotlin/snyk/common/EnvironmentHelper.kt b/src/main/kotlin/snyk/common/EnvironmentHelper.kt new file mode 100644 index 000000000..ed0509b1d --- /dev/null +++ b/src/main/kotlin/snyk/common/EnvironmentHelper.kt @@ -0,0 +1,61 @@ +package snyk.common + +import com.intellij.util.net.HttpConfigurable +import io.snyk.plugin.pluginSettings +import snyk.pluginInfo +import java.net.URI +import java.net.URLEncoder + +object EnvironmentHelper { + fun updateEnvironment( + environment: MutableMap, + apiToken: String + ) { + val endpoint = getEndpointUrl() + + val oauthEnabledEnvVar = "INTERNAL_SNYK_OAUTH_ENABLED" + val oauthEnvVar = "INTERNAL_OAUTH_TOKEN_STORAGE" + val snykTokenEnvVar = "SNYK_TOKEN" + + val endpointURI = URI(endpoint) + val oauthEnabled = endpointURI.isOauth() + if (oauthEnabled) { + environment[oauthEnabledEnvVar] = "1" + environment.remove(snykTokenEnvVar) + } else { + environment.remove(oauthEnvVar) + environment.remove(oauthEnabledEnvVar) + } + + if (apiToken.isNotEmpty()) { + if (oauthEnabled) { + environment[oauthEnvVar] = apiToken + } else { + environment[snykTokenEnvVar] = apiToken + } + } + + environment["SNYK_API"] = endpoint + + if (!pluginSettings().usageAnalyticsEnabled || endpointURI.isFedramp()) { + environment["SNYK_CFG_DISABLE_ANALYTICS"] = "1" + } + + environment["SNYK_INTEGRATION_NAME"] = pluginInfo.integrationName + environment["SNYK_INTEGRATION_VERSION"] = pluginInfo.integrationVersion + environment["SNYK_INTEGRATION_ENVIRONMENT"] = pluginInfo.integrationEnvironment + environment["SNYK_INTEGRATION_ENVIRONMENT_VERSION"] = pluginInfo.integrationEnvironmentVersion + val proxySettings = HttpConfigurable.getInstance() + val proxyHost = proxySettings.PROXY_HOST + if (proxySettings != null && proxySettings.USE_HTTP_PROXY && proxyHost.isNotEmpty()) { + val authentication = if (proxySettings.PROXY_AUTHENTICATION) { + val auth = proxySettings.getPromptedAuthentication(proxyHost, "Snyk: Please enter your proxy password") + if (auth == null) "" else auth.userName.urlEncode() + ":" + String(auth.password).urlEncode() + "@" + } else "" + environment["http_proxy"] = "http://$authentication$proxyHost:${proxySettings.PROXY_PORT}" + environment["https_proxy"] = "http://$authentication$proxyHost:${proxySettings.PROXY_PORT}" + } + } + + private fun String.urlEncode() = URLEncoder.encode(this, "UTF-8") +} diff --git a/src/main/kotlin/snyk/common/RelativePathHelper.kt b/src/main/kotlin/snyk/common/RelativePathHelper.kt new file mode 100644 index 000000000..480d10b2c --- /dev/null +++ b/src/main/kotlin/snyk/common/RelativePathHelper.kt @@ -0,0 +1,19 @@ +package snyk.common + +import com.intellij.ide.util.gotoByName.GotoFileCellRenderer +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class RelativePathHelper { + private var relPathCached: String? = null + + fun getRelativePath(virtualFile: VirtualFile, project: Project): String? { + if (relPathCached == null) { + ApplicationManager.getApplication().runReadAction { + relPathCached = GotoFileCellRenderer.getRelativePath(virtualFile, project) + } + } + return relPathCached + } +} diff --git a/src/main/kotlin/snyk/common/SnykError.kt b/src/main/kotlin/snyk/common/SnykError.kt index 19560fda1..a075e41b9 100644 --- a/src/main/kotlin/snyk/common/SnykError.kt +++ b/src/main/kotlin/snyk/common/SnykError.kt @@ -1,7 +1,22 @@ package snyk.common +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile + data class SnykError( val message: String, val path: String, val code: Int? = null -) +) { + var virtualFile: VirtualFile? = null + + init { + if (path.isNotEmpty()) { + try { + virtualFile = LocalFileSystem.getInstance().findFileByPath(this.path) + } catch (ignore: RuntimeException) { + // ignore because this file is optional + } + } + } +} diff --git a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt index 90792982c..238800280 100644 --- a/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt +++ b/src/main/kotlin/snyk/common/lsp/LanguageServerWrapper.kt @@ -12,6 +12,7 @@ import org.eclipse.lsp4j.jsonrpc.Launcher import org.eclipse.lsp4j.launch.LSPLauncher import org.eclipse.lsp4j.services.LanguageClient import org.eclipse.lsp4j.services.LanguageServer +import snyk.common.EnvironmentHelper import snyk.common.getEndpointUrl import snyk.common.lsp.commands.ScanDoneEvent import snyk.pluginInfo @@ -46,6 +47,8 @@ class LanguageServerWrapper(private val lsPath: String = getCliFile().absolutePa val cmd = listOf(lsPath, "language-server", "-l", logLevel) val processBuilder = ProcessBuilder(cmd) + pluginSettings().token?.let { EnvironmentHelper.updateEnvironment(processBuilder.environment(), it) } + process = processBuilder.start() launcher = LSPLauncher.createClientLauncher(languageClient, process.inputStream, process.outputStream) languageServer = launcher.remoteProxy diff --git a/src/main/kotlin/snyk/container/KubernetesWorkloadImage.kt b/src/main/kotlin/snyk/container/KubernetesWorkloadImage.kt index 6d2a744d1..876938bee 100644 --- a/src/main/kotlin/snyk/container/KubernetesWorkloadImage.kt +++ b/src/main/kotlin/snyk/container/KubernetesWorkloadImage.kt @@ -2,4 +2,9 @@ package snyk.container import com.intellij.openapi.vfs.VirtualFile -data class KubernetesWorkloadImage(val image: String, val virtualFile: VirtualFile, val lineNumber: Int = 0) +data class KubernetesWorkloadImage( + val image: String, + val virtualFile: VirtualFile, + val lineNumber: Int = 0, + val lineStartOffset: Int = 0, +) diff --git a/src/main/kotlin/snyk/container/YAMLImageExtractor.kt b/src/main/kotlin/snyk/container/YAMLImageExtractor.kt index 41d5cf6ba..585532c19 100644 --- a/src/main/kotlin/snyk/container/YAMLImageExtractor.kt +++ b/src/main/kotlin/snyk/container/YAMLImageExtractor.kt @@ -37,9 +37,15 @@ object YAMLImageExtractor { val extractedImages = mutableListOf() getFileLines(psiFile).forEachIndexed { lineNumber, line -> val imageName = extractImagePathFromLine(line) + val lineStartOffset = psiFile.viewProvider.document?.getLineStartOffset(lineNumber) ?: 0 if (imageName.isNotBlank()) { // we report line numbers with a start index of 1 elsewhere (e.g. IaC) - val image = KubernetesWorkloadImage(imageName, psiFile.virtualFile, lineNumber + 1) + val image = KubernetesWorkloadImage( + imageName, + psiFile.virtualFile, + lineNumber + 1, + lineStartOffset + ) extractedImages.add(image) logger.debug("Found image $image") } diff --git a/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt b/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt index 87ab9a729..31e9c7ab5 100644 --- a/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt +++ b/src/main/kotlin/snyk/container/ui/BaseImageRemediationDetailPanel.kt @@ -4,7 +4,7 @@ import com.intellij.ide.util.PsiNavigationSupport import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import com.intellij.ui.components.labels.LinkLabel +import com.intellij.ui.components.ActionLink import com.intellij.uiDesigner.core.GridLayoutManager import icons.SnykIcons import io.snyk.plugin.Severity @@ -175,14 +175,13 @@ class BaseImageRemediationDetailPanel( customLabels = secondRowTitleLabels() ) - private fun secondRowTitleLabels(): List { + private fun secondRowTitleLabels(): List { val affectedFile2Line = targetImages.map { Pair(it.virtualFile, it.lineNumber) } - return listOf(JLabel("${imageIssues.uniqueCount} vulnerabilities")) + - // todo: fix UI(?) + return listOf(ActionLink("${imageIssues.uniqueCount} vulnerabilities")) + affectedFile2Line.map { (file, line) -> val targetFileName = file.name + ":" + line - LinkLabel.create(targetFileName) { + ActionLink(targetFileName) { navigateToTargetFile(file, line - 1) // to 1-based count used in the editor } } diff --git a/src/main/kotlin/snyk/iac/IacIssue.kt b/src/main/kotlin/snyk/iac/IacIssue.kt index 158ff0f86..c96ebc072 100644 --- a/src/main/kotlin/snyk/iac/IacIssue.kt +++ b/src/main/kotlin/snyk/iac/IacIssue.kt @@ -13,7 +13,8 @@ data class IacIssue( val impact: String, val resolve: String? = null, val references: List = emptyList(), - val path: List = emptyList() + val path: List = emptyList(), + val lineStartOffset: Int = 0 ) { var ignored = false var obsolete = false diff --git a/src/main/kotlin/snyk/iac/IacIssuesForFile.kt b/src/main/kotlin/snyk/iac/IacIssuesForFile.kt index a0cb7b370..4071a9d3a 100644 --- a/src/main/kotlin/snyk/iac/IacIssuesForFile.kt +++ b/src/main/kotlin/snyk/iac/IacIssuesForFile.kt @@ -1,19 +1,18 @@ package snyk.iac -import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile data class IacIssuesForFile( val infrastructureAsCodeIssues: List, val targetFile: String, val targetFilePath: String, - val packageManager: String + val packageManager: String, + val virtualFile: VirtualFile?, + val project: Project?, + val relativePath: String? = null, ) { val obsolete: Boolean get() = infrastructureAsCodeIssues.any { it.obsolete } val ignored: Boolean get() = infrastructureAsCodeIssues.all { it.ignored } val uniqueCount: Int get() = infrastructureAsCodeIssues.groupBy { it.id }.size - - val virtualFile: VirtualFile? = LocalFileSystem.getInstance().findFileByPath(this.targetFilePath) } - -/* Real json Example: src/integTest/resources/iac-test-results/infrastructure-as-code-goof.json */ diff --git a/src/main/kotlin/snyk/iac/IacScanService.kt b/src/main/kotlin/snyk/iac/IacScanService.kt index 86b8987c8..b03fd6bed 100644 --- a/src/main/kotlin/snyk/iac/IacScanService.kt +++ b/src/main/kotlin/snyk/iac/IacScanService.kt @@ -1,14 +1,19 @@ package snyk.iac +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.Service +import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile import io.snyk.plugin.services.CliAdapter +import snyk.common.RelativePathHelper import snyk.common.SnykError /** * Wrap work with Snyk CLI for IaC (`iac test` command). */ -@Service +@Service(Service.Level.PROJECT) class IacScanService(project: Project) : CliAdapter(project) { fun scan(): IacResult = execute(listOf("iac", "test")) @@ -16,12 +21,44 @@ class IacScanService(project: Project) : CliAdapter override fun getProductResult(cliIssues: List?, snykErrors: List): IacResult = IacResult(cliIssues, snykErrors) - override fun sanitizeCliIssues(cliIssues: IacIssuesForFile): IacIssuesForFile = + override fun sanitizeCliIssues(cliIssues: IacIssuesForFile): IacIssuesForFile { // .copy() will check nullability of fields - cliIssues.copy( - infrastructureAsCodeIssues = cliIssues.infrastructureAsCodeIssues.map { it.copy() } + // determine relative path for each issue at scan time + + val helper = RelativePathHelper() + val virtualFile = LocalFileSystem.getInstance().findFileByPath(cliIssues.targetFilePath) + val relativePath = virtualFile?.let { helper.getRelativePath(virtualFile, project) } + + val sanitized = cliIssues.copy( + virtualFile = virtualFile, + project = project, + relativePath = relativePath, + infrastructureAsCodeIssues = cliIssues.infrastructureAsCodeIssues + .map { + if (it.lineStartOffset > 0 || virtualFile == null || !virtualFile.isValid) { + return@map it.copy() + } + val lineStartOffset = determineLineStartOffset(it, virtualFile) + return@map it.copy(lineStartOffset = lineStartOffset) + } ) + return sanitized + } + + private fun determineLineStartOffset(it: IacIssue, virtualFile: VirtualFile): Int { + var lineStartOffset = it.lineStartOffset + ApplicationManager.getApplication().runReadAction { + val document = FileDocumentManager.getInstance().getDocument(virtualFile) + if (document != null) { + val candidate = it.lineNumber - 1 // to 1-based count used in the editor + val lineNumber = if (0 <= candidate && candidate < document.lineCount) candidate else 0 + lineStartOffset = document.getLineStartOffset(lineNumber) + } + } + return lineStartOffset + } + override fun getCliIIssuesClass(): Class = IacIssuesForFile::class.java override fun isSuccessCliJsonString(jsonStr: String): Boolean = diff --git a/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt b/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt index cb489dd3d..eb651d365 100644 --- a/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt +++ b/src/main/kotlin/snyk/iac/IacSuggestionDescriptionPanel.kt @@ -12,8 +12,8 @@ import io.snyk.plugin.ui.getFont import io.snyk.plugin.ui.getReadOnlyClickableHtmlJEditorPane import io.snyk.plugin.ui.insertTitleAndResizableTextIntoPanelColumns import io.snyk.plugin.ui.panelGridConstraints -import io.snyk.plugin.ui.toolwindow.panels.IssueDescriptionPanelBase import io.snyk.plugin.ui.toolwindow.LabelProvider +import io.snyk.plugin.ui.toolwindow.panels.IssueDescriptionPanelBase import org.commonmark.parser.Parser import org.commonmark.renderer.html.HtmlRenderer import snyk.common.IgnoreService @@ -123,7 +123,7 @@ class IacSuggestionDescriptionPanel( panel.add(boldLabel("References"), baseGridConstraintsAnchorWest(row = 1)) issue.references.forEachIndexed { index, s -> val label = try { - labelProvider.createLinkLabel(URL(s), s) + labelProvider.createActionLink(URL(s), s) } catch (e: MalformedURLException) { JLabel(s) } diff --git a/src/main/kotlin/snyk/iac/ui/toolwindow/IacIssueTreeNode.kt b/src/main/kotlin/snyk/iac/ui/toolwindow/IacIssueTreeNode.kt index 662a586f5..b16666ee2 100644 --- a/src/main/kotlin/snyk/iac/ui/toolwindow/IacIssueTreeNode.kt +++ b/src/main/kotlin/snyk/iac/ui/toolwindow/IacIssueTreeNode.kt @@ -18,7 +18,7 @@ import javax.swing.tree.DefaultMutableTreeNode class IacIssueTreeNode( private val issue: IacIssue, val project: Project, - override val navigateToSource: () -> Unit + override val navigateToSource: () -> Unit, ) : DefaultMutableTreeNode(issue), NavigatableToSourceTreeNode, DescriptionHolderTreeNode { override fun getDescriptionPanel(logEventNeeded: Boolean): IssueDescriptionPanelBase { diff --git a/src/main/kotlin/snyk/oss/OssService.kt b/src/main/kotlin/snyk/oss/OssService.kt index 66c54a652..481acf458 100644 --- a/src/main/kotlin/snyk/oss/OssService.kt +++ b/src/main/kotlin/snyk/oss/OssService.kt @@ -2,27 +2,36 @@ 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 import snyk.pluginInfo /** * Wrap work with Snyk CLI for OSS (`test` command). */ -@Service +@Service(Service.Level.PROJECT) class OssService(project: Project) : CliAdapter(project) { fun scan(): OssResult = execute(listOf("test")) - override fun getProductResult(cliIssues: List?, snykErrors: List): OssResult = - OssResult(cliIssues, snykErrors) + override fun getProductResult(cliIssues: List?, snykErrors: List): OssResult { + return OssResult(cliIssues, snykErrors) + } - override fun sanitizeCliIssues(cliIssues: OssVulnerabilitiesForFile): OssVulnerabilitiesForFile = + override fun sanitizeCliIssues(cliIssues: OssVulnerabilitiesForFile): OssVulnerabilitiesForFile { // .copy() will check nullability of fields - cliIssues.copy( - vulnerabilities = cliIssues.vulnerabilities.map { it.copy() } + 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 @@ -37,7 +46,8 @@ class OssService(project: Project) : CliAdapter(relaxed = true) + mockkObject(HttpRequestHelper) + every { HttpRequestHelper.createRequest(any()) } returns requestBuilderMockk justRun { requestBuilderMockk.saveToFile(any(), any()) } justRun { requestBuilderMockk.saveToFile(any(), any()) } - mockkObject(HttpRequestHelper) - every { HttpRequestHelper.createRequest(CliDownloader.LATEST_RELEASE_DOWNLOAD_URL) } returns requestBuilderMockk - every { HttpRequestHelper.createRequest(CliDownloader.LATEST_RELEASES_URL) } returns requestBuilderMockk return requestBuilderMockk } diff --git a/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt b/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt index 6c97ee997..ef9f8fcb1 100644 --- a/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt +++ b/src/test/kotlin/io/snyk/plugin/cli/ConsoleCommandRunnerTest.kt @@ -265,7 +265,7 @@ class ConsoleCommandRunnerTest : LightPlatformTestCase() { assertEquals("JETBRAINS_IDE", generalCommandLine.environment["SNYK_INTEGRATION_NAME"]) assertEquals(snykPluginVersion, generalCommandLine.environment["SNYK_INTEGRATION_VERSION"]) assertEquals("INTELLIJ IDEA IC", generalCommandLine.environment["SNYK_INTEGRATION_ENVIRONMENT"]) - assertEquals("2023.1", generalCommandLine.environment["SNYK_INTEGRATION_ENVIRONMENT_VERSION"]) + assertEquals("2023.1".length, generalCommandLine.environment["SNYK_INTEGRATION_ENVIRONMENT_VERSION"]?.length) } @Suppress("SwallowedException") diff --git a/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt b/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt index fbf126a96..24f87972f 100644 --- a/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt +++ b/src/test/kotlin/io/snyk/plugin/services/SnykTaskQueueServiceTest.kt @@ -30,17 +30,17 @@ import io.snyk.plugin.services.download.LatestReleaseInfo import io.snyk.plugin.services.download.SnykCliDownloaderService import io.snyk.plugin.setupDummyCliFile import org.awaitility.Awaitility.await -import org.hamcrest.CoreMatchers.equalTo -import org.hamcrest.MatcherAssert.assertThat 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 @Suppress("FunctionName") class SnykTaskQueueServiceTest : LightPlatformTestCase() { + private lateinit var ossServiceMock: OssService private lateinit var downloaderServiceMock: SnykCliDownloaderService private lateinit var snykApiServiceMock: SnykApiService @@ -48,6 +48,8 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() { super.setUp() unmockkAll() resetSettings(project) + ossServiceMock = mockk(relaxed = true) + project.replaceService(OssService::class.java, ossServiceMock, project) mockSnykApiServiceSastEnabled() replaceSnykApiServiceMockInContainer() mockkStatic("io.snyk.plugin.UtilsKt") @@ -59,6 +61,7 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() { "testTag" ) every { getSnykCliDownloaderService() } returns downloaderServiceMock + every { downloaderServiceMock.isFourDaysPassedSinceLastCheck() } returns false every { confirmScanningAndSetWorkspaceTrustedStateIfNeeded(any(), any()) } returns true } @@ -85,7 +88,6 @@ class SnykTaskQueueServiceTest : LightPlatformTestCase() { fun testSnykTaskQueueService() { setupDummyCliFile() - val snykTaskQueueService = project.service() snykTaskQueueService.scan() diff --git a/src/test/kotlin/io/snyk/plugin/services/download/CliDownloaderServiceIntegTest.kt b/src/test/kotlin/io/snyk/plugin/services/download/CliDownloaderServiceIntegTest.kt index 2c85eb83b..a8c08eff1 100644 --- a/src/test/kotlin/io/snyk/plugin/services/download/CliDownloaderServiceIntegTest.kt +++ b/src/test/kotlin/io/snyk/plugin/services/download/CliDownloaderServiceIntegTest.kt @@ -18,7 +18,6 @@ import io.snyk.plugin.removeDummyCliFile import io.snyk.plugin.resetSettings import io.snyk.plugin.services.SnykApplicationSettingsStateService import org.apache.http.HttpStatus -import org.junit.Test import java.io.File import java.net.SocketTimeoutException import java.time.LocalDate @@ -60,7 +59,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { /** * Needs an internet connection - real test if release info can be downloaded */ - @Test fun testGetLatestReleasesInformation() { val latestReleaseInfo = project.service().requestLatestReleasesInformation() @@ -75,7 +73,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { * Should be THE ONLY test where we actually do download the CLI * !!! Do __MOCK__ cli download in ANY other test to reduce testing time needed !!! */ - @Test fun testDownloadLatestCliRelease() { ensureCliFileExistent() @@ -90,7 +87,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { verify { downloader.verifyChecksum(any(), any()) } } - @Test fun testDownloadLatestCliReleaseFailsWhenShaDoesNotMatch() { ensureCliFileExistent() @@ -113,7 +109,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { } } - @Test fun testDownloadLatestCliReleaseShouldHandleSocketTimeout() { val indicator = EmptyProgressIndicator() val exceptionMessage = "Read Timed Out" @@ -130,7 +125,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { } } - @Test fun testDownloadLatestCliReleaseShouldHandleHttpStatusException() { val httpStatusException = HttpRequests.HttpStatusException("status bad", HttpStatus.SC_GATEWAY_TIMEOUT, "url") @@ -145,7 +139,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { } } - @Test fun testDownloadLatestCliReleaseWhenNoReleaseInfoAvailable() { val cliDownloaderService = project.service() @@ -157,7 +150,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { } } - @Test fun testCliSilentAutoUpdate() { val currentDate = LocalDateTime.now() @@ -178,7 +170,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { ) } - @Test fun testCliSilentAutoUpdateWhenPreviousUpdateInfoIsNull() { val currentDate = LocalDate.now() val settings = pluginSettings() @@ -195,7 +186,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { verify { cutSpy.downloadLatestRelease(any(), any()) } } - @Test fun testIsNewVersionAvailable() { pluginSettings().lastCheckDate = null @@ -212,7 +202,6 @@ class CliDownloaderServiceIntegTest : LightPlatformTestCase() { assertFalse(cliDownloaderService.isNewVersionAvailable("1.342.2", "1.342.2")) } - @Test fun testCheckIsFourDaysPassedSinceLastCheck() { val todayDate = LocalDateTime.now() val lastCheckDate = todayDate.minusDays(4) diff --git a/src/test/kotlin/io/snyk/plugin/ui/UIUtilsTest.kt b/src/test/kotlin/io/snyk/plugin/ui/UIUtilsTest.kt index 5fb884981..c17d182ae 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/UIUtilsTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/UIUtilsTest.kt @@ -1,7 +1,7 @@ package io.snyk.plugin.ui +import com.intellij.ui.components.ActionLink import org.junit.Test -import javax.swing.JLabel class UIUtilsTest { @@ -9,7 +9,7 @@ class UIUtilsTest { fun `descriptionHeaderPanel should not fail with customLabels`() { descriptionHeaderPanel( issueNaming = "test naming", - customLabels = (1..10).toList().map { JLabel(it.toString()) } + customLabels = (1..10).toList().map { ActionLink(it.toString()) } ) } } diff --git a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/LabelProviderTest.kt b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/LabelProviderTest.kt index fee2db79a..04dce2208 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/LabelProviderTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/LabelProviderTest.kt @@ -1,50 +1,27 @@ package io.snyk.plugin.ui.toolwindow -import com.intellij.ui.components.labels.LinkLabel -import io.mockk.mockkStatic -import io.mockk.slot -import io.mockk.unmockkAll -import io.mockk.verify +import com.intellij.ui.components.ActionLink import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.assertTrue -import org.junit.After -import org.junit.Before import org.junit.Test import java.net.URL -import javax.swing.JLabel class LabelProviderTest { - @Before - fun setUp() { - unmockkAll() - // test URL - unfortunately, LinkLabel does not provide an easy way to verify - // the link action, so we're using a static mock of the create method to check the action - mockkStatic(LinkLabel::class) - } - - @After - fun tearDown() { - unmockkAll() - } @Test fun `getDependencyLabel should provide a label with a clickable link to package at npmBaseUrl for npm packages`() { val packageName = "packageName" - val label: JLabel = LabelProvider().getDependencyLabel("npm", packageName) + val label: ActionLink = LabelProvider().getDependencyLabel("npm", packageName) - assertTrue("Expected LinkLabel, but got ${label::class}", label is LinkLabel<*>) assertEquals(packageName, label.text) - verifyLinkLabelCreated(LabelProvider.npmBaseUrl + "/$packageName") } @Test fun `getDependencyLabel should provide a plain text label for non-npm packages`() { val packageName = "package" - val output: JLabel = LabelProvider().getDependencyLabel("maven", packageName) + val output: ActionLink = LabelProvider().getDependencyLabel("maven", packageName) - assertEquals(JLabel::class, output::class) assertEquals(packageName, output.text) } @@ -52,33 +29,27 @@ class LabelProviderTest { fun `getCWELabel should provide a label with a clickable link to the CWE info at cweBaseUrl`() { val cwe = "CWE-400" - val output: JLabel = LabelProvider().getCWELabel(cwe) + val output: ActionLink = LabelProvider().getCWELabel(cwe) - assertEquals(LinkLabel::class, output::class) assertEquals(cwe, output.text) - verifyLinkLabelCreated(LabelProvider.cweBaseUrl + "/${cwe.removePrefix("CWE-")}.html") } @Test fun `getCVELabel should provide a label with a clickable link to the CVE info at cveBaseUrl`() { val cve = "CVE123" - val output: JLabel = LabelProvider().getCVELabel(cve) + val output: ActionLink = LabelProvider().getCVELabel(cve) - assertEquals(LinkLabel::class, output::class) assertEquals(cve, output.text) - verifyLinkLabelCreated(LabelProvider.cveBaseUrl + "=$cve") } @Test fun `getVulnerability should provide a label with a clickable link to the vulnerability at vulnerabilityBaseUrl`() { val id = "VULN123" - val output: JLabel = LabelProvider().getVulnerabilityLabel(id) + val output: ActionLink = LabelProvider().getVulnerabilityLabel(id) - assertEquals(LinkLabel::class, output::class) assertEquals(id, output.text) - verifyLinkLabelCreated(LabelProvider.vulnerabilityBaseUrl + "/$id") } @Test @@ -86,11 +57,9 @@ class LabelProviderTest { val id = "VULN123" val url = "https://some.link" - val output: JLabel = LabelProvider().getVulnerabilityLabel(id, url) + val output: ActionLink = LabelProvider().getVulnerabilityLabel(id, url) - assertEquals(LinkLabel::class, output::class) assertEquals(id, output.text) - verifyLinkLabelCreated(url) } @Test @@ -99,21 +68,16 @@ class LabelProviderTest { val output = LabelProvider().getVulnerabilityLabel(id) - assertEquals(LinkLabel::class, output::class) - assertEquals(id.toUpperCase(), output.text) - verifyLinkLabelCreated(LabelProvider.vulnerabilityBaseUrl + "/$id") + assertEquals(id.uppercase(), output.text) } @Test fun `getCVSSLabel should provide a label with a clickable link to the CVSS scoring calculator for this score`() { val cvssText = "package" val cvssId = "1" - val output: JLabel = LabelProvider().getCVSSLabel(cvssText, cvssId) + val output: ActionLink = LabelProvider().getCVSSLabel(cvssText, cvssId) - assertEquals(LinkLabel::class, output::class) assertEquals(cvssText, output.text) - - verifyLinkLabelCreated(LabelProvider.cvssBaseUrl + "#$cvssId") } @Test @@ -121,18 +85,8 @@ class LabelProviderTest { val url = "https://snyk.io/reference" val text = "reference" - val label: JLabel = LabelProvider().createLinkLabel(URL(url), text) + val label: ActionLink = LabelProvider().createActionLink(URL(url), text) assertEquals(text, label.text) - verifyLinkLabelCreated(url) - } - - private fun verifyLinkLabelCreated(expectedUrl: String) { - val actionSlot = slot() - verify { - LinkLabel.create(any(), capture(actionSlot)) - } - val actualUrl = (actionSlot.captured as LabelProvider.OpenLinkAction).url.toExternalForm() - assertEquals(expectedUrl, actualUrl) } } 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 9ab72bb37..eeb79a615 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelIntegTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/SnykToolWindowPanelIntegTest.kt @@ -67,6 +67,7 @@ import snyk.iac.ui.toolwindow.IacFileTreeNode import snyk.iac.ui.toolwindow.IacIssueTreeNode import snyk.oss.Vulnerability import snyk.trust.confirmScanningAndSetWorkspaceTrustedStateIfNeeded +import java.nio.charset.Charset import javax.swing.JButton import javax.swing.JEditorPane import javax.swing.JLabel @@ -110,7 +111,11 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { unmockkAll() resetSettings(project) removeDummyCliFile() - super.tearDown() + try { + super.tearDown() + } catch (ignore: Exception) { + // nothing to do here + } } private fun setUpIacTest() { @@ -172,10 +177,11 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { } private fun prepareTreeWithFakeCodeResults() { + val virtualFile = super.createTempVirtualFile("test.js", null, "test", Charset.defaultCharset()) val codeResults = SnykCodeResults( mapOf( Pair( - SnykCodeFile(project, mockk(relaxed = true)), + SnykCodeFile(project, virtualFile), listOf(fakeSuggestionForFile) ) ) @@ -368,7 +374,7 @@ class SnykToolWindowPanelIntegTest : HeavyPlatformTestCase() { impact = "" ) val iacIssuesForFile = - IacIssuesForFile(listOf(iacIssue), "k8s-deployment.yaml", "src/k8s-deployment.yaml", "npm") + IacIssuesForFile(listOf(iacIssue), "k8s-deployment.yaml", "src/k8s-deployment.yaml", "npm", null, project) val jsonError = SnykError("Failed to parse JSON file", project.basePath.toString(), 1021) val iacResult = IacResult(listOf(iacIssuesForFile), listOf(jsonError)) scanPublisher.scanningIacFinished(iacResult) diff --git a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt index ea3742465..2f7cda0a4 100644 --- a/src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt +++ b/src/test/kotlin/io/snyk/plugin/ui/toolwindow/VulnerabilityDescriptionPanelTest.kt @@ -1,16 +1,15 @@ package io.snyk.plugin.ui.toolwindow import com.google.gson.Gson -import com.intellij.ui.components.labels.LinkLabel +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.getJLabelByText +import snyk.UIComponentFinder.getJButtonByText import snyk.oss.Vulnerability import java.io.FileReader -import javax.swing.JLabel class VulnerabilityDescriptionPanelTest { private lateinit var cut: VulnerabilityDescriptionPanel @@ -27,9 +26,9 @@ class VulnerabilityDescriptionPanelTest { @Test fun `constructor should build panel with npm introduced-from info as link label`() { val introducingDependency = vulnerability.from[1] - val actual = getJLabelByText(cut, introducingDependency) + val actual = getJButtonByText(cut, introducingDependency) assertNotNull(actual) - assertEquals(LinkLabel::class, actual!!::class) + assertEquals(ActionLink::class, actual!!::class) } @Test @@ -37,16 +36,16 @@ class VulnerabilityDescriptionPanelTest { val introducingDependency = vulnerability.from[1] vulnerability = vulnerability.copy(packageManager = "not npm!") cut = VulnerabilityDescriptionPanel(listOf(vulnerability)) - val actual = getJLabelByText(cut, introducingDependency) + val actual = getJButtonByText(cut, introducingDependency) assertNotNull(actual) - assertEquals(JLabel::class, actual!!::class) + 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 = getJLabelByText(cut, cwe) + val actual = getJButtonByText(cut, cwe) assertNotNull("Expected to find label for $cwe, but was null", actual) } } diff --git a/src/test/kotlin/snyk/advisor/PackageNameProviderTest.kt b/src/test/kotlin/snyk/advisor/PackageNameProviderTest.kt index 1dfbbea36..c6919d04b 100644 --- a/src/test/kotlin/snyk/advisor/PackageNameProviderTest.kt +++ b/src/test/kotlin/snyk/advisor/PackageNameProviderTest.kt @@ -23,7 +23,7 @@ class PackageNameProviderTest : BasePlatformTestCase() { "requirements.txt", getResourceAsString("advisor-build-files/requirements.txt") ) - val packageNameProvider = PackageNameProvider(myFixture.editor) + val packageNameProvider = PackageNameProvider(myFixture.editor, AdvisorPackageManager.PYTHON) fun assertPackageName(name: String?, lineNumber: Int) { assertEquals( @@ -66,7 +66,7 @@ class PackageNameProviderTest : BasePlatformTestCase() { "package.json", getResourceAsString("advisor-build-files/package.json") ) - val packageNameProvider = PackageNameProvider(myFixture.editor) + val packageNameProvider = PackageNameProvider(myFixture.editor, AdvisorPackageManager.NPM) fun assertPackageName(name: String?, lineNumber: Int) { assertEquals( @@ -102,11 +102,11 @@ class PackageNameProviderTest : BasePlatformTestCase() { } """.trimIndent() ) - val packageNameProvider = PackageNameProvider(myFixture.editor) + val provider = AdvisorScoreProvider(myFixture.editor, myFixture.file) assertEquals( null, - packageNameProvider.getPackageName(3) + provider.packageNameProvider ) } } diff --git a/src/test/kotlin/snyk/container/ContainerBulkFileListenerTest.kt b/src/test/kotlin/snyk/container/ContainerBulkFileListenerTest.kt index 5b8820961..7e37e0192 100644 --- a/src/test/kotlin/snyk/container/ContainerBulkFileListenerTest.kt +++ b/src/test/kotlin/snyk/container/ContainerBulkFileListenerTest.kt @@ -39,7 +39,11 @@ class ContainerBulkFileListenerTest : BasePlatformTestCase() { override fun tearDown() { unmockkAll() resetSettings(project) - super.tearDown() + try { + super.tearDown() + } catch (ignore: Exception) { + // nothing to do, as we are in test shutdown + } } private val imageCache get() = project.service() diff --git a/src/test/kotlin/snyk/container/KubernetesImageCacheIntegTest.kt b/src/test/kotlin/snyk/container/KubernetesImageCacheIntegTest.kt index af7a85968..69992e390 100644 --- a/src/test/kotlin/snyk/container/KubernetesImageCacheIntegTest.kt +++ b/src/test/kotlin/snyk/container/KubernetesImageCacheIntegTest.kt @@ -55,7 +55,7 @@ class KubernetesImageCacheIntegTest : LightPlatform4TestCase() { val images = cut.getKubernetesWorkloadImages() assertEquals(1, images.size) - assertEquals(KubernetesWorkloadImage("nginx:1.16.0", file, 8), images.first()) + assertEquals(KubernetesWorkloadImage("nginx:1.16.0", file, 8, 85), images.first()) } @Test @@ -223,7 +223,7 @@ class KubernetesImageCacheIntegTest : LightPlatform4TestCase() { val images = cut.getKubernetesWorkloadImages() assertEquals(1, images.size) - assertEquals(KubernetesWorkloadImage("snyk/code-agent:latest", file, 43), images.first()) + assertEquals(KubernetesWorkloadImage("snyk/code-agent:latest", file, 43, 1113), images.first()) } @Test diff --git a/src/test/kotlin/snyk/container/annotator/ContainerYamlAnnotatorTest.kt b/src/test/kotlin/snyk/container/annotator/ContainerYamlAnnotatorTest.kt index ff125b022..3d9f24c82 100644 --- a/src/test/kotlin/snyk/container/annotator/ContainerYamlAnnotatorTest.kt +++ b/src/test/kotlin/snyk/container/annotator/ContainerYamlAnnotatorTest.kt @@ -3,6 +3,7 @@ package snyk.container.annotator import com.google.gson.Gson +import com.intellij.codeInsight.intention.IntentionAction import com.intellij.lang.annotation.AnnotationBuilder import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.HighlightSeverity @@ -20,10 +21,6 @@ import io.snyk.plugin.Severity import io.snyk.plugin.getContainerService import io.snyk.plugin.pluginSettings import io.snyk.plugin.resetSettings -import org.hamcrest.collection.IsCollectionWithSize.hasSize -import org.junit.Assert.assertThat -import org.junit.Before -import org.junit.Test import snyk.common.SnykCachedResults import snyk.container.BaseImageInfo import snyk.container.BaseImageRemediation @@ -45,10 +42,10 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { javaClass.classLoader.getResource("container-test-results/nginx-with-remediation.json")!! .readText(Charsets.UTF_8) - lateinit var virtualFile: VirtualFile + private lateinit var virtualFile: VirtualFile private lateinit var psiFile: PsiFile - val snykCachedResults: SnykCachedResults = mockk(relaxed = true) + private val snykCachedResults: SnykCachedResults = mockk(relaxed = true) override fun getTestDataPath(): String { val resource = ContainerYamlAnnotator::class.java.getResource("/test-fixtures/container/annotator") @@ -58,7 +55,6 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { override fun isWriteActionRequired(): Boolean = true - @Before override fun setUp() { super.setUp() unmockkAll() @@ -77,25 +73,22 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { super.tearDown() } - @Test fun `test getIssues should not return any issue if no container issue exists`() { every { snykCachedResults.currentContainerResult } returns null val issues = cut.getContainerIssuesForImages(psiFile) - assertThat(issues, hasSize(0)) + assertEquals(0, issues.size) } - @Test fun `test getIssues should return one issue if only one container issue exists`() { every { snykCachedResults.currentContainerResult } returns createContainerResultWithIssueOnLine21() val issues = cut.getContainerIssuesForImages(psiFile) - assertThat(issues, hasSize(1)) + assertEquals(1, issues.size) } - @Test fun `test apply should trigger newAnnotation call`() { every { snykCachedResults.currentContainerResult } returns createContainerResultWithIssueOnLine21() @@ -104,7 +97,6 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { verify { annotationHolderMock.newAnnotation(any(), any()) } } - @Test fun `test apply for disabled Severity should not trigger newAnnotation call`() { every { snykCachedResults.currentContainerResult } returns createContainerResultWithIssueOnLine21() pluginSettings().mediumSeverityEnabled = false @@ -114,35 +106,30 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { verify(exactly = 0) { annotationHolderMock.newAnnotation(HighlightSeverity.WARNING, any()) } } - @Test fun `test severity with one critical`() { val severities = createContainerImageForIssuesWithSeverity(Severity.CRITICAL.toString()).getSeverities() assertEquals(1, severities.size) assertEquals(HighlightSeverity.ERROR, severities.first().getHighlightSeverity()) } - @Test fun `test severity with one high`() { val severities = createContainerImageForIssuesWithSeverity(Severity.HIGH.toString()).getSeverities() assertEquals(1, severities.size) assertEquals(HighlightSeverity.ERROR, severities.first().getHighlightSeverity()) } - @Test fun `test severity with one medium`() { val severities = createContainerImageForIssuesWithSeverity(Severity.MEDIUM.toString()).getSeverities() assertEquals(1, severities.size) assertEquals(HighlightSeverity.WARNING, severities.first().getHighlightSeverity()) } - @Test fun `test severity with one low`() { val severities = createContainerImageForIssuesWithSeverity(Severity.LOW.toString()).getSeverities() assertEquals(1, severities.size) assertEquals(HighlightSeverity.WEAK_WARNING, severities.first().getHighlightSeverity()) } - @Test fun `test textRange`() { val containerResult = createContainerResultWithIssueOnLine21() val line = 21 @@ -156,7 +143,6 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { assertEquals(expectedRange, actualRange) } - @Test fun `test annotation message should display vulnerability count 1 for severity critical and remediation`() { val expected = "Snyk found 1 vulnerability. Upgrade image to a newer version" val image = createContainerImageForIssuesWithSeverity(Severity.CRITICAL.toString()) @@ -167,7 +153,6 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { assertEquals(expected, actual) } - @Test fun `test annotation message should display vulnerability count and no remediation`() { val expected = "Snyk found 1 vulnerability. " @@ -177,7 +162,6 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { assertEquals(expected, actual) } - @Test fun `test apply should add a quickfix if remediation advice available`() { val builderMock = mockk(relaxed = true) every { snykCachedResults.currentContainerResult } returns createContainerResultWithIssueOnLine21() @@ -187,11 +171,10 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { verify { annotationHolderMock.newAnnotation(any(), any()).range(any()) - builderMock.withFix(any()) + builderMock.withFix(any()) } } - @Test fun `test apply should add annotations for all duplicated images`() { val builderMock = mockk(relaxed = true) every { snykCachedResults.currentContainerResult } returns @@ -205,7 +188,6 @@ class ContainerYamlAnnotatorTest : BasePlatformTestCase() { } } - @Test fun `test apply should not add a quickfix if remediation advice base image different`() { val workloadImages = listOf(KubernetesWorkloadImage("nginx:1.16.0", virtualFile, 21)) val builderMock = mockk(relaxed = true) diff --git a/src/test/kotlin/snyk/iac/IacBulkFileListenerTest.kt b/src/test/kotlin/snyk/iac/IacBulkFileListenerTest.kt index 5131cc9a6..95b2d5936 100644 --- a/src/test/kotlin/snyk/iac/IacBulkFileListenerTest.kt +++ b/src/test/kotlin/snyk/iac/IacBulkFileListenerTest.kt @@ -25,7 +25,11 @@ class IacBulkFileListenerTest : BasePlatformTestCase() { override fun tearDown() { resetSettings(project) - super.tearDown() + try { + super.tearDown() + } catch (ignore: Exception) { + // nothing to do as we're shutting down the test + } } /** `filePath == null` is the case when we want to check if _any_ IaC file with issues been marked as obsolete */ @@ -46,7 +50,7 @@ class IacBulkFileListenerTest : BasePlatformTestCase() { lineNumber = 1, severity = "", publicId = "", documentation = "", issue = "", impact = "" ) - val iacIssuesForFile = IacIssuesForFile(listOf(iacIssue), file, filePath, "npm") + val iacIssuesForFile = IacIssuesForFile(listOf(iacIssue), file, filePath, "npm", null, project) val iacVulnerabilities = listOf(iacIssuesForFile) val fakeIacResult = IacResult(iacVulnerabilities) getSnykCachedResults(project)?.currentIacResult = fakeIacResult diff --git a/src/test/kotlin/snyk/iac/IacSuggestionDescriptionPanelTest.kt b/src/test/kotlin/snyk/iac/IacSuggestionDescriptionPanelTest.kt index 238caa37c..ee4c6722e 100644 --- a/src/test/kotlin/snyk/iac/IacSuggestionDescriptionPanelTest.kt +++ b/src/test/kotlin/snyk/iac/IacSuggestionDescriptionPanelTest.kt @@ -7,7 +7,6 @@ import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNotNull import org.junit.Before import org.junit.Test -import snyk.UIComponentFinder import snyk.UIComponentFinder.getJButtonByText class IacSuggestionDescriptionPanelTest { @@ -63,7 +62,7 @@ class IacSuggestionDescriptionPanelTest { cut = IacSuggestionDescriptionPanel(issue, null, project) issue.references.stream().forEach { - val label = UIComponentFinder.getJLabelByText(cut, it) + val label = getJButtonByText(cut, it) assertNotNull("Didn't find reference $it", label) } } diff --git a/src/test/kotlin/snyk/iac/annotator/IacHclAnnotatorTest.kt b/src/test/kotlin/snyk/iac/annotator/IacHclAnnotatorTest.kt index c180823ae..24f6a3de6 100644 --- a/src/test/kotlin/snyk/iac/annotator/IacHclAnnotatorTest.kt +++ b/src/test/kotlin/snyk/iac/annotator/IacHclAnnotatorTest.kt @@ -104,7 +104,8 @@ class IacHclAnnotatorTest : IacBaseAnnotatorCase() { lineNumber = 1, severity = "", publicId = "", documentation = "", issue = "", impact = "" ) - val iacIssuesForFile = IacIssuesForFile(listOf(iacIssue), terraformManifestFile, file.path, "terraform") + val iacIssuesForFile = + IacIssuesForFile(listOf(iacIssue), terraformManifestFile, file.path, "terraform", null, project) return IacResult(listOf(iacIssuesForFile)) } @@ -115,7 +116,8 @@ class IacHclAnnotatorTest : IacBaseAnnotatorCase() { lineNumber = 8, severity = "", publicId = "", documentation = "", issue = "", impact = "" ) - val iacIssuesForFile = IacIssuesForFile(listOf(iacIssue), terraformManifestFile, file.path, "terraform") + val iacIssuesForFile = + IacIssuesForFile(listOf(iacIssue), terraformManifestFile, file.path, "terraform", null, project) return IacResult(listOf(iacIssuesForFile)) } @@ -126,7 +128,8 @@ class IacHclAnnotatorTest : IacBaseAnnotatorCase() { lineNumber = 18, severity = "", publicId = "", documentation = "", issue = "", impact = "" ) - val iacIssuesForFile = IacIssuesForFile(listOf(iacIssue), terraformManifestFile, file.path, "terraform") + val iacIssuesForFile = + IacIssuesForFile(listOf(iacIssue), terraformManifestFile, file.path, "terraform", null, project) return IacResult(listOf(iacIssuesForFile)) } } diff --git a/src/test/kotlin/snyk/iac/annotator/IacJsonAnnotatorTest.kt b/src/test/kotlin/snyk/iac/annotator/IacJsonAnnotatorTest.kt index 868a80ae0..80f12f416 100644 --- a/src/test/kotlin/snyk/iac/annotator/IacJsonAnnotatorTest.kt +++ b/src/test/kotlin/snyk/iac/annotator/IacJsonAnnotatorTest.kt @@ -77,7 +77,7 @@ class IacJsonAnnotatorTest : IacBaseAnnotatorCase() { severity = "medium", publicId = "", documentation = "", issue = "", impact = "" ) val iacIssuesForFile = - IacIssuesForFile(listOf(iacIssue), cloudformationManifestFile, file.path, "cloudformation") + IacIssuesForFile(listOf(iacIssue), cloudformationManifestFile, file.path, "cloudformation", null, project) return IacResult(listOf(iacIssuesForFile)) } } diff --git a/src/test/kotlin/snyk/iac/annotator/IacYamlAnnotatorTest.kt b/src/test/kotlin/snyk/iac/annotator/IacYamlAnnotatorTest.kt index b3f5257f1..229ae2d3c 100644 --- a/src/test/kotlin/snyk/iac/annotator/IacYamlAnnotatorTest.kt +++ b/src/test/kotlin/snyk/iac/annotator/IacYamlAnnotatorTest.kt @@ -92,7 +92,7 @@ class IacYamlAnnotatorTest : IacBaseAnnotatorCase() { severity = "", publicId = "", documentation = "", issue = "", impact = "" ) val iacIssuesForFile = - IacIssuesForFile(listOf(iacIssue), kubernetesManifestFile, file.path, "Kubernetes") + IacIssuesForFile(listOf(iacIssue), kubernetesManifestFile, file.path, "Kubernetes", null, project) return IacResult(listOf(iacIssuesForFile)) } @@ -104,7 +104,7 @@ class IacYamlAnnotatorTest : IacBaseAnnotatorCase() { severity = "", publicId = "", documentation = "", issue = "", impact = "" ) val iacIssuesForFile = - IacIssuesForFile(listOf(iacIssue), kubernetesManifestFile, file.path, "Kubernetes") + IacIssuesForFile(listOf(iacIssue), kubernetesManifestFile, file.path, "Kubernetes", null, project) return IacResult(listOf(iacIssuesForFile)) } } diff --git a/src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt b/src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt index 2b10cda7b..6b2a1f0ed 100644 --- a/src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt +++ b/src/test/kotlin/snyk/oss/OssBulkFileListenerTest.kt @@ -20,7 +20,11 @@ class OssBulkFileListenerTest : BasePlatformTestCase() { override fun tearDown() { resetSettings(project) - super.tearDown() + try { + super.tearDown() + } catch (ignore: Exception) { + // nothing to do + } } @Test diff --git a/src/test/kotlin/snyk/oss/OssServiceTest.kt b/src/test/kotlin/snyk/oss/OssServiceTest.kt index 4d484f6d1..a2bdd2e24 100644 --- a/src/test/kotlin/snyk/oss/OssServiceTest.kt +++ b/src/test/kotlin/snyk/oss/OssServiceTest.kt @@ -41,6 +41,7 @@ class OssServiceTest : LightPlatformTestCase() { settingsStateService.organization = "" project.service().additionalParameters = "" + mockkStatic(GotoFileCellRenderer::class) every { GotoFileCellRenderer.getRelativePath(any(), any()) } returns "abc/" }