@@ -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
@@ -72,6 +76,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_
7276import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_BAR_ACTIONS
7377import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_CHANGE
7478import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_REMOVE
79+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CODE_REVIEW_FINDINGS_SUFFIX
80+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.DISPLAY_FINDINGS_SUFFIX
7581import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedChatParams
7682import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedQuickActionChatParams
7783import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GET_SERIALIZED_CHAT_REQUEST_METHOD
@@ -108,13 +114,23 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.auth.isCodeTestA
108114import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
109115import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
110116import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
117+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeLine
118+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanIssue
119+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanManager
120+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanSession
121+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description
122+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation
123+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix
111124import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
125+ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
112126import software.aws.toolkits.jetbrains.settings.MeetQSettings
113127import software.aws.toolkits.telemetry.MetricResult
114128import software.aws.toolkits.telemetry.Telemetry
129+ import java.nio.file.Path
115130import java.util.concurrent.CompletableFuture
116131import java.util.concurrent.CompletionException
117132import java.util.function.Function
133+ import kotlin.collections.get
118134
119135class BrowserConnector (
120136 private val serializer : MessageSerializer = MessageSerializer .getInstance(),
@@ -124,6 +140,7 @@ class BrowserConnector(
124140 val uiReady = CompletableDeferred <Boolean >()
125141 private val chatCommunicationManager = ChatCommunicationManager .getInstance(project)
126142 private val chatAsyncResultManager = ChatAsyncResultManager .getInstance(project)
143+ private val codeScanManager = CodeWhispererCodeScanManager .getInstance(project)
127144
128145 suspend fun connect (
129146 browser : Browser ,
@@ -572,6 +589,33 @@ class BrowserConnector(
572589 }
573590 }
574591
592+ data class flareCodeScanIssue (
593+ val startLine : Int ,
594+ val endLine : Int ,
595+ val comment : String? ,
596+ val title : String ,
597+ val description : Description ,
598+ val detectorId : String ,
599+ val detectorName : String ,
600+ val findingId : String ,
601+ val ruleId : String? ,
602+ val relatedVulnerabilities : List <String >,
603+ val severity : String ,
604+ val recommendation : Recommendation ,
605+ val suggestedFixes : List <SuggestedFix >,
606+ val scanJobId : String ,
607+ val language : String ,
608+ val autoDetected : Boolean ,
609+ val filePath : String ,
610+ val findingContext : String
611+
612+ )
613+
614+ data class AggregatedCodeScanIssue (
615+ val filePath : String ,
616+ val issues : List <flareCodeScanIssue>
617+ )
618+
575619 private fun showResult (
576620 result : CompletableFuture <String >,
577621 partialResultToken : String ,
@@ -585,10 +629,74 @@ class BrowserConnector(
585629 throw error
586630 }
587631 chatCommunicationManager.removePartialChatMessage(partialResultToken)
632+ val params = value?.let { encryptionManager?.decrypt(it) }.orEmpty()
633+ val jsonObject = Gson ().fromJson(params, Map ::class .java)
634+ val additionalMessages = jsonObject[" additionalMessages" ] as ? MutableList <Map <String , Any >>
635+ val findingsMessage = additionalMessages?.find {message ->
636+ (message[" messageId" ] as String ).endsWith(CODE_REVIEW_FINDINGS_SUFFIX )
637+ || (message[" messageId" ] as String ).endsWith(DISPLAY_FINDINGS_SUFFIX )}
638+ val scannedFiles = mutableListOf<VirtualFile >();
639+ if (findingsMessage != null ) {
640+ additionalMessages.remove(findingsMessage)
641+ val gson = Gson ()
642+ val jsonFindings = gson.fromJson(findingsMessage[" body" ] as String , List ::class .java)
643+ val mappedFindings = mutableListOf<CodeWhispererCodeScanIssue >()
644+ for (aggregatedIssueUnformatted in jsonFindings) {
645+ val aggregatedIssue = gson.fromJson(gson.toJson(aggregatedIssueUnformatted), AggregatedCodeScanIssue ::class .java)
646+ val file = try {
647+ LocalFileSystem .getInstance().findFileByIoFile(
648+ Path .of(aggregatedIssue.filePath).toFile()
649+ )
650+ } catch (e: Exception ) {
651+ null
652+ }
653+ if (file?.isDirectory == false ) {
654+ scannedFiles.add(file)
655+ runReadAction {
656+ FileDocumentManager .getInstance().getDocument(file)
657+ }?.let { document ->
658+ for (issue in aggregatedIssue.issues) {
659+ val endLineInDocument = minOf(maxOf(0 , issue.endLine - 1 ), document.lineCount - 1 )
660+ val endCol = document.getLineEndOffset(endLineInDocument) - document.getLineStartOffset(endLineInDocument) + 1
661+ val isIssueIgnored = codeScanManager.isIgnoredIssue(issue.title, document, file, issue.startLine - 1 )
662+ if (isIssueIgnored) {
663+ continue
664+ }
665+ mappedFindings.add(CodeWhispererCodeScanIssue (
666+ startLine = issue.startLine,
667+ startCol = 1 ,
668+ endLine = issue.endLine,
669+ endCol = endCol,
670+ file = file,
671+ project = project,
672+ title = issue.title,
673+ description = issue.description,
674+ detectorId = issue.detectorId,
675+ detectorName = issue.detectorName,
676+ findingId = issue.findingId,
677+ ruleId = issue.ruleId,
678+ relatedVulnerabilities = issue.relatedVulnerabilities,
679+ severity = issue.severity,
680+ recommendation = issue.recommendation,
681+ suggestedFixes = issue.suggestedFixes,
682+ codeSnippet = emptyList(),
683+ isVisible = true ,
684+ autoDetected = issue.autoDetected,
685+ scanJobId = issue.scanJobId
686+ ))
687+ }
688+ }
689+ }
690+ }
691+
692+ codeScanManager.addOnDemandIssues(mappedFindings, scannedFiles, CodeWhispererConstants .CodeAnalysisScope .AGENTIC )
693+ codeScanManager.showCodeScanUI()
694+ }
695+
588696 val messageToChat = ChatCommunicationManager .convertToJsonToSendToChat(
589697 SEND_CHAT_COMMAND_PROMPT ,
590698 tabId,
591- value?. let { encryptionManager?.decrypt(it) }.orEmpty( ),
699+ Gson ().toJson(jsonObject ),
592700 isPartialResult = false
593701 )
594702 browser.postChat(messageToChat)
0 commit comments