From 845a19a91783d960b735013de13b9d4e2c7b0510 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Sat, 21 Mar 2026 22:19:50 +0800 Subject: [PATCH 1/4] fix: allow Bedrock provider to use AWS SDK default credential chain Remove hard requirement for explicit AWS credentials in Bedrock provider. When access key and secret key are not provided, the AWS SDK automatically falls back to its default credential chain (env vars, instance profile, ECS task role, EKS IRSA, SSO). Closes #3694 Signed-off-by: majiayu000 <1835304752@qq.com> --- apps/sim/blocks/blocks/agent.ts | 8 ++++---- apps/sim/providers/bedrock/index.ts | 27 ++++++++++++--------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/apps/sim/blocks/blocks/agent.ts b/apps/sim/blocks/blocks/agent.ts index 651d3d9295b..f76f8eb7838 100644 --- a/apps/sim/blocks/blocks/agent.ts +++ b/apps/sim/blocks/blocks/agent.ts @@ -367,9 +367,9 @@ Return ONLY the JSON array.`, title: 'AWS Access Key ID', type: 'short-input', password: true, - placeholder: 'Enter your AWS Access Key ID', + placeholder: 'Optional - uses AWS default credential chain if empty', connectionDroppable: false, - required: true, + required: false, condition: { field: 'model', value: providers.bedrock.models, @@ -380,9 +380,9 @@ Return ONLY the JSON array.`, title: 'AWS Secret Access Key', type: 'short-input', password: true, - placeholder: 'Enter your AWS Secret Access Key', + placeholder: 'Optional - uses AWS default credential chain if empty', connectionDroppable: false, - required: true, + required: false, condition: { field: 'model', value: providers.bedrock.models, diff --git a/apps/sim/providers/bedrock/index.ts b/apps/sim/providers/bedrock/index.ts index ec0af6ab04b..6464c7662c5 100644 --- a/apps/sim/providers/bedrock/index.ts +++ b/apps/sim/providers/bedrock/index.ts @@ -50,14 +50,6 @@ export const bedrockProvider: ProviderConfig = { executeRequest: async ( request: ProviderRequest ): Promise => { - if (!request.bedrockAccessKeyId) { - throw new Error('AWS Access Key ID is required for Bedrock') - } - - if (!request.bedrockSecretKey) { - throw new Error('AWS Secret Access Key is required for Bedrock') - } - const region = request.bedrockRegion || 'us-east-1' const bedrockModelId = getBedrockInferenceProfileId(request.model, region) @@ -67,13 +59,18 @@ export const bedrockProvider: ProviderConfig = { region, }) - const client = new BedrockRuntimeClient({ - region, - credentials: { - accessKeyId: request.bedrockAccessKeyId || '', - secretAccessKey: request.bedrockSecretKey || '', - }, - }) + const clientConfig: { + region: string + credentials?: { accessKeyId: string; secretAccessKey: string } + } = { region } + if (request.bedrockAccessKeyId && request.bedrockSecretKey) { + clientConfig.credentials = { + accessKeyId: request.bedrockAccessKeyId, + secretAccessKey: request.bedrockSecretKey, + } + } + + const client = new BedrockRuntimeClient(clientConfig) const messages: BedrockMessage[] = [] const systemContent: SystemContentBlock[] = [] From bd1aab68ad750cc5dbe4fae55ad654beb85a1e23 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Sat, 21 Mar 2026 22:24:30 +0800 Subject: [PATCH 2/4] fix: add partial credential guard for Bedrock provider Reject configurations where only one of bedrockAccessKeyId or bedrockSecretKey is provided, preventing silent fallback to the default credential chain with a potentially different identity. Add tests covering all credential configuration scenarios. Signed-off-by: majiayu000 <1835304752@qq.com> --- apps/sim/providers/bedrock/index.test.ts | 110 +++++++++++++++++++++++ apps/sim/providers/bedrock/index.ts | 9 ++ 2 files changed, 119 insertions(+) create mode 100644 apps/sim/providers/bedrock/index.test.ts diff --git a/apps/sim/providers/bedrock/index.test.ts b/apps/sim/providers/bedrock/index.test.ts new file mode 100644 index 00000000000..f49b01e29a0 --- /dev/null +++ b/apps/sim/providers/bedrock/index.test.ts @@ -0,0 +1,110 @@ +/** + * @vitest-environment node + */ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +const mockSend = vi.fn() + +vi.mock('@aws-sdk/client-bedrock-runtime', () => ({ + BedrockRuntimeClient: vi.fn().mockImplementation((config: any) => { + mockSend._lastConfig = config + return { send: mockSend } + }), + ConverseCommand: vi.fn(), + ConverseStreamCommand: vi.fn(), +})) + +vi.mock('@/providers/bedrock/utils', () => ({ + getBedrockInferenceProfileId: vi.fn().mockReturnValue('us.anthropic.claude-3-5-sonnet-20241022-v2:0'), + checkForForcedToolUsage: vi.fn(), + createReadableStreamFromBedrockStream: vi.fn(), + generateToolUseId: vi.fn().mockReturnValue('tool-1'), +})) + +vi.mock('@/providers/models', () => ({ + getProviderModels: vi.fn().mockReturnValue([]), + getProviderDefaultModel: vi.fn().mockReturnValue('us.anthropic.claude-3-5-sonnet-20241022-v2:0'), +})) + +vi.mock('@/providers/utils', () => ({ + calculateCost: vi.fn().mockReturnValue({ input: 0, output: 0, total: 0, pricing: null }), + prepareToolExecution: vi.fn(), + prepareToolsWithUsageControl: vi.fn(), + sumToolCosts: vi.fn().mockReturnValue(0), +})) + +vi.mock('@/tools', () => ({ + executeTool: vi.fn(), +})) + +import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime' +import { bedrockProvider } from '@/providers/bedrock/index' + +describe('bedrockProvider credential handling', () => { + beforeEach(() => { + vi.clearAllMocks() + mockSend.mockResolvedValue({ + output: { message: { content: [{ text: 'response' }] } }, + usage: { inputTokens: 10, outputTokens: 5 }, + }) + }) + + const baseRequest = { + model: 'us.anthropic.claude-3-5-sonnet-20241022-v2:0', + systemPrompt: 'You are helpful.', + messages: [{ role: 'user' as const, content: 'Hello' }], + } + + it('throws when only bedrockAccessKeyId is provided', async () => { + await expect( + bedrockProvider.executeRequest({ + ...baseRequest, + bedrockAccessKeyId: 'AKIAIOSFODNN7EXAMPLE', + }) + ).rejects.toThrow('Both bedrockAccessKeyId and bedrockSecretKey must be provided together') + }) + + it('throws when only bedrockSecretKey is provided', async () => { + await expect( + bedrockProvider.executeRequest({ + ...baseRequest, + bedrockSecretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + }) + ).rejects.toThrow('Both bedrockAccessKeyId and bedrockSecretKey must be provided together') + }) + + it('creates client with explicit credentials when both are provided', async () => { + await bedrockProvider.executeRequest({ + ...baseRequest, + bedrockAccessKeyId: 'AKIAIOSFODNN7EXAMPLE', + bedrockSecretKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + }) + + expect(BedrockRuntimeClient).toHaveBeenCalledWith({ + region: 'us-east-1', + credentials: { + accessKeyId: 'AKIAIOSFODNN7EXAMPLE', + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + }, + }) + }) + + it('creates client without credentials when neither is provided', async () => { + await bedrockProvider.executeRequest(baseRequest) + + expect(BedrockRuntimeClient).toHaveBeenCalledWith({ + region: 'us-east-1', + }) + }) + + it('uses custom region when provided', async () => { + await bedrockProvider.executeRequest({ + ...baseRequest, + bedrockRegion: 'eu-west-1', + }) + + expect(BedrockRuntimeClient).toHaveBeenCalledWith({ + region: 'eu-west-1', + }) + }) +}) diff --git a/apps/sim/providers/bedrock/index.ts b/apps/sim/providers/bedrock/index.ts index 6464c7662c5..7bc15a360a0 100644 --- a/apps/sim/providers/bedrock/index.ts +++ b/apps/sim/providers/bedrock/index.ts @@ -59,6 +59,15 @@ export const bedrockProvider: ProviderConfig = { region, }) + const hasAccessKey = Boolean(request.bedrockAccessKeyId) + const hasSecretKey = Boolean(request.bedrockSecretKey) + if (hasAccessKey !== hasSecretKey) { + throw new Error( + 'Both bedrockAccessKeyId and bedrockSecretKey must be provided together. ' + + 'Provide both for explicit credentials, or omit both to use the AWS default credential chain.' + ) + } + const clientConfig: { region: string credentials?: { accessKeyId: string; secretAccessKey: string } From 0418a458aacdfa3b7d3d0a42e4bedd6f036ef329 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Sun, 22 Mar 2026 11:54:12 +0800 Subject: [PATCH 3/4] fix: clean up bedrock test lint and dead code Remove unused config parameter and dead _lastConfig assignment from mock factory. Break long mockReturnValue chain to satisfy biome line-length rule. Signed-off-by: majiayu000 <1835304752@qq.com> --- apps/sim/providers/bedrock/index.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/sim/providers/bedrock/index.test.ts b/apps/sim/providers/bedrock/index.test.ts index f49b01e29a0..045837a5829 100644 --- a/apps/sim/providers/bedrock/index.test.ts +++ b/apps/sim/providers/bedrock/index.test.ts @@ -6,8 +6,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' const mockSend = vi.fn() vi.mock('@aws-sdk/client-bedrock-runtime', () => ({ - BedrockRuntimeClient: vi.fn().mockImplementation((config: any) => { - mockSend._lastConfig = config + BedrockRuntimeClient: vi.fn().mockImplementation(() => { return { send: mockSend } }), ConverseCommand: vi.fn(), @@ -15,7 +14,9 @@ vi.mock('@aws-sdk/client-bedrock-runtime', () => ({ })) vi.mock('@/providers/bedrock/utils', () => ({ - getBedrockInferenceProfileId: vi.fn().mockReturnValue('us.anthropic.claude-3-5-sonnet-20241022-v2:0'), + getBedrockInferenceProfileId: vi + .fn() + .mockReturnValue('us.anthropic.claude-3-5-sonnet-20241022-v2:0'), checkForForcedToolUsage: vi.fn(), createReadableStreamFromBedrockStream: vi.fn(), generateToolUseId: vi.fn().mockReturnValue('tool-1'), From e67b9dff179c4d509e4cf294c7cf7d360021d9ad Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Sun, 22 Mar 2026 12:51:49 +0800 Subject: [PATCH 4/4] fix: address greptile review feedback on PR #3708 Use BedrockRuntimeClientConfig from SDK instead of inline type. Add default return value for prepareToolsWithUsageControl mock. Signed-off-by: majiayu000 <1835304752@qq.com> --- apps/sim/providers/bedrock/index.test.ts | 6 +++++- apps/sim/providers/bedrock/index.ts | 6 ++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/sim/providers/bedrock/index.test.ts b/apps/sim/providers/bedrock/index.test.ts index 045837a5829..8c938e01c87 100644 --- a/apps/sim/providers/bedrock/index.test.ts +++ b/apps/sim/providers/bedrock/index.test.ts @@ -30,7 +30,11 @@ vi.mock('@/providers/models', () => ({ vi.mock('@/providers/utils', () => ({ calculateCost: vi.fn().mockReturnValue({ input: 0, output: 0, total: 0, pricing: null }), prepareToolExecution: vi.fn(), - prepareToolsWithUsageControl: vi.fn(), + prepareToolsWithUsageControl: vi.fn().mockReturnValue({ + tools: [], + toolChoice: 'auto', + forcedTools: [], + }), sumToolCosts: vi.fn().mockReturnValue(0), })) diff --git a/apps/sim/providers/bedrock/index.ts b/apps/sim/providers/bedrock/index.ts index 7bc15a360a0..d3223cfebd0 100644 --- a/apps/sim/providers/bedrock/index.ts +++ b/apps/sim/providers/bedrock/index.ts @@ -1,6 +1,7 @@ import { type Message as BedrockMessage, BedrockRuntimeClient, + type BedrockRuntimeClientConfig, type ContentBlock, type ConversationRole, ConverseCommand, @@ -68,10 +69,7 @@ export const bedrockProvider: ProviderConfig = { ) } - const clientConfig: { - region: string - credentials?: { accessKeyId: string; secretAccessKey: string } - } = { region } + const clientConfig: BedrockRuntimeClientConfig = { region } if (request.bedrockAccessKeyId && request.bedrockSecretKey) { clientConfig.credentials = { accessKeyId: request.bedrockAccessKeyId,