From 718874401efdab0accd9163b921c8548ba7902dc Mon Sep 17 00:00:00 2001 From: Ilya Siamionau Date: Tue, 9 Jul 2024 15:52:22 +0200 Subject: [PATCH 1/2] CM-37864 - Integrate Sentry --- .github/workflows/build.yml | 1 + CHANGELOG.md | 8 ++- build.gradle.kts | 16 +++++ gradle.properties | 2 +- gradle/libs.versions.toml | 2 + src/main/kotlin/com/cycode/plugin/Consts.kt | 14 +++- .../plugin/activities/PostStartupActivity.kt | 3 + .../com/cycode/plugin/cli/CliWrapper.kt | 2 + .../kotlin/com/cycode/plugin/cli/UserAgent.kt | 8 +-- .../plugin/cli/models/AuthCheckResult.kt | 6 ++ .../plugin/sentry/SentryErrorReporter.kt | 72 +++++++++++++++++++ .../com/cycode/plugin/sentry/SentryInit.kt | 28 ++++++++ .../com/cycode/plugin/services/CliService.kt | 6 ++ .../cycode/plugin/services/DownloadService.kt | 3 + .../plugin/services/GithubReleaseService.kt | 2 + .../com/cycode/plugin/utils/FileChecksum.kt | 2 + .../kotlin/com/cycode/plugin/utils/Plugin.kt | 8 +++ src/main/resources/META-INF/plugin.xml | 1 + .../messages/CycodeBundle.properties | 4 ++ 19 files changed, 181 insertions(+), 7 deletions(-) create mode 100644 src/main/kotlin/com/cycode/plugin/sentry/SentryErrorReporter.kt create mode 100644 src/main/kotlin/com/cycode/plugin/sentry/SentryInit.kt create mode 100644 src/main/kotlin/com/cycode/plugin/utils/Plugin.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 04d637c..6244f06 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -97,6 +97,7 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' }} env: PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} run: ./gradlew publishPlugin # test: diff --git a/CHANGELOG.md b/CHANGELOG.md index 05fd4fb..955ee0d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## [Unreleased] +## [1.9.2] - 2024-07-10 + +- Integrate Sentry + ## [1.9.1] - 2024-06-25 - Remove forgotten "coming soon" from SAST @@ -93,6 +97,8 @@ The first public release of the plugin. +[1.9.2]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.9.2 + [1.9.1]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.9.1 [1.9.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.9.0 @@ -129,4 +135,4 @@ The first public release of the plugin. [1.0.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.0.0 -[Unreleased]: https://github.com/cycodehq/intellij-platform-plugin/compare/v1.4.0...HEAD +[Unreleased]: https://github.com/cycodehq/intellij-platform-plugin/compare/v1.9.2...HEAD diff --git a/build.gradle.kts b/build.gradle.kts index e59fac0..4d062e5 100755 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,6 +10,7 @@ plugins { alias(libs.plugins.gradleIntelliJPlugin) // Gradle IntelliJ Plugin alias(libs.plugins.changelog) // Gradle Changelog Plugin alias(libs.plugins.kover) // Gradle Kover Plugin + alias(libs.plugins.sentry) } group = properties("pluginGroup").get() @@ -66,6 +67,20 @@ koverReport { } } +// Configure Sentry +sentry { + includeDependenciesReport = false + + // Generates a JVM (Java, Kotlin, etc.) source bundle and uploads your source code to Sentry. + // This enables source context, allowing you to see your source + // code as part of your stack traces in Sentry. + includeSourceContext = true + + org = "cycode" + projectName = "intellij-platform-plugin" + authToken = environment("SENTRY_AUTH_TOKEN") +} + tasks { wrapper { gradleVersion = properties("gradleVersion").get() @@ -124,6 +139,7 @@ tasks { publishPlugin { dependsOn("patchChangelog") + dependsOn("sentryUploadSourceBundleJava") token = environment("PUBLISH_TOKEN") // The pluginVersion is based on the SemVer (https://semver.org) and supports pre-release labels, like 2.1.7-alpha.3 // Specify pre-release label to publish the plugin in a custom Release Channel automatically. Read more: diff --git a/gradle.properties b/gradle.properties index 99bfcac..8bd911a 100755 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginGroup = com.cycode.plugin pluginName = Cycode pluginRepositoryUrl = https://github.com/cycodehq/intellij-platform-plugin # SemVer format -> https://semver.org -pluginVersion = 1.9.1 +pluginVersion = 1.9.2 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 211.1 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9517e89..69a16eb 100755 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,7 @@ kotlin = "1.9.20" changelog = "2.2.0" gradleIntelliJPlugin = "1.17.1" kover = "0.7.3" +sentry = "4.9.0" [libraries] annotations = { group = "org.jetbrains", name = "annotations", version.ref = "annotations" } @@ -22,3 +23,4 @@ dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } gradleIntelliJPlugin = { id = "org.jetbrains.intellij", version.ref = "gradleIntelliJPlugin" } kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } +sentry = { id = "io.sentry.jvm.gradle", version.ref = "sentry" } diff --git a/src/main/kotlin/com/cycode/plugin/Consts.kt b/src/main/kotlin/com/cycode/plugin/Consts.kt index d23f90a..bdabb9c 100644 --- a/src/main/kotlin/com/cycode/plugin/Consts.kt +++ b/src/main/kotlin/com/cycode/plugin/Consts.kt @@ -1,5 +1,6 @@ package com.cycode.plugin +import com.cycode.plugin.utils.getPluginVersion import com.intellij.openapi.application.PathManager import com.intellij.openapi.util.SystemInfo @@ -16,12 +17,17 @@ private fun getDefaultCliPath(): String { return "${Consts.PLUGIN_PATH}/cycode" } +private fun getSentryReleaseVersion(): String { + val appName = CycodeBundle.message("appName") + val version = getPluginVersion() + return "$appName@${version}" +} class Consts { companion object { val PLUGIN_PATH = PathManager.getPluginsPath() + "/cycode-intellij-platform-plugin" val DEFAULT_CLI_PATH = getDefaultCliPath() - const val REQUIRED_CLI_VERSION = "1.10.1" + const val REQUIRED_CLI_VERSION = "1.10.3" const val CLI_GITHUB_ORG = "cycodehq" const val CLI_GITHUB_REPO = "cycode-cli" @@ -30,5 +36,11 @@ class Consts { const val PLUGIN_AUTO_SAVE_FLUSH_INITIAL_DELAY_SEC = 0L const val PLUGIN_AUTO_SAVE_FLUSH_DELAY_SEC = 5L + + const val SENTRY_DSN = "https://0f0524e8d03a4283702a10ed4b6e03d0@o1026942.ingest.us.sentry.io/4507543885774848" + const val SENTRY_DEBUG = false + val SENTRY_RELEASE = getSentryReleaseVersion() + const val SENTRY_SAMPLE_RATE = 1.0 + const val SENTRY_SEND_DEFAULT_PII = false } } diff --git a/src/main/kotlin/com/cycode/plugin/activities/PostStartupActivity.kt b/src/main/kotlin/com/cycode/plugin/activities/PostStartupActivity.kt index 4e5a174..7e2aa08 100644 --- a/src/main/kotlin/com/cycode/plugin/activities/PostStartupActivity.kt +++ b/src/main/kotlin/com/cycode/plugin/activities/PostStartupActivity.kt @@ -1,6 +1,7 @@ package com.cycode.plugin.activities import com.cycode.plugin.annotators.CycodeAnnotator +import com.cycode.plugin.sentry.SentryInit import com.cycode.plugin.services.cycode import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.project.Project @@ -8,6 +9,8 @@ import com.intellij.openapi.startup.StartupActivity class PostStartupActivity : StartupActivity.DumbAware { override fun runActivity(project: Project) { + SentryInit.init() + // we are using singleton here because runActivity is called for each project CycodeAnnotator.INSTANCE.registerForAllLangs() diff --git a/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt b/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt index 60baa9c..28be067 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/CliWrapper.kt @@ -14,6 +14,7 @@ import com.intellij.execution.process.ProcessOutputTypes import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.util.Key import com.intellij.util.io.BaseOutputReader +import io.sentry.Sentry import java.io.File import java.nio.charset.Charset @@ -104,6 +105,7 @@ class CliWrapper(val executablePath: String, val workDirectory: String? = null) val result: CliError = mapper.readValue(stdout) return CliResult.Error(result) } catch (e: Exception) { + Sentry.captureException(e) thisLogger().error("Failed to parse ANY CLI output: $stdout", e) } diff --git a/src/main/kotlin/com/cycode/plugin/cli/UserAgent.kt b/src/main/kotlin/com/cycode/plugin/cli/UserAgent.kt index a71394b..88c0379 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/UserAgent.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/UserAgent.kt @@ -1,17 +1,17 @@ package com.cycode.plugin.cli +import com.cycode.plugin.CycodeBundle import com.cycode.plugin.cli.models.IDEUserAgent +import com.cycode.plugin.utils.getPluginVersion import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.PropertyNamingStrategies -import com.intellij.ide.plugins.PluginManagerCore import com.intellij.openapi.application.ApplicationInfo -import com.intellij.openapi.extensions.PluginId private fun retrieveIDEInfo(): IDEUserAgent { val appInfo = ApplicationInfo.getInstance() - val appName = "jetbrains_plugin" - val appVersion = PluginManagerCore.getPlugin(PluginId.getId("com.cycode.plugin"))?.version ?: "unknown" + val appName = CycodeBundle.message("appName") + val appVersion = getPluginVersion() val envName = appInfo.versionName val envVersion = appInfo.fullVersion diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/AuthCheckResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/AuthCheckResult.kt index 69c7b02..02ef237 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/AuthCheckResult.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/AuthCheckResult.kt @@ -3,4 +3,10 @@ package com.cycode.plugin.cli.models data class AuthCheckResult( val result: Boolean, val message: String, + val data: AuthCheckResultData, +) + +data class AuthCheckResultData( + val userId: String, + val tenantId: String, ) diff --git a/src/main/kotlin/com/cycode/plugin/sentry/SentryErrorReporter.kt b/src/main/kotlin/com/cycode/plugin/sentry/SentryErrorReporter.kt new file mode 100644 index 0000000..b5d6257 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/sentry/SentryErrorReporter.kt @@ -0,0 +1,72 @@ +package com.cycode.plugin.sentry + +import com.cycode.plugin.Consts +import com.cycode.plugin.CycodeBundle +import com.intellij.diagnostic.IdeaReportingEvent +import com.intellij.ide.DataManager +import com.intellij.idea.IdeaLogger +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.ErrorReportSubmitter +import com.intellij.openapi.diagnostic.IdeaLoggingEvent +import com.intellij.openapi.diagnostic.SubmittedReportInfo +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.Task +import com.intellij.openapi.project.Project +import com.intellij.util.Consumer +import io.sentry.Sentry +import io.sentry.SentryEvent +import io.sentry.SentryLevel +import java.awt.Component + + +class SentryErrorReporter : ErrorReportSubmitter() { + override fun getReportActionText(): String { + return CycodeBundle.message("reportActionButton") + } + + override fun submit( + events: Array, + additionalInfo: String?, + parentComponent: Component, + consumer: Consumer + ): Boolean { + val mgr = DataManager.getInstance() + val context = mgr.getDataContext(parentComponent) + val project: Project? = CommonDataKeys.PROJECT.getData(context) + + object : Task.Backgroundable(project, CycodeBundle.message("sentryReporting"), false) { + override fun run(indicator: ProgressIndicator) { + for (ideaEvent in events) { + if (ideaEvent !is IdeaReportingEvent) { + continue + } + + val event = SentryEvent() + event.level = SentryLevel.ERROR + event.release = Consts.SENTRY_RELEASE + event.throwable = ideaEvent.data.throwable + event.serverName = "" + + event.extras = mapOf( + "message" to ideaEvent.data.message, + "additional_info" to additionalInfo, + "last_action" to IdeaLogger.ourLastActionId, + ) + + Sentry.captureEvent(event) + } + + ApplicationManager.getApplication().invokeLater { + consumer.consume( + SubmittedReportInfo( + SubmittedReportInfo.SubmissionStatus.NEW_ISSUE + ) + ) + } + } + }.queue() + + return true + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/cycode/plugin/sentry/SentryInit.kt b/src/main/kotlin/com/cycode/plugin/sentry/SentryInit.kt new file mode 100644 index 0000000..4d5e29a --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/sentry/SentryInit.kt @@ -0,0 +1,28 @@ +package com.cycode.plugin.sentry + +import com.cycode.plugin.Consts +import io.sentry.Sentry +import io.sentry.protocol.User + +object SentryInit { + fun init() { + Sentry.init { options -> + options.dsn = Consts.SENTRY_DSN + options.isDebug = Consts.SENTRY_DEBUG + options.sampleRate = Consts.SENTRY_SAMPLE_RATE + options.release = Consts.SENTRY_RELEASE + options.isSendDefaultPii = Consts.SENTRY_SEND_DEFAULT_PII + options.serverName = "" + } + } + + fun setupScope(userId: String, tenantId: String) { + Sentry.configureScope { scope -> + scope.setTag("tenant_id", tenantId) + scope.user = User().apply { + id = userId + data = mapOf("tenant_id" to tenantId) + } + } + } +} diff --git a/src/main/kotlin/com/cycode/plugin/services/CliService.kt b/src/main/kotlin/com/cycode/plugin/services/CliService.kt index b614cb3..6895cea 100644 --- a/src/main/kotlin/com/cycode/plugin/services/CliService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/CliService.kt @@ -12,6 +12,7 @@ import com.cycode.plugin.cli.models.scanResult.sast.SastScanResult import com.cycode.plugin.cli.models.scanResult.sca.ScaScanResult import com.cycode.plugin.cli.models.scanResult.secret.SecretScanResult import com.cycode.plugin.components.toolWindow.updateToolWindowState +import com.cycode.plugin.sentry.SentryInit import com.cycode.plugin.utils.CycodeNotifier import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer import com.intellij.openapi.components.Service @@ -124,6 +125,11 @@ class CliService(private val project: Project) { showErrorNotification(CycodeBundle.message("checkAuthErrorNotification")) } + SentryInit.setupScope( + processedResult.result.data.userId, + processedResult.result.data.tenantId, + ) + return pluginState.cliAuthed } diff --git a/src/main/kotlin/com/cycode/plugin/services/DownloadService.kt b/src/main/kotlin/com/cycode/plugin/services/DownloadService.kt index 9a53923..0db916f 100644 --- a/src/main/kotlin/com/cycode/plugin/services/DownloadService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/DownloadService.kt @@ -3,6 +3,7 @@ package com.cycode.plugin.services import com.cycode.plugin.utils.verifyFileChecksum import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.thisLogger +import io.sentry.Sentry import java.io.BufferedInputStream import java.io.File import java.io.FileOutputStream @@ -31,6 +32,7 @@ class DownloadService { return content } catch (e: Exception) { + Sentry.captureException(e) thisLogger().error("Failed to download file $e", e) } @@ -88,6 +90,7 @@ class DownloadService { return file } } catch (e: Exception) { + Sentry.captureException(e) thisLogger().error("Failed to download file $e", e) } finally { tempFile.delete() diff --git a/src/main/kotlin/com/cycode/plugin/services/GithubReleaseService.kt b/src/main/kotlin/com/cycode/plugin/services/GithubReleaseService.kt index 9d98835..4cd9396 100644 --- a/src/main/kotlin/com/cycode/plugin/services/GithubReleaseService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/GithubReleaseService.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue import com.intellij.openapi.components.Service +import io.sentry.Sentry import java.net.URL @@ -75,6 +76,7 @@ class GithubReleaseService { null } catch (e: Exception) { + Sentry.captureException(e) e.printStackTrace() null } diff --git a/src/main/kotlin/com/cycode/plugin/utils/FileChecksum.kt b/src/main/kotlin/com/cycode/plugin/utils/FileChecksum.kt index ea3a6c3..6dc3c15 100644 --- a/src/main/kotlin/com/cycode/plugin/utils/FileChecksum.kt +++ b/src/main/kotlin/com/cycode/plugin/utils/FileChecksum.kt @@ -1,5 +1,6 @@ package com.cycode.plugin.utils +import io.sentry.Sentry import java.io.File import java.io.FileInputStream import java.security.MessageDigest @@ -38,6 +39,7 @@ fun verifyFileChecksum(file: File, sha256Checksum: String): Boolean { try { return sha256Checksum.equals(getFileShaHash(file), ignoreCase = true) } catch (e: Exception) { + Sentry.captureException(e) e.printStackTrace() } diff --git a/src/main/kotlin/com/cycode/plugin/utils/Plugin.kt b/src/main/kotlin/com/cycode/plugin/utils/Plugin.kt new file mode 100644 index 0000000..ecb447c --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/utils/Plugin.kt @@ -0,0 +1,8 @@ +package com.cycode.plugin.utils + +import com.intellij.ide.plugins.PluginManagerCore +import com.intellij.openapi.extensions.PluginId + +fun getPluginVersion(): String { + return PluginManagerCore.getPlugin(PluginId.getId("com.cycode.plugin"))?.version ?: "unknown" +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 39c842b..33dc23e 100755 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -16,6 +16,7 @@ + diff --git a/src/main/resources/messages/CycodeBundle.properties b/src/main/resources/messages/CycodeBundle.properties index 3d84d1e..f389d20 100755 --- a/src/main/resources/messages/CycodeBundle.properties +++ b/src/main/resources/messages/CycodeBundle.properties @@ -1,5 +1,6 @@ # general name=Cycode +appName=jetbrains_plugin docsUrl=https://github.com/cycodehq/intellij-platform-plugin/blob/main/README.md # detection titles secretsTitle={0}. {1} @@ -69,6 +70,7 @@ scaScanning=Cycode is scanning files for package vulnerabilities... iacScanning=Cycode is scanning files for Infrastructure As Code... sastScanning=Cycode is scanning files for Code Security... ignoresApplying=Cycode is applying ignores... +sentryReporting=Cycode is reporting the problem... # settings menu settingsCliSectionTitle=Cycode CLI settings settingsOnPremiseSectionTitle=On-premise settings @@ -138,3 +140,5 @@ toolbarFilterBySeverityAction=Filter by {0} severity toolbarClearAction=Clear results toolbarHelpAction=Open documentation toolbarRunAllAction=Run all scans types for the entire project +# sentry +reportActionButton=Report to Cycode From 3e832199d34f27600966b845811a348a130355e7 Mon Sep 17 00:00:00 2001 From: Ilya Siamionau Date: Mon, 15 Jul 2024 12:30:02 +0200 Subject: [PATCH 2/2] update release date --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 955ee0d..fc65360 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ## [Unreleased] -## [1.9.2] - 2024-07-10 +## [1.9.2] - 2024-07-15 - Integrate Sentry