diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 5a463d56add..f9159500a58 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -213,6 +213,24 @@ "items": { "type": "string" } + }, + "amazonQ.proxy.certificateAuthority": { + "type": "string", + "markdownDescription": "%AWS.configuration.description.amazonq.proxy.certificateAuthority%", + "default": null, + "scope": "application" + }, + "amazonQ.proxy.username": { + "type": "string", + "markdownDescription": "%AWS.configuration.description.amazonq.proxy.username%", + "default": null, + "scope": "application" + }, + "amazonQ.proxy.password": { + "type": "string", + "markdownDescription": "%AWS.configuration.description.amazonq.proxy.password%", + "default": null, + "scope": "application" } } }, diff --git a/packages/amazonq/src/extension.ts b/packages/amazonq/src/extension.ts index e5e0700614d..1a9d3c5facc 100644 --- a/packages/amazonq/src/extension.ts +++ b/packages/amazonq/src/extension.ts @@ -34,6 +34,7 @@ import { Experiments, isSageMaker, isAmazonLinux2, + ProxyUtil, } from 'aws-core-vscode/shared' import { ExtStartUpSources } from 'aws-core-vscode/telemetry' import { VSCODE_EXTENSION_ID } from 'aws-core-vscode/utils' @@ -119,6 +120,10 @@ export async function activateAmazonQCommon(context: vscode.ExtensionContext, is const extContext = { extensionContext: context, } + + // Configure proxy settings early + ProxyUtil.configureProxyForLanguageServer() + // This contains every lsp agnostic things (auth, security scan, code scan) await activateCodeWhisperer(extContext as ExtContext) if ( diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index 9922ec6fcd8..1f92a0fa5b7 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -98,7 +98,9 @@ "AWS.configuration.description.amazonq.workspaceIndexIgnoreFilePatterns": "File patterns to ignore when indexing your workspace files", "AWS.configuration.description.amazonq.workspaceIndexCacheDirPath": "The path to the directory that contains the cache of the index of your workspace files", "AWS.configuration.description.amazonq.ignoredSecurityIssues": "Specifies a list of code issue identifiers that Amazon Q should ignore when reviewing your workspace. Each item in the array should be a unique string identifier for a specific code issue. This allows you to suppress notifications for known issues that you've assessed and determined to be false positives or not applicable to your project. Use this setting with caution, as it may cause you to miss important security alerts.", - "AWS.command.apig.copyUrl": "Copy URL", + "AWS.configuration.description.amazonq.proxy.certificateAuthority": "Path to a Certificate Authority (PEM file) for SSL/TLS verification when using a proxy.", + "AWS.configuration.description.amazonq.proxy.username": "Username for proxy authentication.", + "AWS.configuration.description.amazonq.proxy.password": "Password for proxy authentication.", "AWS.command.apig.invokeRemoteRestApi": "Invoke in the cloud", "AWS.command.apig.invokeRemoteRestApi.cn": "Invoke on Amazon", "AWS.appBuilder.explorerTitle": "Application Builder", diff --git a/packages/core/src/shared/index.ts b/packages/core/src/shared/index.ts index f4c78e2093c..799ffb1b35c 100644 --- a/packages/core/src/shared/index.ts +++ b/packages/core/src/shared/index.ts @@ -39,6 +39,7 @@ export { CodewhispererUserDecision, CodewhispererSecurityScan, } from './telemetry/telemetry.gen' +export { ProxyUtil } from './utilities/proxyUtil' export { randomUUID } from './crypto' export * from './environmentVariables' export * from './vscode/setContext' diff --git a/packages/core/src/shared/logger/logger.ts b/packages/core/src/shared/logger/logger.ts index b398ff93162..eb2602c30b9 100644 --- a/packages/core/src/shared/logger/logger.ts +++ b/packages/core/src/shared/logger/logger.ts @@ -21,6 +21,7 @@ export type LogTopic = | 'nextEditPrediction' | 'resourceCache' | 'telemetry' + | 'proxyUtil' class ErrorLog { constructor( diff --git a/packages/core/src/shared/settings-amazonq.gen.ts b/packages/core/src/shared/settings-amazonq.gen.ts index 637c5b1b12e..6b18a5959f6 100644 --- a/packages/core/src/shared/settings-amazonq.gen.ts +++ b/packages/core/src/shared/settings-amazonq.gen.ts @@ -36,7 +36,10 @@ export const amazonqSettings = { "amazonQ.workspaceIndexMaxFileSize": {}, "amazonQ.workspaceIndexCacheDirPath": {}, "amazonQ.workspaceIndexIgnoreFilePatterns": {}, - "amazonQ.ignoredSecurityIssues": {} + "amazonQ.ignoredSecurityIssues": {}, + "amazonQ.proxy.certificateAuthority": {}, + "amazonQ.proxy.username": {}, + "amazonQ.proxy.password": {} } export default amazonqSettings diff --git a/packages/core/src/shared/utilities/proxyUtil.ts b/packages/core/src/shared/utilities/proxyUtil.ts new file mode 100644 index 00000000000..4646b768d45 --- /dev/null +++ b/packages/core/src/shared/utilities/proxyUtil.ts @@ -0,0 +1,99 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import vscode from 'vscode' +import { getLogger } from '../logger/logger' + +interface ProxyConfig { + proxyUrl: string | undefined + certificateAuthority: string | undefined + username: string | undefined + password: string | undefined +} + +/** + * Utility class for handling proxy configuration + */ +export class ProxyUtil { + private static readonly logger = getLogger('proxyUtil') + + /** + * Sets proxy environment variables based on VS Code settings for use with the Flare Language Server + * + * See documentation here for setting the environement variables which are inherited by Flare LS process: + * https://github.com/aws/language-server-runtimes/blob/main/runtimes/docs/proxy.md + */ + public static configureProxyForLanguageServer(): void { + try { + const proxyConfig = this.getProxyConfiguration() + + this.setProxyEnvironmentVariables(proxyConfig) + } catch (err) { + this.logger.error(`Failed to configure proxy: ${err}`) + } + } + + /** + * Gets proxy configuration from VS Code settings + */ + private static getProxyConfiguration(): ProxyConfig { + const httpConfig = vscode.workspace.getConfiguration('http') + const proxyUrl = httpConfig.get('proxy') + this.logger.debug(`Proxy URL: ${proxyUrl}`) + + const amazonQConfig = vscode.workspace.getConfiguration('amazonQ') + const proxySettings = amazonQConfig.get<{ + certificateAuthority?: string + username?: string + password?: string + }>('proxy', {}) + + return { + proxyUrl, + certificateAuthority: proxySettings.certificateAuthority, + username: proxySettings.username, + password: proxySettings.password, + } + } + + /** + * Sets environment variables based on proxy configuration + */ + private static setProxyEnvironmentVariables(config: ProxyConfig): void { + let proxyUrl = config.proxyUrl + + // Add authentication to proxy URL if provided + if (proxyUrl && config.username && config.password) { + try { + const parsedUrl = new URL(proxyUrl) + const protocol = parsedUrl.protocol + const host = parsedUrl.hostname + const port = parsedUrl.port || (protocol === 'https:' ? '443' : '80') + + proxyUrl = `${protocol}//${config.username}:${config.password}@${host}:${port}` + this.logger.debug('Added authentication to proxy URL') + } catch (err) { + this.logger.error(`Failed to parse proxy URL: ${err}`) + } + } + + // Always enable experimental proxy support for better handling of both explicit and transparent proxies + process.env.EXPERIMENTAL_HTTP_PROXY_SUPPORT = 'true' + + // Set proxy environment variables + if (proxyUrl) { + process.env.HTTPS_PROXY = proxyUrl + process.env.HTTP_PROXY = proxyUrl + this.logger.debug(`Set proxy environment variables: ${proxyUrl}`) + } + + // Set certificate bundle environment variables if configured + if (config.certificateAuthority) { + process.env.NODE_EXTRA_CA_CERTS = config.certificateAuthority + process.env.AWS_CA_BUNDLE = config.certificateAuthority + this.logger.debug(`Set certificate bundle path: ${config.certificateAuthority}`) + } + } +}