Skip to content

Commit 6422314

Browse files
Merge master into feature/stepfunctions-workflow
2 parents e5b72af + 62d3ec1 commit 6422314

File tree

6 files changed

+70
-6
lines changed

6 files changed

+70
-6
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": "/review: Code reviews are now created with additional workspace context to enable grouping of related scans in the backend"
4+
}

packages/amazonq/test/unit/codewhisperer/service/securityScanHandler.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ import {
1515
ListCodeScanFindingsResponse,
1616
pollScanJobStatus,
1717
SecurityScanTimedOutError,
18+
generateScanName,
1819
} from 'aws-core-vscode/codewhisperer'
19-
import { timeoutUtils } from 'aws-core-vscode/shared'
20+
import { getStringHash, timeoutUtils } from 'aws-core-vscode/shared'
2021
import assert from 'assert'
2122
import sinon from 'sinon'
2223
import * as vscode from 'vscode'
@@ -320,4 +321,37 @@ describe('securityScanHandler', function () {
320321
await assert.rejects(() => pollPromise, SecurityScanTimedOutError)
321322
})
322323
})
324+
325+
describe('generateScanName', function () {
326+
const clientId = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
327+
328+
it('generates scan name for FILE_AUTO scope', function () {
329+
const result = generateScanName(['/some/root/path'], CodeAnalysisScope.FILE_AUTO, '/path/to/some/file')
330+
assert.strictEqual(result, getStringHash(`${clientId}::/path/to/some/file::FILE_AUTO`))
331+
})
332+
333+
it('generates scan name for FILE_ON_DEMAND scope', function () {
334+
const result = generateScanName(['/some/root/path'], CodeAnalysisScope.FILE_ON_DEMAND, '/path/to/some/file')
335+
assert.strictEqual(result, getStringHash(`${clientId}::/path/to/some/file::FILE_ON_DEMAND`))
336+
})
337+
338+
it('generates scan name for PROJECT scope with a single project root', function () {
339+
const result = generateScanName(['/some/root/path'], CodeAnalysisScope.PROJECT)
340+
assert.strictEqual(result, getStringHash(`${clientId}::/some/root/path::PROJECT`))
341+
})
342+
343+
it('generates scan name for PROJECT scope with multiple project roots', function () {
344+
const result = generateScanName(['/some/root/pathB', '/some/root/pathA'], CodeAnalysisScope.PROJECT)
345+
assert.strictEqual(result, getStringHash(`${clientId}::/some/root/pathA,/some/root/pathB::PROJECT`))
346+
})
347+
348+
it('does not exceed 126 characters', function () {
349+
let reallyDeepFilePath = ''
350+
for (let i = 0; i < 100; i++) {
351+
reallyDeepFilePath += '/some/deep/path'
352+
}
353+
const result = generateScanName(['/some/root/path'], CodeAnalysisScope.FILE_ON_DEMAND, reallyDeepFilePath)
354+
assert.ok(result.length <= 126)
355+
})
356+
})
323357
})

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
listScanResults,
1919
throwIfCancelled,
2020
getLoggerForScope,
21+
generateScanName,
2122
} from '../service/securityScanHandler'
2223
import { runtimeLanguageContext } from '../util/runtimeLanguageContext'
2324
import {
@@ -38,7 +39,6 @@ import path from 'path'
3839
import { ZipMetadata, ZipUtil } from '../util/zipUtil'
3940
import { debounce } from 'lodash'
4041
import { once } from '../../shared/utilities/functionUtils'
41-
import { randomUUID } from '../../shared/crypto'
4242
import { CodeAnalysisScope, ProjectSizeExceededErrorMessage, SecurityScanStep } from '../models/constants'
4343
import {
4444
CodeScanJobFailedError,
@@ -185,7 +185,7 @@ export async function startSecurityScan(
185185
}
186186
let artifactMap: ArtifactMap = {}
187187
const uploadStartTime = performance.now()
188-
const scanName = randomUUID()
188+
const scanName = generateScanName(projectPaths, scope, fileName)
189189
try {
190190
artifactMap = await getPresignedUrlAndUpload(client, zipMetadata, scope, scanName)
191191
} finally {

packages/core/src/codewhisperer/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,12 @@ export { DocumentChangedSource, KeyStrokeHandler, DefaultDocumentChangedType } f
7272
export { ReferenceLogViewProvider } from './service/referenceLogViewProvider'
7373
export { LicenseUtil } from './util/licenseUtil'
7474
export { SecurityIssueProvider } from './service/securityIssueProvider'
75-
export { listScanResults, mapToAggregatedList, pollScanJobStatus } from './service/securityScanHandler'
75+
export {
76+
listScanResults,
77+
mapToAggregatedList,
78+
pollScanJobStatus,
79+
generateScanName,
80+
} from './service/securityScanHandler'
7681
export { CodeWhispererCodeCoverageTracker } from './tracker/codewhispererCodeCoverageTracker'
7782
export { TelemetryHelper } from './util/telemetryHelper'
7883
export { LineSelection, LineTracker } from './tracker/lineTracker'

packages/core/src/codewhisperer/service/securityScanHandler.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ import { runtimeLanguageContext } from '../util/runtimeLanguageContext'
4646
import { FeatureUseCase } from '../models/constants'
4747
import { UploadTestArtifactToS3Error } from '../../amazonqTest/error'
4848
import { ChatSessionManager } from '../../amazonqTest/chat/storages/chatSession'
49+
import { getStringHash } from '../../shared/utilities/textUtilities'
50+
import { getClientId } from '../../shared/telemetry/util'
51+
import globals from '../../shared/extensionGlobals'
4952

5053
export async function listScanResults(
5154
client: DefaultCodeWhispererClient,
@@ -417,3 +420,21 @@ function getPollingTimeoutMsForScope(scope: CodeWhispererConstants.CodeAnalysisS
417420
? CodeWhispererConstants.expressScanTimeoutMs
418421
: CodeWhispererConstants.standardScanTimeoutMs
419422
}
423+
424+
/**
425+
* Generates a scanName that unique identifies a user's workspace configuration for a Q code review.
426+
*
427+
* @param projectPaths List of project root paths
428+
* @param scope {@link CodeWhispererConstants.CodeAnalysisScope} Scope of files included in the code review
429+
* @param fileName File name of the file being reviewed, or pass undefined for workspace review
430+
* @returns A string hash that uniquely identifies the workspace configuration
431+
*/
432+
export function generateScanName(
433+
projectPaths: string[],
434+
scope: CodeWhispererConstants.CodeAnalysisScope,
435+
fileName?: string
436+
) {
437+
const clientId = getClientId(globals.globalState)
438+
const projectId = fileName ?? projectPaths.sort((a, b) => a.localeCompare(b)).join(',')
439+
return getStringHash(`${clientId}::${projectId}::${scope}`)
440+
}

packages/core/src/testE2E/codewhisperer/securityScan.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ import {
1818
createScanJob,
1919
pollScanJobStatus,
2020
listScanResults,
21+
generateScanName,
2122
} from '../../codewhisperer/service/securityScanHandler'
2223
import { makeTemporaryToolkitFolder } from '../../shared/filesystemUtilities'
2324
import fs from '../../shared/fs/fs'
2425
import { ZipUtil } from '../../codewhisperer/util/zipUtil'
25-
import { randomUUID } from '../../shared/crypto'
2626

2727
const filePromptWithSecurityIssues = `from flask import app
2828
@@ -95,7 +95,7 @@ describe('CodeWhisperer security scan', async function () {
9595
const projectPaths = zipUtil.getProjectPaths()
9696
const scope = CodeWhispererConstants.CodeAnalysisScope.PROJECT
9797
const zipMetadata = await zipUtil.generateZip(uri, scope)
98-
const codeScanName = randomUUID()
98+
const codeScanName = generateScanName(projectPaths, scope)
9999

100100
let artifactMap
101101
try {

0 commit comments

Comments
 (0)