@@ -10,9 +10,13 @@ import com.google.gson.Gson
10
10
import com.intellij.ide.BrowserUtil
11
11
import com.intellij.ide.util.RunOnceUtil
12
12
import com.intellij.openapi.application.runInEdt
13
+ import com.intellij.openapi.application.runReadAction
14
+ import com.intellij.openapi.fileEditor.FileDocumentManager
13
15
import com.intellij.openapi.fileEditor.FileEditorManager
14
16
import com.intellij.openapi.options.ShowSettingsUtil
15
17
import com.intellij.openapi.project.Project
18
+ import com.intellij.openapi.vfs.LocalFileSystem
19
+ import com.intellij.openapi.vfs.VirtualFile
16
20
import com.intellij.ui.jcef.JBCefJSQuery.Response
17
21
import kotlinx.coroutines.CancellationException
18
22
import kotlinx.coroutines.CompletableDeferred
@@ -71,6 +75,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_
71
75
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_BAR_ACTIONS
72
76
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_CHANGE
73
77
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_REMOVE
78
+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CODE_REVIEW_FINDINGS_SUFFIX
79
+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.DISPLAY_FINDINGS_SUFFIX
74
80
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedChatParams
75
81
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedQuickActionChatParams
76
82
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GET_SERIALIZED_CHAT_REQUEST_METHOD
@@ -107,10 +113,17 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.auth.isCodeTestA
107
113
import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
108
114
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
109
115
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
116
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanIssue
117
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanManager
118
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description
119
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation
120
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix
110
121
import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
122
+ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
111
123
import software.aws.toolkits.jetbrains.settings.MeetQSettings
112
124
import software.aws.toolkits.telemetry.MetricResult
113
125
import software.aws.toolkits.telemetry.Telemetry
126
+ import java.nio.file.Path
114
127
import java.util.concurrent.CompletableFuture
115
128
import java.util.concurrent.CompletionException
116
129
import java.util.function.Function
@@ -569,6 +582,32 @@ class BrowserConnector(
569
582
}
570
583
}
571
584
585
+ data class FlareCodeScanIssue (
586
+ val startLine : Int ,
587
+ val endLine : Int ,
588
+ val comment : String? ,
589
+ val title : String ,
590
+ val description : Description ,
591
+ val detectorId : String ,
592
+ val detectorName : String ,
593
+ val findingId : String ,
594
+ val ruleId : String? ,
595
+ val relatedVulnerabilities : List <String >,
596
+ val severity : String ,
597
+ val recommendation : Recommendation ,
598
+ val suggestedFixes : List <SuggestedFix >,
599
+ val scanJobId : String ,
600
+ val language : String ,
601
+ val autoDetected : Boolean ,
602
+ val filePath : String ,
603
+ val findingContext : String ,
604
+ )
605
+
606
+ data class AggregatedCodeScanIssue (
607
+ val filePath : String ,
608
+ val issues : List <FlareCodeScanIssue >,
609
+ )
610
+
572
611
private fun showResult (
573
612
result : CompletableFuture <String >,
574
613
partialResultToken : String ,
@@ -582,10 +621,14 @@ class BrowserConnector(
582
621
throw error
583
622
}
584
623
chatCommunicationManager.removePartialChatMessage(partialResultToken)
624
+ val decryptedMessage = Gson ().fromJson(value?.let { encryptionManager?.decrypt(it) }.orEmpty(), Map ::class .java)
625
+ as Map <String , * >
626
+ parseFindingsMessages(decryptedMessage)
627
+
585
628
val messageToChat = ChatCommunicationManager .convertToJsonToSendToChat(
586
629
SEND_CHAT_COMMAND_PROMPT ,
587
630
tabId,
588
- value?. let { encryptionManager?.decrypt(it) }.orEmpty( ),
631
+ Gson ().toJson(decryptedMessage ),
589
632
isPartialResult = false
590
633
)
591
634
browser.postChat(messageToChat)
@@ -599,6 +642,85 @@ class BrowserConnector(
599
642
}
600
643
}
601
644
645
+ fun parseFindingsMessages (messagesMap : Map <String , * >) {
646
+ try {
647
+ val additionalMessages = messagesMap[" additionalMessages" ] as ? MutableList <Map <String , Any >>
648
+ val findingsMessages = additionalMessages?.filter { message ->
649
+ if (message.contains(" messageId" )) {
650
+ (message[" messageId" ] as String ).endsWith(CODE_REVIEW_FINDINGS_SUFFIX ) ||
651
+ (message[" messageId" ] as String ).endsWith(DISPLAY_FINDINGS_SUFFIX )
652
+ } else {
653
+ false
654
+ }
655
+ }
656
+ val scannedFiles = mutableListOf<VirtualFile >()
657
+ if (findingsMessages != null ) {
658
+ for (findingsMessage in findingsMessages) {
659
+ additionalMessages.remove(findingsMessage)
660
+ val gson = Gson ()
661
+ val jsonFindings = gson.fromJson(findingsMessage[" body" ] as String , List ::class .java)
662
+ val mappedFindings = mutableListOf<CodeWhispererCodeScanIssue >()
663
+ for (aggregatedIssueUnformatted in jsonFindings) {
664
+ val aggregatedIssue = gson.fromJson(gson.toJson(aggregatedIssueUnformatted), AggregatedCodeScanIssue ::class .java)
665
+ val file = LocalFileSystem .getInstance().findFileByIoFile(
666
+ Path .of(aggregatedIssue.filePath).toFile()
667
+ )
668
+ if (file?.isDirectory == false ) {
669
+ scannedFiles.add(file)
670
+ runReadAction {
671
+ FileDocumentManager .getInstance().getDocument(file)
672
+ }?.let { document ->
673
+ for (issue in aggregatedIssue.issues) {
674
+ val endLineInDocument = minOf(maxOf(0 , issue.endLine - 1 ), document.lineCount - 1 )
675
+ val endCol = document.getLineEndOffset(endLineInDocument) - document.getLineStartOffset(endLineInDocument) + 1
676
+ val isIssueIgnored = CodeWhispererCodeScanManager .getInstance(project)
677
+ .isIgnoredIssue(issue.title, document, file, issue.startLine - 1 )
678
+ if (isIssueIgnored) {
679
+ continue
680
+ }
681
+ mappedFindings.add(
682
+ CodeWhispererCodeScanIssue (
683
+ startLine = issue.startLine,
684
+ startCol = 1 ,
685
+ endLine = issue.endLine,
686
+ endCol = endCol,
687
+ file = file,
688
+ project = project,
689
+ title = issue.title,
690
+ description = issue.description,
691
+ detectorId = issue.detectorId,
692
+ detectorName = issue.detectorName,
693
+ findingId = issue.findingId,
694
+ ruleId = issue.ruleId,
695
+ relatedVulnerabilities = issue.relatedVulnerabilities,
696
+ severity = issue.severity,
697
+ recommendation = issue.recommendation,
698
+ suggestedFixes = issue.suggestedFixes,
699
+ codeSnippet = emptyList(),
700
+ isVisible = true ,
701
+ autoDetected = issue.autoDetected,
702
+ scanJobId = issue.scanJobId,
703
+ ),
704
+ )
705
+ }
706
+ }
707
+ }
708
+ }
709
+
710
+ CodeWhispererCodeScanManager .getInstance(project)
711
+ .addOnDemandIssues(
712
+ mappedFindings,
713
+ scannedFiles,
714
+ CodeWhispererConstants .CodeAnalysisScope .AGENTIC
715
+ )
716
+ CodeWhispererCodeScanManager .getInstance(project).showCodeScanUI()
717
+ }
718
+ }
719
+ } catch (e: Exception ) {
720
+ LOG .error { " Failed to parse findings message $e " }
721
+ }
722
+ }
723
+
602
724
private suspend fun updateQuickActionsInBrowser (browser : Browser ) {
603
725
val isFeatureDevAvailable = isFeatureDevAvailable(project)
604
726
val isCodeTransformAvailable = isCodeTransformAvailable(project)
0 commit comments