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
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "Feature",
"description": "/review: Support SAS findings"
}
6 changes: 3 additions & 3 deletions packages/amazonq/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -387,12 +387,12 @@
},
{
"command": "aws.amazonq.openSecurityIssuePanel",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix)",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
"group": "inline@4"
},
{
"command": "aws.amazonq.security.ignore",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix)",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
"group": "inline@5"
},
{
Expand All @@ -402,7 +402,7 @@
},
{
"submenu": "aws.amazonq.submenu.securityIssueMoreActions",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix)",
"when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)",
"group": "inline@7"
}
],
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/codewhisperer/commands/basicCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,10 @@ export const generateFix = Commands.declare(
if (!targetIssue) {
return
}
if (targetIssue.ruleId === CodeWhispererConstants.sasRuleId) {
getLogger().warn('GenerateFix is not available for SAS findings.')
return
}
await telemetry.codewhisperer_codeScanIssueGenerateFix.run(async () => {
try {
await vscode.commands
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/codewhisperer/models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,13 @@ export const securityScanLanguageIds = [
'sh',
'shell',
'shellscript',
'brazilPackageConfig',
] as const

export type SecurityScanLanguageId = (typeof securityScanLanguageIds)[number]

export const sasRuleId = 'sbom-software-assurance-services'

// wait time for editor to update editor.selection.active (in milliseconds)
export const vsCodeCursorUpdateDelay = 10

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import globals from '../../shared/extensionGlobals'
import { getLogger } from '../../shared/logger/logger'
import { SecurityIssueProvider } from './securityIssueProvider'
import { sasRuleId } from '../models/constants'

export type SecurityViewTreeItem = FileItem | IssueItem | SeverityItem
type CodeScanIssueWithFilePath = CodeScanIssue & { filePath: string }
Expand Down Expand Up @@ -118,6 +119,7 @@ enum ContextValue {
FILE = 'file',
ISSUE_WITH_FIX = 'issueWithFix',
ISSUE_WITHOUT_FIX = 'issueWithoutFix',
ISSUE_WITH_FIX_DISABLED = 'issueWithFixDisabled',
SEVERITY = 'severity',
}

Expand Down Expand Up @@ -195,9 +197,11 @@ export class IssueItem extends vscode.TreeItem {
}

private getContextValue() {
return this.issue.suggestedFixes.length === 0 || !this.issue.suggestedFixes[0].code
? ContextValue.ISSUE_WITHOUT_FIX
: ContextValue.ISSUE_WITH_FIX
return this.issue.ruleId === sasRuleId
? ContextValue.ISSUE_WITH_FIX_DISABLED
: this.issue.suggestedFixes.length === 0 || !this.issue.suggestedFixes[0].code
? ContextValue.ISSUE_WITHOUT_FIX
: ContextValue.ISSUE_WITH_FIX
}

private getTooltipMarkdown() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,18 @@ export async function listScanResults(
// Project path example: /Users/username/project
// Key example: project/src/main/java/com/example/App.java
for (const projectPath of projectPaths) {
// We need to remove the project path from the key to get the absolute path to the file
// Do not use .. in between because there could be multiple project paths in the same parent dir.
const filePath = path.join(projectPath, key.split('/').slice(1).join('/'))
// There could be multiple projectPaths with the same parent dir
// In that case, make sure to break out of this loop after a filePath is found
// or else it might result in duplicate findings.
const filePath = path.join(projectPath, '..', key)
if (existsSync(filePath) && statSync(filePath).isFile()) {
const document = await vscode.workspace.openTextDocument(filePath)
const aggregatedCodeScanIssue: AggregatedCodeScanIssue = {
filePath: filePath,
issues: issues.map((issue) => mapRawToCodeScanIssue(issue, document, jobId, scope)),
}
aggregatedCodeScanIssueList.push(aggregatedCodeScanIssue)
break
}
}
const maybeAbsolutePath = `/${key}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class SecurityScanLanguageContext {
sh: 'shell',
shell: 'shell',
shellscript: 'shell',
brazilPackageConfig: 'plaintext',
})
}

Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/codewhisperer/views/securityIssue/vue/root.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,19 @@
v-if="!isFixAvailable"
@click="generateFix"
class="mr-8 button-theme-primary"
:disabled="isGenerateFixLoading"
:disabled="isGenerateFixLoading || isGenerateFixDisabled"
>
Generate Fix
</button>
<button v-if="isFixAvailable" @click="applyFix" class="mr-8 button-theme-primary">Accept Fix</button>
<button v-if="isFixAvailable" @click="regenerateFix" class="mr-8 button-theme-secondary">Regenerate Fix</button>
<button
v-if="isFixAvailable"
@click="regenerateFix"
class="mr-8 button-theme-secondary"
:disabled="isGenerateFixDisabled"
>
Regenerate Fix
</button>
<button @click="explainWithQ" class="mr-8 button-theme-secondary">Explain</button>
<button @click="ignoreIssue" class="mr-8 button-theme-secondary">Ignore</button>
<button @click="ignoreAllIssues" class="mr-8 button-theme-secondary">Ignore All</button>
Expand All @@ -105,6 +112,7 @@ import markdownIt from 'markdown-it'
import hljs from 'highlight.js'
import { parsePatch } from 'diff'
import { CodeScanIssue } from '../../../models/model'
import { sasRuleId } from '../../../models/constants'

const client = WebviewClientFactory.create<SecurityIssueWebview>()
const severityImages: Record<string, string> = {
Expand Down Expand Up @@ -198,6 +206,7 @@ export default defineComponent({
fixedCode: '',
referenceText: '',
referenceSpan: [0, 0],
isGenerateFixDisabled: false,
}
},
created() {
Expand Down Expand Up @@ -278,6 +287,7 @@ export default defineComponent({
this.endLine = issue.endLine
this.isFixAvailable = false
this.isFixDescriptionAvailable = false
this.isGenerateFixDisabled = issue.ruleId === sasRuleId
if (suggestedFix) {
this.isFixAvailable = !!suggestedFix.code && suggestedFix.code?.trim() !== ''
this.suggestedFix = suggestedFix.code ?? ''
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/shared/utilities/commentUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const languageCommentConfig: Record<SecurityScanLanguageId, CommentConfig | unde
sh: { lineComment: '#', blockComment: [": '", "'"] },
shell: { lineComment: '#', blockComment: [": '", "'"] },
shellscript: { lineComment: '#', blockComment: [": '", "'"] },
brazilPackageConfig: { lineComment: '#' },
}

export function getLanguageCommentConfig(languageId: string): CommentConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import assert from 'assert'
import * as sinon from 'sinon'
import * as CodeWhispererConstants from '../../../codewhisperer/models/constants'
import { createCodeScanIssue, createMockDocument, resetCodeWhispererGlobalVariables } from '../testUtil'
import { assertTelemetry, assertTelemetryCurried, tryRegister } from '../../testUtil'
import { assertNoTelemetryMatch, assertTelemetry, assertTelemetryCurried, tryRegister } from '../../testUtil'
import {
toggleCodeSuggestions,
showSecurityScan,
Expand Down Expand Up @@ -902,6 +902,20 @@ def execute_input_compliant():
reasonDesc: 'Unexpected error',
})
})

it('exits early for SAS findings', async function () {
targetCommand = testCommand(generateFix, mockClient, mockExtContext)
codeScanIssue = createCodeScanIssue({
ruleId: CodeWhispererConstants.sasRuleId,
})
issueItem = new IssueItem(filePath, codeScanIssue)
await targetCommand.execute(codeScanIssue, filePath, 'webview')
assert.ok(updateSecurityIssueWebviewMock.notCalled)
assert.ok(startCodeFixGenerationStub.notCalled)
assert.ok(updateIssueMock.notCalled)
assert.ok(refreshTreeViewMock.notCalled)
assertNoTelemetryMatch('codewhisperer_codeScanIssueGenerateFix')
})
})

describe('rejectFix', function () {
Expand Down
Loading