mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
fix: support AWS GovCloud and China region ARNs in Bedrock validation
- Updated ARN regex in validateBedrockArn to support aws-us-gov and aws-cn partitions - Updated ARN regex in bedrock.ts parseArn method to support GovCloud and China regions - Added comprehensive tests for GovCloud and China ARN validation - Fixes #9481
This commit is contained in:
parent
852e293610
commit
ad3ad95af2
5 changed files with 351 additions and 3 deletions
|
|
@ -360,6 +360,158 @@ describe("AwsBedrockHandler", () => {
|
|||
expect(result.modelId).toBe("ap.anthropic.claude-3-5-sonnet-20241022-v2:0") // Should be preserved as-is
|
||||
})
|
||||
})
|
||||
|
||||
describe("GovCloud and China partition ARN parsing", () => {
|
||||
it("should parse GovCloud ARNs correctly", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "us-gov-west-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn(
|
||||
"arn:aws-us-gov:bedrock:us-gov-west-1:123456789012:inference-profile/us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0",
|
||||
)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.region).toBe("us-gov-west-1")
|
||||
expect(result.modelType).toBe("inference-profile")
|
||||
expect(result.modelId).toBe("us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0")
|
||||
})
|
||||
|
||||
it("should parse GovCloud ARNs without account ID", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "us-gov-east-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn("arn:aws-us-gov:bedrock:us-gov-east-1::foundation-model/anthropic.claude-v2")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.region).toBe("us-gov-east-1")
|
||||
expect(result.modelType).toBe("foundation-model")
|
||||
expect(result.modelId).toBe("anthropic.claude-v2")
|
||||
})
|
||||
|
||||
it("should parse China region ARNs correctly", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "cn-north-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn(
|
||||
"arn:aws-cn:bedrock:cn-north-1:123456789012:foundation-model/anthropic.claude-3-sonnet-20240229-v1:0",
|
||||
)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.region).toBe("cn-north-1")
|
||||
expect(result.modelType).toBe("foundation-model")
|
||||
expect(result.modelId).toBe("anthropic.claude-3-sonnet-20240229-v1:0")
|
||||
})
|
||||
|
||||
it("should parse China region ARNs without account ID", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "cn-northwest-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn("arn:aws-cn:bedrock:cn-northwest-1::inference-profile/custom-model")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.region).toBe("cn-northwest-1")
|
||||
expect(result.modelType).toBe("inference-profile")
|
||||
expect(result.modelId).toBe("custom-model")
|
||||
})
|
||||
|
||||
it("should handle GovCloud Sagemaker ARNs", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "us-gov-west-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn("arn:aws-us-gov:sagemaker:us-gov-west-1:123456789012:endpoint/gov-endpoint")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.region).toBe("us-gov-west-1")
|
||||
expect(result.modelType).toBe("endpoint")
|
||||
expect(result.modelId).toBe("gov-endpoint")
|
||||
})
|
||||
|
||||
it("should handle China Sagemaker ARNs", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "cn-north-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn("arn:aws-cn:sagemaker:cn-north-1:123456789012:endpoint/china-endpoint")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.region).toBe("cn-north-1")
|
||||
expect(result.modelType).toBe("endpoint")
|
||||
expect(result.modelId).toBe("china-endpoint")
|
||||
})
|
||||
|
||||
it("should detect region mismatch for GovCloud ARNs", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "us-gov-west-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn(
|
||||
"arn:aws-us-gov:bedrock:us-gov-east-1::foundation-model/anthropic.claude-v2",
|
||||
"us-gov-west-1",
|
||||
)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.region).toBe("us-gov-east-1")
|
||||
expect(result.errorMessage).toContain("region")
|
||||
expect(result.errorMessage).toContain("us-gov-east-1")
|
||||
expect(result.errorMessage).toContain("us-gov-west-1")
|
||||
})
|
||||
|
||||
it("should reject invalid partition ARNs", () => {
|
||||
const handler = new AwsBedrockHandler({
|
||||
apiModelId: "test",
|
||||
awsAccessKey: "test",
|
||||
awsSecretKey: "test",
|
||||
awsRegion: "us-east-1",
|
||||
})
|
||||
|
||||
const parseArn = (handler as any).parseArn.bind(handler)
|
||||
|
||||
const result = parseArn("arn:aws-invalid:bedrock:us-east-1::foundation-model/anthropic.claude-v2")
|
||||
|
||||
expect(result.isValid).toBe(false)
|
||||
expect(result.errorMessage).toContain("Invalid ARN format")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("image handling", () => {
|
||||
|
|
|
|||
|
|
@ -808,6 +808,8 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
|
|||
*
|
||||
* This matches ARNs like:
|
||||
* - Foundation Model: arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-v2
|
||||
* - GovCloud Foundation Model: arn:aws-us-gov:bedrock:us-gov-west-1::foundation-model/anthropic.claude-v2
|
||||
* - China Foundation Model: arn:aws-cn:bedrock:cn-north-1::foundation-model/anthropic.claude-v2
|
||||
* - Prompt Router: arn:aws:bedrock:us-west-2:123456789012:prompt-router/anthropic-claude
|
||||
* - Inference Profile: arn:aws:bedrock:us-west-2:123456789012:inference-profile/anthropic.claude-v2
|
||||
* - Cross Region Inference Profile: arn:aws:bedrock:us-west-2:123456789012:inference-profile/us.anthropic.claude-3-5-sonnet-20241022-v2:0
|
||||
|
|
@ -815,13 +817,15 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
|
|||
* - Imported Model: arn:aws:bedrock:us-west-2:123456789012:imported-model/my-imported-model
|
||||
*
|
||||
* match[0] - The entire matched string
|
||||
* match[1] - The region (e.g., "us-east-1")
|
||||
* match[1] - The region (e.g., "us-east-1", "us-gov-west-1", "cn-north-1")
|
||||
* match[2] - The account ID (can be empty string for AWS-managed resources)
|
||||
* match[3] - The resource type (e.g., "foundation-model")
|
||||
* match[4] - The resource ID (e.g., "anthropic.claude-3-sonnet-20240229-v1:0")
|
||||
*/
|
||||
|
||||
const arnRegex = /^arn:aws:(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^\/]+)\/([\w\.\-:]+)|([^\/]+))$/
|
||||
// Support standard AWS (aws), GovCloud (aws-us-gov), and China (aws-cn) partitions
|
||||
const arnRegex =
|
||||
/^arn:(?:aws|aws-us-gov|aws-cn):(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^\/]+)\/([\w\.\-:]+)|([^\/]+))$/
|
||||
let match = arn.match(arnRegex)
|
||||
|
||||
if (match && match[1] && match[3] && match[4]) {
|
||||
|
|
|
|||
190
webview-ui/src/utils/__tests__/validateBedrockArn.spec.ts
Normal file
190
webview-ui/src/utils/__tests__/validateBedrockArn.spec.ts
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
import { vi } from "vitest"
|
||||
import { validateBedrockArn } from "../validate"
|
||||
|
||||
// Mock i18next to return predictable error messages
|
||||
vi.mock("i18next", () => ({
|
||||
default: {
|
||||
t: (key: string, params?: any) => {
|
||||
if (key === "settings:validation.arn.invalidFormat") {
|
||||
return "Invalid ARN format"
|
||||
}
|
||||
if (key === "settings:validation.arn.regionMismatch") {
|
||||
return `Region mismatch: ARN region ${params?.arnRegion} does not match ${params?.region}`
|
||||
}
|
||||
return key
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
describe("validateBedrockArn", () => {
|
||||
describe("Standard AWS partition ARNs", () => {
|
||||
it("should validate standard AWS Bedrock ARNs", () => {
|
||||
const arn = "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-east-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should validate ARNs with account IDs", () => {
|
||||
const arn = "arn:aws:bedrock:us-west-2:123456789012:inference-profile/custom-profile"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-west-2")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should validate Sagemaker ARNs", () => {
|
||||
const arn = "arn:aws:sagemaker:eu-west-1:123456789012:endpoint/my-endpoint"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("eu-west-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("AWS GovCloud partition ARNs", () => {
|
||||
it("should validate GovCloud Bedrock ARNs", () => {
|
||||
const arn =
|
||||
"arn:aws-us-gov:bedrock:us-gov-west-1:123456789012:inference-profile/us-gov.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-gov-west-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should validate GovCloud ARNs without account ID", () => {
|
||||
const arn = "arn:aws-us-gov:bedrock:us-gov-east-1::foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-gov-east-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should validate GovCloud Sagemaker ARNs", () => {
|
||||
const arn = "arn:aws-us-gov:sagemaker:us-gov-west-1:123456789012:endpoint/gov-endpoint"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-gov-west-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("AWS China partition ARNs", () => {
|
||||
it("should validate China Bedrock ARNs", () => {
|
||||
const arn = "arn:aws-cn:bedrock:cn-north-1:123456789012:foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("cn-north-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should validate China ARNs without account ID", () => {
|
||||
const arn = "arn:aws-cn:bedrock:cn-northwest-1::inference-profile/custom-model"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("cn-northwest-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
|
||||
it("should validate China Sagemaker ARNs", () => {
|
||||
const arn = "arn:aws-cn:sagemaker:cn-north-1:123456789012:endpoint/china-endpoint"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("cn-north-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Region validation", () => {
|
||||
it("should detect region mismatch for standard AWS", () => {
|
||||
const arn = "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn, "us-west-2")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-east-1")
|
||||
// Error message should be defined when there's a region mismatch
|
||||
expect(result.errorMessage).toBeDefined()
|
||||
})
|
||||
|
||||
it("should detect region mismatch for GovCloud", () => {
|
||||
const arn = "arn:aws-us-gov:bedrock:us-gov-west-1::foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn, "us-gov-east-1")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-gov-west-1")
|
||||
// Error message should be defined when there's a region mismatch
|
||||
expect(result.errorMessage).toBeDefined()
|
||||
})
|
||||
|
||||
it("should detect region mismatch for China", () => {
|
||||
const arn = "arn:aws-cn:bedrock:cn-north-1::foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn, "cn-northwest-1")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("cn-north-1")
|
||||
// Error message should be defined when there's a region mismatch
|
||||
expect(result.errorMessage).toBeDefined()
|
||||
})
|
||||
|
||||
it("should pass when regions match", () => {
|
||||
const arn = "arn:aws-us-gov:bedrock:us-gov-west-1::foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn, "us-gov-west-1")
|
||||
|
||||
expect(result.isValid).toBe(true)
|
||||
expect(result.arnRegion).toBe("us-gov-west-1")
|
||||
expect(result.errorMessage).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Invalid ARN formats", () => {
|
||||
it("should reject invalid ARN format", () => {
|
||||
const arn = "not-an-arn"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(false)
|
||||
expect(result.arnRegion).toBeUndefined()
|
||||
// Error message should be defined for invalid ARN
|
||||
expect(result.errorMessage).toBeDefined()
|
||||
})
|
||||
|
||||
it("should reject ARN with invalid partition", () => {
|
||||
const arn = "arn:aws-invalid:bedrock:us-east-1::foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(false)
|
||||
expect(result.arnRegion).toBeUndefined()
|
||||
// Error message should be defined for invalid ARN
|
||||
expect(result.errorMessage).toBeDefined()
|
||||
})
|
||||
|
||||
it("should reject ARN with invalid service", () => {
|
||||
const arn = "arn:aws:invalid-service:us-east-1::foundation-model/anthropic.claude-v2"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(false)
|
||||
expect(result.arnRegion).toBeUndefined()
|
||||
// Error message should be defined for invalid ARN
|
||||
expect(result.errorMessage).toBeDefined()
|
||||
})
|
||||
|
||||
it("should reject ARN missing resource", () => {
|
||||
const arn = "arn:aws:bedrock:us-east-1:123456789012:"
|
||||
const result = validateBedrockArn(arn)
|
||||
|
||||
expect(result.isValid).toBe(false)
|
||||
expect(result.arnRegion).toBeUndefined()
|
||||
// Error message should be defined for invalid ARN
|
||||
expect(result.errorMessage).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -225,7 +225,9 @@ function getModelIdForProvider(apiConfiguration: ProviderSettings, provider: Pro
|
|||
*/
|
||||
export function validateBedrockArn(arn: string, region?: string) {
|
||||
// Validate ARN format.
|
||||
const arnRegex = /^arn:aws:(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^/]+)\/([\w.\-:]+)|([^/]+))$/
|
||||
// Support standard AWS (aws), GovCloud (aws-us-gov), and China (aws-cn) partitions
|
||||
const arnRegex =
|
||||
/^arn:(?:aws|aws-us-gov|aws-cn):(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^/]+)\/([\w.\-:]+)|([^/]+))$/
|
||||
const match = arn.match(arnRegex)
|
||||
|
||||
if (!match) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue