Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0d4c188
feat(amazonq): parse message from agentic reviewer
blakelaz-amazon Jun 19, 2025
983a87d
feat(amazonq): support array of AggregatedCodeScanIssue objects from …
blakelaz-amazon Jun 20, 2025
51f72b0
fix(amazonq): change findings suffix to make it more precise
blakelaz-amazon Jun 20, 2025
694a48e
Merge PR #7535: Amazon Q security findings improvements
singhAws Jun 20, 2025
9e58c93
feat(amazonq): store the findings locally and include a path to them …
blakelaz-amazon Jun 21, 2025
3ece0a5
Merge branch 'aws:master' into code-review-tool
Rathore25 Jun 21, 2025
1f4cfc4
fix: update q code review finding message suffix
singhAws Jun 22, 2025
2fc9f36
fix(amazonq): change the findings suffix, adjust the stored findings …
blakelaz-amazon Jun 24, 2025
00a8a71
Merge branch 'aws:master' into code-review-tool
Rathore25 Jun 24, 2025
e6d2f06
feat(amazonq): improve explain with q option for code issue
blakelaz-amazon Jun 25, 2025
1e018a3
Merge pull request #1 from BlakeLazarine/master
Rathore25 Jun 25, 2025
865b0e3
feat(amazonq): update findings bar
blakelaz-amazon Jun 25, 2025
4c92e7f
Merge pull request #2 from BlakeLazarine/master
Rathore25 Jun 25, 2025
5b7df16
fix: remove unused code
singhAws Jun 25, 2025
fae3d78
Merge branch 'aws:master' into code-review-tool
Rathore25 Jun 25, 2025
b3a722c
Merge branch 'aws:master' into code-review-tool
Rathore25 Jun 26, 2025
98b1265
fix(amazonq): commenting out unncalled functions, unused imports
blakelaz-amazon Jun 27, 2025
c986e49
Merge branch 'aws:master' into code-review-tool
Rathore25 Jun 27, 2025
4d82ab5
Merge latest changes from origin/master into code-review-tool
singhAws Jul 1, 2025
794098a
Merge branch 'aws:master' into code-review-tool
Rathore25 Jul 2, 2025
daa3ffe
Updated explain code issue prompt
singhAws Jul 2, 2025
acaaf30
Merge branch 'aws:master' into code-review-tool
Rathore25 Jul 3, 2025
56a664d
Merge branch 'aws:master' into code-review-tool
Rathore25 Jul 3, 2025
3ed1c1a
Merge branch 'master' into code-review-tool
BlakeLazarine Jul 3, 2025
6a6e9b1
Merge pull request #1 from Rathore25/code-review-tool
BlakeLazarine Jul 3, 2025
784ae63
feat(amazonq): separate the Explain and Generate Fix commands
blakelaz-amazon Jul 3, 2025
11bcfc9
fix(amazonq): change 'Generate Fix' to 'Fix' and remove form code is…
blakelaz-amazon Jul 8, 2025
a479a42
fix(amazonq): improve line numbering in Explain and Fix commands
blakelaz-amazon Jul 8, 2025
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
37 changes: 29 additions & 8 deletions packages/amazonq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -442,17 +442,22 @@
},
{
"command": "aws.amazonq.openSecurityIssuePanel",
"when": "false && view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
"group": "inline@4"
},
{
"command": "aws.amazonq.security.explain",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
"group": "inline@4"
},
{
"command": "aws.amazonq.security.ignore",
"command": "aws.amazonq.security.generateFix",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
"group": "inline@5"
},
{
"command": "aws.amazonq.security.generateFix",
"when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithoutFix",
"command": "aws.amazonq.security.ignore",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
"group": "inline@6"
},
{
Expand Down Expand Up @@ -535,16 +540,17 @@
"aws.amazonq.submenu.securityIssueMoreActions": [
{
"command": "aws.amazonq.security.explain",
"when": "false",
"group": "1_more@1"
},
{
"command": "aws.amazonq.applySecurityFix",
"when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
"when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
"group": "1_more@3"
},
{
"command": "aws.amazonq.security.regenerateFix",
"when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
"when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix",
"group": "1_more@4"
},
{
Expand Down Expand Up @@ -778,6 +784,7 @@
{
"command": "aws.amazonq.security.explain",
"title": "%AWS.command.amazonq.explainIssue%",
"icon": "$(search)",
"enablement": "view == aws.amazonq.SecurityIssuesTree"
},
{
Expand Down Expand Up @@ -1325,26 +1332,40 @@
"fontCharacter": "\\f1de"
}
},
"aws-schemas-registry": {
"aws-sagemaker-code-editor": {
"description": "AWS Contributed Icon",
"default": {
"fontPath": "./resources/fonts/aws-toolkit-icons.woff",
"fontCharacter": "\\f1df"
}
},
"aws-schemas-schema": {
"aws-sagemaker-jupyter-lab": {
"description": "AWS Contributed Icon",
"default": {
"fontPath": "./resources/fonts/aws-toolkit-icons.woff",
"fontCharacter": "\\f1e0"
}
},
"aws-stepfunctions-preview": {
"aws-schemas-registry": {
"description": "AWS Contributed Icon",
"default": {
"fontPath": "./resources/fonts/aws-toolkit-icons.woff",
"fontCharacter": "\\f1e1"
}
},
"aws-schemas-schema": {
"description": "AWS Contributed Icon",
"default": {
"fontPath": "./resources/fonts/aws-toolkit-icons.woff",
"fontCharacter": "\\f1e2"
}
},
"aws-stepfunctions-preview": {
"description": "AWS Contributed Icon",
"default": {
"fontPath": "./resources/fonts/aws-toolkit-icons.woff",
"fontCharacter": "\\f1e3"
}
}
},
"walkthroughs": [
Expand Down
78 changes: 56 additions & 22 deletions packages/amazonq/src/lsp/chat/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import { Commands, globals } from 'aws-core-vscode/shared'
import { window } from 'vscode'
import { AmazonQChatViewProvider } from './webviewProvider'
import { CodeScanIssue } from 'aws-core-vscode/codewhisperer'
import { EditorContextExtractor } from 'aws-core-vscode/codewhispererChat'
import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq'
import * as vscode from 'vscode'
import * as path from 'path'

/**
* TODO: Re-enable these once we can figure out which path they're going to live in
Expand All @@ -27,38 +28,71 @@ export function registerCommands(provider: AmazonQChatViewProvider) {
type: 'chatMessage',
})
}),
Commands.register('aws.amazonq.explainIssue', async (issue: CodeScanIssue) => {
Commands.register('aws.amazonq.explainIssue', async (issue: CodeScanIssue, filePath: string) => {
void focusAmazonQPanel().then(async () => {
const editorContextExtractor = new EditorContextExtractor()
const extractedContext = await editorContextExtractor.extractContextForTrigger('ContextMenu')
const selectedCode =
extractedContext?.activeFileContext?.fileText
?.split('\n')
.slice(issue.startLine, issue.endLine)
.join('\n') ?? ''
if (issue && filePath) {
const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0)
await vscode.workspace.openTextDocument(filePath).then((doc) => {
void vscode.window.showTextDocument(doc, {
selection: range,
viewColumn: vscode.ViewColumn.One,
preview: true,
})
})
}

// The message that gets sent to the UI
const uiMessage = [
'Explain the ',
issue.title,
' issue in the following code:',
'\n```\n',
selectedCode,
'\n```',
].join('')
const lineRange =
issue.startLine === issue.endLine - 1
? `[${issue.startLine + 1}]`
: `[${issue.startLine + 1}, ${issue.endLine}]`
const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_`

// The message that gets sent to the backend
const contextMessage = `Explain the issue "${issue.title}" (${JSON.stringify(
issue
)}) and generate code demonstrating the fix`
const contextMessage = `Provide a small description of the issue. Do not attempt to fix the issue, only explain it. Code issue - ${JSON.stringify(issue)}`

void provider.webview?.postMessage({
command: 'sendToPrompt',
params: {
selection: '',
triggerType: 'contextMenu',
prompt: {
prompt: uiMessage, // what gets sent to the user
prompt: visibleMessageInChat, // what gets sent to the user
escapedPrompt: contextMessage, // what gets sent to the backend
},
autoSubmit: true,
},
})
})
}),
Commands.register('aws.amazonq.generateFix', async (issue: CodeScanIssue, filePath: string) => {
void focusAmazonQPanel().then(async () => {
if (issue && filePath) {
const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0)
await vscode.workspace.openTextDocument(filePath).then((doc) => {
void vscode.window.showTextDocument(doc, {
selection: range,
viewColumn: vscode.ViewColumn.One,
preview: true,
})
})
}

const lineRange =
issue.startLine === issue.endLine - 1
? `[${issue.startLine + 1}]`
: `[${issue.startLine + 1}, ${issue.endLine}]`
const visibleMessageInChat = `_Fix **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_`

// The message that gets sent to the backend
const contextMessage = `Generate a fix for the following code issue. Do not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed. Code issue - ${JSON.stringify(issue)}`

void provider.webview?.postMessage({
command: 'sendToPrompt',
params: {
selection: '',
triggerType: 'contextMenu',
prompt: {
prompt: visibleMessageInChat, // what gets sent to the user
escapedPrompt: contextMessage, // what gets sent to the backend
},
autoSubmit: true,
Expand Down
62 changes: 57 additions & 5 deletions packages/amazonq/src/lsp/chat/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,16 @@ import { v4 as uuidv4 } from 'uuid'
import * as vscode from 'vscode'
import { Disposable, LanguageClient, Position, TextDocumentIdentifier } from 'vscode-languageclient'
import { AmazonQChatViewProvider } from './webviewProvider'
import { AuthUtil, ReferenceLogViewProvider } from 'aws-core-vscode/codewhisperer'
import {
AggregatedCodeScanIssue,
AuthUtil,
CodeAnalysisScope,
CodeWhispererSettings,
initSecurityScanRender,
ReferenceLogViewProvider,
SecurityIssueTreeViewProvider,
CodeWhispererConstants,
} from 'aws-core-vscode/codewhisperer'
import { amazonQDiffScheme, AmazonQPromptSettings, messages, openUrl, isTextEditor } from 'aws-core-vscode/shared'
import {
DefaultAmazonQAppInitContext,
Expand All @@ -79,6 +88,7 @@ import { isValidResponseError } from './error'
import { decryptResponse, encryptRequest } from '../encryption'
import { getCursorState } from '../utils'
import { focusAmazonQPanel } from './commands'
import { ChatMessage } from '@aws/language-server-runtimes/server-interface'

export function registerActiveEditorChangeListener(languageClient: LanguageClient) {
let debounceTimer: NodeJS.Timeout | undefined
Expand Down Expand Up @@ -293,7 +303,8 @@ export function registerMessageListeners(
encryptionKey,
provider,
chatParams.tabId,
chatDisposable
chatDisposable,
languageClient
)
} catch (e) {
const errorMsg = `Error occurred during chat request: ${e}`
Expand All @@ -309,7 +320,8 @@ export function registerMessageListeners(
encryptionKey,
provider,
chatParams.tabId,
chatDisposable
chatDisposable,
languageClient
)
} finally {
chatStreamTokens.delete(chatParams.tabId)
Expand Down Expand Up @@ -340,7 +352,8 @@ export function registerMessageListeners(
encryptionKey,
provider,
message.params.tabId,
quickActionDisposable
quickActionDisposable,
languageClient
)
break
}
Expand Down Expand Up @@ -574,6 +587,11 @@ async function handlePartialResult<T extends ChatResult>(
) {
const decryptedMessage = await decryptResponse<T>(partialResult, encryptionKey)

decryptedMessage.additionalMessages = decryptedMessage.additionalMessages?.filter(
(message) =>
!(message.messageId !== undefined && message.messageId.endsWith(CodeWhispererConstants.findingsSuffix))
)

if (decryptedMessage.body !== undefined) {
void provider.webview?.postMessage({
command: chatRequestType.method,
Expand All @@ -594,10 +612,13 @@ async function handleCompleteResult<T extends ChatResult>(
encryptionKey: Buffer | undefined,
provider: AmazonQChatViewProvider,
tabId: string,
disposable: Disposable
disposable: Disposable,
languageClient: LanguageClient
) {
const decryptedMessage = await decryptResponse<T>(result, encryptionKey)

handleSecurityFindings(decryptedMessage, languageClient)

void provider.webview?.postMessage({
command: chatRequestType.method,
params: decryptedMessage,
Expand All @@ -611,6 +632,37 @@ async function handleCompleteResult<T extends ChatResult>(
disposable.dispose()
}

function handleSecurityFindings(
decryptedMessage: { additionalMessages?: ChatMessage[] },
languageClient: LanguageClient
): void {
if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) {
return
}
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.body !== undefined) {
try {
const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body)
for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) {
for (const issue of aggregatedCodeScanIssue.issues) {
issue.visible = !CodeWhispererSettings.instance
.getIgnoredSecurityIssues()
.includes(issue.title)
}
}
initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT)
SecurityIssueTreeViewProvider.focus()
} catch (e) {
languageClient.info('Failed to parse findings')
}
}
decryptedMessage.additionalMessages.splice(i, 1)
}
}
}

async function resolveChatResponse(
requestMethod: string,
params: any,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
"AWS.command.amazonq.generateUnitTests": "Generate Tests",
"AWS.command.amazonq.security.scan": "Run Project Review",
"AWS.command.amazonq.security.fileScan": "Run File Review",
"AWS.command.amazonq.generateFix": "Generate Fix",
"AWS.command.amazonq.generateFix": "Fix",
"AWS.command.amazonq.viewDetails": "View Details",
"AWS.command.amazonq.explainIssue": "Explain",
"AWS.command.amazonq.ignoreIssue": "Ignore Issue",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/codewhisperer/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ export async function activate(context: ExtContext): Promise<void> {
CodeWhispererConstants.amazonqIgnoreNextLine
)
)
// SecurityIssueProvider.instance.cleanOldFiles()
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: remove this comment

}),
vscode.window.createTreeView(SecurityIssueTreeViewProvider.viewType, {
treeDataProvider: SecurityIssueTreeViewProvider.instance,
Expand Down
Loading