Skip to content

Commit

Permalink
fix: automatically migrate old endpoint (#547)
Browse files Browse the repository at this point in the history
* fix: automatically migrate old endpoint

* docs: update CHANGELOG.md
  • Loading branch information
bastiandoetsch authored Jun 13, 2024
1 parent 9237abd commit 80f8911
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 18 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Snyk Security Changelog

## [2.8.6]
### Fixed
- automatically migrate old-format endpoint to https://api.xxx.snyk.io endpoint and save it in settings. Also, add tooltip to custom endpoint field explaining the format.

## [2.8.5]
### Fixed
- don't display balloon warnings if IaC error is ignored (e.g. no IaC files found)
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/io/snyk/plugin/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ fun <L : Any> getSyncPublisher(project: Project, topic: Topic<L>): L? {
val <T> List<T>.head: T
get() = first()

fun isAdditionalParametersValid(params: String?): Boolean {
params.isNullOrEmpty() && return true

val list = params!!.split(" ")
return !list.contains("-d")
}

fun isUrlValid(url: String?): Boolean {
url.isNullOrEmpty() && return true

Expand Down
19 changes: 12 additions & 7 deletions src/main/kotlin/io/snyk/plugin/ui/SnykSettingsDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import io.snyk.plugin.events.SnykCliDownloadListener
import io.snyk.plugin.getCliFile
import io.snyk.plugin.getSnykCliAuthenticationService
import io.snyk.plugin.getSnykCliDownloaderService
import io.snyk.plugin.isAdditionalParametersValid
import io.snyk.plugin.isProjectSettingsAvailable
import io.snyk.plugin.isUrlValid
import io.snyk.plugin.pluginSettings
Expand Down Expand Up @@ -77,12 +78,12 @@ class SnykSettingsDialog(
private val tokenTextField = JBPasswordField()
private val receiveTokenButton = JButton("Connect IDE to Snyk")
private val customEndpointTextField = JTextField()
private val organizationTextField: JTextField = JTextField()
private val ignoreUnknownCACheckBox: JCheckBox = JCheckBox()
private val usageAnalyticsCheckBox: JCheckBox = JCheckBox()
private val crashReportingCheckBox = JCheckBox()
private val scanOnSaveCheckbox = JCheckBox()
private val additionalParametersTextField: JTextField = ExpandableTextField()
private val organizationTextField: JTextField = JTextField().apply { toolTipText = "The UUID of your organization or the org stub" }
private val ignoreUnknownCACheckBox: JCheckBox = JCheckBox().apply { toolTipText = "Enabling this causes SSL certificate validation to be disabled" }
private val usageAnalyticsCheckBox: JCheckBox = JCheckBox().apply { toolTipText = "If enabled, send analytics to Amplitude" }
private val crashReportingCheckBox = JCheckBox().apply { toolTipText = "If enabled, send error reports to Sentry" }
private val scanOnSaveCheckbox = JCheckBox().apply { toolTipText = "If enabled, automatically scan on save, start-up and configuration change" }
private val additionalParametersTextField: JTextField = ExpandableTextField().apply { toolTipText = "--all-projects is already defaulted, -d causes problems" }

private val scanTypesPanelOuter = ScanTypesPanel(project, rootPanel)
private val codeAlertPanel = scanTypesPanelOuter.codeAlertPanel
Expand Down Expand Up @@ -215,7 +216,10 @@ class SnykSettingsDialog(
)

val customEndpointLabel = JLabel("Custom endpoint:")
val customEndpointTooltip = "The correct endpoint format is https://api.xxx.snyk[gov].io, e.g. https://api.eu.snyk.io"
customEndpointLabel.toolTipText = customEndpointTooltip
customEndpointLabel.labelFor = customEndpointTextField
customEndpointTextField.toolTipText = customEndpointTooltip
generalSettingsPanel.add(
customEndpointLabel,
baseGridConstraintsAnchorWest(
Expand Down Expand Up @@ -642,7 +646,8 @@ class SnykSettingsDialog(

private fun initializeValidation() {
setupValidation(tokenTextField, "Invalid token", ::isTokenValid)
setupValidation(customEndpointTextField, "Invalid custom endpoint URL", ::isUrlValid)
setupValidation(customEndpointTextField, "Invalid custom endpoint URL, please use https://api.xxx.snyk[gov].io", ::isUrlValid)
setupValidation(additionalParametersTextField, "The -d option is not supported by the Snyk IntelliJ plugin", ::isAdditionalParametersValid)
}

private fun setupValidation(textField: JTextField, message: String, isValidText: (sourceStr: String?) -> Boolean) {
Expand Down
9 changes: 7 additions & 2 deletions src/main/kotlin/snyk/common/CustomEndpoints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,17 @@ fun isSnykCodeAvailable(endpointUrl: String?): Boolean {
*/
internal fun resolveCustomEndpoint(endpointUrl: String?): String {
return if (endpointUrl.isNullOrEmpty()) {
"https://api.snyk.io"
val normalizedEndpointURL = "https://api.snyk.io"
pluginSettings().customEndpointUrl = normalizedEndpointURL
normalizedEndpointURL
} else {
endpointUrl
val normalizedEndpointURL = endpointUrl
.removeTrailingSlashesIfPresent()
.removeSuffix("/api")
.replace("https://snyk.io", "https://api.snyk.io")
.replace("https://app.", "https://api.")
pluginSettings().customEndpointUrl = normalizedEndpointURL
normalizedEndpointURL
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/test/kotlin/io/snyk/plugin/UtilsKtTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,10 @@ class UtilsKtTest {

assertFalse(virtualFile.urlContainsDriveLetter())
}

@Test
fun isAdditionalParametersValid() {
assertFalse(isAdditionalParametersValid("-d"))
assertTrue(isAdditionalParametersValid("asdf"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class ConsoleCommandRunnerTest : LightPlatformTestCase() {
val oldEndpoint = pluginSettings().customEndpointUrl
try {
val generalCommandLine = GeneralCommandLine("")
val expectedEndpoint = "https://snyk.io/v1"
val expectedEndpoint = "https://api.snyk.io/v1"
generalCommandLine.environment["INTERNAL_OAUTH_TOKEN_STORAGE"] = "{}"
assertFalse(URI(expectedEndpoint).isOauth())

Expand Down
42 changes: 34 additions & 8 deletions src/test/kotlin/snyk/common/CustomEndpointsTest.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package snyk.common

import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.snyk.plugin.pluginSettings
Expand All @@ -15,11 +14,12 @@ import org.junit.Test
import java.net.URI

class CustomEndpointsTest {
lateinit var settings :SnykApplicationSettingsStateService
@Before
fun setUp() {
unmockkAll()
mockkStatic("io.snyk.plugin.UtilsKt")
val settings = mockk<SnykApplicationSettingsStateService>(relaxed = true)
settings = SnykApplicationSettingsStateService()
every { pluginSettings() } returns settings
}

Expand All @@ -37,6 +37,32 @@ class CustomEndpointsTest {
assertEquals("https://api.snyk.io", endpointForEmpty)
}

@Test
fun `resolveCustomEndpoint converts app endpoint to api endpoint and saves it in settings`() {
val endpointUrl = "https://app.snyk.io/api"
val snykEndpointUrl = "https://snyk.io/api"
val expected = "https://api.snyk.io"

settings.customEndpointUrl = endpointUrl
var actual = resolveCustomEndpoint(endpointUrl)

assertEquals(expected, actual)
assertEquals(expected, pluginSettings().customEndpointUrl)

settings.customEndpointUrl = snykEndpointUrl
actual = resolveCustomEndpoint(snykEndpointUrl)

assertEquals(expected, actual)
assertEquals(expected, pluginSettings().customEndpointUrl)

settings.customEndpointUrl = ""
actual = resolveCustomEndpoint("")

assertEquals(expected, actual)
assertEquals(expected, pluginSettings().customEndpointUrl)
}


@Test
fun `resolveCustomEndpoint removes all trailing slashes if present`() {
val endpointWithSingleTrailingSlash = resolveCustomEndpoint("https://app.on-prem.internal/api")
Expand Down Expand Up @@ -64,8 +90,8 @@ class CustomEndpointsTest {

@Test
fun `isSnykCodeAvailable returns true if local engine is enabled`() {
every { pluginSettings().localCodeEngineUrl } returns "https://api.foo.bar"
every { pluginSettings().localCodeEngineEnabled } returns true
settings.localCodeEngineUrl = "https://api.foo.bar"
settings.localCodeEngineEnabled = true

assertEquals(isSnykCodeAvailable("https://api.foo.bar"), true)
}
Expand Down Expand Up @@ -96,8 +122,8 @@ class CustomEndpointsTest {

@Test
fun `toSnykCodeApiUrl returns local engine URL if local engine is enabled`() {
every { pluginSettings().localCodeEngineUrl } returns "https://api.foo.bar"
every { pluginSettings().localCodeEngineEnabled } returns true
settings.localCodeEngineUrl = "https://api.foo.bar"
settings.localCodeEngineEnabled = true
val apiUrlForProduction = toSnykCodeApiUrl("https://api.snyk.io")

assertEquals("https://api.foo.bar", apiUrlForProduction)
Expand Down Expand Up @@ -131,8 +157,8 @@ class CustomEndpointsTest {

@Test
fun `toSnykCodeSettingsUrl returns APP-URL for local engine`() {
every { pluginSettings().localCodeEngineUrl } returns "https://api.foo.bar"
every { pluginSettings().localCodeEngineEnabled } returns true
settings.localCodeEngineUrl = "https://api.foo.bar"
settings.localCodeEngineEnabled = true

assertEquals("https://app.foo.bar", toSnykCodeSettingsUrl("https://api.foo.bar"))
}
Expand Down

0 comments on commit 80f8911

Please sign in to comment.