From f7fc26035ea70fe44ba08d89286d5bc1c6b29e79 Mon Sep 17 00:00:00 2001 From: Ilya Siamionau Date: Tue, 26 Mar 2024 23:30:38 +0100 Subject: [PATCH 1/4] CM-32772 - Add Infrastructure as Code (IaC) support --- CHANGELOG.md | 6 + gradle.properties | 2 +- .../plugin/annotators/CycodeAnnotator.kt | 214 +----------------- .../AnnotationApplierBase.kt | 8 + .../annotationAppliers/IacApplier.kt | 83 +++++++ .../annotationAppliers/ScaApplier.kt | 109 +++++++++ .../annotationAppliers/SecretApplier.kt | 103 +++++++++ .../com/cycode/plugin/annotators/utils.kt | 21 ++ .../com/cycode/plugin/cli/ScaHelpers.kt | 22 ++ .../cli/models/scanResult/iac/IacDetection.kt | 25 ++ .../scanResult/iac/IacDetectionDetails.kt | 18 ++ .../models/scanResult/iac/IacScanResult.kt | 9 + .../actions/RunAllAction.kt | 2 + .../scanContentTab/ScanContentTab.kt | 5 + .../components/treeView/TreeView.kt | 50 +++- .../DetectionNodeContextMenu.kt | 28 ++- .../treeView/nodes/DetectionNodes.kt | 8 + .../toolWindow/components/treeView/utils.kt | 9 + .../plugin/listeners/FileSaveListener.kt | 10 + .../com/cycode/plugin/services/CliService.kt | 20 ++ .../cycode/plugin/services/CycodeService.kt | 32 +++ .../plugin/services/ScanResultsService.kt | 15 +- .../messages/CycodeBundle.properties | 7 +- 23 files changed, 583 insertions(+), 223 deletions(-) create mode 100644 src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/AnnotationApplierBase.kt create mode 100644 src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt create mode 100644 src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/ScaApplier.kt create mode 100644 src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/SecretApplier.kt create mode 100644 src/main/kotlin/com/cycode/plugin/annotators/utils.kt create mode 100644 src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt create mode 100644 src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt create mode 100644 src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 0054db0..d54a56b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ ## [Unreleased] +## [1.6.0] - 2024-03-XX + +- Add Infrastructure as Code (IaC) support + ## [1.5.0] - 2024-03-13 - Add SCA Violation Card @@ -62,6 +66,8 @@ The first public release of the plugin. +[1.6.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.6.0 + [1.5.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.5.0 [1.4.0]: https://github.com/cycodehq/intellij-platform-plugin/releases/tag/v1.4.0 diff --git a/gradle.properties b/gradle.properties index 01e8b12..dec331e 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.5.0 +pluginVersion = 1.6.0 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 211.1 diff --git a/src/main/kotlin/com/cycode/plugin/annotators/CycodeAnnotator.kt b/src/main/kotlin/com/cycode/plugin/annotators/CycodeAnnotator.kt index 5f1e6c4..dfdadab 100644 --- a/src/main/kotlin/com/cycode/plugin/annotators/CycodeAnnotator.kt +++ b/src/main/kotlin/com/cycode/plugin/annotators/CycodeAnnotator.kt @@ -1,22 +1,16 @@ package com.cycode.plugin.annotators -import com.cycode.plugin.CycodeBundle -import com.cycode.plugin.cli.CliResult -import com.cycode.plugin.cli.CliScanType -import com.cycode.plugin.cli.getPackageFileForLockFile -import com.cycode.plugin.cli.isSupportedLockFile -import com.cycode.plugin.intentions.CycodeIgnoreIntentionQuickFix -import com.cycode.plugin.intentions.CycodeIgnoreType +import com.cycode.plugin.annotators.annotationAppliers.IacApplier +import com.cycode.plugin.annotators.annotationAppliers.ScaApplier +import com.cycode.plugin.annotators.annotationAppliers.SecretApplier import com.cycode.plugin.services.ScanResultsService import com.cycode.plugin.services.scanResults import com.intellij.lang.ExternalLanguageAnnotators import com.intellij.lang.Language import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.ExternalAnnotator -import com.intellij.lang.annotation.HighlightSeverity import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.DumbAware -import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiFile private val LOG = logger() @@ -123,208 +117,18 @@ class CycodeAnnotator : DumbAware, ExternalAnnotator() { applyAnnotationsForSecrets(psiFile, holder) applyAnnotationsForSca(psiFile, holder) - } - - private fun convertSeverity(severity: String): HighlightSeverity { - return when (severity.toLowerCase()) { - "critical" -> HighlightSeverity.ERROR - "high" -> HighlightSeverity.ERROR - "medium" -> HighlightSeverity.WARNING - "low" -> HighlightSeverity.WEAK_WARNING - else -> HighlightSeverity.INFORMATION - } - } - - private fun validateSecretTextRange(textRange: TextRange, psiFile: PsiFile): Boolean { - val scanResults = getScanResults(psiFile) - val detectedSubstr = psiFile.text.substring(textRange.startOffset, textRange.endOffset) - val detectedSegment = scanResults.getDetectedSegment(CliScanType.Secret, textRange) - if (detectedSegment == null) { - scanResults.saveDetectedSegment(CliScanType.Secret, textRange, detectedSubstr) - } else if (detectedSegment != detectedSubstr) { - // case: the code has been added or deleted before the detection - LOG.debug( - "[Secret] Text range of detection has been shifted. " + - "Annotation is not relevant to this state of the file content anymore" - ) - return false - } - - return true - } - - private fun validateScaTextRange(textRange: TextRange, psiFile: PsiFile, expectedPackageName: String): Boolean { - // text range is dynamic and calculated from the line number, - // so we can't use the same validation as for secrets - // instead, we check if the package name is still in the text range - val detectedSubstr = psiFile.text.substring(textRange.startOffset, textRange.endOffset) - if (!detectedSubstr.contains(expectedPackageName)) { - LOG.debug( - "[SCA] Text range of detection has been shifted. " + - "Annotation is not relevant to this state of the file content anymore" - ) - return false - } - - return true - } - - private fun validateTextRange(textRange: TextRange, psiFile: PsiFile): Boolean { - if (textRange.endOffset > psiFile.text.length || textRange.startOffset < 0) { - // check if text range fits in file - - // case: row with detections has been deleted, but detection is still in the local results DB - LOG.debug("Text range of detection is out of file bounds") - return false - } - - return true + applyAnnotationsForIac(psiFile, holder) } private fun applyAnnotationsForSecrets(psiFile: PsiFile, holder: AnnotationHolder) { - val scanResults = getScanResults(psiFile) - val latestScanResult = scanResults.getSecretResults() - if (latestScanResult !is CliResult.Success) { - return - } - - val relevantDetections = latestScanResult.result.detections.filter { detection -> - detection.detectionDetails.getFilepath() == psiFile.virtualFile.path - } - - relevantDetections.forEach { detection -> - val severity = convertSeverity(detection.severity) - - val detectionDetails = detection.detectionDetails - val textRange = TextRange( - detectionDetails.startPosition, - detectionDetails.startPosition + detectionDetails.length - ) - - if (!validateTextRange(textRange, psiFile) || !validateSecretTextRange(textRange, psiFile)) { - return@forEach - } - - val detectedValue = psiFile.text.substring(textRange.startOffset, textRange.endOffset) - detectionDetails.detectedValue = detectedValue - - val message = detection.getFormattedMessage() - val title = CycodeBundle.message("annotationTitle", detection.getFormattedTitle()) - - var companyGuidelineMessage = "" - if (detectionDetails.customRemediationGuidelines != null) { - companyGuidelineMessage = CycodeBundle.message( - "secretsAnnotationTooltipCompanyGuideline", - detectionDetails.customRemediationGuidelines - ) - } - - val tooltip = CycodeBundle.message( - "secretsAnnotationTooltip", - detection.severity, - detection.type, - message, - detection.detectionRuleId, - detectionDetails.fileName, - detectionDetails.sha512, - companyGuidelineMessage - ) - holder.newAnnotation(severity, title) - .range(textRange) - .tooltip(tooltip) - .withFix( - CycodeIgnoreIntentionQuickFix( - CliScanType.Secret, - CycodeIgnoreType.PATH, - detection.detectionDetails.getFilepath() - ) - ) - .withFix( - CycodeIgnoreIntentionQuickFix( - CliScanType.Secret, - CycodeIgnoreType.RULE, - detection.detectionRuleId - ) - ) - .withFix(CycodeIgnoreIntentionQuickFix(CliScanType.Secret, CycodeIgnoreType.VALUE, detectedValue)) - .create() - - } + SecretApplier(getScanResults(psiFile)).apply(psiFile, holder) } private fun applyAnnotationsForSca(psiFile: PsiFile, holder: AnnotationHolder) { - val scanResults = getScanResults(psiFile) - val latestScanResult = scanResults.getScaResults() - if (latestScanResult !is CliResult.Success) { - return - } - - val relevantDetections = latestScanResult.result.detections.filter { detection -> - detection.detectionDetails.getFilepath() == psiFile.virtualFile.path - } - - relevantDetections.forEach { detection -> - val severity = convertSeverity(detection.severity) - - // SCA doesn't provide start and end positions, so we have to calculate them from the line number - val line = detection.detectionDetails.lineInFile - 1 - val startOffset = psiFile.text.lines().take(line).sumOf { it.length + 1 } - val endOffset = startOffset + psiFile.text.lines()[line].length - - val detectionDetails = detection.detectionDetails - val textRange = TextRange(startOffset, endOffset) - - if (!validateTextRange(textRange, psiFile) || !validateScaTextRange( - textRange, - psiFile, - detectionDetails.packageName - ) - ) { - return@forEach - } - - val title = CycodeBundle.message("annotationTitle", detection.getFormattedTitle()) - - var firstPatchedVersionMessage = "" - if (detectionDetails.alert?.firstPatchedVersion != null) { - firstPatchedVersionMessage = CycodeBundle.message( - "scaAnnotationTooltipFirstPatchedVersion", - detectionDetails.alert.firstPatchedVersion - ) - } - - var lockFileNote = "" - if (isSupportedLockFile(psiFile.virtualFile.name)) { - val packageFileName = getPackageFileForLockFile(psiFile.virtualFile.name) - lockFileNote = CycodeBundle.message("scaAnnotationTooltipLockFileNote", packageFileName) - } + ScaApplier(getScanResults(psiFile)).apply(psiFile, holder) + } - val tooltip = CycodeBundle.message( - "scaAnnotationTooltip", - detection.severity, - firstPatchedVersionMessage, - detection.message, - detection.detectionRuleId, - lockFileNote, - ) - holder.newAnnotation(severity, title) - .range(textRange) - .tooltip(tooltip) - .withFix( - CycodeIgnoreIntentionQuickFix( - CliScanType.Sca, - CycodeIgnoreType.PATH, - detection.detectionDetails.getFilepath() - ) - ) - .withFix( - CycodeIgnoreIntentionQuickFix( - CliScanType.Sca, - CycodeIgnoreType.RULE, - detection.detectionRuleId - ) - ) - .create() - } + private fun applyAnnotationsForIac(psiFile: PsiFile, holder: AnnotationHolder) { + IacApplier(getScanResults(psiFile)).apply(psiFile, holder) } } diff --git a/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/AnnotationApplierBase.kt b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/AnnotationApplierBase.kt new file mode 100644 index 0000000..0a83532 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/AnnotationApplierBase.kt @@ -0,0 +1,8 @@ +package com.cycode.plugin.annotators.annotationAppliers + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.psi.PsiFile + +abstract class AnnotationApplierBase { + abstract fun apply(psiFile: PsiFile, holder: AnnotationHolder) +} diff --git a/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt new file mode 100644 index 0000000..b138572 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt @@ -0,0 +1,83 @@ +package com.cycode.plugin.annotators.annotationAppliers + +import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.annotators.convertSeverity +import com.cycode.plugin.annotators.validateTextRange +import com.cycode.plugin.cli.CliResult +import com.cycode.plugin.cli.CliScanType +import com.cycode.plugin.services.ScanResultsService +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiFile + +class IacApplier(private val scanResults: ScanResultsService) : AnnotationApplierBase() { + private fun validateIacTextRange(textRange: TextRange, psiFile: PsiFile): Boolean { + val detectedSubstr = psiFile.text.substring(textRange.startOffset, textRange.endOffset) + val detectedSegment = scanResults.getDetectedSegment(CliScanType.Iac, textRange) + if (detectedSegment == null) { + scanResults.saveDetectedSegment(CliScanType.Iac, textRange, detectedSubstr) + } else if (detectedSegment != detectedSubstr) { + // case: the code has been added or deleted before the detection + thisLogger().debug( + "[IaC] Text range of detection has been shifted. " + + "Annotation is not relevant to this state of the file content anymore" + ) + return false + } + + return true + } + + override fun apply(psiFile: PsiFile, holder: AnnotationHolder) { + val latestScanResult = scanResults.getIacResults() + if (latestScanResult !is CliResult.Success) { + return + } + + val relevantDetections = latestScanResult.result.detections.filter { detection -> + detection.detectionDetails.getFilepath() == psiFile.virtualFile.path + } + + relevantDetections.forEach { detection -> + val severity = convertSeverity(detection.severity) + + // IaC doesn't provide start and end positions, so we have to calculate them from the line number + val line = detection.detectionDetails.lineInFile - 1 + val startOffset = psiFile.text.lines().take(line).sumOf { it.length + 1 } + val endOffset = startOffset + psiFile.text.lines()[line].length + + val detectionDetails = detection.detectionDetails + val textRange = TextRange(startOffset, endOffset) + + if (!validateTextRange(textRange, psiFile) || !validateIacTextRange(textRange, psiFile)) { + return@forEach + } + + val message = detection.getFormattedMessage() + val title = CycodeBundle.message("annotationTitle", detection.getFormattedTitle()) + + var companyGuidelineMessage = "" + if (detectionDetails.customRemediationGuidelines != null) { + companyGuidelineMessage = CycodeBundle.message( + "secretsAnnotationTooltipCompanyGuideline", + detectionDetails.customRemediationGuidelines + ) + } + + val tooltip = CycodeBundle.message( + "iacAnnotationTooltip", + detection.severity, + detection.type, + message, + detection.detectionRuleId, + detectionDetails.fileName, + companyGuidelineMessage + ) + holder.newAnnotation(severity, title) + .range(textRange) + .tooltip(tooltip) + .create() + } + } +} diff --git a/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/ScaApplier.kt b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/ScaApplier.kt new file mode 100644 index 0000000..6c7c31c --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/ScaApplier.kt @@ -0,0 +1,109 @@ +package com.cycode.plugin.annotators.annotationAppliers + +import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.annotators.convertSeverity +import com.cycode.plugin.annotators.validateTextRange +import com.cycode.plugin.cli.CliResult +import com.cycode.plugin.cli.CliScanType +import com.cycode.plugin.cli.getPackageFileForLockFile +import com.cycode.plugin.cli.isSupportedLockFile +import com.cycode.plugin.intentions.CycodeIgnoreIntentionQuickFix +import com.cycode.plugin.intentions.CycodeIgnoreType +import com.cycode.plugin.services.ScanResultsService +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiFile + +class ScaApplier(private val scanResults: ScanResultsService) : AnnotationApplierBase() { + private fun validateScaTextRange(textRange: TextRange, psiFile: PsiFile, expectedPackageName: String): Boolean { + // text range is dynamic and calculated from the line number, + // so we can't use the same validation as for secrets + // instead, we check if the package name is still in the text range + val detectedSubstr = psiFile.text.substring(textRange.startOffset, textRange.endOffset) + if (!detectedSubstr.contains(expectedPackageName)) { + thisLogger().debug( + "[SCA] Text range of detection has been shifted. " + + "Annotation is not relevant to this state of the file content anymore" + ) + return false + } + + return true + } + + override fun apply(psiFile: PsiFile, holder: AnnotationHolder) { + val latestScanResult = scanResults.getScaResults() + if (latestScanResult !is CliResult.Success) { + return + } + + val relevantDetections = latestScanResult.result.detections.filter { detection -> + detection.detectionDetails.getFilepath() == psiFile.virtualFile.path + } + + relevantDetections.forEach { detection -> + val severity = convertSeverity(detection.severity) + + // SCA doesn't provide start and end positions, so we have to calculate them from the line number + val line = detection.detectionDetails.lineInFile - 1 + val startOffset = psiFile.text.lines().take(line).sumOf { it.length + 1 } + val endOffset = startOffset + psiFile.text.lines()[line].length + + val detectionDetails = detection.detectionDetails + val textRange = TextRange(startOffset, endOffset) + + if (!validateTextRange(textRange, psiFile) || !validateScaTextRange( + textRange, + psiFile, + detectionDetails.packageName + ) + ) { + return@forEach + } + + val title = CycodeBundle.message("annotationTitle", detection.getFormattedTitle()) + + var firstPatchedVersionMessage = "" + if (detectionDetails.alert?.firstPatchedVersion != null) { + firstPatchedVersionMessage = CycodeBundle.message( + "scaAnnotationTooltipFirstPatchedVersion", + detectionDetails.alert.firstPatchedVersion + ) + } + + var lockFileNote = "" + if (isSupportedLockFile(psiFile.virtualFile.name)) { + val packageFileName = getPackageFileForLockFile(psiFile.virtualFile.name) + lockFileNote = CycodeBundle.message("scaAnnotationTooltipLockFileNote", packageFileName) + } + + val tooltip = CycodeBundle.message( + "scaAnnotationTooltip", + detection.severity, + firstPatchedVersionMessage, + detection.message, + detection.detectionRuleId, + lockFileNote, + ) + holder.newAnnotation(severity, title) + .range(textRange) + .tooltip(tooltip) + .withFix( + CycodeIgnoreIntentionQuickFix( + CliScanType.Sca, + CycodeIgnoreType.PATH, + detection.detectionDetails.getFilepath() + ) + ) + .withFix( + CycodeIgnoreIntentionQuickFix( + CliScanType.Sca, + CycodeIgnoreType.RULE, + detection.detectionRuleId + ) + ) + .create() + } + } +} diff --git a/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/SecretApplier.kt b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/SecretApplier.kt new file mode 100644 index 0000000..7bf6c03 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/SecretApplier.kt @@ -0,0 +1,103 @@ +package com.cycode.plugin.annotators.annotationAppliers + +import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.annotators.convertSeverity +import com.cycode.plugin.annotators.validateTextRange +import com.cycode.plugin.cli.CliResult +import com.cycode.plugin.cli.CliScanType +import com.cycode.plugin.intentions.CycodeIgnoreIntentionQuickFix +import com.cycode.plugin.intentions.CycodeIgnoreType +import com.cycode.plugin.services.ScanResultsService +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiFile + +class SecretApplier(private val scanResults: ScanResultsService) : AnnotationApplierBase() { + private fun validateSecretTextRange(textRange: TextRange, psiFile: PsiFile): Boolean { + val detectedSubstr = psiFile.text.substring(textRange.startOffset, textRange.endOffset) + val detectedSegment = scanResults.getDetectedSegment(CliScanType.Secret, textRange) + if (detectedSegment == null) { + scanResults.saveDetectedSegment(CliScanType.Secret, textRange, detectedSubstr) + } else if (detectedSegment != detectedSubstr) { + // case: the code has been added or deleted before the detection + thisLogger().debug( + "[Secret] Text range of detection has been shifted. " + + "Annotation is not relevant to this state of the file content anymore" + ) + return false + } + + return true + } + + override fun apply(psiFile: PsiFile, holder: AnnotationHolder) { + val latestScanResult = scanResults.getSecretResults() + if (latestScanResult !is CliResult.Success) { + return + } + + val relevantDetections = latestScanResult.result.detections.filter { detection -> + detection.detectionDetails.getFilepath() == psiFile.virtualFile.path + } + + relevantDetections.forEach { detection -> + val severity = convertSeverity(detection.severity) + + val detectionDetails = detection.detectionDetails + val textRange = TextRange( + detectionDetails.startPosition, + detectionDetails.startPosition + detectionDetails.length + ) + + if (!validateTextRange(textRange, psiFile) || !validateSecretTextRange(textRange, psiFile)) { + return@forEach + } + + val detectedValue = psiFile.text.substring(textRange.startOffset, textRange.endOffset) + detectionDetails.detectedValue = detectedValue + + val message = detection.getFormattedMessage() + val title = CycodeBundle.message("annotationTitle", detection.getFormattedTitle()) + + var companyGuidelineMessage = "" + if (detectionDetails.customRemediationGuidelines != null) { + companyGuidelineMessage = CycodeBundle.message( + "secretsAnnotationTooltipCompanyGuideline", + detectionDetails.customRemediationGuidelines + ) + } + + val tooltip = CycodeBundle.message( + "secretsAnnotationTooltip", + detection.severity, + detection.type, + message, + detection.detectionRuleId, + detectionDetails.fileName, + detectionDetails.sha512, + companyGuidelineMessage + ) + holder.newAnnotation(severity, title) + .range(textRange) + .tooltip(tooltip) + .withFix( + CycodeIgnoreIntentionQuickFix( + CliScanType.Secret, + CycodeIgnoreType.PATH, + detection.detectionDetails.getFilepath() + ) + ) + .withFix( + CycodeIgnoreIntentionQuickFix( + CliScanType.Secret, + CycodeIgnoreType.RULE, + detection.detectionRuleId + ) + ) + .withFix(CycodeIgnoreIntentionQuickFix(CliScanType.Secret, CycodeIgnoreType.VALUE, detectedValue)) + .create() + + } + } +} diff --git a/src/main/kotlin/com/cycode/plugin/annotators/utils.kt b/src/main/kotlin/com/cycode/plugin/annotators/utils.kt new file mode 100644 index 0000000..5f4faa5 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/annotators/utils.kt @@ -0,0 +1,21 @@ +package com.cycode.plugin.annotators + +import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiFile + +fun convertSeverity(severity: String): HighlightSeverity { + return when (severity.toLowerCase()) { + "critical" -> HighlightSeverity.ERROR + "high" -> HighlightSeverity.ERROR + "medium" -> HighlightSeverity.WARNING + "low" -> HighlightSeverity.WEAK_WARNING + else -> HighlightSeverity.INFORMATION + } +} + +fun validateTextRange(textRange: TextRange, psiFile: PsiFile): Boolean { + // check if text range fits in file + // case: row with detections has been deleted, but detection is still in the local results DB + return !(textRange.endOffset > psiFile.text.length || textRange.startOffset < 0) +} diff --git a/src/main/kotlin/com/cycode/plugin/cli/ScaHelpers.kt b/src/main/kotlin/com/cycode/plugin/cli/ScaHelpers.kt index 924465b..6ac5b19 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/ScaHelpers.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/ScaHelpers.kt @@ -58,6 +58,28 @@ private val SCA_CONFIGURATION_SCAN_LOCK_FILE_TO_PACKAGE_FILE: Map = SCA_CONFIGURATION_SCAN_LOCK_FILE_TO_PACKAGE_FILE.keys.toList() +// keep in lowercase. +// source: https://github.com/cycodehq/cycode-cli/blob/ec8333707ab2590518fd0f36454c8636ccbf1061/cycode/cli/consts.py#L16 +private val INFRA_CONFIGURATION_SCAN_SUPPORTED_FILE_SUFFIXES: List = listOf( + ".tf", + ".tf.json", + ".json", + ".yaml", + ".yml", + "dockerfile", +) + +fun isSupportedIacFile(filename: String): Boolean { + val lowercaseFilename = filename.toLowerCase() + INFRA_CONFIGURATION_SCAN_SUPPORTED_FILE_SUFFIXES.forEach { + if (lowercaseFilename.endsWith(it)) { + return true + } + } + + return false +} + fun isSupportedPackageFile(filename: String): Boolean { val lowercaseFilename = filename.toLowerCase() SCA_CONFIGURATION_SCAN_SUPPORTED_FILES.forEach { diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt new file mode 100644 index 0000000..da9437d --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt @@ -0,0 +1,25 @@ +package com.cycode.plugin.cli.models.scanResult.iac + +import com.cycode.plugin.CycodeBundle +import com.cycode.plugin.cli.models.scanResult.DetectionBase + +data class IacDetection( + val message: String, + override val detectionDetails: IacDetectionDetails, + override val severity: String, + val type: String, + val detectionRuleId: String, // UUID + val detectionTypeId: String, // UUID +) : DetectionBase { + fun getFormattedMessage(): String { + return message + } + + fun getFormattedTitle(): String { + return CycodeBundle.message("iacTitle", type, getFormattedMessage()) + } + + override fun getFormattedNodeTitle(): String { + return CycodeBundle.message("iacNodeTitle", detectionDetails.lineInFile, message) + } +} diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt new file mode 100644 index 0000000..1618e15 --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt @@ -0,0 +1,18 @@ +package com.cycode.plugin.cli.models.scanResult.iac + +import com.cycode.plugin.cli.models.scanResult.ScanDetectionDetailsBase + +data class IacDetectionDetails( + val info: String, + val failureType: String, + val lineInFile: Int, + val startPosition: Int, + val endPosition: Int, + val filePath: String, + val fileName: String, + val customRemediationGuidelines: String?, +) : ScanDetectionDetailsBase { + override fun getFilepath(): String { + return fileName + } +} diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt new file mode 100644 index 0000000..ace88af --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt @@ -0,0 +1,9 @@ +package com.cycode.plugin.cli.models.scanResult.iac + +import com.cycode.plugin.cli.models.CliError +import com.cycode.plugin.cli.models.scanResult.ScanResultBase + +data class IacScanResult( + override val detections: List, + val errors: List, +) : ScanResultBase diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt index 564fbd0..73a4ec7 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/cycodeActionToolBar/actions/RunAllAction.kt @@ -22,8 +22,10 @@ class RunAllAction : // we can provide "stop" action only after that val project = e.project ?: return val service = cycode(project) + service.startSecretScanForCurrentProject() service.startScaScanForCurrentProject() + service.startIacScanForCurrentProject() } override fun update(e: AnActionEvent) { diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt index ab0624b..d6a5539 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/scanContentTab/ScanContentTab.kt @@ -37,6 +37,11 @@ class ScanContentTab : Component() { addActionListener { service.startScaScanForCurrentProject() } }, ) + addComponentToPanel( + JButton(CycodeBundle.message("scanTabIacBtn")).apply { + addActionListener { service.startIacScanForCurrentProject() } + }, + ) addComponentToPanel(createClickableLabel(CycodeBundle.message("scanTabOnSaveTip"))) addComponentToPanel(createClickableLabel(CycodeBundle.message("howToUseLabel"))) diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt index 6c396d2..b92ebfb 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt @@ -5,6 +5,7 @@ import com.cycode.plugin.cli.CliResult import com.cycode.plugin.cli.CliScanType import com.cycode.plugin.cli.models.scanResult.DetectionBase import com.cycode.plugin.cli.models.scanResult.ScanResultBase +import com.cycode.plugin.cli.models.scanResult.iac.IacDetection import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection import com.cycode.plugin.cli.models.scanResult.secret.SecretDetection import com.cycode.plugin.components.toolWindow.components.scaViolationCardContentTab.ScaViolationCardContentTab @@ -33,8 +34,6 @@ import javax.swing.tree.DefaultMutableTreeNode import javax.swing.tree.TreePath import javax.swing.tree.TreeSelectionModel -const val DIFFERENCE_BETWEEN_SCA_LINE_NUMBERS = 1 - class TreeView( val project: Project, defaultRightPane: JComponent? = null ) : JPanel(GridLayout(1, 0)), TreeSelectionListener { @@ -84,14 +83,21 @@ class TreeView( val node = tree.getLastSelectedPathComponent() as DefaultMutableTreeNode - if (node.userObject is SecretDetectionNode) { - openSecretDetectionInFile(project, node.userObject as SecretDetectionNode) - displaySecretViolationCard(node.userObject as SecretDetectionNode) - } + when (node.userObject) { + is SecretDetectionNode -> { + openSecretDetectionInFile(project, node.userObject as SecretDetectionNode) + displaySecretViolationCard(node.userObject as SecretDetectionNode) + } - if (node.userObject is ScaDetectionNode) { - openScaDetectionInFile(project, node.userObject as ScaDetectionNode) - displayScaViolationCard(node.userObject as ScaDetectionNode) + is ScaDetectionNode -> { + openScaDetectionInFile(project, node.userObject as ScaDetectionNode) + displayScaViolationCard(node.userObject as ScaDetectionNode) + } + + is IacDetectionNode -> { + openIacDetectionInFile(project, node.userObject as IacDetectionNode) + displayIacViolationCard(node.userObject as IacDetectionNode) + } } } @@ -118,6 +124,12 @@ class TreeView( replaceRightPanel(ScanContentTab().getContent(service)) } + fun displayIacViolationCard(node: IacDetectionNode) { + // we don't have a dedicated card yet for IaC violations, + // so we are returning to the main content tab + replaceRightPanel(ScanContentTab().getContent(service)) + } + fun displayScaViolationCard(node: ScaDetectionNode) { replaceRightPanel(ScaViolationCardContentTab().getContent(node.detection)) } @@ -203,6 +215,25 @@ class TreeView( createDetectionNodes(CliScanType.Sca, scaDetections.result, ::createScaDetectionNode) } + private fun createIacDetectionNodes() { + val iacDetections = scanResults.getIacResults() + if (iacDetections !is CliResult.Success) { + return + } + + fun createIacDetectionNode(detection: DetectionBase): DefaultMutableTreeNode { + return createNode( + IacDetectionNode( + detection.getFormattedNodeTitle(), + PluginIcons.getSeverityIcon(detection.severity), + detection as IacDetection + ) + ) + } + + createDetectionNodes(CliScanType.Iac, iacDetections.result, ::createIacDetectionNode) + } + fun replaceRightPanel(newRightPanel: JComponent): TreeView { splitPane.secondComponent = newRightPanel return this @@ -219,6 +250,7 @@ class TreeView( rootNodes.createNodes(top) createSecretDetectionNodes() createScaDetectionNodes() + createIacDetectionNodes() } fun getTree() = tree diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt index 566f60f..68a37af 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/components/detectionNodeContextMenu/DetectionNodeContextMenu.kt @@ -2,9 +2,11 @@ package com.cycode.plugin.components.toolWindow.components.treeView.components.d import com.cycode.plugin.CycodeBundle import com.cycode.plugin.components.toolWindow.components.treeView.TreeView +import com.cycode.plugin.components.toolWindow.components.treeView.nodes.IacDetectionNode import com.cycode.plugin.components.toolWindow.components.treeView.nodes.ScaDetectionNode import com.cycode.plugin.components.toolWindow.components.treeView.nodes.ScanTypeNode import com.cycode.plugin.components.toolWindow.components.treeView.nodes.SecretDetectionNode +import com.cycode.plugin.components.toolWindow.components.treeView.openIacDetectionInFile import com.cycode.plugin.components.toolWindow.components.treeView.openScaDetectionInFile import com.cycode.plugin.components.toolWindow.components.treeView.openSecretDetectionInFile import com.cycode.plugin.services.cycode @@ -49,6 +51,7 @@ class DetectionNodeContextMenu( is ScanTypeNode -> listOf(RUN_OPTION) is SecretDetectionNode -> listOf(OPEN_IN_EDITOR_OPTION, RESCAN_OPTION) is ScaDetectionNode -> listOf(OPEN_VIOLATION_CARD_OPTION, OPEN_IN_EDITOR_OPTION, RESCAN_OPTION) + is IacDetectionNode -> listOf(OPEN_IN_EDITOR_OPTION, RESCAN_OPTION) else -> listOf() } } @@ -72,6 +75,7 @@ class DetectionNodeContextMenu( when (node.name) { CycodeBundle.message("secretDisplayName") -> service.startSecretScanForCurrentProject() CycodeBundle.message("scaDisplayName") -> service.startScaScanForCurrentProject() + CycodeBundle.message("iacDisplayName") -> service.startIacScanForCurrentProject() } } @@ -86,6 +90,11 @@ class DetectionNodeContextMenu( node.detection.detectionDetails.getFilepath(), onDemand = true ) + + is IacDetectionNode -> service.startPathIacScan( + node.detection.detectionDetails.getFilepath(), + onDemand = true + ) } } @@ -93,16 +102,23 @@ class DetectionNodeContextMenu( when (val node = getUnknownNode()) { is SecretDetectionNode -> openSecretDetectionInFile(project, node) is ScaDetectionNode -> openScaDetectionInFile(project, node) + is IacDetectionNode -> openIacDetectionInFile(project, node) } } private fun onOpenViolationCardOptionClicked() { - val node = getUnknownNode() - if (node is ScaDetectionNode) { - treeView.displayScaViolationCard(node) - } - if (node is SecretDetectionNode) { - treeView.displaySecretViolationCard(node) + when (val node = getUnknownNode()) { + is ScaDetectionNode -> { + treeView.displayScaViolationCard(node) + } + + is SecretDetectionNode -> { + treeView.displaySecretViolationCard(node) + } + + is IacDetectionNode -> { + treeView.displayIacViolationCard(node) + } } } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/DetectionNodes.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/DetectionNodes.kt index 61d5707..a0d9c17 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/DetectionNodes.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/DetectionNodes.kt @@ -1,5 +1,6 @@ package com.cycode.plugin.components.toolWindow.components.treeView.nodes +import com.cycode.plugin.cli.models.scanResult.iac.IacDetection import com.cycode.plugin.cli.models.scanResult.sca.ScaDetection import com.cycode.plugin.cli.models.scanResult.secret.SecretDetection import javax.swing.Icon @@ -17,3 +18,10 @@ data class ScaDetectionNode( val detection: ScaDetection, override var summary: String? = null, ) : AbstractNode() + +data class IacDetectionNode( + override var name: String, + override var icon: Icon?, + val detection: IacDetection, + override var summary: String? = null, +) : AbstractNode() diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt index 1004acd..090c316 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt @@ -1,5 +1,6 @@ package com.cycode.plugin.components.toolWindow.components.treeView +import com.cycode.plugin.components.toolWindow.components.treeView.nodes.IacDetectionNode import com.cycode.plugin.components.toolWindow.components.treeView.nodes.ScaDetectionNode import com.cycode.plugin.components.toolWindow.components.treeView.nodes.SecretDetectionNode import com.intellij.openapi.fileEditor.FileEditorManager @@ -8,6 +9,8 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.LocalFileSystem import java.io.File +const val DIFFERENCE_BETWEEN_SCA_LINE_NUMBERS = 1 +const val DIFFERENCE_BETWEEN_IAC_LINE_NUMBERS = 1 private fun openFileInEditor(project: Project, filePath: String, lineNumber: Int) { val file = File(filePath) @@ -30,3 +33,9 @@ fun openScaDetectionInFile(project: Project, node: ScaDetectionNode) { val line = node.detection.detectionDetails.lineInFile - DIFFERENCE_BETWEEN_SCA_LINE_NUMBERS openFileInEditor(project, filePath, line) } + +fun openIacDetectionInFile(project: Project, node: IacDetectionNode) { + val filePath = node.detection.detectionDetails.getFilepath() + val line = node.detection.detectionDetails.lineInFile - DIFFERENCE_BETWEEN_IAC_LINE_NUMBERS + openFileInEditor(project, filePath, line) +} diff --git a/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt b/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt index fa6e118..c3dcf19 100644 --- a/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt +++ b/src/main/kotlin/com/cycode/plugin/listeners/FileSaveListener.kt @@ -1,6 +1,7 @@ package com.cycode.plugin.listeners import com.cycode.plugin.Consts +import com.cycode.plugin.cli.isSupportedIacFile import com.cycode.plugin.cli.isSupportedPackageFile import com.cycode.plugin.services.cycode import com.cycode.plugin.services.pluginSettings @@ -43,6 +44,11 @@ class FileSaveListener(private val project: Project) : FileDocumentManagerListen if (scaPathsToScan.isNotEmpty()) { service.startPathScaScan(scaPathsToScan) } + + val iacPathsToScan = excludeNonIacRelatedPaths(pathsToScan) + if (iacPathsToScan.isNotEmpty()) { + service.startPathIacScan(iacPathsToScan) + } } private fun excludeNotExistingPaths(paths: List): List { @@ -53,6 +59,10 @@ class FileSaveListener(private val project: Project) : FileDocumentManagerListen return paths.filter { isSupportedPackageFile(it) } } + private fun excludeNonIacRelatedPaths(paths: List): List { + return paths.filter { isSupportedIacFile(it) } + } + private fun scheduleScanPathsFlush() { AppExecutorUtil.getAppScheduledExecutorService().scheduleWithFixedDelay( { diff --git a/src/main/kotlin/com/cycode/plugin/services/CliService.kt b/src/main/kotlin/com/cycode/plugin/services/CliService.kt index f1ea810..460ad99 100644 --- a/src/main/kotlin/com/cycode/plugin/services/CliService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/CliService.kt @@ -5,6 +5,7 @@ import com.cycode.plugin.cli.* import com.cycode.plugin.cli.models.AuthCheckResult import com.cycode.plugin.cli.models.AuthResult import com.cycode.plugin.cli.models.VersionResult +import com.cycode.plugin.cli.models.scanResult.iac.IacScanResult 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 @@ -232,4 +233,23 @@ class CliService(private val project: Project) { DaemonCodeAnalyzer.getInstance(project).restart() updateToolWindowState(project) } + + fun scanPathsIac(paths: List, onDemand: Boolean = true, cancelledCallback: TaskCancelledCallback = null) { + val results = scanPaths(paths, CliScanType.Iac, cancelledCallback) + if (results == null) { + thisLogger().warn("Failed to scan paths: $paths") + return + } + + var detectionsCount = 0 + if (results is CliResult.Success) { + detectionsCount = results.result.detections.count() + } + + showScanFileResultNotification(CliScanType.Iac, detectionsCount, onDemand) + + scanResults.setIacResults(results) + DaemonCodeAnalyzer.getInstance(project).restart() + updateToolWindowState(project) + } } diff --git a/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt b/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt index 9c16b56..5959554 100755 --- a/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt @@ -97,6 +97,28 @@ class CycodeService(val project: Project) : Disposable { }.queue() } + fun startPathIacScan(path: String, onDemand: Boolean = false) { + startPathIacScan(listOf(path), onDemand = onDemand) + } + + fun startPathIacScan(pathsToScan: List, onDemand: Boolean = false) { + object : Task.Backgroundable(project, CycodeBundle.message("iacScanning"), true) { + override fun run(indicator: ProgressIndicator) { + if (!pluginState.cliAuthed) { + return + } + + thisLogger().debug("[IAC] Start scanning paths: $pathsToScan") + cliService.scanPathsIac( + pathsToScan, + onDemand = onDemand, + cancelledCallback = { indicator.isCanceled } + ) + thisLogger().debug("[IAC] Finish scanning paths: $pathsToScan") + } + }.queue() + } + fun applyIgnoreFromFileAnnotation(optionScanType: String, optionName: String, optionValue: String) { object : Task.Backgroundable(project, CycodeBundle.message("ignoresApplying"), true) { override fun run(indicator: ProgressIndicator) { @@ -142,6 +164,16 @@ class CycodeService(val project: Project) : Disposable { startPathScaScan(projectRoot, onDemand = true) } + fun startIacScanForCurrentProject() { + val projectRoot = cliService.getProjectRootDirectory() + if (projectRoot == null) { + CycodeNotifier.notifyInfo(project, CycodeBundle.message("noProjectRootErrorNotification")) + return + } + + startPathIacScan(projectRoot, onDemand = true) + } + override fun dispose() { CycodeToolWindowFactory.TabManager.removeTab(project) } diff --git a/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt b/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt index 415dba9..d5bbc7a 100755 --- a/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt @@ -2,6 +2,7 @@ package com.cycode.plugin.services import com.cycode.plugin.cli.CliResult import com.cycode.plugin.cli.CliScanType +import com.cycode.plugin.cli.models.scanResult.iac.IacScanResult import com.cycode.plugin.cli.models.scanResult.sca.ScaScanResult import com.cycode.plugin.cli.models.scanResult.secret.SecretScanResult import com.cycode.plugin.services.scanResultsFilters.ScaScanResultsFilter @@ -13,8 +14,10 @@ import com.intellij.openapi.util.TextRange @Service(Service.Level.PROJECT) class ScanResultsService { private val detectedSegments = mutableMapOf, String>() + private var secretResults: CliResult? = null private var scaResults: CliResult? = null + private var iacResults: CliResult? = null init { thisLogger().info("CycodeResultsService init") @@ -38,14 +41,24 @@ class ScanResultsService { return scaResults } + fun setIacResults(result: CliResult) { + clearDetectedSegments(CliScanType.Iac) + iacResults = result + } + + fun getIacResults(): CliResult? { + return iacResults + } + fun clear() { secretResults = null scaResults = null + iacResults = null clearDetectedSegments() } fun hasResults(): Boolean { - return secretResults != null || scaResults != null + return secretResults != null || scaResults != null || iacResults != null } fun saveDetectedSegment(scanType: CliScanType, textRange: TextRange, value: String) { diff --git a/src/main/resources/messages/CycodeBundle.properties b/src/main/resources/messages/CycodeBundle.properties index f812cfb..096c053 100755 --- a/src/main/resources/messages/CycodeBundle.properties +++ b/src/main/resources/messages/CycodeBundle.properties @@ -6,6 +6,8 @@ secretsTitle={0}. {1} secretsNodeTitle=line {0}: a hardcoded {1} is used scaTitle={0}@{1} - {2} scaNodeTitle=line {0}: {1} +iacTitle={0}. {1} +iacNodeTitle=line {0}: {1} # code annotator annotationTitle=Cycode: {0} secretsAnnotationTooltip=Severity: {0}
{1}: {2}
Rule ID: {3}
In file: {4}
Secret SHA: {5}{6} @@ -13,6 +15,7 @@ secretsAnnotationTooltipCompanyGuideline=
Company Guideline: {0} scaAnnotationTooltip=Severity: {0}
{1}
{2}
Rule ID: {3}{4} scaAnnotationTooltipFirstPatchedVersion=First patched version: {0} scaAnnotationTooltipLockFileNote=

Avoid manual packages upgrades in lock files. Update the {0} file and re-generate the lock file. +iacAnnotationTooltip=Severity: {0}
{1}: {2}
Rule ID: {3}
In file: {4}
{5} # notifications notificationGroupId=Cycode notificationTitle=Cycode @@ -51,11 +54,13 @@ scanTabTitleLabel=Ready to scan. scanTabOnSaveTip=To easily scan your edited files, enable Scan on Save in settings (enabled by default). scanTabSecretsBtn=Scan for hardcoded secrets scanTabScaBtn=Scan for package vulnerabilities +scanTabIacBtn=Scan for Infrastructure As Code # progress indicators pluginLoading=Cycode is loading... authProcessing=Start auth... secretScanning=Cycode is scanning files for hardcoded secrets... scaScanning=Cycode is scanning files for package vulnerabilities... +iacScanning=Cycode is scanning files for Infrastructure As Code... ignoresApplying=Cycode is applying ignores... # settings menu settingsCliSectionTitle=Cycode CLI settings @@ -79,7 +84,7 @@ fileNodeSummary={0} vulnerabilities secretNodeSummary= scaNodeSummary= sastNodeSummary=(coming soon) -iacNodeSummary=(coming soon) +iacNodeSummary= # tree view node context menu items runOption=Run rescanOption=Rescan From 037397053c0780772e8619f13ec905b94c68dbeb Mon Sep 17 00:00:00 2001 From: Ilya Siamionau Date: Thu, 28 Mar 2024 13:53:32 +0100 Subject: [PATCH 2/4] add ignoring as quick fixes; add infra provider; add icons of files to tree view; add full paths of files in tree view; update readme and changelog; fix error handling; fix handling of IaC virtual files --- CHANGELOG.md | 2 + README.md | 3 +- .../annotationAppliers/IacApplier.kt | 38 ++++++++++-------- .../com/cycode/plugin/cli/models/CliError.kt | 4 +- .../cli/models/scanResult/ScanResultBase.kt | 3 ++ .../cli/models/scanResult/iac/IacDetection.kt | 4 +- .../scanResult/iac/IacDetectionDetails.kt | 1 + .../models/scanResult/iac/IacScanResult.kt | 2 +- .../models/scanResult/sca/ScaScanResult.kt | 2 +- .../scanResult/secret/SecretScanResult.kt | 2 +- .../components/treeView/TreeView.kt | 11 ++++-- .../components/treeView/nodes/RootNodes.kt | 4 +- .../toolWindow/components/treeView/utils.kt | 8 ++++ .../com/cycode/plugin/services/CliService.kt | 39 ++++++++++++++++++- .../cycode/plugin/services/CycodeService.kt | 15 ------- .../plugin/services/ScanResultsService.kt | 6 +++ .../IacScanResultsFilter.kt | 35 +++++++++++++++++ .../messages/CycodeBundle.properties | 5 ++- 18 files changed, 139 insertions(+), 45 deletions(-) create mode 100644 src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index d54a56b..14256e4 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ ## [1.6.0] - 2024-03-XX - Add Infrastructure as Code (IaC) support +- Add icons for file nodes in the tree view +- Add a full path of file nodes in the tree view ## [1.5.0] - 2024-03-13 diff --git a/README.md b/README.md index 9702ca6..2df4c67 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ The extension provides functionalities such as: * Scanning your code for exposed secrets, passwords, tokens, keys, and other credentials. * Scanning your code for open-source package`s vulnerabilities. +* Scanning your code for Infrastructure as Code (IaC). * Company’s Custom Remediation Guidelines - If your company has set custom remediation guidelines via the Cycode portal, you'll see a field for "Company Guidelines" that contains those guidelines. * Running a new scan from your IDE even before committing the code. * Triggering a scan automatically whenever a file is saved. @@ -22,7 +23,7 @@ The extension provides functionalities such as: * Removing a detected secret or ignoring it by secret value, rule (type) or by path. * Upgrading a detected vulnerable package or ignoring it by rule (type) or by path. -Coming soon: Code Security (SAST), and Infrastructure as Code (IaC). +Coming soon: Code Security (SAST). ## Installation diff --git a/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt index b138572..ab63c89 100644 --- a/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt +++ b/src/main/kotlin/com/cycode/plugin/annotators/annotationAppliers/IacApplier.kt @@ -5,27 +5,19 @@ import com.cycode.plugin.annotators.convertSeverity import com.cycode.plugin.annotators.validateTextRange import com.cycode.plugin.cli.CliResult import com.cycode.plugin.cli.CliScanType +import com.cycode.plugin.intentions.CycodeIgnoreIntentionQuickFix +import com.cycode.plugin.intentions.CycodeIgnoreType import com.cycode.plugin.services.ScanResultsService import com.intellij.lang.annotation.AnnotationHolder -import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiFile class IacApplier(private val scanResults: ScanResultsService) : AnnotationApplierBase() { private fun validateIacTextRange(textRange: TextRange, psiFile: PsiFile): Boolean { - val detectedSubstr = psiFile.text.substring(textRange.startOffset, textRange.endOffset) - val detectedSegment = scanResults.getDetectedSegment(CliScanType.Iac, textRange) - if (detectedSegment == null) { - scanResults.saveDetectedSegment(CliScanType.Iac, textRange, detectedSubstr) - } else if (detectedSegment != detectedSubstr) { - // case: the code has been added or deleted before the detection - thisLogger().debug( - "[IaC] Text range of detection has been shifted. " + - "Annotation is not relevant to this state of the file content anymore" - ) - return false - } - + // FIXME(MarshalX): for now, I dont see any way to validate the text range for IaC + // small explanation: + // - IaC doesn't provide end positions, so we have to calculate them from the line number (get the last character in the line) + // - we can't use the same validation as for SCA because value in the range is unknown (for SCA we expect package name) return true } @@ -60,7 +52,7 @@ class IacApplier(private val scanResults: ScanResultsService) : AnnotationApplie var companyGuidelineMessage = "" if (detectionDetails.customRemediationGuidelines != null) { companyGuidelineMessage = CycodeBundle.message( - "secretsAnnotationTooltipCompanyGuideline", + "iacAnnotationTooltipCompanyGuideline", detectionDetails.customRemediationGuidelines ) } @@ -68,8 +60,8 @@ class IacApplier(private val scanResults: ScanResultsService) : AnnotationApplie val tooltip = CycodeBundle.message( "iacAnnotationTooltip", detection.severity, - detection.type, message, + detectionDetails.infraProvider, detection.detectionRuleId, detectionDetails.fileName, companyGuidelineMessage @@ -77,6 +69,20 @@ class IacApplier(private val scanResults: ScanResultsService) : AnnotationApplie holder.newAnnotation(severity, title) .range(textRange) .tooltip(tooltip) + .withFix( + CycodeIgnoreIntentionQuickFix( + CliScanType.Iac, + CycodeIgnoreType.PATH, + detection.detectionDetails.getFilepath() + ) + ) + .withFix( + CycodeIgnoreIntentionQuickFix( + CliScanType.Iac, + CycodeIgnoreType.RULE, + detection.detectionRuleId + ) + ) .create() } } diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/CliError.kt b/src/main/kotlin/com/cycode/plugin/cli/models/CliError.kt index f2199f7..3795f35 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/CliError.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/CliError.kt @@ -1,7 +1,9 @@ package com.cycode.plugin.cli.models data class CliError( - val code: Int, + // FIXME(MarshalX): sometimes CLI uses `code` and sometimes `error` for the same thing + val code: String? = "Unknown", + val error: String? = "Unknown", val message: String, val softFail: Boolean? = false, ) diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/ScanResultBase.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/ScanResultBase.kt index 48ebe28..d6cf042 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/ScanResultBase.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/ScanResultBase.kt @@ -1,5 +1,8 @@ package com.cycode.plugin.cli.models.scanResult +import com.cycode.plugin.cli.models.CliError + interface ScanResultBase { val detections: List + val errors: List } diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt index da9437d..d793d91 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetection.kt @@ -16,10 +16,10 @@ data class IacDetection( } fun getFormattedTitle(): String { - return CycodeBundle.message("iacTitle", type, getFormattedMessage()) + return CycodeBundle.message("iacTitle", getFormattedMessage()) } override fun getFormattedNodeTitle(): String { - return CycodeBundle.message("iacNodeTitle", detectionDetails.lineInFile, message) + return CycodeBundle.message("iacNodeTitle", detectionDetails.lineInFile, getFormattedMessage()) } } diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt index 1618e15..dda2ad2 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacDetectionDetails.kt @@ -5,6 +5,7 @@ import com.cycode.plugin.cli.models.scanResult.ScanDetectionDetailsBase data class IacDetectionDetails( val info: String, val failureType: String, + val infraProvider: String, val lineInFile: Int, val startPosition: Int, val endPosition: Int, diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt index ace88af..cd2bd19 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/iac/IacScanResult.kt @@ -5,5 +5,5 @@ import com.cycode.plugin.cli.models.scanResult.ScanResultBase data class IacScanResult( override val detections: List, - val errors: List, + override val errors: List, ) : ScanResultBase diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaScanResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaScanResult.kt index 0bce73a..35e327a 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaScanResult.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/sca/ScaScanResult.kt @@ -5,5 +5,5 @@ import com.cycode.plugin.cli.models.scanResult.ScanResultBase data class ScaScanResult( override val detections: List, - val errors: List, + override val errors: List, ) : ScanResultBase diff --git a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretScanResult.kt b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretScanResult.kt index 5784eb6..fe6dd71 100644 --- a/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretScanResult.kt +++ b/src/main/kotlin/com/cycode/plugin/cli/models/scanResult/secret/SecretScanResult.kt @@ -5,5 +5,5 @@ import com.cycode.plugin.cli.models.scanResult.ScanResultBase data class SecretScanResult( override val detections: List, - val errors: List, + override val errors: List, ) : ScanResultBase diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt index b92ebfb..d9013b5 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/TreeView.kt @@ -15,7 +15,9 @@ import com.cycode.plugin.components.toolWindow.components.treeView.nodes.* import com.cycode.plugin.icons.PluginIcons import com.cycode.plugin.services.cycode import com.cycode.plugin.services.scanResults +import com.intellij.icons.AllIcons import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Iconable import com.intellij.ui.JBColor import com.intellij.ui.JBSplitter import com.intellij.ui.SideBorder @@ -25,7 +27,6 @@ import java.awt.Dimension import java.awt.GridLayout import java.awt.event.MouseAdapter import java.awt.event.MouseEvent -import java.io.File import javax.swing.JComponent import javax.swing.JPanel import javax.swing.event.TreeSelectionEvent @@ -165,10 +166,14 @@ class TreeView( rootNodes.setNodeSummary(scanType, getDetectionSummary(sortedDetections)) for ((filePath, detections) in detectionsByFile) { - val fileName = File(filePath).name val summary = CycodeBundle.message("fileNodeSummary", detections.size) - val fileNode = createNode(FileNode(fileName, summary)) + val psiFile = getPsiFile(project, filePath) + val icon = if (psiFile != null) + psiFile.getIcon(Iconable.ICON_FLAG_READ_STATUS) else + AllIcons.Actions.Annotate + + val fileNode = createNode(FileNode(filePath, summary, icon)) for (detection in detections) { fileNode.add(createNodeCallback(detection)) } diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/RootNodes.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/RootNodes.kt index 4376012..6065cb1 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/RootNodes.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/nodes/RootNodes.kt @@ -65,8 +65,10 @@ class RootNodes { // the order of adding nodes is important top.add(secretNode) top.add(scaNode) - top.add(sastNode) top.add(iacNode) + + // temporary at the bottom because of "coming soon" + top.add(sastNode) } fun getScanTypeNode(scanType: CliScanType): DefaultMutableTreeNode { diff --git a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt index 090c316..034ad72 100644 --- a/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt +++ b/src/main/kotlin/com/cycode/plugin/components/toolWindow/components/treeView/utils.kt @@ -7,11 +7,19 @@ import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.fileEditor.OpenFileDescriptor import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager import java.io.File const val DIFFERENCE_BETWEEN_SCA_LINE_NUMBERS = 1 const val DIFFERENCE_BETWEEN_IAC_LINE_NUMBERS = 1 +fun getPsiFile(project: Project, filePath: String): PsiFile? { + val virtualFile = VirtualFileManager.getInstance().findFileByUrl("file://$filePath") ?: return null + return PsiManager.getInstance(project).findFile(virtualFile) +} + private fun openFileInEditor(project: Project, filePath: String, lineNumber: Int) { val file = File(filePath) val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file) ?: return diff --git a/src/main/kotlin/com/cycode/plugin/services/CliService.kt b/src/main/kotlin/com/cycode/plugin/services/CliService.kt index 460ad99..775129d 100644 --- a/src/main/kotlin/com/cycode/plugin/services/CliService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/CliService.kt @@ -5,6 +5,8 @@ import com.cycode.plugin.cli.* import com.cycode.plugin.cli.models.AuthCheckResult import com.cycode.plugin.cli.models.AuthResult import com.cycode.plugin.cli.models.VersionResult +import com.cycode.plugin.cli.models.scanResult.ScanResultBase +import com.cycode.plugin.cli.models.scanResult.iac.IacDetection import com.cycode.plugin.cli.models.scanResult.iac.IacScanResult import com.cycode.plugin.cli.models.scanResult.sca.ScaScanResult import com.cycode.plugin.cli.models.scanResult.secret.SecretScanResult @@ -53,6 +55,7 @@ class CliService(private val project: Project) { showErrorNotification(result.result.message) return null } + if (result is CliResult.Panic) { if (result.exitCode == ExitCodes.TERMINATION) { // don't notify user about user-requested terminations @@ -63,6 +66,20 @@ class CliService(private val project: Project) { return null } + if (result is CliResult.Success && result.result is ScanResultBase) { + val errors = result.result.errors + if (errors.isEmpty()) { + return result + } + + for (error in errors) { + showErrorNotification(error.message) + } + + // we trust that it is not possible to have both errors and detections + return null + } + return result } @@ -234,8 +251,20 @@ class CliService(private val project: Project) { updateToolWindowState(project) } + private fun filterUnsupportedIacDetections(detections: List): List { + return detections.filter { detection -> + val detectionDetails = detection.detectionDetails + val filePath = detectionDetails.getFilepath() + + // TF plans are virtual files what is not exist in the file system + // "file_name": "1711298252-/Users/ilyasiamionau/projects/cycode/ilya-siamionau-payloads/tfplan.tf", + // skip such detections + return@filter filePath.startsWith("/") + } + } + fun scanPathsIac(paths: List, onDemand: Boolean = true, cancelledCallback: TaskCancelledCallback = null) { - val results = scanPaths(paths, CliScanType.Iac, cancelledCallback) + var results = scanPaths(paths, CliScanType.Iac, cancelledCallback) if (results == null) { thisLogger().warn("Failed to scan paths: $paths") return @@ -243,6 +272,14 @@ class CliService(private val project: Project) { var detectionsCount = 0 if (results is CliResult.Success) { + // filter unsupported detections + results = CliResult.Success( + IacScanResult( + filterUnsupportedIacDetections(results.result.detections), + results.result.errors + ) + ) + detectionsCount = results.result.detections.count() } diff --git a/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt b/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt index 5959554..0c2aa3b 100755 --- a/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/CycodeService.kt @@ -8,11 +8,9 @@ import com.cycode.plugin.utils.CycodeNotifier import com.intellij.openapi.Disposable import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.thisLogger -import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project -import com.intellij.psi.PsiDocumentManager @Service(Service.Level.PROJECT) @@ -131,19 +129,6 @@ class CycodeService(val project: Project) : Disposable { }.queue() } - fun startSecretScanForCurrentFile() { - val currentOpenedDocument = FileEditorManager.getInstance(project).selectedTextEditor?.document - if (currentOpenedDocument == null) { - CycodeNotifier.notifyInfo(project, CycodeBundle.message("noOpenFileErrorNotification")) - return - } - - val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(currentOpenedDocument) - val vFile = psiFile!!.originalFile.virtualFile - - startPathSecretScan(vFile.path, onDemand = true) - } - fun startSecretScanForCurrentProject() { val projectRoot = cliService.getProjectRootDirectory() if (projectRoot == null) { diff --git a/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt b/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt index d5bbc7a..25581b0 100755 --- a/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt +++ b/src/main/kotlin/com/cycode/plugin/services/ScanResultsService.kt @@ -5,6 +5,7 @@ import com.cycode.plugin.cli.CliScanType import com.cycode.plugin.cli.models.scanResult.iac.IacScanResult import com.cycode.plugin.cli.models.scanResult.sca.ScaScanResult import com.cycode.plugin.cli.models.scanResult.secret.SecretScanResult +import com.cycode.plugin.services.scanResultsFilters.IacScanResultsFilter import com.cycode.plugin.services.scanResultsFilters.ScaScanResultsFilter import com.cycode.plugin.services.scanResultsFilters.SecretScanResultsFilter import com.intellij.openapi.components.Service @@ -89,5 +90,10 @@ class ScanResultsService { filter.exclude(byValue, byPath, byRuleId) scaResults = CliResult.Success(filter.getFilteredScanResults()) } + if (iacResults is CliResult.Success) { + val filter = IacScanResultsFilter((iacResults as CliResult.Success).result) + filter.exclude(byValue, byPath, byRuleId) + iacResults = CliResult.Success(filter.getFilteredScanResults()) + } } } diff --git a/src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt b/src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt new file mode 100644 index 0000000..c3bad2b --- /dev/null +++ b/src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt @@ -0,0 +1,35 @@ +package com.cycode.plugin.services.scanResultsFilters + +import com.cycode.plugin.cli.models.scanResult.iac.IacDetection +import com.cycode.plugin.cli.models.scanResult.iac.IacScanResult + +class IacScanResultsFilter(scanResults: IacScanResult) : ScanResultsFilterBase(scanResults) { + private var filteredScanResults = scanResults + + private fun filter(predicative: (IacDetection) -> Boolean) { + filteredScanResults = IacScanResult( + detections = scanResults.detections.filter(predicative), + errors = scanResults.errors, + ) + } + + override fun excludeByValue(value: String) { + // do nothing because we don't have a value field in SCA + } + + override fun excludeByPath(path: String) { + filter { detection -> + detection.detectionDetails.getFilepath() != path + } + } + + override fun excludeByRuleId(ruleId: String) { + filter { detection -> + detection.detectionRuleId != ruleId + } + } + + override fun getFilteredScanResults(): IacScanResult { + return filteredScanResults + } +} diff --git a/src/main/resources/messages/CycodeBundle.properties b/src/main/resources/messages/CycodeBundle.properties index 096c053..c608498 100755 --- a/src/main/resources/messages/CycodeBundle.properties +++ b/src/main/resources/messages/CycodeBundle.properties @@ -6,7 +6,7 @@ secretsTitle={0}. {1} secretsNodeTitle=line {0}: a hardcoded {1} is used scaTitle={0}@{1} - {2} scaNodeTitle=line {0}: {1} -iacTitle={0}. {1} +iacTitle={0} iacNodeTitle=line {0}: {1} # code annotator annotationTitle=Cycode: {0} @@ -15,7 +15,8 @@ secretsAnnotationTooltipCompanyGuideline=
Company Guideline: {0} scaAnnotationTooltip=Severity: {0}
{1}
{2}
Rule ID: {3}{4} scaAnnotationTooltipFirstPatchedVersion=First patched version: {0} scaAnnotationTooltipLockFileNote=

Avoid manual packages upgrades in lock files. Update the {0} file and re-generate the lock file. -iacAnnotationTooltip=Severity: {0}
{1}: {2}
Rule ID: {3}
In file: {4}
{5} +iacAnnotationTooltip=Severity: {0}
Description: {1}
IaC Provider: {2}
Rule ID: {3}
In file: {4}
{5} +iacAnnotationTooltipCompanyGuideline=
Company Guideline: {0} # notifications notificationGroupId=Cycode notificationTitle=Cycode From 6464f6200a488c9fc276a94a110e46ab1d724787 Mon Sep 17 00:00:00 2001 From: Ilya Siamionau Date: Thu, 28 Mar 2024 13:56:25 +0100 Subject: [PATCH 3/4] fix typo --- .../plugin/services/scanResultsFilters/IacScanResultsFilter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt b/src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt index c3bad2b..1080fac 100644 --- a/src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt +++ b/src/main/kotlin/com/cycode/plugin/services/scanResultsFilters/IacScanResultsFilter.kt @@ -14,7 +14,7 @@ class IacScanResultsFilter(scanResults: IacScanResult) : ScanResultsFilterBase Date: Thu, 28 Mar 2024 15:42:29 +0100 Subject: [PATCH 4/4] bump CLI version to bring severities for IaC --- src/main/kotlin/com/cycode/plugin/Consts.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/cycode/plugin/Consts.kt b/src/main/kotlin/com/cycode/plugin/Consts.kt index 9b6faaa..b7702b1 100644 --- a/src/main/kotlin/com/cycode/plugin/Consts.kt +++ b/src/main/kotlin/com/cycode/plugin/Consts.kt @@ -21,7 +21,7 @@ class Consts { companion object { val PLUGIN_PATH = PathManager.getPluginsPath() + "/cycode-intellij-platform-plugin" val DEFAULT_CLI_PATH = getDefaultCliPath() - const val REQUIRED_CLI_VERSION = "1.9.1" + const val REQUIRED_CLI_VERSION = "1.9.2" const val CLI_GITHUB_ORG = "cycodehq" const val CLI_GITHUB_REPO = "cycode-cli"