Skip to content

Commit b563a1c

Browse files
authored
codewhisperer: fix bugs for proactive scans aws#4768
Problem Fix remaining bugs in `feature/cw-proactive-scan` branch Solution - Add changelogs - Update telemetry package and pass scope field to `codewhisperer_securityScan` - Don't run file scans if auth is not connected - Don't run file scans if monthly quota is exceeded
1 parent 7a479f1 commit b563a1c

File tree

9 files changed

+62
-18
lines changed

9 files changed

+62
-18
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "CodeWhisperer: Security scans can now run on all files in the project"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type": "Feature",
3+
"description": "CodeWhisperer: Security scans can now run automatically when file changes are made"
4+
}

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4245,7 +4245,7 @@
42454245
},
42464246
"devDependencies": {
42474247
"@aws-sdk/types": "^3.13.1",
4248-
"@aws-toolkits/telemetry": "^1.0.201",
4248+
"@aws-toolkits/telemetry": "^1.0.203",
42494249
"@aws/fully-qualified-names": "^2.1.4",
42504250
"@cspotcode/source-map-support": "^0.8.1",
42514251
"@sinonjs/fake-timers": "^10.0.2",

packages/core/src/codewhisperer/activation.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,6 @@ export async function activate(context: ExtContext): Promise<void> {
9696
*/
9797
const client = new codewhispererClient.DefaultCodeWhispererClient()
9898

99-
/**
100-
* CodeWhisperer auto scans
101-
*/
102-
setSubscriptionsForAutoScans()
103-
10499
// Service initialization
105100
const container = Container.instance
106101
ReferenceInlineProvider.instance
@@ -286,11 +281,18 @@ export async function activate(context: ExtContext): Promise<void> {
286281
await CodeScansState.instance.setScansEnabled(false)
287282
}
288283

284+
/**
285+
* CodeWhisperer auto scans
286+
*/
287+
setSubscriptionsForAutoScans()
288+
289289
function setSubscriptionsForAutoScans() {
290290
// Initial scan when the editor opens for the first time
291291
const editor = vscode.window.activeTextEditor
292292
if (
293293
CodeScansState.instance.isScansEnabled() &&
294+
!CodeScansState.instance.isMonthlyQuotaExceeded() &&
295+
auth.isConnected() &&
294296
!auth.isBuilderIdInUse() &&
295297
editor &&
296298
securityScanLanguageContext.isLanguageSupported(editor.document.languageId) &&
@@ -310,6 +312,8 @@ export async function activate(context: ExtContext): Promise<void> {
310312
vscode.window.onDidChangeActiveTextEditor(editor => {
311313
if (
312314
CodeScansState.instance.isScansEnabled() &&
315+
!CodeScansState.instance.isMonthlyQuotaExceeded() &&
316+
auth.isConnected() &&
313317
!auth.isBuilderIdInUse() &&
314318
editor &&
315319
securityScanLanguageContext.isLanguageSupported(editor.document.languageId)
@@ -339,6 +343,8 @@ export async function activate(context: ExtContext): Promise<void> {
339343
const editor = vscode.window.activeTextEditor
340344
if (
341345
CodeScansState.instance.isScansEnabled() &&
346+
!CodeScansState.instance.isMonthlyQuotaExceeded() &&
347+
auth.isConnected() &&
342348
!auth.isBuilderIdInUse() &&
343349
editor &&
344350
event.document === editor.document &&
@@ -361,6 +367,8 @@ export async function activate(context: ExtContext): Promise<void> {
361367
const editor = vscode.window.activeTextEditor
362368
if (
363369
isScansEnabled &&
370+
!CodeScansState.instance.isMonthlyQuotaExceeded() &&
371+
auth.isConnected() &&
364372
!auth.isBuilderIdInUse() &&
365373
editor &&
366374
securityScanLanguageContext.isLanguageSupported(editor.document.languageId) &&

packages/core/src/codewhisperer/commands/startSecurityScan.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
throwIfCancelled,
2121
} from '../service/securityScanHandler'
2222
import { runtimeLanguageContext } from '../util/runtimeLanguageContext'
23-
import { AggregatedCodeScanIssue, codeScanState, CodeScanTelemetryEntry } from '../models/model'
23+
import { AggregatedCodeScanIssue, CodeScansState, codeScanState, CodeScanTelemetryEntry } from '../models/model'
2424
import { cancel, ok } from '../../shared/localizedText'
2525
import { getFileExt } from '../util/commonUtil'
2626
import { getDirSize } from '../../shared/filesystemUtilities'
@@ -33,6 +33,7 @@ import { ZipMetadata, ZipUtil } from '../util/zipUtil'
3333
import { debounce } from 'lodash'
3434
import { once } from '../../shared/utilities/functionUtils'
3535
import { randomUUID } from '../../common/crypto'
36+
import { CodeAnalysisScope } from '../models/constants'
3637

3738
const localize = nls.loadMessageBundle()
3839
export const stopScanButton = localize('aws.codewhisperer.stopscan', 'Stop Scan')
@@ -109,6 +110,7 @@ export async function startSecurityScan(
109110
codewhispererCodeScanTotalIssues: 0,
110111
codewhispererCodeScanIssuesWithFixes: 0,
111112
credentialStartUrl: AuthUtil.instance.startUrl,
113+
codewhispererCodeScanScope: scope,
112114
}
113115
try {
114116
getLogger().verbose(`Starting security scan `)
@@ -219,14 +221,20 @@ export async function startSecurityScan(
219221
codeScanTelemetryEntry.result = 'Failed'
220222
}
221223

222-
if (isAwsError(error)) {
224+
if (isAwsError(error) && error.code === 'ThrottlingException') {
223225
if (
224-
error.code === 'ThrottlingException' &&
225-
error.message.includes(CodeWhispererConstants.throttlingMessage)
226+
scope === CodeAnalysisScope.PROJECT &&
227+
error.message.includes(CodeWhispererConstants.projectScansThrottlingMessage)
226228
) {
227-
void vscode.window.showErrorMessage(CodeWhispererConstants.freeTierLimitReachedCodeScan)
229+
void vscode.window.showErrorMessage(CodeWhispererConstants.projectScansLimitReached)
228230
// TODO: Should we set a graphical state?
229231
// We shouldn't set vsCodeState.isFreeTierLimitReached here because it will hide CW and Q chat options.
232+
} else if (
233+
scope === CodeAnalysisScope.FILE &&
234+
error.message.includes(CodeWhispererConstants.fileScansThrottlingMessage)
235+
) {
236+
void vscode.window.showErrorMessage(CodeWhispererConstants.fileScansLimitReached)
237+
CodeScansState.instance.setMonthlyQuotaExceeded()
230238
}
231239
}
232240
codeScanTelemetryEntry.reason = (error as Error).message
@@ -298,7 +306,6 @@ function populateCodeScanLogStream(scannedFiles: Set<string>) {
298306
const uri = vscode.Uri.file(file)
299307
codeScanLogger.info(`File scanned: ${uri.fsPath}`)
300308
}
301-
codeScanOutpuChan.show()
302309
}
303310

304311
export async function confirmStopSecurityScan() {

packages/core/src/codewhisperer/models/constants.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,18 @@ export const freeTierLimitReached = 'You have reached the monthly fair use limit
290290

291291
export const freeTierLimitReachedCodeScan = 'You have reached the monthly quota of code scans.'
292292

293+
export const fileScansLimitReached = 'You have reached the monthly quota of automatic file scans.'
294+
295+
export const projectScansLimitReached = 'You have reached the monthly quota of full project scans.'
296+
293297
export const throttlingLearnMore = `Learn More`
294298

295299
export const throttlingMessage = `Maximum recommendation count reached for this month`
296300

301+
export const fileScansThrottlingMessage = `Maximum automatic file scan count reached for this month`
302+
303+
export const projectScansThrottlingMessage = `Maximum full project scan count reached for this month`
304+
297305
export const connectionChangeMessage = `Keep using CodeWhisperer with `
298306

299307
// TODO: align this text with service side

packages/core/src/codewhisperer/models/model.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as vscode from 'vscode'
66
import { ToolkitError } from '../../shared/errors'
77
import { getIcon } from '../../shared/icons'
88
import {
9+
CodewhispererCodeScanScope,
910
CodewhispererCompletionType,
1011
CodewhispererLanguage,
1112
CodewhispererTriggerType,
@@ -119,6 +120,8 @@ export class CodeScansState {
119120
/** Set a callback for when state of code scans changes */
120121
onDidChangeState = this.#onDidChangeState.event
121122

123+
private exceedsMonthlyQuota = false
124+
122125
static #instance: CodeScansState
123126
static get instance() {
124127
return (this.#instance ??= new this())
@@ -147,6 +150,14 @@ export class CodeScansState {
147150
const isEnabled = get(autoScansEnabledKey, this.#context)
148151
return isEnabled !== undefined ? isEnabled : this.#fallback
149152
}
153+
154+
setMonthlyQuotaExceeded() {
155+
this.exceedsMonthlyQuota = true
156+
}
157+
158+
isMonthlyQuotaExceeded() {
159+
return this.exceedsMonthlyQuota
160+
}
150161
}
151162

152163
export interface AcceptedSuggestionEntry {
@@ -626,6 +637,7 @@ export interface CodeScanTelemetryEntry {
626637
codewhispererCodeScanTotalIssues: number
627638
codewhispererCodeScanIssuesWithFixes: number
628639
credentialStartUrl: string | undefined
640+
codewhispererCodeScanScope: CodewhispererCodeScanScope
629641
}
630642

631643
export interface RecommendationDescription {

packages/core/src/codewhisperer/ui/codeWhispererNodes.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
toggleCodeScans,
2323
} from '../commands/basicCommands'
2424
import { CodeWhispererCommandDeclarations } from '../commands/gettingStartedPageCommands'
25-
import { codeScanState } from '../models/model'
25+
import { CodeScansState, codeScanState } from '../models/model'
2626
import { getNewCustomizationsAvailable, getSelectedCustomization } from '../util/customizationUtil'
2727
import { cwQuickPickSource, cwTreeNodeSource } from '../commands/types'
2828
import { AuthUtil } from '../util/authUtil'
@@ -48,11 +48,12 @@ export function createAutoScans(pause: boolean): DataQuickPickItem<'autoScans'>
4848
const iconResume = getIcon('vscode-debug-alt')
4949
const labelPause = localize('AWS.codewhisperer.pauseCodeWhispererNode.label', 'Pause Automatic File Scanning')
5050
const iconPause = getIcon('vscode-debug-pause')
51+
const monthlyQuotaExceeded = CodeScansState.instance.isMonthlyQuotaExceeded()
5152

5253
return {
5354
data: 'autoScans',
5455
label: pause ? codicon`${iconPause} ${labelPause}` : codicon`${iconResume} ${labelResume}`,
55-
description: pause ? 'RUNNING' : 'PAUSED',
56+
description: monthlyQuotaExceeded ? 'Monthly quota exceeded' : pause ? 'RUNNING' : 'PAUSED',
5657
onClick: () => toggleCodeScans.execute(placeholder, cwQuickPickSource),
5758
} as DataQuickPickItem<'autoScans'>
5859
}

0 commit comments

Comments
 (0)