From 94add880e11194a34a41752d9982474e3a7c4ae1 Mon Sep 17 00:00:00 2001 From: invictus <149003065+ashishrp-aws@users.noreply.github.com> Date: Wed, 8 May 2024 13:48:34 -0700 Subject: [PATCH] Removing Active Open File Requirement for Project Scans (#4446) * Removing ActiveFile as requirement for Project Scan. * Adding an Exception for fast fail in case of no supported language files in project scan. * Updated the unsupported files error message. --- .../CodeWhispererCodeScanException.kt | 3 + .../codescan/CodeWhispererCodeScanManager.kt | 4 +- .../sessionconfig/CodeScanSessionConfig.kt | 60 +++++++++++-------- .../resources/MessagesBundle.properties | 1 + 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanException.kt b/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanException.kt index d05e534515..398c0bd612 100644 --- a/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanException.kt +++ b/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanException.kt @@ -27,3 +27,6 @@ internal fun fileTooLarge(presentableSize: String): Nothing = internal fun uploadArtifactFailedError(): Nothing = throw CodeWhispererCodeScanException(message("codewhisperer.codescan.upload_to_s3_failed")) + +internal fun noSupportedFilesError(): Nothing = + throw CodeWhispererCodeScanException(message("codewhisperer.codescan.unsupported_language_error")) diff --git a/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt b/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt index e6df852d94..9e9e389d65 100644 --- a/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt +++ b/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/CodeWhispererCodeScanManager.kt @@ -222,9 +222,8 @@ class CodeWhispererCodeScanManager(val project: Project) { } else { FileEditorManager.getInstance(project).selectedEditor?.file } - ?: noFileOpenError() val codeScanSessionConfig = CodeScanSessionConfig.create(file, project, scope) - language = codeScanSessionConfig.getSelectedFile().programmingLanguage() + language = codeScanSessionConfig.getSelectedFile()?.programmingLanguage() ?: CodeWhispererUnknownLanguage.INSTANCE if (scope == CodeWhispererConstants.CodeAnalysisScope.FILE && !language.isAutoFileScanSupported() ) { @@ -237,6 +236,7 @@ class CodeWhispererCodeScanManager(val project: Project) { val sessionContext = CodeScanSessionContext(project, codeScanSessionConfig, scope) val session = CodeWhispererCodeScanSession(sessionContext) val codeScanResponse = session.run() + language = codeScanSessionConfig.getProgrammingLanguage() codeScanResponseContext = codeScanResponse.responseContext codeScanJobId = codeScanResponseContext.codeScanJobId when (codeScanResponse) { diff --git a/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/sessionconfig/CodeScanSessionConfig.kt b/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/sessionconfig/CodeScanSessionConfig.kt index 57cbe6504c..0fc6f5c744 100644 --- a/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/sessionconfig/CodeScanSessionConfig.kt +++ b/plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/codescan/sessionconfig/CodeScanSessionConfig.kt @@ -18,6 +18,11 @@ import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.putNextEntry import software.aws.toolkits.jetbrains.services.amazonq.FeatureDevSessionContext import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.fileTooLarge +import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.noFileOpenError +import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.noSupportedFilesError +import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage +import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererPlainText +import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererUnknownLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.CODE_SCAN_CREATE_PAYLOAD_TIMEOUT_IN_SECONDS import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.CodeAnalysisScope @@ -37,7 +42,7 @@ import java.util.Stack import kotlin.io.path.relativeTo class CodeScanSessionConfig( - private val selectedFile: VirtualFile, + private val selectedFile: VirtualFile?, private val project: Project, private val scope: CodeAnalysisScope ) { @@ -69,27 +74,32 @@ class CodeScanSessionConfig( return exceedsLimit } - fun getSelectedFile(): VirtualFile = selectedFile + private var programmingLanguage: CodeWhispererProgrammingLanguage = selectedFile?.programmingLanguage() ?: CodeWhispererUnknownLanguage.INSTANCE + + fun getProgrammingLanguage(): CodeWhispererProgrammingLanguage = programmingLanguage + + fun getSelectedFile(): VirtualFile? = selectedFile fun createPayload(): Payload { + // Fail fast if the selected file is null for File Scan + if (scope == CodeAnalysisScope.FILE && selectedFile == null) { + noFileOpenError() + } + // Fail fast if the selected file size is greater than the payload limit. - if (selectedFile.length > getPayloadLimitInBytes()) { + if (selectedFile != null && selectedFile.length > getPayloadLimitInBytes()) { fileTooLarge(getPresentablePayloadLimit()) } val start = Instant.now().toEpochMilli() - LOG.debug { "Creating payload. File selected as root for the context truncation: ${selectedFile.path}" } + LOG.debug { "Creating payload. File selected as root for the context truncation: ${projectRoot.path}" } - val payloadMetadata = when (selectedFile.path.startsWith(projectRoot.path)) { - true -> when (scope) { + val payloadMetadata = when (selectedFile) { + null -> getProjectPayloadMetadata() + else -> when (scope) { CodeAnalysisScope.PROJECT -> getProjectPayloadMetadata() - CodeAnalysisScope.FILE -> getFilePayloadMetadata() - } - false -> { - // Set project root as the parent of the selected file. - projectRoot = selectedFile.parent - getFilePayloadMetadata() + CodeAnalysisScope.FILE -> getFilePayloadMetadata(selectedFile) } } @@ -108,13 +118,13 @@ class CodeScanSessionConfig( return Payload(payloadContext, srcZip) } - fun getFilePayloadMetadata(): PayloadMetadata = + fun getFilePayloadMetadata(file: VirtualFile): PayloadMetadata = // Handle the case where the selected file is outside the project root. PayloadMetadata( - setOf(selectedFile.path), - selectedFile.length, - Files.lines(selectedFile.toNioPath()).count().toLong(), - selectedFile.programmingLanguage().toTelemetryType() + setOf(file.path), + file.length, + Files.lines(file.toNioPath()).count().toLong(), + file.programmingLanguage().toTelemetryType() ) /** @@ -137,7 +147,7 @@ class CodeScanSessionConfig( try { withTimeout(Duration.ofSeconds(TELEMETRY_TIMEOUT_IN_SECONDS)) { if (scope == CodeAnalysisScope.FILE) { - totalSize = selectedFile.length + totalSize = selectedFile?.length ?: 0L } else { VfsUtil.collectChildrenRecursively(projectRoot).filter { !it.isDirectory && !it.`is`((VFileProperty.SYMLINK)) && ( @@ -168,7 +178,7 @@ class CodeScanSessionConfig( val stack = Stack() var currentTotalFileSize = 0L var currentTotalLines = 0L - val languageCounts = mutableMapOf() + val languageCounts = mutableMapOf() stack.push(projectRoot) while (stack.isNotEmpty()) { @@ -179,8 +189,8 @@ class CodeScanSessionConfig( if (willExceedPayloadLimit(currentTotalFileSize, current.length)) { break } else { - val language = current.programmingLanguage().toTelemetryType() - if (language != CodewhispererLanguage.Plaintext && language != CodewhispererLanguage.Unknown) { + val language = current.programmingLanguage() + if (language != CodeWhispererPlainText.INSTANCE && language != CodeWhispererUnknownLanguage.INSTANCE) { languageCounts[language] = (languageCounts[language] ?: 0) + 1 } @@ -202,9 +212,11 @@ class CodeScanSessionConfig( val maxCountLanguage = languageCounts.filter { it.value == maxCount }.keys.firstOrNull() if (maxCountLanguage == null) { - return PayloadMetadata(files, currentTotalFileSize, currentTotalLines, CodewhispererLanguage.Unknown) + programmingLanguage = CodeWhispererUnknownLanguage.INSTANCE + noSupportedFilesError() } - return PayloadMetadata(files, currentTotalFileSize, currentTotalLines, maxCountLanguage) + programmingLanguage = maxCountLanguage + return PayloadMetadata(files, currentTotalFileSize, currentTotalLines, maxCountLanguage.toTelemetryType()) } fun isProjectTruncated() = isProjectTruncated @@ -221,7 +233,7 @@ class CodeScanSessionConfig( companion object { private val LOG = getLogger() private const val TELEMETRY_TIMEOUT_IN_SECONDS: Long = 10 - fun create(file: VirtualFile, project: Project, scope: CodeAnalysisScope): CodeScanSessionConfig = CodeScanSessionConfig(file, project, scope) + fun create(file: VirtualFile?, project: Project, scope: CodeAnalysisScope): CodeScanSessionConfig = CodeScanSessionConfig(file, project, scope) } } diff --git a/plugins/toolkit/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties b/plugins/toolkit/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties index 8ef57a387e..ccd6ae3ab7 100644 --- a/plugins/toolkit/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties +++ b/plugins/toolkit/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties @@ -736,6 +736,7 @@ codewhisperer.codescan.stop_scan_confirm_message=Are you sure you want to stop o codewhisperer.codescan.stopping_scan=Stopping Security Scan... codewhisperer.codescan.suggested_fix_description=Why are we recommending this? codewhisperer.codescan.suggested_fix_label=Suggested code fix preview +codewhisperer.codescan.unsupported_language_error=Project does not contain valid files to scan codewhisperer.codescan.upload_to_s3_failed=Amazon Q is unable to upload workspace artifacts to Amazon S3 for security scans. For more information, see the Amazon Q documentation or contact your network or organization administrator.
https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/security_iam_manage-access-with-policies.html codewhisperer.codescan.view_scanned_files=View {0} scanned files codewhisperer.credential.login.dialog.exception.cancel_login=Login cancelled