Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.services.amazonq.webview

import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.treeToValue
import com.google.gson.Gson
import com.intellij.ide.BrowserUtil
Expand Down Expand Up @@ -34,6 +35,7 @@ import org.cef.browser.CefBrowser
import org.eclipse.lsp4j.TextDocumentIdentifier
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException
import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode
import org.intellij.lang.annotations.Language
import software.aws.toolkits.core.utils.error
import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.info
Expand Down Expand Up @@ -115,9 +117,6 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeature
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanIssue
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanManager
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix
import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
import software.aws.toolkits.jetbrains.settings.MeetQSettings
Expand Down Expand Up @@ -581,32 +580,6 @@ class BrowserConnector(
}
}

data class FlareCodeScanIssue(
val startLine: Int,
val endLine: Int,
val comment: String?,
val title: String,
val description: Description,
val detectorId: String,
val detectorName: String,
val findingId: String,
val ruleId: String?,
val relatedVulnerabilities: List<String>,
val severity: String,
val recommendation: Recommendation,
val suggestedFixes: List<SuggestedFix>,
val scanJobId: String,
val language: String,
val autoDetected: Boolean,
val filePath: String,
val findingContext: String,
)

data class AggregatedCodeScanIssue(
val filePath: String,
val issues: List<FlareCodeScanIssue>,
)

private fun showResult(
result: CompletableFuture<String>,
partialResultToken: String,
Expand All @@ -620,14 +593,13 @@ class BrowserConnector(
throw error
}
chatCommunicationManager.removePartialChatMessage(partialResultToken)
val decryptedMessage = Gson().fromJson(value?.let { encryptionManager?.decrypt(it) }.orEmpty(), Map::class.java)
as Map<String, *>
val decryptedMessage = value?.let { encryptionManager?.decrypt(it) }.orEmpty()
parseFindingsMessages(decryptedMessage)

val messageToChat = ChatCommunicationManager.convertToJsonToSendToChat(
SEND_CHAT_COMMAND_PROMPT,
tabId,
Gson().toJson(decryptedMessage),
decryptedMessage,
isPartialResult = false
)
browser.postChat(messageToChat)
Expand All @@ -641,26 +613,23 @@ class BrowserConnector(
}
}

fun parseFindingsMessages(messagesMap: Map<String, *>) {
fun deserializeFindings(@Language("JSON") responsePayload: String): List<FlareAggregatedFindings> {
val additionalMessages = serializer.objectMapper.readValue<FlareAdditionalMessages>(responsePayload).additionalMessages
?: return emptyList()

return additionalMessages.filter { message ->
message.messageId.endsWith(CODE_REVIEW_FINDINGS_SUFFIX) ||
message.messageId.endsWith(DISPLAY_FINDINGS_SUFFIX)
}
}

fun parseFindingsMessages(@Language("JSON") responsePayload: String) {
try {
val additionalMessages = messagesMap["additionalMessages"] as? MutableList<Map<String, Any>>
val findingsMessages = additionalMessages?.filter { message ->
if (message.contains("messageId")) {
(message["messageId"] as String).endsWith(CODE_REVIEW_FINDINGS_SUFFIX) ||
(message["messageId"] as String).endsWith(DISPLAY_FINDINGS_SUFFIX)
} else {
false
}
}
val findings = deserializeFindings(responsePayload)
val scannedFiles = mutableListOf<VirtualFile>()
if (findingsMessages != null) {
for (findingsMessage in findingsMessages) {
additionalMessages.remove(findingsMessage)
val gson = Gson()
val jsonFindings = gson.fromJson(findingsMessage["body"] as String, List::class.java)
val mappedFindings = mutableListOf<CodeWhispererCodeScanIssue>()
for (aggregatedIssueUnformatted in jsonFindings) {
val aggregatedIssue = gson.fromJson(gson.toJson(aggregatedIssueUnformatted), AggregatedCodeScanIssue::class.java)
val mappedFindings = buildList {
for (finding in findings) {
for (aggregatedIssue in finding.body) {
val file = LocalFileSystem.getInstance().findFileByIoFile(
Path.of(aggregatedIssue.filePath).toFile()
)
Expand All @@ -677,7 +646,8 @@ class BrowserConnector(
if (isIssueIgnored) {
continue
}
mappedFindings.add(

add(
CodeWhispererCodeScanIssue(
startLine = issue.startLine,
startCol = 1,
Expand Down Expand Up @@ -705,18 +675,20 @@ class BrowserConnector(
}
}
}

CodeWhispererCodeScanManager.getInstance(project)
.addOnDemandIssues(
mappedFindings,
scannedFiles,
CodeWhispererConstants.CodeAnalysisScope.AGENTIC
)
CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI()
}
}

if (mappedFindings.isNotEmpty()) {
CodeWhispererCodeScanManager.getInstance(project)
.addOnDemandIssues(
mappedFindings,
scannedFiles,
CodeWhispererConstants.CodeAnalysisScope.AGENTIC
)
CodeWhispererCodeScanManager.getInstance(project).showCodeScanUI()
}
} catch (e: Exception) {
LOG.error { "Failed to parse findings message $e" }
LOG.error(e) { "Failed to parse findings message" }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// TODO: move to software.aws.toolkits.jetbrains.services.amazonq.lsp.model
package software.aws.toolkits.jetbrains.services.amazonq.webview

import com.fasterxml.jackson.annotation.JsonSetter
import com.fasterxml.jackson.annotation.Nulls
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix

/**
* Solely used to extract the aggregated findings from the response
*/
data class FlareAdditionalMessages(
@get:JsonDeserialize(contentUsing = FlareAggregatedFindingsDeserializer::class)
@get:JsonSetter(contentNulls = Nulls.SKIP)
val additionalMessages: List<FlareAggregatedFindings>?,
)

data class FlareAggregatedFindings(
val messageId: String,
val body: List<AggregatedCodeScanIssue>,
)

data class FlareCodeScanIssue(
val startLine: Int,
val endLine: Int,
val comment: String?,
val title: String,
val description: Description,
val detectorId: String,
val detectorName: String,
val findingId: String,
val ruleId: String?,
val relatedVulnerabilities: List<String>,
val severity: String,
val recommendation: Recommendation,
val suggestedFixes: List<SuggestedFix>,
val scanJobId: String,
val language: String,
val autoDetected: Boolean,
val filePath: String,
val findingContext: String,
)

data class AggregatedCodeScanIssue(
val filePath: String,
val issues: List<FlareCodeScanIssue>,
)

class FlareAggregatedFindingsDeserializer : JsonDeserializer<FlareAggregatedFindings>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): FlareAggregatedFindings? =
// drop values that do not look like FlareAggregatedFindings
try {
ctxt.readValue(p, FlareAggregatedFindings::class.java)
} catch (_: Exception) {
null
}
}
Loading
Loading