Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
19 changes: 16 additions & 3 deletions packages/amazonq/src/lsp/chat/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,11 @@ async function handlePartialResult<T extends ChatResult>(
// This is to filter out the message containing findings from CodeReview tool to update CodeIssues panel
decryptedMessage.additionalMessages = decryptedMessage.additionalMessages?.filter(
(message) =>
!(message.messageId !== undefined && message.messageId.endsWith(CodeWhispererConstants.findingsSuffix))
!(
message.messageId !== undefined &&
(message.messageId.endsWith(CodeWhispererConstants.codeReviewFindingsSuffix) ||
message.messageId.endsWith(CodeWhispererConstants.displayFindingsSuffix))
)
)

if (decryptedMessage.body !== undefined) {
Expand Down Expand Up @@ -784,7 +788,11 @@ async function handleSecurityFindings(
}
for (let i = decryptedMessage.additionalMessages.length - 1; i >= 0; i--) {
const message = decryptedMessage.additionalMessages[i]
if (message.messageId !== undefined && message.messageId.endsWith(CodeWhispererConstants.findingsSuffix)) {
if (
message.messageId !== undefined &&
(message.messageId.endsWith(CodeWhispererConstants.codeReviewFindingsSuffix) ||
message.messageId.endsWith(CodeWhispererConstants.displayFindingsSuffix))
) {
if (message.body !== undefined) {
try {
const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body)
Expand All @@ -803,7 +811,12 @@ async function handleSecurityFindings(
issue.visible = !isIssueTitleIgnored && !isSingleIssueIgnored
}
}
initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT)
initSecurityScanRender(
aggregatedCodeScanIssues,
undefined,
CodeAnalysisScope.AGENTIC,
message.messageId.endsWith(CodeWhispererConstants.codeReviewFindingsSuffix)
)
SecurityIssueTreeViewProvider.focus()
} catch (e) {
languageClient.info('Failed to parse findings')
Expand Down
1 change: 1 addition & 0 deletions packages/amazonq/src/lsp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export async function startLanguageServer(
modelSelection: true,
workspaceFilePath: vscode.workspace.workspaceFile?.fsPath,
codeReviewInChat: codeReviewInChat,
displayFindings: true,
},
window: {
notifications: true,
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/codewhisperer/commands/startSecurityScan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ export async function startSecurityScan(
zipUtil: ZipUtil = new ZipUtil(),
scanUuid?: string
) {
if (scope === CodeAnalysisScope.AGENTIC) {
throw new CreateCodeScanFailedError('Cannot use Agentic scope')
}
const profile = AuthUtil.instance.regionProfileManager.activeRegionProfile
const logger = getLoggerForScope(scope)
/**
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/codewhisperer/models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ export enum CodeAnalysisScope {
FILE_AUTO = 'FILE_AUTO',
FILE_ON_DEMAND = 'FILE_ON_DEMAND',
PROJECT = 'PROJECT',
AGENTIC = 'AGENTIC',
}

export enum TestGenerationJobStatus {
Expand Down Expand Up @@ -907,4 +908,7 @@ export const predictionTrackerDefaultConfig = {
maxSupplementalContext: 15,
}

export const findingsSuffix = '_codeReviewFindings'
export const codeReviewFindingsSuffix = '_codeReviewFindings'
export const displayFindingsSuffix = '_displayFindings'

export const displayFindingsDetectorName = 'DisplayFindings'
23 changes: 13 additions & 10 deletions packages/core/src/codewhisperer/service/diagnosticsProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ export const securityScanRender: SecurityScanRender = {
export function initSecurityScanRender(
securityRecommendationList: AggregatedCodeScanIssue[],
editor: vscode.TextEditor | undefined,
scope: CodeAnalysisScope
scope: CodeAnalysisScope,
fromQCA: boolean = true
) {
// fromQCA parameter is used to determine if the findings are coming from QCA or from displayFindings tool.
// if the incoming findings are from QCA review, then keep only existing findings from displayFindings
// if the incoming findings are not from QCA review, then keep only the existing QCA findings
securityScanRender.securityDiagnosticCollection = createSecurityDiagnosticCollection()
securityScanRender.initialized = false
if (scope === CodeAnalysisScope.FILE_ON_DEMAND && editor) {
securityScanRender.securityDiagnosticCollection?.delete(editor.document.uri)
Expand All @@ -36,22 +41,20 @@ export function initSecurityScanRender(
}
for (const securityRecommendation of securityRecommendationList) {
updateSecurityDiagnosticCollection(securityRecommendation)
updateSecurityIssuesForProviders(securityRecommendation, scope === CodeAnalysisScope.FILE_AUTO)
updateSecurityIssuesForProviders(securityRecommendation, scope === CodeAnalysisScope.FILE_AUTO, fromQCA)
}
securityScanRender.initialized = true
}

function updateSecurityIssuesForProviders(securityRecommendation: AggregatedCodeScanIssue, isAutoScope?: boolean) {
function updateSecurityIssuesForProviders(
securityRecommendation: AggregatedCodeScanIssue,
isAutoScope?: boolean,
fromQCA: boolean = true
) {
if (isAutoScope) {
SecurityIssueProvider.instance.mergeIssues(securityRecommendation)
} else {
const updatedSecurityRecommendationList = [
...SecurityIssueProvider.instance.issues.filter(
(group) => group.filePath !== securityRecommendation.filePath
),
securityRecommendation,
]
SecurityIssueProvider.instance.issues = updatedSecurityRecommendationList
SecurityIssueProvider.instance.mergeIssuesDisplayFindings(securityRecommendation, fromQCA)
}
SecurityIssueTreeViewProvider.instance.refresh()
}
Expand Down
25 changes: 25 additions & 0 deletions packages/core/src/codewhisperer/service/securityIssueProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import * as vscode from 'vscode'
import { AggregatedCodeScanIssue, CodeScanIssue, SuggestedFix } from '../models/model'
import { randomUUID } from '../../shared/crypto'
import { displayFindingsDetectorName } from '../models/constants'

export class SecurityIssueProvider {
static #instance: SecurityIssueProvider
Expand Down Expand Up @@ -161,6 +162,30 @@ export class SecurityIssueProvider {
)
}

public mergeIssuesDisplayFindings(newIssues: AggregatedCodeScanIssue, fromQCA: boolean) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add tests for this?

const existingGroup = this._issues.find((group) => group.filePath === newIssues.filePath)
if (!existingGroup) {
this._issues.push(newIssues)
return
}

this._issues = this._issues.map((group) =>
group.filePath !== newIssues.filePath
? group
: {
...group,
issues: [
...group.issues.filter(
// if the incoming findings are from QCA review, then keep only existing findings from displayFindings
// if the incoming findings are not from QCA review, then keep only the existing QCA findings
(issue) => fromQCA === (issue.detectorName === displayFindingsDetectorName)
),
...newIssues.issues,
],
}
)
}

private isExistingIssue(issue: CodeScanIssue, filePath: string) {
return this._issues
.find((group) => group.filePath === filePath)
Expand Down
Loading