@@ -10,9 +10,13 @@ import com.google.gson.Gson
1010import com.intellij.ide.BrowserUtil
1111import com.intellij.ide.util.RunOnceUtil
1212import com.intellij.openapi.application.runInEdt
13+ import com.intellij.openapi.application.runReadAction
14+ import com.intellij.openapi.fileEditor.FileDocumentManager
1315import com.intellij.openapi.fileEditor.FileEditorManager
1416import com.intellij.openapi.options.ShowSettingsUtil
1517import com.intellij.openapi.project.Project
18+ import com.intellij.openapi.vfs.LocalFileSystem
19+ import com.intellij.openapi.vfs.VirtualFile
1620import com.intellij.ui.jcef.JBCefJSQuery.Response
1721import kotlinx.coroutines.CancellationException
1822import kotlinx.coroutines.CompletableDeferred
@@ -71,6 +75,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_
7175import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_BAR_ACTIONS
7276import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_CHANGE
7377import 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
7480import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedChatParams
7581import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedQuickActionChatParams
7682import 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
107113import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
108114import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
109115import 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
110121import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
122+ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
111123import software.aws.toolkits.jetbrains.settings.MeetQSettings
112124import software.aws.toolkits.telemetry.MetricResult
113125import software.aws.toolkits.telemetry.Telemetry
126+ import java.nio.file.Path
114127import java.util.concurrent.CompletableFuture
115128import java.util.concurrent.CompletionException
116129import java.util.function.Function
@@ -569,6 +582,32 @@ class BrowserConnector(
569582 }
570583 }
571584
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+
572611 private fun showResult (
573612 result : CompletableFuture <String >,
574613 partialResultToken : String ,
@@ -582,10 +621,14 @@ class BrowserConnector(
582621 throw error
583622 }
584623 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+
585628 val messageToChat = ChatCommunicationManager .convertToJsonToSendToChat(
586629 SEND_CHAT_COMMAND_PROMPT ,
587630 tabId,
588- value?. let { encryptionManager?.decrypt(it) }.orEmpty( ),
631+ Gson ().toJson(decryptedMessage ),
589632 isPartialResult = false
590633 )
591634 browser.postChat(messageToChat)
@@ -599,6 +642,85 @@ class BrowserConnector(
599642 }
600643 }
601644
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+
602724 private suspend fun updateQuickActionsInBrowser (browser : Browser ) {
603725 val isFeatureDevAvailable = isFeatureDevAvailable(project)
604726 val isCodeTransformAvailable = isCodeTransformAvailable(project)
0 commit comments